diff -r -u -N linux.orig/.bash_history linux.arm/.bash_history
--- linux.orig/.bash_history	Thu Jan  1 01:00:00 1970
+++ linux.arm/.bash_history	Fri Oct 27 23:16:06 1995
@@ -0,0 +1,271 @@
+dir
+cd mm
+ue swap.c
+cp ~root/.emacsrc ~
+ue swap.c
+ue swap.c
+ue memory.c
+less ../include/asm/pgtable.h 
+ue /etc/shutdown.allow 
+dir
+cd mm
+ue swap.c
+ue memory.c
+ue swap.c
+objdump --disassemble swap.o
+objdump --disassemble swap.o | less
+dir
+ue swap.c
+make
+cd ../
+make
+cd mm
+ue swap.c
+ue Makefile
+ue swap.c
+cd ..
+make
+shutdown -t3 -rf now ; logout
+nohup shutdown -r3 -rf now &; logout
+nohup shutdown -r3 -rf now & ; logout
+shutdown -r3 -rf now ; logout
+cd mm
+objdump --disassemble swap.o | less
+ue swap.c
+mcd ..
+cd ..
+make
+ue mm/swap.c
+make
+/sbin/bootmap /dev/hda1
+sync
+cd /etc
+mv rc rc.old
+cd /sbin
+mv init init.old
+init.old R
+sync
+ue /etc/rc.d/rc.M
+HOME=/root
+cd arch/arm/drivers/scsi
+dir
+diff NCR5380.c ~lin/drivers/scsi/NCR5380.c
+diff --help
+diff NCR5380.c ~lin/drivers/scsi/NCR5380.c -bw
+rm NCR5380.c
+ln -s ../../../../drivers/scsi/NCR5380.c .
+ue ecoscsi.c
+rm NCR5380.h
+ln -s ../../../../drivers/scsi/NCR5380.h .
+rm ecoscsi.o
+ue ecoscsi.c
+make
+make ecoscsi.o
+dir
+ue oak.c
+ue cumana_1.c
+ue oak.c
+ue cumana_1.c
+ue ecoscsi.c
+ue ecoscsi.c
+ue oak.c
+ue cumana_1.c
+ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+ifconfig eth0 promisc
+/sbin/ifconfig
+/sbin/ifconfig eth0 promisc
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+man tcpdump
+/sbin/ifconfig
+/sbin/ifconfig
+/sbin/ifconfig
+man tcpdump
+/sbin/ifconfig
+/sbin/ifconfig
+fdir
+dir
+make -n
+ue cumana_1.c
+less ../podules/scsi.c 
+less ../podules/podlist.c 
+%1
+%2
+%3
+%2
+less ../podules/podlist.c 
+jobs
+%3
+jobs
+ue cumana_1.c
+less ../podules/scsi.c 
+%2
+grep scsi_mem_init *.c
+grep scsi_mem_init ~lin/drivers/scsi/*.c
+diff hosts.c ~lin/drivers/scsi/hosts.c -bw | less
+pwd
+mv hosts.c hosts.c.old
+cp ~lin/drivers/scsi/hosts.c .
+ue hosts.c
+cd ../../../..
+cd arch/arm/drivers/scsi/
+ue hosts.c
+nm hosts.o | grep *UND* | less
+nm hosts.o
+ue hosts.c
+nm hosts.o
+dir
+less hosts.c
+dir
+cd ../..
+ue Makefile 
+cd ../../init
+less init.c
+less main.c
+rm main.o
+cd ..
+cd /
+dir
+dir boot
+dir
+dir etc
+dir
+dir home
+dir home/arm
+dir home/arm/a
+dir home/arm/1
+dir home/arm/1/users/
+dir /root
+rm -f /root/project
+rm -rf /root/project
+df
+dir /root
+rm /root/checklinux 
+df
+dir
+dir sbin
+rm /sbin/connect.cua2
+dir /bin
+cd /bin
+rm sh
+ln -s bash sh
+dir
+chgrp bin bash bashbug csh 
+dir
+cd ..
+df
+cd ../ld.so/
+dir
+less fixups.c
+less ld.so.c
+%1
+less fixups.h
+%1
+jobs
+%1
+%2
+cd arch/arm/drivers/scsi
+HOME=~root
+ue acornscsi.c
+less oak.h
+less acornscsi.h
+%1
+%1
+ue acornscsi.c
+less oak.c
+%1
+ue acornscsi.c
+ps -aux
+%1
+less acornscsi.h
+less scsi.h
+less hosts.c
+less hosts.h
+%1
+less acornscsi.h
+%1
+%2
+less hosts.h
+ue acornscsi.h
+%1
+jobs
+%2
+ue acornscsi.c
+ps -aux
+kill -9 459
+ kill -TERM 459
+ps -aux
+kill -KILL 459
+ps -aux
+kill -SEGV 459
+kill -ILL 459
+kill -PWR 459
+kill -l
+kill -BUS 459
+kill -IO 459
+kill -FPE 459
+kill -IOT 459
+ps -aux
+ps -aux
+cat /proc/devices 
+ue acornscsi.c
+mount -w -o remount ~lin
+ue acornscsi.c
+source ~root/.bashrc
+cd arch/arm/drivers/block
+ue blk.h
+cp ~root/.bashrc ./bashrc
+mount -w -o remount .
+cp ~root/.bashrc ./bashrc
+source ~/.bashrc
+pwd
+cp ~root/.bashrc ./.bashrc
+rm bashrc 
+source ~/.bashrc
+cd arch/arm/drivers/block/
+ue mfmhd.c
+../cdplay
+cd arch/arm/drivers/block/
+dir
+ue mfmhd.c
diff -r -u -N linux.orig/.bashrc linux.arm/.bashrc
--- linux.orig/.bashrc	Thu Jan  1 01:00:00 1970
+++ linux.arm/.bashrc	Fri Oct 27 23:16:06 1995
@@ -0,0 +1,4 @@
+PATH=/sbin:/usr/sbin:$PATH
+PS1="[33m\u@\h[31m:[32m[\W][31m:[36m<\!>[37m "
+alias rm='rm -i'
+
diff -r -u -N linux.orig/.config linux.arm/.config
--- linux.orig/.config	Fri Oct 27 23:09:26 1995
+++ linux.arm/.config	Fri Oct 27 23:16:04 1995
@@ -1,27 +1,26 @@
 #
 # Automatically generated make config: don't edit
 #
+CONFIG_ARM=1
+CONFIG_ARMMOUSE=1
 
 #
 # General setup
 #
-# CONFIG_MATH_EMULATION is not set
+CONFIG_PAGE_32K=y
+CONFIG_MATH_EMULATION=y
 CONFIG_BLK_DEV_FD=y
 CONFIG_ST506=y
 
 #
 # Please see drivers/block/README.ide for help/info on IDE drives
 #
-# CONFIG_BLK_DEV_HD is not set
-CONFIG_BLK_DEV_IDE=y
-# CONFIG_BLK_DEV_IDECD is not set
+CONFIG_BLK_DEV_HD=y
+# CONFIG_BLK_DEV_IDE is not set
 # CONFIG_BLK_DEV_XD is not set
 CONFIG_NET=y
 # CONFIG_MAX_16M is not set
-# CONFIG_PCI is not set
 CONFIG_SYSVIPC=y
-CONFIG_BINFMT_ELF=y
-CONFIG_M486=y
 
 #
 # Loadable module support
@@ -32,8 +31,8 @@
 # Networking options
 #
 CONFIG_INET=y
-# CONFIG_IP_FORWARD is not set
-# CONFIG_IP_MULTICAST is not set
+CONFIG_IP_FORWARD=y
+CONFIG_IP_MULTICAST=y
 # CONFIG_IP_FIREWALL is not set
 # CONFIG_IP_ACCT is not set
 
@@ -49,44 +48,37 @@
 #
 # SCSI support
 #
-# CONFIG_SCSI is not set
+CONFIG_SCSI=y
 
 #
-# Skipping SCSI configuration options...
+# SCSI support type (disk, tape, CDrom)
 #
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+CONFIG_BLK_DEV_SR=y
+# CONFIG_CHR_DEV_SG is not set
+
+#
+# SCSI low-level drivers
+#
+CONFIG_SCSI_ACORNSCSI_3=y
+# CONFIG_SCSI_CUMANA_1 is not set
+# CONFIG_SCSI_ECOSCSI is not set
+# CONFIG_SCSI_OAK is not set
+# CONFIG_SCSI_DEBUG is not set
 
 #
 # Network device support
 #
 CONFIG_NETDEVICES=y
 CONFIG_DUMMY=y
-# CONFIG_SLIP is not set
+CONFIG_SLIP=y
+CONFIG_SLIP_COMPRESSED=y
+# SL_SLIP_LOTS is not set
+# SL_DUMP is not set
 # CONFIG_PPP is not set
 # CONFIG_PLIP is not set
-# CONFIG_NET_ALPHA is not set
-# CONFIG_NET_VENDOR_SMC is not set
-# CONFIG_LANCE is not set
-# CONFIG_NET_VENDOR_3COM is not set
-CONFIG_NET_ISA=y
-# CONFIG_E2100 is not set
-# CONFIG_DEPCA is not set
-# CONFIG_EWRK3 is not set
-# CONFIG_HPLAN_PLUS is not set
-# CONFIG_HPLAN is not set
-CONFIG_NE2000=y
-# CONFIG_SK_G16 is not set
-# CONFIG_NET_EISA is not set
-# CONFIG_NET_POCKET is not set
-
-#
-# CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
-#
-# CONFIG_CDU31A is not set
-# CONFIG_MCD is not set
-CONFIG_SBPCD=y
-# CONFIG_SBPCD2 is not set
-# CONFIG_AZTCD is not set
-# CONFIG_CDU535 is not set
+CONFIG_ETHER3=y
 
 #
 # Filesystems
@@ -100,28 +92,19 @@
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
 CONFIG_ISO9660_FS=y
-# CONFIG_HPFS_FS is not set
-# CONFIG_SYSV_FS is not set
 
 #
 # character devices
 #
-# CONFIG_CYCLADES is not set
 CONFIG_PRINTER=y
-CONFIG_BUSMOUSE=y
-# CONFIG_PSMOUSE is not set
-# CONFIG_MS_BUSMOUSE is not set
-# CONFIG_ATIXL_BUSMOUSE is not set
-# CONFIG_QIC02_TAPE is not set
-CONFIG_FTAPE=y
-NR_FTAPE_BUFFERS=3
 
 #
 # Sound
 #
-# CONFIG_SOUND is not set
+CONFIG_SOUND=y
 
 #
 # Kernel hacking
 #
 # CONFIG_PROFILE is not set
+CONFIG_SCSI_CONSTANTS=y
diff -r -u -N linux.orig/.config.old linux.arm/.config.old
--- linux.orig/.config.old	Fri Oct 27 23:09:26 1995
+++ linux.arm/.config.old	Fri Oct 27 23:16:05 1995
@@ -1,27 +1,27 @@
 #
 # Automatically generated make config: don't edit
 #
+CONFIG_ARM=1
+CONFIG_ARMMOUSE=1
 
 #
 # General setup
 #
-# CONFIG_MATH_EMULATION is not set
+CONFIG_PAGE_32K=y
+CONFIG_MATH_EMULATION=y
 CONFIG_BLK_DEV_FD=y
 CONFIG_ST506=y
 
 #
 # Please see drivers/block/README.ide for help/info on IDE drives
 #
-# CONFIG_BLK_DEV_HD is not set
-CONFIG_BLK_DEV_IDE=y
-# CONFIG_BLK_DEV_IDECD is not set
+CONFIG_BLK_DEV_HD=y
+# CONFIG_BLK_DEV_IDE is not set
 # CONFIG_BLK_DEV_XD is not set
 CONFIG_NET=y
 # CONFIG_MAX_16M is not set
-# CONFIG_PCI is not set
 CONFIG_SYSVIPC=y
-CONFIG_BINFMT_ELF=y
-CONFIG_M486=y
+# CONFIG_BINFMT_ELF is not set
 
 #
 # Loadable module support
@@ -32,8 +32,8 @@
 # Networking options
 #
 CONFIG_INET=y
-# CONFIG_IP_FORWARD is not set
-# CONFIG_IP_MULTICAST is not set
+CONFIG_IP_FORWARD=y
+CONFIG_IP_MULTICAST=y
 # CONFIG_IP_FIREWALL is not set
 # CONFIG_IP_ACCT is not set
 
@@ -49,43 +49,36 @@
 #
 # SCSI support
 #
-# CONFIG_SCSI is not set
+CONFIG_SCSI=y
 
 #
-# Skipping SCSI configuration options...
+# SCSI support type (disk, tape, CDrom)
 #
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+CONFIG_BLK_DEV_SR=y
+# CONFIG_CHR_DEV_SG is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_CUMANA_1 is not set
+# CONFIG_SCSI_ECOSCSI is not set
+# CONFIG_SCSI_OAK is not set
+# CONFIG_SCSI_DEBUG is not set
 
 #
 # Network device support
 #
 CONFIG_NETDEVICES=y
 CONFIG_DUMMY=y
-# CONFIG_SLIP is not set
+CONFIG_SLIP=y
+CONFIG_SLIP_COMPRESSED=y
+# SL_SLIP_LOTS is not set
+# SL_DUMP is not set
 # CONFIG_PPP is not set
 # CONFIG_PLIP is not set
-# CONFIG_NET_ALPHA is not set
-# CONFIG_NET_VENDOR_SMC is not set
-# CONFIG_LANCE is not set
-# CONFIG_NET_VENDOR_3COM is not set
-CONFIG_NET_ISA=y
-# CONFIG_E2100 is not set
-# CONFIG_DEPCA is not set
-# CONFIG_EWRK3 is not set
-# CONFIG_HPLAN_PLUS is not set
-# CONFIG_HPLAN is not set
-CONFIG_NE2000=y
-# CONFIG_SK_G16 is not set
-# CONFIG_NET_EISA is not set
-# CONFIG_NET_POCKET is not set
-
-#
-# CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
-#
-# CONFIG_CDU31A is not set
-# CONFIG_MCD is not set
-# CONFIG_SBPCD is not set
-# CONFIG_AZTCD is not set
-# CONFIG_CDU535 is not set
+CONFIG_ETHER3=y
 
 #
 # Filesystems
@@ -99,28 +92,19 @@
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
 CONFIG_ISO9660_FS=y
-# CONFIG_HPFS_FS is not set
-# CONFIG_SYSV_FS is not set
 
 #
 # character devices
 #
-# CONFIG_CYCLADES is not set
 CONFIG_PRINTER=y
-CONFIG_BUSMOUSE=y
-# CONFIG_PSMOUSE is not set
-# CONFIG_MS_BUSMOUSE is not set
-# CONFIG_ATIXL_BUSMOUSE is not set
-# CONFIG_QIC02_TAPE is not set
-CONFIG_FTAPE=y
-NR_FTAPE_BUFFERS=3
 
 #
 # Sound
 #
-# CONFIG_SOUND is not set
+CONFIG_SOUND=y
 
 #
 # Kernel hacking
 #
 # CONFIG_PROFILE is not set
+CONFIG_SCSI_CONSTANTS=y
diff -r -u -N linux.orig/.emacsrc linux.arm/.emacsrc
--- linux.orig/.emacsrc	Thu Jan  1 01:00:00 1970
+++ linux.arm/.emacsrc	Fri Oct 27 23:16:06 1995
@@ -0,0 +1,259 @@
+;	EMACS.RC:	Standard MicroEMACS Startup program
+;			for MicroEMACS 3.12 and above
+;			(C)opyright 1987,92 by Daniel M Lawrence
+;			Last Update: 12/28/92
+
+set $discmd FALSE
+write-message "[Setting up....]"
+
+; If you screen "SNOWS", comment this line
+	set $flicker "FALSE"
+
+; To use an IBM-PC EGA card, uncomment the following line
+;	set $sres "EGA"
+
+;	If you hate clocks or position counters, comment these
+set $timeflag TRUE
+set $posflag TRUE
+
+;	Set Default Global modes
+
+add-global-mode "blue"
+add-global-mode "WHITE"
+;bind-to-key meta-prefix `	;for annoying keyboards with ` at the top left
+
+;	Toggle function key window display
+
+store-procedure toggle-fkeys
+	!if %rcfkeys
+		!goto rcfoff
+	!endif
+
+;	toggle function key window on
+	save-window
+	1 next-window
+	!if &sequal $cbufname "emacs.hlp"
+		delete-window
+	!endif
+	!if &not &sequal $cbufname "Function Keys"
+		1 split-current-window
+		1 select-buffer "Function Keys"
+		add-mode "red"
+		!force 5 resize-window
+		1 goto-line
+	!endif
+	set %rcfkeys TRUE
+	!force restore-window
+	!if &sequal $cbufname "Function Keys"
+		next-window
+	!endif
+	write-message "[Function key window ON]"
+	!return
+
+	;Toggle the function key window off
+*rcfoff
+	save-window
+	1 next-window
+	!if &sequal "Function Keys" $cbufname
+		delete-window
+	!endif
+	!force restore-window
+	write-message "[Function key window OFF]"
+	set %rcfkeys FALSE
+!endm
+
+;	Bring up Online-help system
+
+store-procedure get-help
+	set $discmd FALSE
+	source ehelp.cmd
+	set $discmd TRUE
+!endm
+
+;	Load a new page
+
+store-procedure get-page-loader
+	!if &seq &find newpage.cmd ""
+		write-message "[Can not find NEWPAGE.CMD]"
+		!return
+	!endif
+	execute-file newpage.cmd
+!endm
+
+;procedure to clean out the current page (which is nothing right now)
+
+store-procedure clean
+	; nothing by default
+!endm
+
+;	Set up auto CMODE
+
+store-procedure set-default-mode
+	set %rctmp &sin $cfname "."
+	!if &equ %rctmp 0
+		!return
+	!endif
+	set %rctmp &mid $cfname &add %rctmp 1 5
+	!if &or &seq %rctmp "c" &seq %rctmp "h"
+		add-mode "cmode"
+	!endif
+	!if &or &seq %rctmp "cpp" &seq %rctmp "hpp"
+		add-mode "cmode"
+	!endif
+	!if &or &seq %rctmp "mss" &seq %rctmp "txt"
+		add-mode "wrap"
+	!endif
+!endm
+set $readhook set-default-mode
+
+;	This function activates the function key window as
+;	a legitimate place to call up function keys using the mouse
+
+store-procedure mouse-clicks
+
+	;remember where we started, and do the mouse movement
+	save-window
+	!force mouse-move-down
+
+	;If not in the function key window... leave
+	!if &not &sequal $cbufname "Function Keys"
+		!return
+	!endif
+
+	;First pos is a screen reposition, let it through
+	!if &and &equ $xpos 0 &equ $ypos 0
+		restore-window
+		!return
+	!endif
+
+	;Find out what function key were gonna do
+	add-mode magic
+	2 forward-character
+	set %rctmp $search
+	!force search-reverse "[fF][0-9]"
+	!if &seq $status FALSE
+		delete-mode magic
+		set $search %rctmp
+		!return
+	!endif
+
+	;we are on the "f" or "F".  Get the function key type and number now
+	set $search %rctmp
+	set %fcase lower
+	!if &equ $curchar 70
+		set %fcase upper
+	!endif
+	1 forward-character
+	set %fnum &chr $curchar
+	1 forward-character
+	set %fnum &cat %fnum &chr $curchar
+	set %fnum &add %fnum 0
+	!if &equ %fnum 10
+		set %fnum "0"
+	!endif
+	set %fname &cat "FN" %fnum
+	!if &seq %fcase upper
+		set %fname &cat "S-" %fname
+	!endif
+
+	;save the function
+	set %rccmd &bind %fname
+	delete-mode MAGIC
+
+	;swallow the up-button
+	set %rctmp &gtc
+
+	;restore the window and exit
+	restore-window
+
+	;procedures don't need the square brackets
+	!if &seq &left %rccmd 1 "["
+		set %rccmd &mid %rccmd 2 &sub &len %rccmd 2
+		%rccmd
+		!return
+	!endif
+
+	;and then execute it
+	!force execute-named-command %rccmd
+!endm   
+macro-to-key mouse-clicks MSa
+
+;	***** Rebind the Function key group
+
+bind-to-key search-forward		FN1
+bind-to-key search-reverse		FN2
+bind-to-key hunt-forward		FN3
+bind-to-key hunt-backward		FN4
+macro-to-key toggle-fkeys		FN5
+macro-to-key get-help			FN6
+bind-to-key next-window 		FN7
+macro-to-key get-page-loader		FN8
+bind-to-key save-file			FN9
+bind-to-key exit-emacs			FN0
+
+bind-to-key execute-macro-10		S-FN1
+bind-to-key execute-macro-11		S-FN2
+bind-to-key execute-macro-12		S-FN3
+bind-to-key execute-macro-13		S-FN4
+bind-to-key execute-macro-14		S-FN5
+bind-to-key execute-macro-15		S-FN6
+bind-to-key execute-macro-16		S-FN7
+bind-to-key execute-macro-17		S-FN8
+bind-to-key execute-macro-18		S-FN9
+bind-to-key execute-macro-19		S-FN0
+
+;	bring up the function key window
+
+	1 split-current-window
+	select-buffer "Function Keys"
+	insert-string "f1 search-> f2 <-search Û    MicroEMACS:  Text Editor~n"
+	insert-string "f3 hunt->   f4 <-hunt   Û ~n"
+	insert-string "f5 fkeys    f6 help     Û  Available function key Pages include:~n"
+	insert-string "f7 nxt wind f8 pg[    ] Û    Word  Box  Emacs  Pascal  C  cObol Lisp~n"
+	insert-string "f9 save     f10 exit    Û  [use the f8 key to load Pages]~n"
+	unmark-buffer
+	delete-window
+	set %rcfkeys TRUE
+
+	!if &seq $os "UNIX"
+
+		;Allow mainframes to simulate function
+		;keys with ^C<n> and ^C shifted-<n>
+		
+		store-procedure emulate-fkeys
+			!if &not $pending
+				write-message "FN-"
+			!endif
+			set %rcchar &gtkey
+			set %rcchar &sindex "1234567890!@#$%^&*()" %rcchar
+			!if &equ %rcchar 0
+				write-message "[Not Bound]"
+				!return
+			!endif
+			clear-message-line
+			set %rctmp "FN"
+			!if &gre %rcchar 10
+				set %rctmp &cat "S-" %rctmp
+			!endif
+			set %rcchar &mid "12345678901234567890" %rcchar 1
+			set %rctmp &bind &cat %rctmp %rcchar
+			!if &seq &lef %rctmp 1 "["
+				set %rctmp &mid %rctmp 2 &sub &len %rctmp 2
+				run %rctmp
+				!return
+			!endif
+			!force execute-named-command %rctmp
+		!endm
+
+		macro-to-key emulate-fkeys ^C
+		
+	!endif
+
+	!if &seq $os "MSWIN"
+		source "mewin.cmd"
+	!else
+		toggle-fkeys
+	!endif
+
+	set $discmd TRUE
+
diff -r -u -N linux.orig/.version linux.arm/.version
--- linux.orig/.version	Fri Oct 27 23:09:26 1995
+++ linux.arm/.version	Fri Oct 27 23:16:05 1995
@@ -1 +1 @@
-4
+0
diff -r -u -N linux.orig/Configure linux.arm/Configure
--- linux.orig/Configure	Fri Oct 27 23:08:06 1995
+++ linux.arm/Configure	Fri Oct 27 23:11:54 1995
@@ -126,8 +126,10 @@
 fi
 . $CONFIG_IN
 
-if [ "$CONFIG_SOUND" = "y" ] ; then
+if [ "$HOSTTYPE" != "arm" ] ; then
+  if [ "$CONFIG_SOUND" = "y" ] ; then
 	$MAKE -C drivers/sound config || exit 1
+  fi
 fi
 
 rm -f .config.old
diff -r -u -N linux.orig/Makefile linux.arm/Makefile
--- linux.orig/Makefile	Fri Oct 27 23:08:06 1995
+++ linux.arm/Makefile	Fri Oct 27 23:09:17 1995
@@ -2,7 +2,7 @@
 PATCHLEVEL = 2
 SUBLEVEL = 8
 
-ARCH = i386
+ARCH = arm
 
 .EXPORT_ALL_VARIABLES:
 
@@ -86,11 +86,13 @@
 
 ARCHIVES	=kernel/kernel.o mm/mm.o fs/fs.o net/net.o ipc/ipc.o
 FILESYSTEMS	=fs/filesystems.a
-DRIVERS		=drivers/block/block.a \
-		 drivers/char/char.a \
-		 drivers/net/net.a
+DRIVERS		=
 LIBS		=$(TOPDIR)/lib/lib.a
-SUBDIRS		=kernel drivers mm fs net ipc lib
+SUBDIRS		=kernel mm fs net ipc lib
+
+DRIVERS := drivers/block/block.a \
+	   drivers/char/char.a \
+	   drivers/net/net.a
 
 ifdef CONFIG_SCSI
 DRIVERS := $(DRIVERS) drivers/scsi/scsi.a
@@ -125,7 +127,8 @@
 		$(FILESYSTEMS) \
 		$(DRIVERS) \
 		$(LIBS) -o vmlinux
-	$(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | sort > System.map
+#	$(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
@@ -198,7 +201,7 @@
 MODV = -DCONFIG_MODVERSIONS
 endif
 
-modules: include/linux/version.h
+modules: include/linux/version.h dummy
 	@set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i CFLAGS="$(CFLAGS) -DMODULE $(MODV)" modules; done
 	
 modules_install:
diff -r -u -N linux.orig/arch/alpha/Makefile linux.arm/arch/alpha/Makefile
--- linux.orig/arch/alpha/Makefile	Fri Oct 27 23:08:07 1995
+++ linux.arm/arch/alpha/Makefile	Fri Oct 27 23:13:50 1995
@@ -15,7 +15,7 @@
 
 HEAD := arch/alpha/kernel/head.o
 
-SUBDIRS := $(SUBDIRS) arch/alpha/kernel arch/alpha/mm arch/alpha/lib
+SUBDIRS := drivers $(SUBDIRS) arch/alpha/kernel arch/alpha/mm arch/alpha/lib
 ARCHIVES := arch/alpha/kernel/kernel.o arch/alpha/mm/mm.o $(ARCHIVES)
 LIBS := $(TOPDIR)/arch/alpha/lib/lib.a $(LIBS) $(TOPDIR)/arch/alpha/lib/lib.a
 
diff -r -u -N linux.orig/arch/arm/Makefile linux.arm/arch/arm/Makefile
--- linux.orig/arch/arm/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/Makefile	Fri Oct 27 23:14:02 1995
@@ -0,0 +1,56 @@
+#
+# 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)
+#
+
+ZLINKFLAGS =-N -Ttext 0x01800000
+LINKFLAGS  =-Ttext 0x01800000
+CFLAGS := $(CFLAGS) -fno-omit-frame-pointer
+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: vmlinux
+	@$(MAKEBOOT) install
+
+archclean:
+	@$(MAKEBOOT) clean
+
+archdep:
+	@$(MAKEBOOT) dep
diff -r -u -N linux.orig/arch/arm/boot/Makefile linux.arm/arch/arm/boot/Makefile
--- linux.orig/arch/arm/boot/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/Makefile	Fri Oct 27 23:15:23 1995
@@ -0,0 +1,68 @@
+#
+# 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
+
+install: $(CONFIGURE) Image
+	sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) Image $(TOPDIR)/System.map "$(INSTALL_PATH)"
+
+
+tools/build: tools/build.c
+	$(HOSTCC) $(CFLAGS) -o $@ $< -I$(TOPDIR)/include -static
+
+clean:
+	rm -f Image
+
+#zImage: $(CONFIGURE) bootsect setup compressed/vmlinux tools/build
+#	tools/build bootsect setup compressed/vmlinux $(ROOT_DEV) > zImage
+#	sync
+
+#compressed/vmlinux: $(TOPDIR)/vmlinux
+#	@$(MAKE) -C compressed vmlinux
+
+#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)"
+
+#setup: setup.o
+#	$(LD86) -s -o $@ $<
+
+#setup.o: setup.s
+#	$(AS86) -o $@ $<
+
+#setup.s: setup.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile
+#	$(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@
+
+#bootsect: bootsect.o
+#	$(LD86) -s -o $@ $<
+
+#bootsect.o: bootsect.s
+#	$(AS86) -o $@ $<
+
+#bootsect.s: bootsect.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile
+#	$(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@
+
+dep:
+
+#clean:
+#	rm -f bootsect setup
+#	rm -f zImage tools/build
+#	@$(MAKE) -C compressed clean
diff -r -u -N linux.orig/arch/arm/boot/install.sh linux.arm/arch/arm/boot/install.sh
--- linux.orig/arch/arm/boot/install.sh	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/install.sh	Fri Oct 27 23:15:25 1995
@@ -0,0 +1,40 @@
+#!/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
+
+# Default install - same as make zlilo
+
+if [ -f $4/vmlinux ]; then
+	rm $4/vmlinux.$1
+#	mv $4/vmlinux $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
+
+if [ -x /sbin/bootmap ]; then /sbin/bootmap /dev/hda1; else echo "You have to install it yourself"; fi
diff -r -u -N linux.orig/arch/arm/boot/tools/build.c linux.arm/arch/arm/boot/tools/build.c
--- linux.orig/arch/arm/boot/tools/build.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/boot/tools/build.c	Fri Oct 27 23:15:25 1995
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <a.out.h>
+
+
+int main(int argc, char **argv)
+{
+	void *data;
+	struct exec ex;
+	FILE *f, *fo;
+	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 -r -u -N linux.orig/arch/arm/config.in linux.arm/arch/arm/config.in
--- linux.orig/arch/arm/config.in	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/config.in	Fri Oct 27 23:14:02 1995
@@ -0,0 +1,199 @@
+#
+# For a description of the syntax of this configuration file,
+# see the Configure script.
+#
+
+echo "#define CONFIG_ARM" >> $CONFIG_H
+echo "#define CONFIG_ARMMOUSE 1" >> $CONFIG_H
+echo "CONFIG_ARM=1" >> $CONFIG
+echo "CONFIG_ARMMOUSE=1" >> $CONFIG
+
+comment 'General setup'
+
+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
+bool 'Kernel math emulation' CONFIG_MATH_EMULATION y
+bool 'Normal floppy disk support' CONFIG_BLK_DEV_FD y
+bool '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
+
+bool '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
+#bool 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF n
+
+comment 'Loadable module support'
+bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS n
+
+if [ "$CONFIG_NET" = "y" ]; then
+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
+comment '(it is safe to leave these untouched)'
+bool 'PC/TCP compatibility mode' CONFIG_INET_PCTCP n
+bool 'Reverse ARP' CONFIG_INET_RARP n
+bool 'Assume subnets are local' CONFIG_INET_SNARL y
+bool 'Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF 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
+fi
+
+comment 'SCSI support'
+
+bool 'SCSI support?' CONFIG_SCSI n
+
+if [ "$CONFIG_SCSI" = "n" ]; then
+
+comment 'Skipping SCSI configuration options...'
+
+else
+
+comment 'SCSI support type (disk, tape, CDrom)'
+
+bool 'Scsi disk support' CONFIG_BLK_DEV_SD y
+bool 'Scsi tape support' CONFIG_CHR_DEV_ST n
+bool 'Scsi CDROM support' CONFIG_BLK_DEV_SR n
+bool 'Scsi generic support' CONFIG_CHR_DEV_SG n
+
+comment 'SCSI low-level drivers'
+
+bool 'Acorn SCSI card (aka30) support' CONFIG_SCSI_ACORNSCSI_3 y
+bool 'Cumana 1 support' CONFIG_SCSI_CUMANA_1 n
+bool 'EcoScsi support' CONFIG_SCSI_ECOSCSI n
+bool 'Oak SCSI support' CONFIG_SCSI_OAK n
+bool 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG n
+fi
+
+
+if [ "$CONFIG_NET" = "y" ]; then
+
+comment 'Network device support'
+
+bool 'Network device support?' CONFIG_NETDEVICES y
+if [ "$CONFIG_NETDEVICES" = "n" ]; then
+
+comment 'Skipping network driver configuration options...'
+
+else
+bool 'Dummy net driver support' CONFIG_DUMMY y
+bool 'SLIP (serial line) support' CONFIG_SLIP y
+if [ "$CONFIG_SLIP" = "y" ]; then
+  bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED y
+  bool ' 16 channels instead of 4' SL_SLIP_LOTS n
+  bool ' SLIP debugging on' SL_DUMP y
+fi
+bool 'PPP (point-to-point) support' CONFIG_PPP y
+bool 'PLIP (parallel port) support' CONFIG_PLIP y
+bool 'Ether3 (NQ8005) support' CONFIG_ETHER3 n
+fi
+fi
+
+#comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)'
+
+#bool 'Sony CDU31A/CDU33A CDROM driver support' CONFIG_CDU31A n
+#bool 'Mitsumi (not IDE/ATAPI) CDROM driver support' CONFIG_MCD n
+#bool 'Matsushita/Panasonic CDROM driver support' CONFIG_SBPCD n
+#if [ "$CONFIG_SBPCD" = "y" ]; then
+#  bool 'Matsushita/Panasonic second CDROM controller support' CONFIG_SBPCD2 n
+#  if [ "$CONFIG_SBPCD2" = "y" ]; then
+#    bool 'Matsushita/Panasonic third CDROM controller support' CONFIG_SBPCD3 n
+#    if [ "$CONFIG_SBPCD3" = "y" ]; then
+#      bool 'Matsushita/Panasonic fourth CDROM controller support' CONFIG_SBPCD4 n
+#    fi
+#  fi
+#fi
+#bool 'Aztech/Orchid/Okano/Wearnes (non IDE) CDROM support' CONFIG_AZTCD n
+#bool 'Sony CDU535 CDROM driver support' CONFIG_CDU535 n
+
+comment 'Filesystems'
+
+bool 'Standard (minix) fs support' CONFIG_MINIX_FS n
+bool 'Extended fs support' CONFIG_EXT_FS n
+bool 'Second extended fs support' CONFIG_EXT2_FS y
+bool 'xiafs filesystem support' CONFIG_XIA_FS n
+bool 'msdos fs support' CONFIG_MSDOS_FS y
+if [ "$CONFIG_MSDOS_FS" = "y" ]; then
+bool 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n
+fi
+bool '/proc filesystem support' CONFIG_PROC_FS y
+if [ "$CONFIG_INET" = "y" ]; then
+bool 'NFS filesystem support' CONFIG_NFS_FS y
+fi
+if [ "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_CDU31A" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_AZTCD" = "y" -o "$CONFIG_CDU535" = "y" ]; then
+	bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS y
+else
+	bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n
+fi
+#bool 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n
+#bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS n
+
+comment 'character devices'
+
+#bool 'Cyclades async mux support' CONFIG_CYCLADES n
+bool 'Parallel printer support' CONFIG_PRINTER y
+#bool 'Logitech busmouse support' CONFIG_BUSMOUSE n
+#bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE n
+#if [ "$CONFIG_PSMOUSE" = "y" ]; then
+#bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE y
+#fi
+#bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n
+#bool 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n
+
+
+#bool 'QIC-02 tape support' CONFIG_QIC02_TAPE n
+#if [ "$CONFIG_QIC02_TAPE" = "y" ]; then
+#bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF y
+#if [ "$CONFIG_QIC02_DYNCONF" != "y" ]; then
+
+#comment '>>> Edit configuration parameters in ./include/linux/tpqic02.h!'
+
+#else
+
+#comment '>>> Setting runtime QIC-02 configuration is done with qic02conf'
+#comment '>>> Which is available from ftp://ftp.funet.fi/pub/OS/Linux/BETA/QIC-02/'
+
+#fi
+#fi
+
+#bool 'QIC-117 tape support' CONFIG_FTAPE n
+#if [ "$CONFIG_FTAPE" = "y" ]; then
+#int ' number of ftape buffers' NR_FTAPE_BUFFERS 3
+#fi
+
+comment 'Sound'
+
+bool 'Sound support' CONFIG_SOUND n
+
+comment 'Kernel hacking'
+
+#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n
+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 -r -u -N linux.orig/arch/arm/drivers/Makefile linux.arm/arch/arm/drivers/Makefile
--- linux.orig/arch/arm/drivers/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/Makefile	Fri Oct 27 23:14:58 1995
@@ -0,0 +1,48 @@
+#
+# 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...
+
+.S.s:
+	$(CPP) -traditional $< -o $*.s
+.c.s:
+	$(CC) $(CFLAGS) -S $<
+.s.o:
+	$(AS) -c -o $*.o $<
+.c.o:
+	$(CC) $(CFLAGS) -c $<
+
+SUBDIRS	= block char net #streams
+
+ifdef CONFIG_SCSI
+SUBDIRS := $(SUBDIRS) scsi
+endif
+
+ifdef CONFIG_SOUND
+SUBDIRS := $(SUBDIRS) sound
+endif
+
+all: driversubdirs
+
+driversubdirs: dummy
+	set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i; done
+
+modules: dummy
+	set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i modules; done
+
+dep:
+	set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i dep; done
+
+dummy:
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
+
diff -r -u -N linux.orig/arch/arm/drivers/block/Makefile linux.arm/arch/arm/drivers/block/Makefile
--- linux.orig/arch/arm/drivers/block/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/block/Makefile	Fri Oct 27 23:14:45 1995
@@ -0,0 +1,75 @@
+#
+# 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.
+#
+
+.c.s:
+	$(CC) $(CFLAGS) -S $<
+.s.o:
+	$(AS) -c -o $*.o $<
+.c.o:
+	$(CC) $(CFLAGS) -c $<
+
+#
+# Note : at this point, these files are compiled on all systems. 
+# In the future, some of these should be built conditionally.
+#
+
+all: block.a
+
+OBJS := ll_rw_blk.o ramdisk.o genhd.o hdsrch.o
+SRCS := ll_rw_blk.c ramdisk.c genhd.c hdsrch.c
+BLOCK_MODULE_OBJS =
+
+ifdef CONFIG_BLK_DEV_FD
+OBJS := $(OBJS) floppy.o
+SRCS := $(SRCS) floppy.c
+endif
+
+ifdef CONFIG_BLK_DEV_HD
+OBJS := $(OBJS) hd.o
+SRCS := $(SRCS) hd.c
+endif
+
+ifdef CONFIG_BLK_DEV_IDE
+OBJS := ide.o $(OBJS)
+SRCS := ide.c $(SRCS)
+endif
+
+ifdef CONFIG_BLK_DEV_MFM
+OBJS := mfmhd.o $(OBJS)
+SRCS := mfmhd.c $(OBJS)
+else
+BLOCK_MODULE_OBJS := mfm-hd.o
+
+mfm-hd.o: mfmhd.o
+	ld -r -o mfm-hd.o mfmhd.o `gcc --print-libgcc-file-name`
+
+endif
+
+block.a: $(OBJS)
+	rm -f block.a
+	$(AR) rcs block.a $(OBJS)
+	sync
+
+dep:
+	$(CPP) -M $(SRCS) > .depend
+
+modules: $(BLOCK_MODULE_OBJS)
+	echo $(BLOCK_MODULE_OBJS) > ../../../../modules/BLOCK_MODULES
+	(cd ../../../../modules;for i in $(BLOCK_MODULE_OBJS); do ln -sf ../arch/arm/drivers/block/$$i .; done)
+
+dummy:
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff -r -u -N linux.orig/arch/arm/drivers/block/blk.h linux.arm/arch/arm/drivers/block/blk.h
--- linux.orig/arch/arm/drivers/block/blk.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/block/blk.h	Fri Oct 27 23:14:45 1995
@@ -0,0 +1,295 @@
+#ifndef _BLK_H
+#define _BLK_H
+
+#include <linux/blkdev.h>
+#include <linux/locks.h>
+#include <linux/config.h>
+
+/*
+ * NR_REQUEST is the number of entries in the request-queue.
+ * NOTE that writes may use only the low 2/3 of these: reads
+ * take precedence.
+ *
+ * 32 seems to be a reasonable number: enough to get some benefit
+ * from the elevator-mechanism, but not so much as to lock a lot of
+ * buffers when they are in the queue. 64 seems to be too many (easily
+ * long pauses in reading when heavy writing/syncing is going on)
+ */
+#define NR_REQUEST	64
+
+/*
+ * This is used in the elevator algorithm: Note that
+ * reads always go before writes. This is natural: reads
+ * are much more time-critical than writes.
+ */
+#define IN_ORDER(s1,s2) \
+((s1)->cmd < (s2)->cmd || ((s1)->cmd == (s2)->cmd && \
+((s1)->dev < (s2)->dev || (((s1)->dev == (s2)->dev && \
+(s1)->sector < (s2)->sector)))))
+
+/*
+ * These will have to be changed to be aware of different buffer
+ * sizes etc.. It actually needs a major cleanup.
+ */
+#define SECTOR_MASK (blksize_size[MAJOR_NR] &&     \
+	blksize_size[MAJOR_NR][MINOR(CURRENT->dev)] ? \
+	((blksize_size[MAJOR_NR][MINOR(CURRENT->dev)] >> 9) - 1) :  \
+	((BLOCK_SIZE >> 9)  -  1))
+
+#define SUBSECTOR(block) (CURRENT->current_nr_sectors > 0)
+
+extern unsigned long hd_init(unsigned long mem_start, unsigned long mem_end);
+extern unsigned long cdu31a_init(unsigned long mem_start, unsigned long mem_end);
+extern unsigned long mcd_init(unsigned long mem_start, unsigned long mem_end);
+#ifdef CONFIG_SBPCD
+extern unsigned long sbpcd_init(unsigned long, unsigned long);
+#endif /* CONFIG_SBPCD */
+extern void set_device_ro(int dev,int flag);
+
+extern void rd_load(void);
+extern long rd_init(long mem_start, int length);
+extern int ramdisk_size;
+
+extern unsigned long xd_init(unsigned long mem_start, unsigned long mem_end);
+
+#define RO_IOCTLS(dev,where) \
+  case BLKROSET: if (!suser()) return -EACCES; \
+		 set_device_ro((dev),get_fs_long((long *) (where))); return 0; \
+  case BLKROGET: { int __err = verify_area(VERIFY_WRITE, (void *) (where), sizeof(long)); \
+		   if (!__err) put_fs_long(0!=is_read_only(dev),(long *) (where)); return __err; }
+
+#ifdef MAJOR_NR
+
+/*
+ * Add entries as needed. Currently the only block devices
+ * supported are hard-disks and floppies.
+ */
+
+#if (MAJOR_NR == MEM_MAJOR)
+
+/* ram disk */
+#define DEVICE_NAME "ramdisk"
+#define DEVICE_REQUEST do_rd_request
+#define DEVICE_NR(device) ((device) & 7)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == FLOPPY_MAJOR)
+
+static void floppy_off(unsigned int nr);
+
+#define DEVICE_NAME "floppy"
+#define DEVICE_INTR do_floppy
+#define DEVICE_REQUEST do_fd_request
+#define DEVICE_NR(device) ( ((device) & 3) | (((device) & 0x80 ) >> 5 ))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device) floppy_off(DEVICE_NR(device))
+
+#elif (MAJOR_NR == HD_MAJOR)
+
+/* harddisk: timeout is 6 seconds.. */
+#define DEVICE_NAME "harddisk"
+#define DEVICE_INTR do_hd
+#define DEVICE_TIMEOUT HD_TIMER
+#define TIMEOUT_VALUE 600
+#define DEVICE_REQUEST do_hd_request
+#define DEVICE_NR(device) (MINOR(device)>>6)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == SCSI_DISK_MAJOR)
+
+#define DEVICE_NAME "scsidisk"
+#define DEVICE_INTR do_sd
+#define TIMEOUT_VALUE 200
+#define DEVICE_REQUEST do_sd_request
+#define DEVICE_NR(device) (MINOR(device) >> 4)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == SCSI_TAPE_MAJOR)
+
+#define DEVICE_NAME "scsitape"
+#define DEVICE_INTR do_st
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == SCSI_CDROM_MAJOR)
+
+#define DEVICE_NAME "CD-ROM"
+#define DEVICE_INTR do_sr
+#define DEVICE_REQUEST do_sr_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == XT_DISK_MAJOR)
+
+#define DEVICE_NAME "xt disk"
+#define DEVICE_REQUEST do_xd_request
+#define DEVICE_NR(device) (MINOR(device) >> 6)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif defined(MFM_DISK_MAJOR) && (MAJOR_NR == MFM_DISK_MAJOR)
+
+#define DEVICE_NAME "mfm disk"
+#define DEVICE_INTR do_mfm
+#define DEVICE_REQUEST do_mfm_request
+#define DEVICE_NR(device) (MINOR(device) >> 6)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == CDU31A_CDROM_MAJOR)
+
+#define DEVICE_NAME "CDU31A"
+#define DEVICE_REQUEST do_cdu31a_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MITSUMI_CDROM_MAJOR)
+
+#define DEVICE_NAME "Mitsumi CD-ROM"
+/* #define DEVICE_INTR do_mcd */
+#define DEVICE_REQUEST do_mcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MATSUSHITA_CDROM_MAJOR)
+
+#define DEVICE_NAME "Matsushita CD-ROM controller #1"
+#define DEVICE_REQUEST do_sbpcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MATSUSHITA_CDROM2_MAJOR)
+
+#define DEVICE_NAME "Matsushita CD-ROM controller #2"
+#define DEVICE_REQUEST do_sbpcd2_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MATSUSHITA_CDROM3_MAJOR)
+
+#define DEVICE_NAME "Matsushita CD-ROM controller #3"
+#define DEVICE_REQUEST do_sbpcd3_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MATSUSHITA_CDROM4_MAJOR)
+
+#define DEVICE_NAME "Matsushita CD-ROM controller #4"
+#define DEVICE_REQUEST do_sbpcd4_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#endif
+
+#if (MAJOR_NR != SCSI_TAPE_MAJOR)
+
+#ifndef CURRENT
+#define CURRENT (blk_dev[MAJOR_NR].current_request)
+#endif
+
+#define CURRENT_DEV DEVICE_NR(CURRENT->dev)
+
+#ifdef DEVICE_INTR
+void (*DEVICE_INTR)(void) = NULL;
+#endif
+#ifdef DEVICE_TIMEOUT
+
+#define SET_TIMER \
+((timer_table[DEVICE_TIMEOUT].expires = jiffies + TIMEOUT_VALUE), \
+(timer_active |= 1<<DEVICE_TIMEOUT))
+
+#define CLEAR_TIMER \
+timer_active &= ~(1<<DEVICE_TIMEOUT)
+
+#define SET_INTR(x) \
+if ((DEVICE_INTR = (x)) != NULL) \
+	SET_TIMER; \
+else \
+	CLEAR_TIMER;
+
+#else
+
+#define SET_INTR(x) (DEVICE_INTR = (x))
+
+#endif
+static void (DEVICE_REQUEST)(void);
+
+/* end_request() - SCSI devices have their own version */
+
+#if ! SCSI_MAJOR(MAJOR_NR)
+
+static void end_request(int uptodate)
+{
+	struct request * req;
+	struct buffer_head * bh;
+
+	req = CURRENT;
+	req->errors = 0;
+	if (!uptodate) {
+		printk(DEVICE_NAME " I/O error\n");
+		printk("dev %04lX, sector %lu\n",
+		       (unsigned long)req->dev, req->sector);
+		req->nr_sectors--;
+		req->nr_sectors &= ~SECTOR_MASK;
+		req->sector += (BLOCK_SIZE / 512);
+		req->sector &= ~SECTOR_MASK;
+	}
+
+	if ((bh = req->bh) != NULL) {
+		req->bh = bh->b_reqnext;
+		bh->b_reqnext = NULL;
+		bh->b_uptodate = uptodate;
+		if (!uptodate) bh->b_req = 0; /* So no "Weird" errors */
+		unlock_buffer(bh);
+		if ((bh = req->bh) != NULL) {
+			req->current_nr_sectors = bh->b_size >> 9;
+			if (req->nr_sectors < req->current_nr_sectors) {
+				req->nr_sectors = req->current_nr_sectors;
+				printk("end_request: buffer-list destroyed\n");
+			}
+			req->buffer = bh->b_data;
+			return;
+		}
+	}
+	DEVICE_OFF(req->dev);
+	CURRENT = req->next;
+	if (req->sem != NULL)
+		up(req->sem);
+	req->dev = -1;
+	wake_up(&wait_for_request);
+}
+#endif
+
+#ifdef DEVICE_INTR
+#define CLEAR_INTR SET_INTR(NULL)
+#else
+#define CLEAR_INTR
+#endif
+
+#define INIT_REQUEST \
+	if (!CURRENT) {\
+		CLEAR_INTR; \
+		return; \
+	} \
+	if (MAJOR(CURRENT->dev) != MAJOR_NR) \
+		panic(DEVICE_NAME ": request list destroyed"); \
+	if (CURRENT->bh) { \
+		if (!CURRENT->bh->b_lock) \
+			panic(DEVICE_NAME ": block not locked"); \
+	}
+
+#endif
+
+#endif
+#endif
diff -r -u -N linux.orig/arch/arm/drivers/block/floppy.c linux.arm/arch/arm/drivers/block/floppy.c
--- linux.orig/arch/arm/drivers/block/floppy.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/block/floppy.c	Fri Oct 27 23:14:43 1995
@@ -0,0 +1,3227 @@
+/*
+ *  linux/kernel/floppy.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *  Copyright (C) 1993, 1994  Alain Knaff
+ */
+
+/* Configuration */
+/* The following does some extra sanity checks */
+#define SANITY
+
+/* 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. */
+#define ALLOWED_DRIVE_MASK 0x0F
+
+/* Undefine the following if you have to floppy disk controllers:
+ * This works at least for me; if you get two controllers working, with
+ * drives attached to both, please mail me: Alain.Knaff@imag.fr */
+/* #define HAVE_2_CONTROLLERS */
+
+
+/* Define the following if you don't like that your drives seek audibly
+ * after a disk change (but it may not work correctly for everybody)
+ */
+#define SILENT_DC_CLEAR
+
+
+/* End of configuration */
+
+/*
+ * 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.
+ */
+int no_floppies;
+#define FLOPPY_IRQ 12
+#define FLOPPY_DMA 2
+#define DEBUGT 2
+
+#include <linux/config.h>
+#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>
+#include <linux/fd.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/delay.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"
+
+static unsigned int changed_floppies = 0xff, fake_change = 0;
+static int initialising=1;
+
+#ifdef HAVE_2_CONTROLLERS
+#define N_FDC 2
+#define N_DRIVE 8
+#else
+#define N_FDC 1
+#define N_DRIVE 4
+#endif
+
+#define TYPE(x) ( ((x)>>2) & 0x1f )
+#define DRIVE(x) ( ((x)&0x03) | (((x)&0x80 ) >> 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 UDP (&drive_params[drive])
+#define UDRS (&drive_state[drive])
+#define UDRWE (&write_errors[drive])
+#define UFDCS (&fdc_state[FDC(drive)])
+
+#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));
+
+/* 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.
+ */
+#define MAX_DISK_SIZE 3984
+
+
+
+/*
+ * 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.
+ */
+#if 0
+#define LAST_DMA_ADDR	(0x1000000)
+#else
+/* Everything has to go via dma buffer */
+#define LAST_DMA_ADDR	(0x1)
+#endif
+#define K_64 (0x10000) /* 64 k */
+/*
+ * 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])
+
+/*
+ * this struct defines the different floppy drive types.
+ */
+static struct {
+	struct floppy_drive_params params;
+	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, 100, 300,  0, 2, 5,  80, 3*HZ, 20, {3,1,2,0,2}, 0,
+      0, { 7, 4, 8, 2, 1, 5, 3,10}, 150, 0 }, "unknown" },
+
+{{1,  300, 16, 16, 8000, 100, 300,  0, 2, 5,  40, 3*HZ, 17, {3,1,2,0,2}, 0,
+      0, { 1, 0, 0, 0, 0, 0, 0, 0}, 150, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/
+
+{{2,  500, 16, 16, 6000,  40, 300, 14, 2, 6,  83, 3*HZ, 17, {3,1,2,0,2}, 0,
+      0, { 2, 5, 6,23,10,20,11, 0}, 150, 2 }, "1.2M" }, /*5 1/4 HD AT*/
+
+{{3,  250, 16, 16, 3000, 100, 300,  0, 2, 5,  83, 3*HZ, 20, {3,1,2,0,2}, 0,
+      0, { 4,22,21,30, 3, 0, 0, 0}, 150, 4 }, "720k" }, /*3 1/2 DD*/
+
+#if 0
+{{4,  500, 16, 16, 4000,  40, 300, 10, 2, 5,  83, 3*HZ, 20, {3,1,2,0,2}, 0,
+      0, { 7, 4,25,22,31,21,29,11}, 150, 7 }, "1.44M" }, /*3 1/2 HD*/
+#else
+{{4,  500, 16, 16, 3000,  40, 300, 10, 2, 5,  83, 3*HZ, 20, {3,1,2,0,0}, FTD_MSG,
+      0, { 7, 4,25,22,31,21,29,11}, 150, 7 }, "1.44M" }, /*3 1/2 HD*/
+#endif
+
+{{5, 1000, 15,  8, 3000,  40, 300, 10, 2, 5,  83, 3*HZ, 40, {3,1,2,0,2}, 0,
+      0, { 7, 8, 4,25,28,22,31,21}, 150, 8 }, "2.88M AMI BIOS" }, /*3 1/2 ED*/
+
+{{6, 1000, 15,  8, 3000,  40, 300, 10, 2, 5,  83, 3*HZ, 40, {3,1,2,0,2}, 0,
+      0, { 7, 8, 4,25,28,22,31,21}, 150, 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 volatile drive_state[N_DRIVE];
+static struct floppy_write_errors volatile write_errors[N_DRIVE];
+static struct floppy_raw_cmd raw_cmd;
+
+/*
+ * This struct defines the different floppy types.
+ *
+ * The 'stretch' tells if the tracks need to be doubled for some
+ * types (ie 360kB diskette in 1.2MB drive etc). Others should
+ * be self-explanatory.
+ */
+/*
+             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
+#ifdef HAVE_2_CONTROLLERS
+	,
+	NULL, NULL, NULL, NULL
+#endif
+};
+
+/*
+ * 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];
+
+/*
+ * 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;
+
+/* 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.
+ */
+#if 0
+extern char floppy_track_buffer[512*2*MAX_BUFFER_SECTORS];
+#else
+char floppy_track_buffer[512*2*MAX_BUFFER_SECTORS];
+#endif
+#define max_buffer_sectors MAX_BUFFER_SECTORS
+
+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;
+
+static void floppy_start(void);
+static void redo_fd_request(void);
+static void recalibrate_floppy(void);
+static void seek_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
+#define PROVEN_ABSENT -4
+
+/* 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];
+int fdc; /* current fdc */
+
+static struct floppy_struct * floppy = floppy_type;
+static unsigned char current_drive = 255;
+static long current_count_sectors = 0;
+static char *current_addr = 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.
+ */
+
+unsigned char floppy_selects1[]={ 0x10, 0x21, 0x23, 0x33 };
+unsigned char floppy_selects2[]={ 0   , 0   , 0   , 0    };
+
+int fd_sectsizes[256];
+
+/*
+ * Debugging
+ * =========
+ */
+static inline void set_debugt(void)
+{
+#ifdef DEBUGT
+	debugtimer = jiffies;
+#endif
+}
+
+static inline void debugt(char *message)
+{
+#ifdef DEBUGT
+  if ( DP->flags & DEBUGT )
+	printk("%s dtime=%lu\n", message, jiffies-debugtimer );
+#endif
+}
+
+/*
+ * 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 changed_floppies flag,
+ * and the last_checked date.
+ *
+ * last_checked is the date of the last check which showed 'no disk change'
+ * changed_floppies 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.
+ *
+ * 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)
+{
+	if(jiffies < DP->select_delay + DRS->select_date)
+		udelay(20000);
+
+	if(inb_p(FD_DIR) & 0x80){
+		UDRS->flags |= FD_VERIFY; /* verify write protection */
+
+		if(UDRS->maxblock || /* disk change check */
+		   !(UDRS->flags & FD_DISK_NEWCHANGE)){/* disk presence check */
+			/* mark it changed or absent */
+			set_bit(drive,&changed_floppies);
+
+			/* invalidate its geometry */
+			if (UDRS->keep_data >= 0) {
+				if ((DP->flags & FTD_MSG) &&
+				    current_type[drive] != NULL)
+					DPRINT("Disk type is undefined after "
+					       "disk change\n");
+				current_type[drive] = NULL;
+				floppy_sizes[drive] = MAX_DISK_SIZE;
+			}
+		}
+		UDRS->flags |= FD_DISK_NEWCHANGE;
+		return 1;
+	} else {
+		UDRS->last_checked=jiffies;
+		UDRS->flags &= ~FD_DISK_NEWCHANGE;
+		return 0;
+	}
+}
+
+static 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 locked=0;
+static int set_dor(int fdc, char mask, char data)
+{
+	register unsigned char drive, unit, newdor,olddor;
+
+	locked=1;
+	olddor = FDCS->dor;
+	newdor =  (olddor & mask) | data;
+	if ( newdor != olddor ){
+		unit = olddor & 0x3;
+		drive = REVDRIVE(fdc,unit);
+		if ( olddor & ( 0x10 << unit ))
+			disk_change(drive);
+		FDCS->dor = newdor;
+		arm_set_dor(newdor);
+	}
+	locked=0;
+	return olddor;
+}
+
+static void twaddle(void)
+{
+	cli();
+#if 0
+	outb_p(FDCS->dor & ~(0x10<<UNIT(current_drive)),FD_DOR);
+	outb_p(FDCS->dor, FD_DOR);
+#else
+	arm_set_dor(FDCS->dor);
+#endif
+	sti();
+}
+
+/* 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 &&
+		    UDRS->track != PROVEN_ABSENT &&
+		    ( 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 ){
+		fdc = FDC(drive);
+		current_drive = drive;
+	}
+	set_dor(fdc,~0,8);
+#ifdef HAVE_2_CONTROLLERS
+	set_dor(1-fdc, ~8, 0);
+#endif
+	if ( FDCS->rawcmd == 2 )
+		reset_fdc_info(1);
+	if( inb_p(FD_STATUS) != STATUS_READY )
+		FDCS->reset = 1;
+}
+
+static int usage_count = 0;
+/* 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);
+	return 0;
+}
+
+#define LOCK_FDC(drive,interruptible) \
+if(lock_fdc(drive,interruptible)) return -EINTR;
+
+/* unlocks the driver */
+static inline void unlock_fdc(void)
+{
+	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;
+	timer_active &= ~(1 << FLOPPY_TIMER);
+	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));
+
+	if(locked)
+		floppy_off(nr);
+	else
+		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 }
+#ifdef HAVE_2_CONTROLLERS
+	,
+	{ 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 }
+#endif
+};
+
+/* schedules motor off */
+static void floppy_off(unsigned int nr)
+{
+	unsigned long volatile delta;
+	register int fdc=FDC(nr);
+
+	if( !(FDCS->dor & ( 0x10 << UNIT(nr))))
+		return;
+
+	del_timer(motor_off_timer+nr);
+
+	/* make spindle stop in a position which minimizes spinup time
+	 * next time */
+	if ( drive_params[nr].rps ){
+		delta = jiffies - drive_state[nr].first_read_date + HZ -
+			drive_params[nr].spindown_offset;
+		delta = (( delta * drive_params[nr].rps) % HZ ) /
+			drive_params[nr].rps;
+		motor_off_timer[nr].expires = drive_params[nr].spindown - delta;
+	}
+	add_timer(motor_off_timer+nr);
+}
+
+/*
+ * 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;
+
+	saved_drive = current_drive % N_DRIVE;
+	for(i=0; i< N_DRIVE; i++){
+		drive = (saved_drive + i + 1 ) % N_DRIVE;
+		if ( UDRS->fd_ref == 0 )
+			continue; /* skip closed drives */
+		set_fdc(drive);
+		if (!(FDCS->dor & (0x10 << UNIT(drive))) ||
+		    ((FDCS->dor & 0x3) != UNIT(drive)))
+			UDRS->select_date = jiffies;
+		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 );
+	}
+	current_drive = saved_drive;
+}
+
+typedef void (*timeout_fn)(unsigned long);
+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)
+{
+	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 = 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 nr, 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  - jiffies;
+		add_timer(&fd_timer);
+		return 1;
+	}
+	return 0;
+}
+
+static void setup_DMA(void)
+{
+#ifdef SANITY
+	if ((!CURRENT ||
+	     CURRENT->buffer != current_addr ||
+	     raw_cmd.length > 512 * CURRENT->nr_sectors) &&
+	    (current_addr < floppy_track_buffer ||
+	     current_addr + raw_cmd.length >
+	     floppy_track_buffer + 1024 * max_buffer_sectors)){
+		printk("bad address. start=%p lg=%lx tb=%p\n",
+		       current_addr, 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 0
+	if ((long) current_addr % 512 ){
+		printk("non aligned address: %p\n", current_addr );
+		cont->done(0);
+		FDCS->reset=1;
+		return;
+	}
+	if ( ( (long)current_addr & ~(64*1024-1) ) !=
+	    ((long)(current_addr + raw_cmd.length-1)  & ~(64*1024-1))){
+		printk("DMA crossing 64-K boundary %p-%p\n",
+		       current_addr, current_addr + 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, (long) current_addr);
+	set_dma_count(FLOPPY_DMA, raw_cmd.length);
+	enable_dma(FLOPPY_DMA);
+	sti();
+}
+
+/* sends a command byte to the fdc */
+static int output_byte(char byte)
+{
+	int counter;
+	unsigned char status;
+
+	if (FDCS->reset)
+		return -1;
+	for(counter = 0 ; counter < 10000 && !FDCS->reset ; counter++) {
+		status = inb_p(FD_STATUS) &(STATUS_READY|STATUS_DIR|STATUS_DMA);
+		if (status == STATUS_READY){
+			outb_p(byte,FD_DATA);
+			return 0;
+		}
+		if (!(status & STATUS_READY))
+			continue;
+		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;
+
+	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)
+			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(0x1A);	/* 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 void fdc_dtr(void)
+{
+	/* If data rate not already set to desired value, set it. */
+	if ( raw_cmd.rate == FDCS->dtr)
+		return;
+
+	/* 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.
+	 */
+	udelay(5000);
+	FDCS->dtr = raw_cmd.rate;
+} /* 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)>>6) {
+		case 1:	/* error occured during command execution */
+			bad = 1;
+			if (ST1 & ST1_WP) {
+				DPRINT("Drive is write protected\n");
+				DRS->flags &= ~FD_DISK_WRITABLE;
+				cont->done(0);
+				bad = 2;
+			} else if (ST1 & ST1_ND) {
+				DRS->flags |= 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)
+{
+printk("Wrong cylinder!\n");
+				/* wrong cylinder => recal */
+				DRS->track = NEED_2_RECAL;
+}
+			return bad;
+		case 2: /* invalid command given */
+			DPRINT("Invalid FDC command given!\n");
+			cont->done(0);
+			return 2;
+		case 3:
+			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(current_drive,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();
+}
+
+#ifdef SILENT_DC_CLEAR
+static int blind_seek;
+#endif
+
+/*
+ * 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
+#ifdef SILENT_DC_CLEAR
+	set_dor(fdc, ~0,  (0x10 << UNIT(current_drive)));
+#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
+#ifdef SILENT_DC_CLEAR
+	    && !blind_seek
+#endif
+	    )
+		DRS->flags &= ~FD_DISK_NEWCHANGE; /* effective seek */
+	DRS->track = ST1;
+	DRS->select_date = jiffies;
+	seek_floppy();
+}
+
+static void check_wp(void)
+{
+	if (DRS->flags & FD_VERIFY) {
+		/* check write protection */
+		output_byte( FD_GETSTATUS );
+		output_byte( UNIT(current_drive) );
+		if ( result() != 1 ){
+			FDCS->reset = 1;
+			return;
+		}
+		DRS->flags &= ~(FD_VERIFY | FD_DISK_WRITABLE | FD_NEED_TWADDLE);
+
+		if (!( ST3  & 0x40))
+			DRS->flags |= FD_DISK_WRITABLE;
+	}
+}
+
+static void seek_floppy(void)
+{
+	int track;
+
+#ifdef SILENT_DC_CLEAR
+	blind_seek=0;
+#endif
+	disk_change(current_drive);
+	if ((raw_cmd.flags & FD_RAW_NEED_DISK) &&
+	    test_bit(current_drive,&changed_floppies)){
+		/* 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.
+		 */
+		cont->done(0);
+		cont->redo();
+		return;
+	}
+	if ( DRS->track <= NEED_1_RECAL ){
+		recalibrate_floppy();
+		return;
+	} else if ((DRS->flags & 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 {
+#ifdef SILENT_DC_CLEAR
+			set_dor(fdc, ~ (0x10 << UNIT(current_drive)), 0);
+			blind_seek = 1;
+#endif
+			track = 1;
+		}
+	} else {
+		check_wp();
+		if (raw_cmd.track != DRS->track)
+			track = raw_cmd.track;
+		else {
+			setup_rw_floppy();
+			return;
+		}
+	}
+
+#ifndef SILENT_DC_CLEAR
+	if ( !track && DRS->track >= 0 && DRS->track < 80 ){
+		DRS->flags &= ~FD_DISK_NEWCHANGE;
+		/* if we go to track 0 anyways, we can just as well use
+		 * recalibrate */
+		recalibrate_floppy();
+	} else
+#endif
+	{
+		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 PROVEN_ABSENT:
+#ifdef DEBUGT
+			debugt("recal interrupt proven absent:");
+#endif
+			/* fall through */
+	       	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
+			 */
+			DRS->flags &= ~FD_DISK_NEWCHANGE;
+			/* 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;
+	seek_floppy();
+}
+
+/*
+ * 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;
+	cli();
+	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;
+		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 unused, struct pt_regs *regs)
+{
+	void (*handler)(void) = DEVICE_INTR;
+
+	CLEAR_INTR;
+	if ( fdc >= N_FDC ){ /* we don't even know which FDC is the culprit */
+		printk("floppy interrupt on bizarre fdc\n");
+		return;
+	}
+	inr = result();
+	if (!handler){
+		unexpected_floppy_interrupt();
+		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);
+}
+
+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");
+	for(i=0; i<N_FDC; i++){
+		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... */
+	}
+	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( timer_active & (1 << FLOPPY_TIMER)){
+		printk("timer_table=%p\n",timer_table[FLOPPY_TIMER].fn);
+		printk("expires=%ld\n",timer_table[FLOPPY_TIMER].expires);
+		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)
+{
+	CLEAR_INTR;
+	floppy_tq.routine = (void (*)(void *)) empty;
+	del_timer( &fd_timer);
+	disable_dma(FLOPPY_DMA);
+	/* avoid dma going to a random drive after shutdown */
+	if(!initialising)
+		DPRINT("floppy timeout\n");
+	FDCS->reset = 1;
+	cont->done(0);
+	cont->redo(); /* this will recall reset when needed */
+}
+
+/* start motor, check media-changed condition and write protection */
+static void start_motor(void)
+{
+	int mask, data;
+
+	mask = 0xfc;
+	data = UNIT(current_drive);
+
+	if ( (FDCS->dor & 0x03) != UNIT(current_drive) ||
+	    !(FDCS->dor & ( 0x10 << UNIT(current_drive) ) ))
+		/* notes select time if floppy is not yet selected */
+		DRS->select_date = jiffies;
+
+	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);
+	if( raw_cmd.flags & FD_RAW_NO_MOTOR)
+		return;
+
+	disk_change(current_drive);
+
+	return;
+}
+
+static void floppy_ready(void)
+{
+	CHECK_RESET;
+	start_motor();
+
+	/* wait_for_completion also schedules reset if needed. */
+	if(wait_for_completion(current_drive,
+			       DRS->select_date+DP->select_delay,
+			       (timeout_fn) floppy_ready))
+		return;
+	fdc_dtr();
+	if ( raw_cmd.flags & FD_RAW_NEED_SEEK ){
+		perpendicular_mode();
+		fdc_specify(); /* must be done here because of hut, hlt ... */
+		seek_floppy();
+	} else
+		setup_rw_floppy();
+}
+
+static void floppy_start(void)
+{
+	timer_table[FLOPPY_TIMER].expires = jiffies + DP->timeout;
+	timer_active |= 1 << FLOPPY_TIMER;
+	scandrives();
+	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)
+{
+	timer_active &= ~(1 << FLOPPY_TIMER);
+	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)
+		if (current->pid)
+			interruptible_sleep_on(&command_done);
+		else {
+			sti();
+			run_task_queue(&tq_timer);
+			cli();
+		}
+	if(command_status < 2){
+		sti();
+		floppy_shutdown();
+		redo_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)
+{
+	generic_done(1);
+}
+
+static void generic_failure(void)
+{
+	generic_done(0);
+}
+
+static void success_and_wakeup(void)
+{
+	generic_success();
+	do_wakeup();
+}
+
+static void failure_and_wakeup(void)
+{
+	generic_failure();
+	do_wakeup();
+}
+
+/*
+ * formatting and rw support.
+ * ==========================
+ */
+
+static int next_valid_format(void)
+{
+	int probed_format;
+	while(1){
+		probed_format = DRS->probed_format;
+		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(int 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(void)
+{
+	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.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) + ( format_req.head << 2 );
+	F_SIZECODE = FD_SIZECODE(floppy);
+	F_SECT_PER_TRACK = floppy->sect << 2 >> F_SIZECODE;
+	F_GAP = floppy->fmt_gap;
+	F_FILL = FD_FILL_BYTE;
+
+	current_addr = 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 + 1;
+
+	/* 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)
+{
+	raw_cmd.track = format_req.track << floppy->stretch;
+	buffer_track = -1;
+	setup_format_params();
+	clear_bit(current_drive, &changed_floppies);
+	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(int device, struct format_descr *tmp_format_req)
+{
+	int okay;
+
+	LOCK_FDC(DRIVE(device),1);
+	set_floppy(device);
+	if (!floppy ||
+	    tmp_format_req->track >= floppy->track ||
+	    tmp_format_req->head >= floppy->head){
+		redo_fd_request();
+		return -EINVAL;
+	}
+	format_req = *tmp_format_req;
+	format_errors = 0;
+	cont = &format_cont;
+	errors = &format_errors;
+	CALL(okay=wait_til_done(redo_format,1));
+	redo_fd_request();
+	return okay;
+}
+
+/*
+ * 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;
+	timer_active &= ~(1 << FLOPPY_TIMER);
+
+	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 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[DRIVE(current_drive) + (FDC(current_drive) << 7)] =
+			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[DRIVE(current_drive) + (FDC(current_drive) << 7)] =
+			floppy->size >> 1;
+		probing = 0;
+	}
+
+	if ( CT(COMMAND) != FD_READ || current_addr == 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 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 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",
+				(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 ( ((int)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 SANITY
+		if ( !bh){
+			DPRINT("bh=null in copy buffer after copy\n");
+			break;
+		}
+#endif
+		size = bh->b_size;
+		buffer = bh->b_data;
+	}
+#ifdef 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;
+
+	current_drive = DRIVE(CURRENT->dev);
+
+	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 ( (DRS->flags & 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 << floppy->stretch;
+	DR_SELECT = UNIT(current_drive) + ( HEAD << 2 );
+	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 ((long)CURRENT->buffer <= LAST_DMA_ADDR ) {
+		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 > ( LAST_DMA_ADDR - ((long) CURRENT->buffer))>>9)
+			max_size=(LAST_DMA_ADDR - ((long)CURRENT->buffer))>>9;
+		/* 64 kb boundaries */
+		if ( ((max_size << 9) + ((long) CURRENT->buffer)) / K_64 !=
+		     ((long) CURRENT->buffer ) / K_64 )
+			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 ((indirect - sector_t) * 2 > (direct - sector_t) * 3 &&
+		    *errors < DP->max_errors.read_track &&
+		    /*!(DRS->flags & FD_NEED_TWADDLE) &&*/
+		    ( ( !probing || (DP->read_track &
+			   (1 <<DRS->probed_format))))){
+			max_size = CURRENT->nr_sectors;
+		} else {
+			current_addr = CURRENT->buffer;
+			raw_cmd.length = current_count_sectors << 9;
+			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_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;
+	}
+	current_addr = 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 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 SANITY
+	if ((raw_cmd.length < current_count_sectors << 9) ||
+	    (current_addr != 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 ( current_addr != CURRENT->buffer )
+			printk("addr=%d, length=%ld\n",
+			       (current_addr - 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 (current_addr != CURRENT->buffer ){
+		if (current_addr < floppy_track_buffer ||
+		    current_count_sectors < 0 ||
+		    raw_cmd.length < 0 ||
+		    current_addr + 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);
+	}
+#endif
+	return 2;
+}
+
+static struct cont_t rw_cont={
+	rw_interrupt,
+	redo_fd_request,
+	bad_flp_intr,
+	request_done };
+
+static void redo_fd_request(void)
+{
+#define REPEAT {request_done(0); continue; }
+	int device;
+	int tmp;
+
+	if (current_drive < N_DRIVE)
+		floppy_off(current_drive);
+
+	if (CURRENT && CURRENT->dev < 0) return;
+
+	cont = &rw_cont;
+	while(1){
+		if (!CURRENT) {
+			CLEAR_INTR;
+			unlock_fdc();
+			return;
+		}
+		if (MAJOR(CURRENT->dev) != MAJOR_NR)
+			panic(DEVICE_NAME ": request list destroyed");
+		if (CURRENT->bh && !CURRENT->bh->b_lock)
+			panic(DEVICE_NAME ": block not locked");
+
+		device = CURRENT->dev;
+		set_fdc( DRIVE(device));
+
+		timer_table[FLOPPY_TIMER].expires = jiffies + DP->timeout;
+		timer_active |= 1 << FLOPPY_TIMER;
+		raw_cmd.flags=0;
+		start_motor();
+		if(test_bit( DRIVE(device), &fake_change) ||
+		   test_bit( DRIVE(device), &changed_floppies)){
+			DPRINT("disk absent or changed during operation\n");
+			REPEAT;
+		}
+		set_floppy(device);
+		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;
+		}
+
+		floppy_tq.routine = (void (*)(void *)) floppy_start;
+		queue_task(&floppy_tq, &tq_timer);
+#ifdef DEBUGT
+		debugt("queue fd request");
+#endif
+		return;
+	}
+#undef REPEAT
+}
+
+void do_fd_request(void)
+{
+	if (fdc_busy)
+		/* fdc busy, this new request will be treated when the
+		   current one is done */
+		return;
+	/* fdc_busy cannot be set by an interrupt or a bh */
+	floppy_grab_irq_and_dma();
+	fdc_busy=1;
+	redo_fd_request();
+}
+
+/*
+ * 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 result;
+
+	result=0;
+	LOCK_FDC(drive,interruptible);
+	switch(arg){
+	case FD_RESET_ALWAYS:
+		FDCS->reset=1;
+		break;
+	case FD_RESET_IF_RAWCMD:
+		if(FDCS->rawcmd == 2 )
+			reset_fdc_info(1);
+		break;
+	}
+	if ( FDCS->reset ){
+		cont = &reset_cont;
+		timer_table[FLOPPY_TIMER].expires = jiffies + 5;
+		timer_active |= 1 << FLOPPY_TIMER;
+		CALL(result=wait_til_done(reset_fdc,interruptible));
+	}
+	if ( UDRS->track == PROVEN_ABSENT )
+		UDRS->track = NEED_2_RECAL;
+	redo_fd_request();
+	return result;
+}
+
+/*
+ * 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;
+}
+
+#define COPYOUT(x) (fd_copyout( (void *)param, &(x), sizeof(x)))
+#define COPYIN(x) (memcpy_fromfs( &(x), (void *) param, sizeof(x)),0)
+
+static 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 struct cont_t raw_cmd_cont={
+	success_and_wakeup,
+	failure_and_wakeup,
+	generic_failure,
+	generic_done };
+
+static int raw_cmd_ioctl(int drive, void *param)
+{
+	int i, count, ret;
+
+	if ( FDCS->rawcmd <= 1 )
+		FDCS->rawcmd = 1;
+	for ( i= 0; i < N_DRIVE; i++){
+		if ( FDC(i) != fdc)
+			continue;
+		if ( i == drive ){
+			if ( drive_state[i].fd_ref > 1 ){
+				FDCS->rawcmd = 2;
+				break;
+			}
+		} else if ( drive_state[i].fd_ref ){
+			FDCS->rawcmd = 2;
+			break;
+		}
+	}
+
+	if(FDCS->reset)
+		return -EIO;
+
+	COPYIN(raw_cmd);
+	raw_cmd.rate &= 0x03;
+	count = raw_cmd.length;
+	if (raw_cmd.flags & (FD_RAW_WRITE | FD_RAW_READ)){
+		if(count > max_buffer_sectors * 1024 )
+			return -ENOMEM;
+		buffer_track = -1;
+	}
+	if ( raw_cmd.flags & FD_RAW_WRITE ){
+		i = verify_area(VERIFY_READ, raw_cmd.data, count );
+		if (i)
+			return i;
+		memcpy_fromfs(floppy_track_buffer, raw_cmd.data, count);
+	}
+
+	current_addr = floppy_track_buffer;
+	cont = &raw_cmd_cont;
+	CALL(ret=wait_til_done(floppy_start,1));
+	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(drive);
+
+	if ( !ret && !FDCS->reset ){
+		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);
+	} else
+		ret = -EIO;
+
+	DRS->track = NO_TRACK;
+	if ( ret )
+		return ret;
+
+	if ( raw_cmd.flags & FD_RAW_READ ){
+		i=fd_copyout( raw_cmd.data, floppy_track_buffer, count);
+		if (i)
+			return i;
+	}
+
+	return COPYOUT(raw_cmd);
+}
+
+static int invalidate_drive(int rdev)
+{
+	/* invalidate the buffer track to force a reread */
+	set_bit( DRIVE(rdev), &fake_change);
+	redo_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 IOCTL_ALLOWED (filp && (filp->f_mode & IOCTL_MODE_BIT))
+
+	struct floppy_struct newparams;
+	struct format_descr tmp_format_req;
+	int i,device,drive,type,cnt;
+	struct floppy_struct *this_floppy;
+	char *name;
+
+	device = inode->i_rdev;
+	switch (cmd) {
+		RO_IOCTLS(device,param);
+	}
+	type = TYPE(device);
+	drive = DRIVE(device);
+	switch (cmd) {
+	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_fs_byte(name[cnt],
+				    ((char*)param)+cnt);
+			if ( ! *name )
+				break;
+		}
+		return 0;
+	case FDGETMAXERRS:
+		return COPYOUT(UDP->max_errors);
+	case FDGETPRM:
+		if (type)
+			this_floppy = &floppy_type[type];
+		else if ((this_floppy = current_type[drive]) ==
+			 NULL)
+			return -ENODEV;
+		return COPYOUT(this_floppy[0]);
+	case FDPOLLDRVSTAT:
+		check_disk_change(device);
+		/* fall through */
+	case FDGETDRVSTAT:
+		return COPYOUT(*UDRS);
+	case FDGETFDCSTAT:
+		return COPYOUT(*UFDCS);
+	case FDGETDRVPRM:
+		return COPYOUT(*UDP);
+	case FDWERRORGET:
+		return COPYOUT(*UDRWE);
+	}
+
+	if (!IOCTL_ALLOWED)
+		return -EPERM;
+
+	switch (cmd) {
+	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 FDRAWCMD:
+		if (type)
+			return -EINVAL;
+		LOCK_FDC(drive,1);
+		set_floppy(device);
+		CALL(i = raw_cmd_ioctl(drive, (void *) param));
+		redo_fd_request();
+		return i;
+	case FDFMTTRK:
+		if (UDRS->fd_ref != 1)
+			return -EBUSY;
+		if (UDRS->track == PROVEN_ABSENT)
+			return -ENXIO;
+		COPYIN(tmp_format_req);
+		return do_format(device, &tmp_format_req);
+	case FDSETMAXERRS:
+		return COPYIN(UDP->max_errors);
+	case FDFMTBEG:
+		return 0;
+	case FDCLRPRM:
+		LOCK_FDC(drive,1);
+		current_type[drive] = NULL;
+		floppy_sizes[drive] = 2;
+		UDRS->keep_data = 0;
+		return invalidate_drive(device);
+	case FDFMTEND:
+	case FDFLUSH:
+		LOCK_FDC(drive,1);
+		return invalidate_drive(device);
+	case FDSETPRM:
+	case FDDEFPRM:
+		COPYIN(newparams);
+		/* sanity checking for parameters.*/
+		if(newparams.sect <= 0 ||
+		   newparams.head <= 0 ||
+		   newparams.track <= 0 ||
+		   newparams.track >
+		   UDP->tracks>>newparams.stretch)
+			return -EINVAL;
+		if ( type){
+			if ( !suser() )
+				return -EPERM;
+			LOCK_FDC(-1,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]=
+#ifdef HAVE_2_CONTROLLERS
+					floppy_sizes[cnt+0x80]=
+#endif
+						floppy_type[type].size>>1;
+			redo_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*/
+			raw_cmd.flags = 0;
+			start_motor();
+		}
+		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
+			redo_fd_request();
+		return 0;
+	case FDRESET:
+		return user_reset_fdc( drive, (int)param, 1);
+	case FDMSGON:
+		UDP->flags |= FTD_MSG;
+		return 0;
+	case FDMSGOFF:
+		UDP->flags &= ~FTD_MSG;
+		return 0;
+	case FDSETEMSGTRESH:
+		UDP->max_errors.reporting =
+			(unsigned short) (param & 0x0f);
+		return 0;
+	case FDTWADDLE:
+		LOCK_FDC(drive,1);
+		twaddle();
+		redo_fd_request();
+	}
+	if ( ! suser() )
+		return -EPERM;
+	switch(cmd){
+	case FDSETDRVPRM:
+		return COPYIN(*UDP);
+	default:
+		return -EINVAL;
+	}
+	return 0;
+#undef IOCTL_ALLOWED
+}
+
+static void set_base_type(int drive, int code)
+{
+	if(code>0 && code <= NUMBER(default_drive_params)+1) {
+		memcpy((char *)UDP,
+			(char *)(&default_drive_params[code-1].params),
+			sizeof(struct floppy_drive_params));
+		printk("fd%d is %s", drive, default_drive_params[code-1].name);
+		return;
+	}
+	else if (!code)
+		printk("fd%d is not installed", drive);
+	else
+		printk("fd%d is unknown type %d", drive, code);
+}
+
+static void config_types(void)
+{
+	int drive;
+
+	for (drive=0; drive<N_DRIVE ; drive++){
+	  	/* default type for unidentifiable drives */
+	  	memcpy((char *) UDP, (char *) (&default_drive_params->params),
+	  		sizeof( struct floppy_drive_params ));
+	}
+	printk("Floppy drive(s): ");
+	for(drive=0; drive<no_floppies; drive++)
+	{
+	  set_base_type(drive, 1+4);
+	  if(drive < no_floppies-1)
+	    printk(", ");
+	}
+	printk("\n");
+}
+
+int floppy_is_wp( int minor)
+{
+	check_disk_change(minor + (MAJOR_NR << 8));
+	return ! ( drive_state[ DRIVE(minor) ].flags & FD_DISK_WRITABLE );
+}
+
+
+#define WRAPPER(op) \
+static int floppy_##op(struct inode * inode, struct file * filp, \
+		     char * buf, int count) \
+{ \
+	check_disk_change(inode->i_rdev); \
+	if ( drive_state[DRIVE(inode->i_rdev)].track == PROVEN_ABSENT ) \
+ 		return -ENXIO; \
+	if ( test_bit(DRIVE(inode->i_rdev),&changed_floppies)) \
+		return -ENXIO; \
+	return block_##op(inode, filp, buf, count); \
+}
+
+WRAPPER(read)
+WRAPPER(write)
+
+static void floppy_release(struct inode * inode, struct file * filp)
+{
+	int drive;
+
+	drive = DRIVE(inode->i_rdev);
+
+	fsync_dev(inode->i_rdev);
+
+	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;
+
+	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)) )
+		return -ENXIO;
+
+	if (TYPE(inode->i_rdev) >= NUMBER(floppy_type))
+		return -ENXIO;
+
+	if ((filp->f_mode & 3)  &&
+	    UDRS->track == PROVEN_ABSENT )
+		return -ENXIO;
+
+	old_dev = UDRS->fd_device;
+	if (UDRS->fd_ref && old_dev != inode->i_rdev)
+		return -EBUSY;
+
+	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++;
+
+	UDRS->fd_device = 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 (UFDCS->rawcmd == 1)
+	       UFDCS->rawcmd = 2;
+
+	if (filp->f_flags & O_NDELAY)
+		return 0;
+
+	if (filp->f_mode && UDRS->track == PROVEN_ABSENT )
+		RETERR(ENXIO);
+
+	if (user_reset_fdc(drive, FD_RESET_IF_NEEDED,0))
+		RETERR(EIO);
+
+	if (filp->f_mode & 3) {
+		UDRS->last_checked = 0;
+		check_disk_change(inode->i_rdev);
+		if (test_bit(drive,&changed_floppies))
+			RETERR(ENXIO);
+	}
+
+	if (filp->f_mode && UDRS->track == PROVEN_ABSENT )
+		RETERR(ENXIO);
+
+	if ((filp->f_mode & 2) && !(UDRS->flags & 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(dev_t dev)
+{
+	int drive = DRIVE( dev );
+
+	if (MAJOR(dev) != MAJOR_NR) {
+		DPRINT("floppy_changed: not a floppy\n");
+		return 0;
+	}
+
+	if(test_bit(drive, &changed_floppies))
+		return 1;
+
+	if(UDRS->last_checked + UDP->checkfreq < jiffies){
+		lock_fdc(drive,0);
+		start_motor();
+		redo_fd_request();
+	}
+
+	if(test_bit(drive, &changed_floppies))
+		return 1;
+	if(test_bit(drive, &fake_change))
+		return 1;
+	return 0;
+}
+
+static struct cont_t poll_cont={
+	success_and_wakeup,
+	floppy_ready,
+	generic_failure,
+	generic_done };
+
+
+/* revalidate the floppy disk, i.e. trigger format autodetection by reading
+ * the bootblock (block 0). "Autodetection" is also needed to check wether
+ * there is a disk in the drive at all... Thus we also do it for fixed
+ * geometry formats */
+static int floppy_revalidate(dev_t dev)
+{
+	struct buffer_head * bh;
+	int drive=DRIVE(dev);
+	int cf;
+
+	cf = test_bit(drive, &changed_floppies);
+	if(cf || test_bit(drive, &fake_change)){
+		lock_fdc(drive,0);
+		cf = test_bit(drive, &changed_floppies);
+		if(! (cf || test_bit(drive, &fake_change))){
+			redo_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);
+		clear_bit(drive, &changed_floppies);
+		if(cf){
+			UDRS->generation++;
+			if(!current_type[drive] && !TYPE(dev)){
+				/* auto-sensing */
+				if (!(bh = getblk(dev,0,1024))){
+					redo_fd_request();
+					return 1;
+				}
+				if ( bh && ! bh->b_uptodate)
+					ll_rw_block(READ, 1, &bh);
+				redo_fd_request();
+				wait_on_buffer(bh);
+				brelse(bh);
+				return 0;
+			} else {
+				/* no auto-sense, just clear dcl */
+				raw_cmd.flags=FD_RAW_NEED_SEEK|FD_RAW_NEED_DISK;
+				raw_cmd.track=0;
+				raw_cmd.cmd_count=0;
+				cont = &poll_cont;
+				wait_til_done(floppy_ready,0);
+			}
+		}
+		redo_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 */
+} /* fdc_init */
+
+void floppy_init(void)
+{
+	int i;
+
+	sti();
+
+	if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
+		printk("Unable to get major %d for floppy\n",MAJOR_NR);
+		return;
+	}
+
+	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;
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	hardsect_size[MAJOR_NR] = fd_sectsizes;
+	timer_table[FLOPPY_TIMER].fn = floppy_shutdown;
+	timer_active &= ~(1 << FLOPPY_TIMER);
+	config_types();
+
+	for(i = 0; i<256; i++)
+		fd_sectsizes[i] = 1024;			/* patch for msdos driver */
+
+	fdc_state[0].address = 0x3f0;
+#if N_FDC > 1
+	fdc_state[1].address = 0x370;
+#endif
+	for (i = 0 ; i < N_FDC ; i++) {
+		fdc = i;
+		FDCS->dtr = -1;
+		FDCS->dor = 0;
+		FDCS->reset = 0;
+		FDCS->version = FDC_NONE;
+		set_dor(fdc, ~0, 0xc );
+	}
+
+	/* initialise drive state */
+	for (i = 0; i < N_DRIVE ; i++) {
+		current_drive = i;
+		DRS->flags = FD_VERIFY | FD_DISK_NEWCHANGE;
+		DRS->generation = 0;
+		DRS->keep_data = 0;
+		DRS->fd_ref = 0;
+		DRS->fd_device = 0;
+		DRWE->write_errors = 0;
+		DRWE->first_error_sector = 0;
+		DRWE->first_error_generation = 0;
+		DRWE->last_error_sector = 0;
+		DRWE->last_error_generation = 0;
+		DRWE->badness = 0;
+	}
+
+	floppy_grab_irq_and_dma();
+	for (i = 0 ; i < N_FDC ; i++) {
+		fdc = i;
+		FDCS->rawcmd = 2;
+		if(user_reset_fdc(-1,FD_RESET_IF_NEEDED,0))
+			continue;
+		/* Try to determine the floppy controller type */
+		FDCS->version = get_fdc_version();
+		if (FDCS->version == FDC_NONE)
+			continue;
+
+		/* 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;
+	current_drive = 0;
+	floppy_release_irq_and_dma();
+	initialising=0;
+}
+
+static int floppy_grab_irq_and_dma(void)
+{
+	int i;
+	cli();
+	if (usage_count++){
+		sti();
+		return 0;
+	}
+	sti();
+
+	for(i=0; i< N_FDC; i++){
+		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, "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;
+	}
+	enable_irq(FLOPPY_IRQ);
+	return 0;
+}
+
+static void floppy_release_irq_and_dma(void)
+{
+	int i;
+	cli();
+	if (--usage_count){
+		sti();
+		return;
+	}
+	sti();
+	disable_dma(FLOPPY_DMA);
+	free_dma(FLOPPY_DMA);
+	disable_irq(FLOPPY_IRQ);
+	free_irq(FLOPPY_IRQ);
+	/* switch off dma gates */
+	for(i=0; i< N_FDC; i++)
+		set_dor(i, ~8, 0);
+}
diff -r -u -N linux.orig/arch/arm/drivers/block/genhd.c linux.arm/arch/arm/drivers/block/genhd.c
--- linux.orig/arch/arm/drivers/block/genhd.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/block/genhd.c	Fri Oct 27 23:14:43 1995
@@ -0,0 +1,295 @@
+/*
+ *  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
+ */
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+
+#ifndef __ARM__
+#define __ARM__
+#endif
+
+#ifndef __ARM__
+struct gendisk *gendisk_head = NULL;
+#else
+struct gendisk *gendisk_head;
+#endif
+
+static int current_minor;
+extern int *blk_size[];
+extern void rd_load(void);
+extern int ramdisk_size;
+
+#ifndef __ARM__
+/*
+ * 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, int dev)
+{
+	struct buffer_head *bh;
+	struct partition *p;
+	unsigned long first_sector, this_sector;
+	int mask = (1 << hd->minor_shift) - 1;
+
+	first_sector = hd->part[MINOR(dev)].start_sect;
+	this_sector = first_sector;
+
+	while (1) {
+		if ((current_minor & mask) >= (4 + hd->max_p))
+			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) {
+			p = (struct partition *) (0x1BE + bh->b_data);
+		/*
+		 * Process the first entry, which should be the real
+		 * data partition.
+		 */
+			if (p->sys_ind == EXTENDED_PARTITION ||
+			    !(hd->part[current_minor].nr_sects = p->nr_sects))
+				goto done;  /* shouldn't happen */
+			hd->part[current_minor].start_sect = this_sector + p->start_sect;
+			printk(" %s%c%d", hd->major_name,
+				'a'+(current_minor >> hd->minor_shift),
+				mask & current_minor);
+			current_minor++;
+			p++;
+		/*
+		 * Process the second entry, which 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 real
+		 * data partition.
+		 */
+			if (p->sys_ind != EXTENDED_PARTITION ||
+			    !(hd->part[current_minor].nr_sects = p->nr_sects))
+				goto done;  /* no more logicals in this partition */
+			hd->part[current_minor].start_sect = first_sector + p->start_sect;
+			this_sector = first_sector + p->start_sect;
+			dev = ((hd->major) << 8) | current_minor;
+			brelse(bh);
+		} else
+			goto done;
+	}
+done:
+	brelse(bh);
+}
+
+static void check_partition(struct gendisk *hd, unsigned int dev)
+{
+	static int first_time = 1;
+	int i, minor = current_minor;
+	struct buffer_head *bh;
+	struct partition *p;
+	unsigned long first_sector;
+	int mask = (1 << hd->minor_shift) - 1;
+
+	if (first_time)
+		printk("Partition check:\n");
+	first_time = 0;
+	first_sector = hd->part[MINOR(dev)].start_sect;
+	if (!(bh = bread(dev,0,1024))) {
+		printk("  unable to read partition table of device %04x\n",dev);
+		return;
+	}
+	printk("  %s%c:", hd->major_name, 'a'+(minor >> hd->minor_shift));
+	current_minor += 4;  /* first "extra" minor */
+	if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
+		p = (struct partition *) (0x1BE + bh->b_data);
+		for (i=1 ; i<=4 ; minor++,i++,p++) {
+			if (!(hd->part[minor].nr_sects = p->nr_sects))
+				continue;
+			hd->part[minor].start_sect = first_sector + p->start_sect;
+			printk(" %s%c%d", hd->major_name,'a'+(minor >> hd->minor_shift), i);
+			if ((current_minor & 0x3f) >= 60)
+				continue;
+			if (p->sys_ind == EXTENDED_PARTITION) {
+				printk(" <");
+				extended_partition(hd, (hd->major << 8) | minor);
+				printk(" >");
+			}
+		}
+		/*
+		 * check for Disk Manager partition table
+		 */
+		if (*(unsigned short *) (bh->b_data+0xfc) == 0x55AA) {
+			p = (struct partition *) (0x1BE + bh->b_data);
+			for (i = 4 ; i < 16 ; i++, current_minor++) {
+				p--;
+				if ((current_minor & mask) >= mask-2)
+					break;
+				if (!(p->start_sect && p->nr_sects))
+					continue;
+				hd->part[current_minor].start_sect = p->start_sect;
+				hd->part[current_minor].nr_sects = p->nr_sects;
+				printk(" %s%c%d", hd->major_name,
+					'a'+(current_minor >> hd->minor_shift),
+					current_minor & mask);
+			}
+		}
+	} else
+		printk(" bad partition table");
+	printk("\n");
+	brelse(bh);
+}
+#else
+/* 'ARC hard drives' minor allocation:
+ *   physical hd	= minor >> hd->minor_shift
+ *   image file		= (minor-1) & 3
+ *   extra file		= image file + n<<2
+ */
+
+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);
+			printk(" %s%c%d", hd->major_name,'a'+(minor >> hd->minor_shift), minor);
+		}
+		printk(" >");
+	}
+#undef GET_WORD
+}
+
+extern int image_allocate_list(int dev,int minor,struct hd_struct *);
+extern void image_release_dev(int dev);
+
+void check_imagefiles(struct gendisk *hd, int real_dev)
+{
+	static int first_time = 1;
+	int minor = current_minor;
+	int i,p = 0;
+
+	if(first_time)
+		printk("Image file check:\n");
+	first_time = 0;
+
+	current_minor += 4;  /* first "extra" minor */
+
+	for (i=1 ; i<=4 ; minor++,i++) {
+		int len;
+		len = image_allocate_list(real_dev, minor, &hd->part[minor]);
+		if(len && !p)
+		{
+			printk("  %s%c:", hd->major_name, 'a'+(minor >> hd->minor_shift));
+			p = 1;
+		}
+		if(len)
+		{
+			printk(" %s%c%d", hd->major_name,'a'+(minor >> hd->minor_shift), i);
+/* Now scan it for pc partitions */
+			pc_check_hd(hd, minor);
+		}
+	}
+	image_release_dev(real_dev);
+	printk("\n");
+}
+#endif
+/* 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 start = drive<<dev->minor_shift;
+	int j = start + dev->max_p;
+	int major = dev->major << 8;
+
+	current_minor = 1+(drive<<dev->minor_shift);
+#ifndef __ARM__
+	check_partition(dev, major+(drive<<dev->minor_shift));
+#else
+	check_imagefiles(dev, major+(drive<<dev->minor_shift));
+#endif
+
+	for (i=start ; i < j ; i++)
+		dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
+}
+
+static void setup_dev(struct gendisk *dev)
+{
+	int i;
+	int j = dev->max_nr * dev->max_p;
+	int major = dev->major << 8;
+	int drive;
+
+
+	for (i = 0 ; i < j; i++)  {
+		dev->part[i].start_sect = 0;
+		dev->part[i].nr_sects = 0;
+	}
+	dev->init();
+	for (drive=0 ; drive<dev->nr_real ; drive++) {
+		current_minor = 1+(drive<<dev->minor_shift);
+#ifndef __ARM__
+		check_partition(dev, major+(drive<<dev->minor_shift));
+#else
+		check_imagefiles(dev, major+(drive<<dev->minor_shift));
+#endif
+	}
+	for (i=0 ; i < j ; i++)
+		dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
+	blk_size[dev->major] = dev->sizes;
+}
+
+void device_setup(void)
+{
+	struct gendisk *p;
+	int nr=0;
+
+	for (p = gendisk_head ; p ; p=p->next) {
+		setup_dev(p);
+		nr += p->nr_real;
+	}
+
+	if (ramdisk_size)
+		rd_load();
+}
diff -r -u -N linux.orig/arch/arm/drivers/block/hd.c linux.arm/arch/arm/drivers/block/hd.c
--- linux.orig/arch/arm/drivers/block/hd.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/block/hd.c	Fri Oct 27 23:14:44 1995
@@ -0,0 +1,1231 @@
+/*
+ *  linux/arch/arm/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).
+ */
+
+/* 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>
+
+#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);
+int issue_request(int dev, unsigned int block, unsigned int nsect, struct request *current);
+#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->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->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->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->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->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.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.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->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->dev < 0) return;
+	if (DEVICE_INTR)
+		return;
+repeat:
+	timer_active &= ~(1<<HD_TIMER);
+	sti();
+	INIT_REQUEST;
+	if (reset) {
+		cli();
+		reset_hd();
+		return;
+	}
+	dev = MINOR(CURRENT->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->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->dev,CURRENT->cmd))
+			goto repeat;
+		frag_count = image_file_map(CURRENT->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)
+{
+	unsigned int dev, block, nsect;
+
+	dev = MINOR(current->dev);
+	frag_pos++;
+	if(frag_pos >= MAX_FRAGS)
+	{
+		block = current->sector;
+		block += hd[dev].start_sect;
+		nsect = current->nr_sectors;
+		if(!image_file_check(current->dev,current->cmd))
+		{
+			end_request(0);
+			hd_request();
+			return;
+		}
+		frag_count = image_file_map(current->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))
+	{
+		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)
+{
+	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->cmd == READ)?"read":"writ",
+		cyl, head, sec, nsect, (unsigned long) current->buffer,current);
+#endif
+	frag_sectors = nsect;
+	if (!unmask_intr[dev])
+		cli();
+	if (current->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->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->buffer,256);
+if(d == 1 && block < 10) while(1);
+#else
+if(d == 1)
+		printk("write: buffer = %p (%08lX)\n", current->buffer, *(unsigned long *)current->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(void);
+
+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.
+ */
+int no_hds;
+static void hd_geninit(void)
+{
+	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 */
+};
+
+unsigned long hd_init(unsigned long mem_start, unsigned long mem_end)
+{
+	if (register_blkdev(MAJOR_NR,"hd",&hd_fops)) {
+		printk("hd: unable to get major %d for harddisk\n",MAJOR_NR);
+		return mem_start;
+	}
+	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 mem_start;
+}
+
+#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 -r -u -N linux.orig/arch/arm/drivers/block/hd.diff linux.arm/arch/arm/drivers/block/hd.diff
--- linux.orig/arch/arm/drivers/block/hd.diff	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/block/hd.diff	Fri Oct 27 23:14:52 1995
@@ -0,0 +1,2672 @@
+--- /usr/src/linux/arch/arm/drivers/block/floppy.c	Thu Aug 31 22:19:59 1995
++++ /usr/src/linux/drivers/block/floppy.c	Sun Sep 24 10:23:30 1995
+@@ -4,32 +4,6 @@
+  *  Copyright (C) 1991, 1992  Linus Torvalds
+  *  Copyright (C) 1993, 1994  Alain Knaff
+  */
+-
+-/* Configuration */
+-/* The following does some extra sanity checks */
+-#define SANITY
+-
+-/* 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. */
+-#define ALLOWED_DRIVE_MASK 0x0F
+-
+-/* Undefine the following if you have to floppy disk controllers:
+- * This works at least for me; if you get two controllers working, with
+- * drives attached to both, please mail me: Alain.Knaff@imag.fr */
+-/* #define HAVE_2_CONTROLLERS */
+-
+-
+-/* Define the following if you don't like that your drives seek audibly
+- * after a disk change (but it may not work correctly for everybody)
+- */
+-#define SILENT_DC_CLEAR
+-
+-
+-/* End of configuration */
+-
+ /*
+  * 02.12.91 - Changed to static variables to indicate need for reset
+  * and recalibrate. This makes some things easier (output_byte reset
+@@ -103,15 +77,39 @@
+  * format bug fixes, but unfortunately some new bugs too...
+  */
+ 
+-/* 1994/9/17 -- Koen Holtman -- added logging of physical floppy write
++/* 1994/9/17 -- Koen Holtman -- added logging of physical floppy write 
+  * errors to allow safe writing by specialized programs.
+  */
+-int no_floppies;
+-#define FLOPPY_IRQ 12
+-#define FLOPPY_DMA 2
++
++#define CONFIG_FLOPPY_SANITY
++#undef  CONFIG_FLOPPY_SILENT_DCL_CLEAR
++
++#define REALLY_SLOW_IO
++
+ #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;
++
++#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 6
++#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>
+@@ -125,6 +123,7 @@
+ #include <linux/string.h>
+ #include <linux/fcntl.h>
+ #include <linux/delay.h>
++#include <linux/mc146818rtc.h> /* CMOS defines */
+ 
+ #include <asm/dma.h>
+ #include <asm/irq.h>
+@@ -135,16 +134,24 @@
+ #define MAJOR_NR FLOPPY_MAJOR
+ #include "blk.h"
+ 
+-static unsigned int changed_floppies = 0xff, fake_change = 0;
++static unsigned int fake_change = 0;
+ static int initialising=1;
+ 
+-#ifdef HAVE_2_CONTROLLERS
++#define FLOPPY0_TYPE	((CMOS_READ(0x10) >> 4) & 15)
++#define FLOPPY1_TYPE	(CMOS_READ(0x10) & 15)
++
++/*
++ * Again, the CMOS information doesn't work on the alpha..
++ */
++#ifdef __alpha__
++#undef FLOPPY0_TYPE
++#undef FLOPPY1_TYPE
++#define FLOPPY0_TYPE 6
++#define FLOPPY1_TYPE 0
++#endif
++
+ #define N_FDC 2
+ #define N_DRIVE 8
+-#else
+-#define N_FDC 1
+-#define N_DRIVE 4
+-#endif
+ 
+ #define TYPE(x) ( ((x)>>2) & 0x1f )
+ #define DRIVE(x) ( ((x)&0x03) | (((x)&0x80 ) >> 5))
+@@ -156,22 +163,28 @@
+ #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 DPRINT(x) printk(DEVICE_NAME "%d: " x,current_drive)
+ 
+ #define DPRINT1(x,x1) \
+-printk(DEVICE_NAME "%d: " x,current_drive,(x1));
++printk(DEVICE_NAME "%d: " x,current_drive,(x1))
+ 
+ #define DPRINT2(x,x1,x2) \
+-printk(DEVICE_NAME "%d: " x,current_drive,(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));
++printk(DEVICE_NAME "%d: " x,current_drive,(x1),(x2),(x3))
+ 
+ /* read/write */
+ #define COMMAND raw_cmd.cmd[0]
+@@ -195,8 +208,9 @@
+ /*
+  * 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 3984
++#define MAX_DISK_SIZE 2 /* 3984*/
+ 
+ 
+ 
+@@ -208,13 +222,9 @@
+  * driver otherwise. It doesn't matter much for performance anyway, as most
+  * floppy accesses go through the track buffer.
+  */
+-#if 0
+ #define LAST_DMA_ADDR	(0x1000000)
+-#else
+-/* Everything has to go via dma buffer */
+-#define LAST_DMA_ADDR	(0x1)
+-#endif
+ #define K_64 (0x10000) /* 64 k */
++
+ /*
+  * globals used by 'result()'
+  */
+@@ -230,6 +240,9 @@
+ #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.
+  */
+@@ -243,49 +256,44 @@
+   |     |   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, 100, 300,  0, 2, 5,  80, 3*HZ, 20, {3,1,2,0,2}, 0,
+-      0, { 7, 4, 8, 2, 1, 5, 3,10}, 150, 0 }, "unknown" },
+-
+-{{1,  300, 16, 16, 8000, 100, 300,  0, 2, 5,  40, 3*HZ, 17, {3,1,2,0,2}, 0,
+-      0, { 1, 0, 0, 0, 0, 0, 0, 0}, 150, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/
+-
+-{{2,  500, 16, 16, 6000,  40, 300, 14, 2, 6,  83, 3*HZ, 17, {3,1,2,0,2}, 0,
+-      0, { 2, 5, 6,23,10,20,11, 0}, 150, 2 }, "1.2M" }, /*5 1/4 HD AT*/
+-
+-{{3,  250, 16, 16, 3000, 100, 300,  0, 2, 5,  83, 3*HZ, 20, {3,1,2,0,2}, 0,
+-      0, { 4,22,21,30, 3, 0, 0, 0}, 150, 4 }, "720k" }, /*3 1/2 DD*/
+-
+-#if 0
+-{{4,  500, 16, 16, 4000,  40, 300, 10, 2, 5,  83, 3*HZ, 20, {3,1,2,0,2}, 0,
+-      0, { 7, 4,25,22,31,21,29,11}, 150, 7 }, "1.44M" }, /*3 1/2 HD*/
+-#else
+-{{4,  500, 16, 16, 3000,  40, 300, 10, 2, 5,  83, 3*HZ, 20, {3,1,2,0,0}, FTD_MSG,
+-      0, { 7, 4,25,22,31,21,29,11}, 150, 7 }, "1.44M" }, /*3 1/2 HD*/
+-#endif
+-
+-{{5, 1000, 15,  8, 3000,  40, 300, 10, 2, 5,  83, 3*HZ, 40, {3,1,2,0,2}, 0,
+-      0, { 7, 8, 4,25,28,22,31,21}, 150, 8 }, "2.88M AMI BIOS" }, /*3 1/2 ED*/
+-
+-{{6, 1000, 15,  8, 3000,  40, 300, 10, 2, 5,  83, 3*HZ, 40, {3,1,2,0,2}, 0,
+-      0, { 7, 8, 4,25,28,22,31,21}, 150, 8 }, "2.88M" } /*3 1/2 ED*/
+-/*    |  ---autodetected formats--   |   |      |
+-      read_track                     |   |    Name printed when booting
+-                                     |  Native format
++  |     |   |   |     |       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*/
++
++{{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*/
++
++{{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 volatile drive_state[N_DRIVE];
+-static struct floppy_write_errors volatile write_errors[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;
+ 
+ /*
+@@ -295,16 +303,6 @@
+  * types (ie 360kB diskette in 1.2MB drive etc). Others should
+  * be self-explanatory.
+  */
+-/*
+-             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      */
+@@ -348,11 +346,8 @@
+ 
+ /* 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
+-#ifdef HAVE_2_CONTROLLERS
+-	,
+-	NULL, NULL, NULL, NULL
+-#endif
+ };
+ 
+ /*
+@@ -362,6 +357,7 @@
+ 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
+@@ -380,6 +376,9 @@
+ 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 _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;
+@@ -400,11 +399,7 @@
+  * corrupted/lost. Alignment of these is enforced in boot/head.S.
+  * Note that you must not change the sizes below without updating head.S.
+  */
+-#if 0
+ extern char floppy_track_buffer[512*2*MAX_BUFFER_SECTORS];
+-#else
+-char floppy_track_buffer[512*2*MAX_BUFFER_SECTORS];
+-#endif
+ #define max_buffer_sectors MAX_BUFFER_SECTORS
+ 
+ int *errors;
+@@ -417,10 +412,10 @@
+ done_f done; /* this is called to say if the operation has succeeded/failed */
+ } *cont;
+ 
++static void floppy_ready(void);
+ static void floppy_start(void);
+-static void redo_fd_request(void);
++static void process_fd_request(void);
+ static void recalibrate_floppy(void);
+-static void seek_floppy(void);
+ static void floppy_shutdown(void);
+ 
+ static int floppy_grab_irq_and_dma(void);
+@@ -444,7 +439,10 @@
+ #define NO_TRACK -1
+ #define NEED_1_RECAL -2
+ #define NEED_2_RECAL -3
+-#define PROVEN_ABSENT -4
++
++/* */
++static int usage_count = 0;
++
+ 
+ /* buffer related variables */
+ static int buffer_track = -1;
+@@ -454,10 +452,10 @@
+ 
+ /* fdc related variables, should end up in a struct */
+ static struct floppy_fdc_state fdc_state[N_FDC];
+-int fdc; /* current fdc */
++static int fdc; /* current fdc */
+ 
+ static struct floppy_struct * floppy = floppy_type;
+-static unsigned char current_drive = 255;
++static unsigned char current_drive = 0;
+ static long current_count_sectors = 0;
+ static char *current_addr = 0;
+ static unsigned char sector_t; /* sector in track */
+@@ -467,20 +465,6 @@
+ #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.
+- */
+-
+-unsigned char floppy_selects1[]={ 0x10, 0x21, 0x23, 0x33 };
+-unsigned char floppy_selects2[]={ 0   , 0   , 0   , 0    };
+-
+-int fd_sectsizes[256];
+-
+-/*
+  * Debugging
+  * =========
+  */
+@@ -509,14 +493,17 @@
+ 
+ /*
+  * disk change.
+- * This routine is responsible for maintaining the changed_floppies flag,
++ * 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'
+- * changed_floppies is set under two conditions:
++ * 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.
++ * 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
+@@ -531,74 +518,102 @@
+ 
+ static int disk_change(int drive)
+ {
+-	if(jiffies < DP->select_delay + DRS->select_date)
+-		udelay(20000);
+-
+-	if(inb_p(FD_DIR) & 0x80){
+-		UDRS->flags |= FD_VERIFY; /* verify write protection */
++	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
+ 
+-		if(UDRS->maxblock || /* disk change check */
+-		   !(UDRS->flags & FD_DISK_NEWCHANGE)){/* disk presence check */
+-			/* mark it changed or absent */
+-			set_bit(drive,&changed_floppies);
++#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 ((DP->flags & FTD_MSG) &&
++				if ((UDP->flags & FTD_MSG) &&
+ 				    current_type[drive] != NULL)
+ 					DPRINT("Disk type is undefined after "
+ 					       "disk change\n");
+ 				current_type[drive] = NULL;
+-				floppy_sizes[drive] = MAX_DISK_SIZE;
++				floppy_sizes[DRIVE(current_drive) + (FDC(current_drive) << 7)] = MAX_DISK_SIZE;
+ 			}
+ 		}
+-		UDRS->flags |= FD_DISK_NEWCHANGE;
++		/*USETF(FD_DISK_NEWCHANGE);*/
+ 		return 1;
+ 	} else {
+ 		UDRS->last_checked=jiffies;
+-		UDRS->flags &= ~FD_DISK_NEWCHANGE;
+-		return 0;
++		UCLEARF(FD_DISK_NEWCHANGE);
+ 	}
++	return 0;
+ }
+ 
+-static void arm_set_dor(int dor)
++static inline int is_selected(int dor, int unit)
+ {
+-	if(dor & 0xf0)
+-		outb_p((dor & 0x0c) | floppy_selects1[dor & 3], FD_DOR);
+-	else
+-		outb_p((dor & 0x0c), FD_DOR);
++	return ( (dor  & (0x10 << unit)) && (dor &3) == unit);
+ }
+ 
+-static int locked=0;
+ static int set_dor(int fdc, char mask, char data)
+ {
+ 	register unsigned char drive, unit, newdor,olddor;
+ 
+-	locked=1;
++	if(FDCS->address == -1)
++		return -1;
++
+ 	olddor = FDCS->dor;
+ 	newdor =  (olddor & mask) | data;
+ 	if ( newdor != olddor ){
+ 		unit = olddor & 0x3;
+-		drive = REVDRIVE(fdc,unit);
+-		if ( olddor & ( 0x10 << unit ))
++		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);
++		outb_p(newdor, FD_DOR);
++
++		unit = newdor & 0x3;
++		if(!is_selected(olddor, unit) && is_selected(newdor,unit)){
++			drive = REVDRIVE(fdc,unit);
++			UDRS->select_date = jiffies;
++		}
+ 	}
+-	locked=0;
++	if ( newdor & 0xf0 )
++		floppy_grab_irq_and_dma();
++	if( olddor & 0xf0 )
++		floppy_release_irq_and_dma();
+ 	return olddor;
+ }
+ 
+ static void twaddle(void)
+ {
+-	cli();
+-#if 0
++	if (DP->select_delay)
++		return;
+ 	outb_p(FDCS->dor & ~(0x10<<UNIT(current_drive)),FD_DOR);
+ 	outb_p(FDCS->dor, FD_DOR);
+-#else
+-	arm_set_dor(FDCS->dor);
+-#endif
+-	sti();
++	DRS->select_date = jiffies;
+ }
+ 
+ /* reset all driver information about the current fdc. This is needed after
+@@ -613,7 +628,6 @@
+ 	FDCS->rawcmd = 0;
+ 	for ( drive = 0; drive < N_DRIVE; drive++)
+ 		if (FDC(drive) == fdc &&
+-		    UDRS->track != PROVEN_ABSENT &&
+ 		    ( mode || UDRS->track != NEED_1_RECAL))
+ 			UDRS->track = NEED_2_RECAL;
+ }
+@@ -621,30 +635,28 @@
+ /* selects the fdc and drive, and enables the fdc's input/dma. */
+ static void set_fdc(int drive)
+ {
+-	if ( drive >= 0 ){
++	if (drive >= 0 && drive < N_DRIVE){
+ 		fdc = FDC(drive);
+ 		current_drive = drive;
+ 	}
+ 	set_dor(fdc,~0,8);
+-#ifdef HAVE_2_CONTROLLERS
+ 	set_dor(1-fdc, ~8, 0);
+-#endif
+ 	if ( FDCS->rawcmd == 2 )
+ 		reset_fdc_info(1);
+ 	if( inb_p(FD_STATUS) != STATUS_READY )
+ 		FDCS->reset = 1;
+ }
+ 
+-static int usage_count = 0;
+ /* 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();
++	if (!current->pid)
++		run_task_queue(&tq_timer);
+ 	cli();
+ 	while (fdc_busy && NO_SIGNAL)
+ 		interruptible_sleep_on(&fdc_wait);
+@@ -662,6 +674,10 @@
+ #define LOCK_FDC(drive,interruptible) \
+ if(lock_fdc(drive,interruptible)) return -EINTR;
+ 
++typedef void (*timeout_fn)(unsigned long);
++static struct timer_list fd_timeout ={ NULL, NULL, 0, 0, 
++					       (timeout_fn) floppy_shutdown };
++
+ /* unlocks the driver */
+ static inline void unlock_fdc(void)
+ {
+@@ -672,7 +688,7 @@
+ 		DPRINT1("device interrupt still active at FDC release: %p!\n",
+ 			DEVICE_INTR);
+ 	command_status = FD_COMMAND_NONE;
+-	timer_active &= ~(1 << FLOPPY_TIMER);
++	del_timer(&fd_timeout);
+ 	fdc_busy = 0;
+ 	floppy_release_irq_and_dma();
+ 	wake_up(&fdc_wait);
+@@ -683,47 +699,40 @@
+ {
+ 	unsigned char mask = ~(0x10 << UNIT(nr));
+ 
+-	if(locked)
+-		floppy_off(nr);
+-	else
+-		set_dor( FDC(nr), mask, 0 );
++	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 }
+-#ifdef HAVE_2_CONTROLLERS
+-	,
++	{ 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 }
+-#endif
+ };
+ 
+ /* schedules motor off */
+-static void floppy_off(unsigned int nr)
++static void floppy_off(unsigned int drive)
+ {
+ 	unsigned long volatile delta;
+-	register int fdc=FDC(nr);
++	register int fdc=FDC(drive);
+ 
+-	if( !(FDCS->dor & ( 0x10 << UNIT(nr))))
++	if( !(FDCS->dor & ( 0x10 << UNIT(drive))))
+ 		return;
+ 
+-	del_timer(motor_off_timer+nr);
++	del_timer(motor_off_timer+drive);
+ 
+ 	/* make spindle stop in a position which minimizes spinup time
+ 	 * next time */
+-	if ( drive_params[nr].rps ){
+-		delta = jiffies - drive_state[nr].first_read_date + HZ -
+-			drive_params[nr].spindown_offset;
+-		delta = (( delta * drive_params[nr].rps) % HZ ) /
+-			drive_params[nr].rps;
+-		motor_off_timer[nr].expires = drive_params[nr].spindown - delta;
++	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 = UDP->spindown - delta;
+ 	}
+-	add_timer(motor_off_timer+nr);
++	add_timer(motor_off_timer+drive);
+ }
+ 
+ /*
+@@ -734,36 +743,41 @@
+ static void scandrives(void)
+ {
+ 	int i, drive, saved_drive;
++	
++	if (DP->select_delay)
++		return;
+ 
+-	saved_drive = current_drive % N_DRIVE;
++	saved_drive = current_drive;
+ 	for(i=0; i< N_DRIVE; i++){
+ 		drive = (saved_drive + i + 1 ) % N_DRIVE;
+-		if ( UDRS->fd_ref == 0 )
++		if ( UDRS->fd_ref == 0 || UDP->select_delay != 0)
+ 			continue; /* skip closed drives */
+ 		set_fdc(drive);
+-		if (!(FDCS->dor & (0x10 << UNIT(drive))) ||
+-		    ((FDCS->dor & 0x3) != UNIT(drive)))
+-			UDRS->select_date = jiffies;
+ 		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 );
+ 	}
+-	current_drive = saved_drive;
++	set_fdc(saved_drive);
+ }
+ 
+-typedef void (*timeout_fn)(unsigned long);
+ 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 {
++	} else {		
+ 		del_timer(&fd_timer);
+ 		fd_timer.function = (timeout_fn) fd_watchdog;
+ 		fd_timer.expires = 10;
+@@ -778,7 +792,7 @@
+ }
+ 
+ /* waits for a delay (spinup or select) to pass */
+-static int wait_for_completion(int nr, int delay, timeout_fn function)
++static int wait_for_completion(int delay, timeout_fn function)
+ {
+ 	if ( FDCS->reset ){
+ 		reset_fdc(); /* do the reset during sleep to win time
+@@ -797,9 +811,50 @@
+ 	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 SANITY
++#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 ((!CURRENT ||
+ 	     CURRENT->buffer != current_addr ||
+ 	     raw_cmd.length > 512 * CURRENT->nr_sectors) &&
+@@ -817,7 +872,6 @@
+ 		FDCS->reset=1;
+ 		return;
+ 	}
+-#if 0
+ 	if ((long) current_addr % 512 ){
+ 		printk("non aligned address: %p\n", current_addr );
+ 		cont->done(0);
+@@ -832,7 +886,7 @@
+ 		FDCS->reset=1;
+ 		return;
+ 	}
+-#endif
++
+ #endif
+ 	cli();
+ 	disable_dma(FLOPPY_DMA);
+@@ -844,6 +898,7 @@
+ 	set_dma_count(FLOPPY_DMA, raw_cmd.length);
+ 	enable_dma(FLOPPY_DMA);
+ 	sti();
++	floppy_disable_hlt();
+ }
+ 
+ /* sends a command byte to the fdc */
+@@ -856,13 +911,13 @@
+ 		return -1;
+ 	for(counter = 0 ; counter < 10000 && !FDCS->reset ; counter++) {
+ 		status = inb_p(FD_STATUS) &(STATUS_READY|STATUS_DIR|STATUS_DMA);
++		if (!(status & STATUS_READY))
++			continue;
+ 		if (status == STATUS_READY){
+ 			outb_p(byte,FD_DATA);
+ 			return 0;
+-		}
+-		if (!(status & STATUS_READY))
+-			continue;
+-		break;
++		} else
++			break;
+ 	}
+ 	FDCS->reset = 1;
+ 	if ( !initialising )
+@@ -930,7 +985,7 @@
+ 		}
+ 	} else
+ 		perp_mode = 0;
+-
++			
+ 	if ( FDCS->perp_mode == perp_mode )
+ 		return;
+ 	if (FDCS->version >= FDC_82077_ORIG && FDCS->has_fifo) {
+@@ -979,7 +1034,7 @@
+ 		/* TODO: lock this in via LOCK during initialization */
+ 		output_byte(FD_CONFIGURE);
+ 		output_byte(0);
+-		output_byte(0x1A);	/* FIFO on, polling off, 10 byte threshold */
++		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;
+@@ -1042,22 +1097,23 @@
+  * NOTE: with 82072/82077 FDCs, changing the data rate requires a reissue
+  * of the specify command (i.e. using the fdc_specify function).
+  */
+-static void fdc_dtr(void)
++static int fdc_dtr(void)
+ {
+ 	/* If data rate not already set to desired value, set it. */
+ 	if ( raw_cmd.rate == FDCS->dtr)
+-		return;
+-
++		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.
++	 * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies)
+ 	 */
+-	udelay(5000);
+ 	FDCS->dtr = raw_cmd.rate;
++	return(wait_for_completion(jiffies+2,
++				   (timeout_fn) floppy_ready));
+ } /* fdc_dtr */
+ 
+ static void tell_sector(void)
+@@ -1077,8 +1133,7 @@
+ 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;
+@@ -1087,15 +1142,15 @@
+ 
+ 	/* check IC to find cause of interrupt */
+ 	switch ((ST0 & ST0_INTR)>>6) {
+-		case 1:	/* error occured during command execution */
++		case 1:	/* error occurred during command execution */
+ 			bad = 1;
+ 			if (ST1 & ST1_WP) {
+ 				DPRINT("Drive is write protected\n");
+-				DRS->flags &= ~FD_DISK_WRITABLE;
++				CLEARF(FD_DISK_WRITABLE);
+ 				cont->done(0);
+ 				bad = 2;
+ 			} else if (ST1 & ST1_ND) {
+-				DRS->flags |= FD_NEED_TWADDLE;
++				SETF(FD_NEED_TWADDLE);
+ 			} else if (ST1 & ST1_OR) {
+ 				if (DP->flags & FTD_MSG )
+ 					DPRINT("Over/Underrun - retrying\n");
+@@ -1128,11 +1183,8 @@
+ 
+ 			}
+ 			if ( ST2 & ST2_WC || ST2 & ST2_BC)
+-{
+-printk("Wrong cylinder!\n");
+ 				/* wrong cylinder => recal */
+ 				DRS->track = NEED_2_RECAL;
+-}
+ 			return bad;
+ 		case 2: /* invalid command given */
+ 			DPRINT("Invalid FDC command given!\n");
+@@ -1162,7 +1214,7 @@
+ 		flags |= FD_RAW_INTR;
+ 
+ 	if ((flags & FD_RAW_SPIN) && !(flags & FD_RAW_NO_MOTOR)){
+-		ready_date = DRS->spinup_date + DP->spinup;
++		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.
+@@ -1174,14 +1226,14 @@
+ 			function = (timeout_fn) setup_rw_floppy;
+ 
+ 		/* wait until the floppy is spinning fast enough */
+-		if (wait_for_completion(current_drive,ready_date,function))
++		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);
+ 
+@@ -1204,9 +1256,7 @@
+ 		fd_watchdog();
+ }
+ 
+-#ifdef SILENT_DC_CLEAR
+ static int blind_seek;
+-#endif
+ 
+ /*
+  * This is the routine called after every seek (or recalibrate) interrupt
+@@ -1217,9 +1267,6 @@
+ #ifdef DEBUGT
+ 	debugt("seek interrupt:");
+ #endif
+-#ifdef SILENT_DC_CLEAR
+-	set_dor(fdc, ~0,  (0x10 << UNIT(current_drive)));
+-#endif
+ 	if (inr != 2 || (ST0 & 0xF8) != 0x20 ) {
+ 		DPRINT("seek failed\n");
+ 		DRS->track = NEED_2_RECAL;
+@@ -1227,20 +1274,23 @@
+ 		cont->redo();
+ 		return;
+ 	}
+-	if (DRS->track >= 0 && DRS->track != ST1
+-#ifdef SILENT_DC_CLEAR
+-	    && !blind_seek
++	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
+-	    )
+-		DRS->flags &= ~FD_DISK_NEWCHANGE; /* effective seek */
++		CLEARF(FD_DISK_NEWCHANGE); /* effective seek */
++		DRS->select_date = jiffies;
++	}
+ 	DRS->track = ST1;
+-	DRS->select_date = jiffies;
+-	seek_floppy();
++	floppy_ready();
+ }
+ 
+ static void check_wp(void)
+ {
+-	if (DRS->flags & FD_VERIFY) {
++	if (TESTF(FD_VERIFY)) {
+ 		/* check write protection */
+ 		output_byte( FD_GETSTATUS );
+ 		output_byte( UNIT(current_drive) );
+@@ -1248,10 +1298,18 @@
+ 			FDCS->reset = 1;
+ 			return;
+ 		}
+-		DRS->flags &= ~(FD_VERIFY | FD_DISK_WRITABLE | FD_NEED_TWADDLE);
+-
++		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))
+-			DRS->flags |= FD_DISK_WRITABLE;
++			SETF(FD_DISK_WRITABLE);
++		else
++			CLEARF(FD_DISK_WRITABLE);
+ 	}
+ }
+ 
+@@ -1259,16 +1317,22 @@
+ {
+ 	int track;
+ 
+-#ifdef SILENT_DC_CLEAR
+ 	blind_seek=0;
++
++#ifdef DCL_DEBUG
++	if (DP->flags & FD_DEBUG){
++		DPRINT("calling disk change from seek\n");
++	}
+ #endif
+-	disk_change(current_drive);
+-	if ((raw_cmd.flags & FD_RAW_NEED_DISK) &&
+-	    test_bit(current_drive,&changed_floppies)){
++
++	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;
+@@ -1276,7 +1340,7 @@
+ 	if ( DRS->track <= NEED_1_RECAL ){
+ 		recalibrate_floppy();
+ 		return;
+-	} else if ((DRS->flags & FD_DISK_NEWCHANGE) &&
++	} 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
+@@ -1284,15 +1348,17 @@
+ 		if ( raw_cmd.track )
+ 			track = raw_cmd.track - 1;
+ 		else {
+-#ifdef SILENT_DC_CLEAR
+-			set_dor(fdc, ~ (0x10 << UNIT(current_drive)), 0);
+-			blind_seek = 1;
+-#endif
++			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)
++		if (raw_cmd.track != DRS->track &&
++		    (raw_cmd.flags & FD_RAW_NEED_SEEK))
+ 			track = raw_cmd.track;
+ 		else {
+ 			setup_rw_floppy();
+@@ -1300,23 +1366,13 @@
+ 		}
+ 	}
+ 
+-#ifndef SILENT_DC_CLEAR
+-	if ( !track && DRS->track >= 0 && DRS->track < 80 ){
+-		DRS->flags &= ~FD_DISK_NEWCHANGE;
+-		/* if we go to track 0 anyways, we can just as well use
+-		 * recalibrate */
+-		recalibrate_floppy();
+-	} else
+-#endif
+-	{
+-		SET_INTR(seek_interrupt);
+-		output_byte(FD_SEEK);
+-		output_byte(UNIT(current_drive));
+-		LAST_OUT(track);
++	SET_INTR(seek_interrupt);
++	output_byte(FD_SEEK);
++	output_byte(UNIT(current_drive));
++	LAST_OUT(track);
+ #ifdef DEBUGT
+-		debugt("seek command:");
++	debugt("seek command:");
+ #endif
+-	}
+ }
+ 
+ static void recal_interrupt(void)
+@@ -1328,18 +1384,13 @@
+ 		FDCS->reset = 1;
+ 	else if (ST0 & ST0_ECE) {
+ 	       	switch(DRS->track){
+-		case PROVEN_ABSENT:
+-#ifdef DEBUGT
+-			debugt("recal interrupt proven absent:");
+-#endif
+-			/* fall through */
+ 	       	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
++			 * error, as failing immediately might upset 
+ 			 * computers possessed by the Devil :-) */
+ 			cont->error();
+ 			cont->redo();
+@@ -1353,7 +1404,14 @@
+ 			 * not to move at recalibration is to be already at
+ 			 * track 0.) Clear the new change flag
+ 			 */
+-			DRS->flags &= ~FD_DISK_NEWCHANGE;
++#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
+@@ -1369,7 +1427,7 @@
+ 		}
+ 	} else
+ 		DRS->track = ST1;
+-	seek_floppy();
++	floppy_ready();
+ }
+ 
+ /*
+@@ -1381,34 +1439,41 @@
+ 	int i;
+ 	if ( initialising )
+ 		return;
+-	cli();
+-	DPRINT("unexpected interrupt\n");
+-	if ( inr >= 0 )
+-		for(i=0; i<inr; i++)
+-			printk("%d %x\n", i, reply_buffer[i] );
++	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;
+-		printk("sensei\n");
+-		for(i=0; i<inr; i++)
+-			printk("%d %x\n", i, reply_buffer[i] );
++		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 };
++struct tq_struct floppy_tq = 
++{ 0, 0, (void *) (void *) unexpected_floppy_interrupt, 0 };
+ 
+ /* interrupt handler */
+-static void floppy_interrupt(int unused, struct pt_regs *regs)
++static void floppy_interrupt(int irq, struct pt_regs * regs)
+ {
+ 	void (*handler)(void) = DEVICE_INTR;
+ 
++	floppy_enable_hlt();
+ 	CLEAR_INTR;
+-	if ( fdc >= N_FDC ){ /* we don't even know which FDC is the culprit */
+-		printk("floppy interrupt on bizarre fdc\n");
++	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);
+ 		return;
+ 	}
+ 	inr = result();
+@@ -1422,7 +1487,7 @@
+ 			inr = result();
+ 		} while ( (ST0 & 0x83) != UNIT(current_drive) && inr == 2);
+ 	}
+-	floppy_tq.routine = (void (*)(void *)) handler;
++	floppy_tq.routine = (void *)(void *) handler;
+ 	queue_task_irq(&floppy_tq, &tq_timer);
+ }
+ 
+@@ -1463,9 +1528,9 @@
+ 	if ( FDCS->version >= FDC_82077 )
+ 		outb_p(0x80 | ( FDCS->dtr &3), FD_STATUS);
+ 	else {
+-		arm_set_dor(FDCS->dor & ~0x04);
++		outb_p(FDCS->dor & ~0x04, FD_DOR);
+ 		udelay(FD_RESET_DELAY);
+-		arm_set_dor(FDCS->dor);
++		outb(FDCS->dor, FD_DOR);
+ 	}
+ }
+ 
+@@ -1481,9 +1546,11 @@
+ 	printk("floppy driver state\n");
+ 	printk("-------------------\n");
+ 	for(i=0; i<N_FDC; i++){
+-		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... */
++		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... */
++		}
+ 	}
+ 	printk("status=%x\n", inb_p(FD_STATUS));
+ 	printk("fdc_busy=%d\n", fdc_busy);
+@@ -1493,9 +1560,9 @@
+ 		printk("floppy_tq.routine=%p\n", floppy_tq.routine);
+ 	if(fd_timer.prev)
+ 		printk("fd_timer.function=%p\n", fd_timer.function);
+-	if( timer_active & (1 << FLOPPY_TIMER)){
+-		printk("timer_table=%p\n",timer_table[FLOPPY_TIMER].fn);
+-		printk("expires=%ld\n",timer_table[FLOPPY_TIMER].expires);
++	if(fd_timeout.prev){
++		printk("timer_table=%p\n",fd_timeout.function);
++		printk("expires=%ld\n",fd_timeout.expires);
+ 		printk("now=%ld\n",jiffies);
+ 	}
+ 	printk("cont=%p\n", cont);
+@@ -1507,30 +1574,28 @@
+ static void floppy_shutdown(void)
+ {
+ 	CLEAR_INTR;
+-	floppy_tq.routine = (void (*)(void *)) empty;
++	floppy_tq.routine = (void *)(void *) empty;
+ 	del_timer( &fd_timer);
++
++	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;
+ 	cont->done(0);
+ 	cont->redo(); /* this will recall reset when needed */
+ }
++/*typedef void (*timeout_fn)(unsigned long);*/
+ 
+ /* start motor, check media-changed condition and write protection */
+-static void start_motor(void)
++static int start_motor( void (*function)(void)  )
+ {
+ 	int mask, data;
+ 
+ 	mask = 0xfc;
+ 	data = UNIT(current_drive);
+-
+-	if ( (FDCS->dor & 0x03) != UNIT(current_drive) ||
+-	    !(FDCS->dor & ( 0x10 << UNIT(current_drive) ) ))
+-		/* notes select time if floppy is not yet selected */
+-		DRS->select_date = jiffies;
+-
+ 	if (!(raw_cmd.flags & FD_RAW_NO_MOTOR)){
+ 		if(!(FDCS->dor & ( 0x10 << UNIT(current_drive) ) )){
+ 			set_debugt();
+@@ -1547,26 +1612,31 @@
+ 	/* starts motor and selects floppy */
+ 	del_timer(motor_off_timer + current_drive);
+ 	set_dor( fdc, mask, data);
+-	if( raw_cmd.flags & FD_RAW_NO_MOTOR)
+-		return;
+ 
+-	disk_change(current_drive);
+-
+-	return;
++	/* 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;
+-	start_motor();
++	if(start_motor(floppy_ready)) return;
++	if(fdc_dtr()) return;
+ 
+-	/* wait_for_completion also schedules reset if needed. */
+-	if(wait_for_completion(current_drive,
+-			       DRS->select_date+DP->select_delay,
+-			       (timeout_fn) floppy_ready))
+-		return;
+-	fdc_dtr();
+-	if ( raw_cmd.flags & FD_RAW_NEED_SEEK ){
++#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();
+@@ -1576,9 +1646,17 @@
+ 
+ static void floppy_start(void)
+ {
+-	timer_table[FLOPPY_TIMER].expires = jiffies + DP->timeout;
+-	timer_active |= 1 << FLOPPY_TIMER;
++	del_timer(&fd_timeout);
++	fd_timeout.expires = DP->timeout;
++	add_timer(&fd_timeout);
++
+ 	scandrives();
++#ifdef DCL_DEBUG
++		if (DP->flags & FD_DEBUG){
++			DPRINT("setting NEWCHANGE in floppy_start\n");
++		}
++#endif
++	SETF(FD_DISK_NEWCHANGE);
+ 	floppy_ready();
+ }
+ 
+@@ -1598,7 +1676,7 @@
+ 
+ static void do_wakeup(void)
+ {
+-	timer_active &= ~(1 << FLOPPY_TIMER);
++	del_timer(&fd_timeout);
+ 	cont = 0;
+ 	command_status += 2;
+ 	wake_up(&command_done);
+@@ -1615,7 +1693,7 @@
+ {
+ 	int ret;
+ 
+-	floppy_tq.routine = (void (*)(void *)) handler;
++	floppy_tq.routine = (void *)(void *) handler;
+ 	queue_task(&floppy_tq, &tq_timer);
+ 
+ 	cli();
+@@ -1630,7 +1708,7 @@
+ 	if(command_status < 2){
+ 		sti();
+ 		floppy_shutdown();
+-		redo_fd_request();
++		process_fd_request();
+ 		return -EINTR;
+ 	}
+ 	sti();
+@@ -1681,9 +1759,10 @@
+ static int next_valid_format(void)
+ {
+ 	int probed_format;
++
++	probed_format = DRS->probed_format;
+ 	while(1){
+-		probed_format = DRS->probed_format;
+-		if ( probed_format > 8 ||
++		if ( probed_format >= 8 ||
+ 		    ! DP->autodetect[probed_format] ){
+ 			DRS->probed_format = 0;
+ 			return 1;
+@@ -1738,6 +1817,7 @@
+ 	}
+ 	cont->redo();
+ }
++
+ #define CODE2SIZE (ssize = ( ( 1 << SIZECODE ) + 3 ) >> 2)
+ #define FM_MODE(x,y) ((y) & ~(((x)->rate & 0x80 ) >>1))
+ #define CT(x) ( (x) | 0x40 )
+@@ -1804,7 +1884,6 @@
+ 	raw_cmd.track = format_req.track << floppy->stretch;
+ 	buffer_track = -1;
+ 	setup_format_params();
+-	clear_bit(current_drive, &changed_floppies);
+ 	floppy_start();
+ #ifdef DEBUGT
+ 	debugt("queue format request");
+@@ -1819,23 +1898,27 @@
+ 
+ static int do_format(int device, struct format_descr *tmp_format_req)
+ {
+-	int okay;
++	int ret;
++	int drive=DRIVE(device);
+ 
+-	LOCK_FDC(DRIVE(device),1);
++	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){
+-		redo_fd_request();
++	    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;
+-	CALL(okay=wait_til_done(redo_format,1));
+-	redo_fd_request();
+-	return okay;
++	IWAIT(redo_format);
++	process_fd_request();
++	return ret;
+ }
+ 
+ /*
+@@ -1850,7 +1933,7 @@
+ 	int block;
+ 
+ 	probing = 0;
+-	timer_active &= ~(1 << FLOPPY_TIMER);
++	del_timer(&fd_timeout);
+ 
+ 	if (!CURRENT){
+ 		DPRINT("request list destroyed in floppy request done\n");
+@@ -1914,7 +1997,7 @@
+ 		floppy->sect + ((R_SECTOR-SECTOR) <<  SIZECODE >> 2) -
+ 			(sector_t % floppy->sect) % ssize;
+ 
+-#ifdef SANITY
++#ifdef CONFIG_FLOPPY_SANITY
+ 	if ( nr_sectors > current_count_sectors + ssize -
+ 	    (current_count_sectors + sector_t) % ssize +
+ 	    sector_t % ssize){
+@@ -1987,7 +2070,7 @@
+ 	size = CURRENT->current_nr_sectors << 9;
+ 	bh = CURRENT->bh;
+ 
+-	if(bh){
++	if (bh){
+ 		bh = bh->b_reqnext;
+ 		while ( bh && bh->b_data == base + size ){
+ 			size += bh->b_size;
+@@ -2034,8 +2117,8 @@
+ 			current_count_sectors = CURRENT->nr_sectors;
+ 	}
+ 	remaining = current_count_sectors << 9;
+-#ifdef SANITY
+-	if ((remaining >> 9) > CURRENT->nr_sectors  &&
++#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);
+@@ -2060,12 +2143,12 @@
+ 	while ( remaining > 0){
+ 		if ( size > remaining )
+ 			size = remaining;
+-#ifdef SANITY
++#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",
+-				(floppy_track_buffer - dma_buffer) >>9);
++				(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",
+@@ -2076,7 +2159,7 @@
+ 				printk("write\n");
+ 			break;
+ 		}
+-		if ( ((int)buffer) % 512 )
++		if ( ((unsigned long)buffer) % 512 )
+ 			DPRINT1("%p buffer not aligned\n", buffer);
+ #endif
+ 		if ( CT(COMMAND) == FD_READ )
+@@ -2089,7 +2172,7 @@
+ 
+ 		dma_buffer += size;
+ 		bh = bh->b_reqnext;
+-#ifdef SANITY
++#ifdef CONFIG_FLOPPY_SANITY
+ 		if ( !bh){
+ 			DPRINT("bh=null in copy buffer after copy\n");
+ 			break;
+@@ -2098,7 +2181,7 @@
+ 		size = bh->b_size;
+ 		buffer = bh->b_data;
+ 	}
+-#ifdef SANITY
++#ifdef CONFIG_FLOPPY_SANITY
+ 	if ( remaining ){
+ 		if ( remaining > 0 )
+ 			max_sector -= remaining >> 9;
+@@ -2122,7 +2205,7 @@
+ 	int aligned_sector_t;
+ 	int max_sector, max_size, tracksize, ssize;
+ 
+-	current_drive = DRIVE(CURRENT->dev);
++	set_fdc(DRIVE(CURRENT->dev));
+ 
+ 	raw_cmd.flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_DISK |
+ 		FD_RAW_NEED_SEEK;
+@@ -2145,7 +2228,7 @@
+ 		return 0;
+ 	HEAD = sector_t / floppy->sect;
+ 
+-	if ( (DRS->flags & FD_NEED_TWADDLE) && sector_t < floppy->sect )
++	if ( TESTF( FD_NEED_TWADDLE) && sector_t < floppy->sect )
+ 		max_sector = floppy->sect;
+ 
+ 	/* 2M disks have phantom sectors on the first track */
+@@ -2213,22 +2296,19 @@
+ 		raw_cmd.flags &= ~FD_RAW_WRITE;
+ 		raw_cmd.flags |= FD_RAW_READ;
+ 		COMMAND = FM_MODE(floppy,FD_READ);
+-	}
+-	else if ((long)CURRENT->buffer <= LAST_DMA_ADDR ) {
++	} else if ((long)CURRENT->buffer <= LAST_DMA_ADDR ) {
+ 		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 > ( LAST_DMA_ADDR - ((long) CURRENT->buffer))>>9)
+ 			max_size=(LAST_DMA_ADDR - ((long)CURRENT->buffer))>>9;
+ 		/* 64 kb boundaries */
+ 		if ( ((max_size << 9) + ((long) CURRENT->buffer)) / K_64 !=
+ 		     ((long) CURRENT->buffer ) / K_64 )
+ 			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
+@@ -2237,15 +2317,21 @@
+ 		 * This means we should be able to read a sector even if there
+ 		 * are other bad sectors on this track.
+ 		 */
+-		if ((indirect - sector_t) * 2 > (direct - sector_t) * 3 &&
+-		    *errors < DP->max_errors.read_track &&
+-		    /*!(DRS->flags & FD_NEED_TWADDLE) &&*/
+-		    ( ( !probing || (DP->read_track &
+-			   (1 <<DRS->probed_format))))){
++		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 {
+ 			current_addr = 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;
+ 		}
+ 	}
+@@ -2256,6 +2342,7 @@
+ 	/* 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 ))&&
+@@ -2273,7 +2360,7 @@
+ 		 * if we get here, we know that the write
+ 		 * is either aligned or the data already in the buffer
+ 		 * (buffer will be overwritten) */
+-#ifdef SANITY
++#ifdef CONFIG_FLOPPY_SANITY
+ 		if (sector_t != aligned_sector_t && buffer_track == -1 )
+ 			DPRINT("internal error offset !=0 on write\n");
+ #endif
+@@ -2288,8 +2375,7 @@
+ 	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 SANITY
++#ifdef CONFIG_FLOPPY_SANITY
+ 	if ((raw_cmd.length < current_count_sectors << 9) ||
+ 	    (current_addr != CURRENT->buffer &&
+ 	     CT(COMMAND) == FD_WRITE &&
+@@ -2301,7 +2387,7 @@
+ 			raw_cmd.length, current_count_sectors);
+ 		if ( current_addr != CURRENT->buffer )
+ 			printk("addr=%d, length=%ld\n",
+-			       (current_addr - floppy_track_buffer ) >> 9,
++			       (int) ((current_addr - 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);
+@@ -2342,28 +2428,27 @@
+ 		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 struct cont_t rw_cont={
+-	rw_interrupt,
+-	redo_fd_request,
+-	bad_flp_intr,
+-	request_done };
+-
+ static void redo_fd_request(void)
+ {
+ #define REPEAT {request_done(0); continue; }
+ 	int device;
+ 	int tmp;
++	int error;
+ 
++	error = -1;
+ 	if (current_drive < N_DRIVE)
+ 		floppy_off(current_drive);
+ 
+ 	if (CURRENT && CURRENT->dev < 0) return;
+ 
+-	cont = &rw_cont;
+ 	while(1){
+ 		if (!CURRENT) {
+ 			CLEAR_INTR;
+@@ -2374,20 +2459,29 @@
+ 			panic(DEVICE_NAME ": request list destroyed");
+ 		if (CURRENT->bh && !CURRENT->bh->b_lock)
+ 			panic(DEVICE_NAME ": block not locked");
+-
++#if 0
++		if (!CURRENT->bh->b_count && 
++		    (CURRENT->errors || error == CURRENT->dev)){
++			error=CURRENT->dev;
++			DPRINT("skipping read ahead buffer\n");
++			REPEAT;
++		}
++#endif
++		error=-1;
+ 		device = CURRENT->dev;
+ 		set_fdc( DRIVE(device));
+ 
+-		timer_table[FLOPPY_TIMER].expires = jiffies + DP->timeout;
+-		timer_active |= 1 << FLOPPY_TIMER;
+-		raw_cmd.flags=0;
+-		start_motor();
+-		if(test_bit( DRIVE(device), &fake_change) ||
+-		   test_bit( DRIVE(device), &changed_floppies)){
++		del_timer(&fd_timeout);
++		fd_timeout.expires = DP->timeout;
++		add_timer(&fd_timeout);
++
++		set_floppy(device);
++		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;
+ 		}
+-		set_floppy(device);
+ 		if (!floppy) { /* Autodetection */
+ 			if (!probing){
+ 				DRS->probed_format = 0;
+@@ -2408,7 +2502,9 @@
+ 			continue;
+ 		}
+ 
+-		floppy_tq.routine = (void (*)(void *)) floppy_start;
++		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");
+@@ -2418,7 +2514,22 @@
+ #undef REPEAT
+ }
+ 
+-void do_fd_request(void)
++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
+@@ -2427,7 +2538,30 @@
+ 	/* fdc_busy cannot be set by an interrupt or a bh */
+ 	floppy_grab_irq_and_dma();
+ 	fdc_busy=1;
+-	redo_fd_request();
++	process_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.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;
+ }
+ 
+ /*
+@@ -2448,29 +2582,23 @@
+ 
+ static int user_reset_fdc(int drive, int arg, int interruptible)
+ {
+-	int result;
++	int ret;
+ 
+-	result=0;
++	ret=0;
++	if(arg == FD_RESET_IF_NEEDED && !FDCS->reset)
++		return 0;
+ 	LOCK_FDC(drive,interruptible);
+-	switch(arg){
+-	case FD_RESET_ALWAYS:
++	if(arg == FD_RESET_ALWAYS)
+ 		FDCS->reset=1;
+-		break;
+-	case FD_RESET_IF_RAWCMD:
+-		if(FDCS->rawcmd == 2 )
+-			reset_fdc_info(1);
+-		break;
+-	}
+ 	if ( FDCS->reset ){
+ 		cont = &reset_cont;
+-		timer_table[FLOPPY_TIMER].expires = jiffies + 5;
+-		timer_active |= 1 << FLOPPY_TIMER;
+-		CALL(result=wait_til_done(reset_fdc,interruptible));
+-	}
+-	if ( UDRS->track == PROVEN_ABSENT )
+-		UDRS->track = NEED_2_RECAL;
+-	redo_fd_request();
+-	return result;
++		del_timer(&fd_timeout);
++		fd_timeout.expires = DP->timeout;
++		add_timer(&fd_timeout);
++		WAIT(reset_fdc);
++	}
++	process_fd_request();
++	return ret;
+ }
+ 
+ /*
+@@ -2493,7 +2621,7 @@
+ 
+ static char *drive_name(int type, int drive )
+ {
+-	struct floppy_struct *floppy;
++	struct floppy_struct *floppy;	
+ 
+ 	if ( type )
+ 		floppy = floppy_type + type;
+@@ -2516,21 +2644,21 @@
+ 	generic_failure,
+ 	generic_done };
+ 
+-static int raw_cmd_ioctl(int drive, void *param)
++static int raw_cmd_ioctl(void *param)
+ {
+-	int i, count, ret;
++	int i, drive, count, ret;
+ 
+ 	if ( FDCS->rawcmd <= 1 )
+ 		FDCS->rawcmd = 1;
+-	for ( i= 0; i < N_DRIVE; i++){
+-		if ( FDC(i) != fdc)
++	for ( drive= 0; drive < N_DRIVE; drive++){
++		if ( FDC(drive) != fdc)
+ 			continue;
+-		if ( i == drive ){
+-			if ( drive_state[i].fd_ref > 1 ){
++		if ( drive == current_drive ){
++			if ( UDRS->fd_ref > 1 ){
+ 				FDCS->rawcmd = 2;
+ 				break;
+ 			}
+-		} else if ( drive_state[i].fd_ref ){
++		} else if ( UDRS->fd_ref ){
+ 			FDCS->rawcmd = 2;
+ 			break;
+ 		}
+@@ -2540,11 +2668,15 @@
+ 		return -EIO;
+ 
+ 	COPYIN(raw_cmd);
+-	raw_cmd.rate &= 0x03;
++	raw_cmd.rate &= 0x03;	
+ 	count = raw_cmd.length;
+ 	if (raw_cmd.flags & (FD_RAW_WRITE | FD_RAW_READ)){
+ 		if(count > max_buffer_sectors * 1024 )
+ 			return -ENOMEM;
++		if(count == 0){
++			printk("attempt to do a 0 byte dma transfer\n");
++			return -EINVAL;
++		}
+ 		buffer_track = -1;
+ 	}
+ 	if ( raw_cmd.flags & FD_RAW_WRITE ){
+@@ -2556,13 +2688,18 @@
+ 
+ 	current_addr = floppy_track_buffer;
+ 	cont = &raw_cmd_cont;
+-	CALL(ret=wait_til_done(floppy_start,1));
++	IWAIT(floppy_start);
++#ifdef DCL_DEBUG
++	if (DP->flags & FD_DEBUG){
++		DPRINT("calling disk change from raw_cmd ioctl\n");
++	}
++#endif
+ 	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(drive);
++		motor_off_callback(current_drive);	
+ 
+ 	if ( !ret && !FDCS->reset ){
+ 		raw_cmd.reply_count = inr;
+@@ -2572,7 +2709,6 @@
+ 			raw_cmd.length = get_dma_residue(FLOPPY_DMA);
+ 	} else
+ 		ret = -EIO;
+-
+ 	DRS->track = NO_TRACK;
+ 	if ( ret )
+ 		return ret;
+@@ -2582,7 +2718,7 @@
+ 		if (i)
+ 			return i;
+ 	}
+-
++       
+ 	return COPYOUT(raw_cmd);
+ }
+ 
+@@ -2590,7 +2726,7 @@
+ {
+ 	/* invalidate the buffer track to force a reread */
+ 	set_bit( DRIVE(rdev), &fake_change);
+-	redo_fd_request();
++	process_fd_request();
+ 	check_disk_change(rdev);
+ 	return 0;
+ }
+@@ -2599,6 +2735,7 @@
+     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;
+@@ -2636,7 +2773,9 @@
+ 			return -ENODEV;
+ 		return COPYOUT(this_floppy[0]);
+ 	case FDPOLLDRVSTAT:
+-		check_disk_change(device);
++		LOCK_FDC(drive,1);
++		CALL(poll_drive(1, FD_RAW_NEED_DISK));
++		process_fd_request();
+ 		/* fall through */
+ 	case FDGETDRVSTAT:
+ 		return COPYOUT(*UDRS);
+@@ -2647,10 +2786,8 @@
+ 	case FDWERRORGET:
+ 		return COPYOUT(*UDRWE);
+ 	}
+-
+ 	if (!IOCTL_ALLOWED)
+ 		return -EPERM;
+-
+ 	switch (cmd) {
+ 	case FDWERRORCLR:
+                 UDRWE->write_errors = 0;
+@@ -2665,14 +2802,12 @@
+ 			return -EINVAL;
+ 		LOCK_FDC(drive,1);
+ 		set_floppy(device);
+-		CALL(i = raw_cmd_ioctl(drive, (void *) param));
+-		redo_fd_request();
++		CALL(i = raw_cmd_ioctl((void *) param));
++		process_fd_request();
+ 		return i;
+ 	case FDFMTTRK:
+ 		if (UDRS->fd_ref != 1)
+ 			return -EBUSY;
+-		if (UDRS->track == PROVEN_ABSENT)
+-			return -ENXIO;
+ 		COPYIN(tmp_format_req);
+ 		return do_format(device, &tmp_format_req);
+ 	case FDSETMAXERRS:
+@@ -2682,7 +2817,7 @@
+ 	case FDCLRPRM:
+ 		LOCK_FDC(drive,1);
+ 		current_type[drive] = NULL;
+-		floppy_sizes[drive] = 2;
++		floppy_sizes[drive] = MAX_DISK_SIZE;
+ 		UDRS->keep_data = 0;
+ 		return invalidate_drive(device);
+ 	case FDFMTEND:
+@@ -2702,7 +2837,7 @@
+ 		if ( type){
+ 			if ( !suser() )
+ 				return -EPERM;
+-			LOCK_FDC(-1,1);
++			LOCK_FDC(drive,1);
+ 			for ( cnt = 0; cnt < N_DRIVE; cnt++){
+ 				if (TYPE(drive_state[cnt].fd_device) == type &&
+ 				    drive_state[cnt].fd_ref)
+@@ -2714,11 +2849,9 @@
+ 			     cnt < (type << 2 ) + 4 ;
+ 			     cnt++)
+ 				floppy_sizes[cnt]=
+-#ifdef HAVE_2_CONTROLLERS
+ 					floppy_sizes[cnt+0x80]=
+-#endif
+ 						floppy_type[type].size>>1;
+-			redo_fd_request();
++			process_fd_request();
+ 			for ( cnt = 0; cnt < N_DRIVE; cnt++){
+ 				if (TYPE(drive_state[cnt].fd_device) == type &&
+ 				    drive_state[cnt].fd_ref)
+@@ -2729,12 +2862,10 @@
+ 		}
+ 
+ 		LOCK_FDC(drive,1);
+-		if ( cmd != FDDEFPRM ){
++		if ( cmd != FDDEFPRM )
+ 			/* notice a disk change immediately, else
+ 			 * we loose our settings immediately*/
+-			raw_cmd.flags = 0;
+-			start_motor();
+-		}
++			CALL(poll_drive(1,0));
+ 		user_params[drive] = newparams;
+ 		if (buffer_drive == drive &&
+ 		    buffer_max > user_params[drive].sect)
+@@ -2755,7 +2886,7 @@
+ 		    DRS->maxtrack )
+ 			invalidate_drive(device);
+ 		else
+-			redo_fd_request();
++			process_fd_request();
+ 		return 0;
+ 	case FDRESET:
+ 		return user_reset_fdc( drive, (int)param, 1);
+@@ -2772,7 +2903,7 @@
+ 	case FDTWADDLE:
+ 		LOCK_FDC(drive,1);
+ 		twaddle();
+-		redo_fd_request();
++		process_fd_request();
+ 	}
+ 	if ( ! suser() )
+ 		return -EPERM;
+@@ -2786,70 +2917,89 @@
+ #undef IOCTL_ALLOWED
+ }
+ 
+-static void set_base_type(int drive, int code)
+-{
+-	if(code>0 && code <= NUMBER(default_drive_params)+1) {
+-		memcpy((char *)UDP,
+-			(char *)(&default_drive_params[code-1].params),
+-			sizeof(struct floppy_drive_params));
+-		printk("fd%d is %s", drive, default_drive_params[code-1].name);
+-		return;
+-	}
+-	else if (!code)
+-		printk("fd%d is not installed", drive);
+-	else
+-		printk("fd%d is unknown type %d", drive, code);
+-}
+-
+ static void config_types(void)
+ {
++	int first=1;
+ 	int drive;
+ 
+-	for (drive=0; drive<N_DRIVE ; drive++){
+-	  	/* default type for unidentifiable drives */
+-	  	memcpy((char *) UDP, (char *) (&default_drive_params->params),
+-	  		sizeof( struct floppy_drive_params ));
+-	}
+-	printk("Floppy drive(s): ");
+-	for(drive=0; drive<no_floppies; drive++)
+-	{
+-	  set_base_type(drive, 1+4);
+-	  if(drive < no_floppies-1)
+-	    printk(", ");
++	/* 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);
++		}
+ 	}
+-	printk("\n");
++	if(!first)
++		printk("\n");
+ }
+ 
+-int floppy_is_wp( int minor)
++static int floppy_read(struct inode * inode, struct file * filp,
++		       char * buf, int count)
+ {
+-	check_disk_change(minor + (MAJOR_NR << 8));
+-	return ! ( drive_state[ DRIVE(minor) ].flags & FD_DISK_WRITABLE );
++	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,
++			char * buf, int count)
++{
++	int block;
++	int ret;
++	int drive = DRIVE(inode->i_rdev);
+ 
+-#define WRAPPER(op) \
+-static int floppy_##op(struct inode * inode, struct file * filp, \
+-		     char * buf, int count) \
+-{ \
+-	check_disk_change(inode->i_rdev); \
+-	if ( drive_state[DRIVE(inode->i_rdev)].track == PROVEN_ABSENT ) \
+- 		return -ENXIO; \
+-	if ( test_bit(DRIVE(inode->i_rdev),&changed_floppies)) \
+-		return -ENXIO; \
+-	return block_##op(inode, filp, buf, count); \
++	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;
+ }
+ 
+-WRAPPER(read)
+-WRAPPER(write)
+-
+ static void floppy_release(struct inode * inode, struct file * filp)
+ {
+ 	int drive;
+-
++	
+ 	drive = DRIVE(inode->i_rdev);
+ 
+-	fsync_dev(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--) {
+@@ -2879,20 +3029,22 @@
+ 	}
+ 
+ 	drive = DRIVE(inode->i_rdev);
+-	if ( drive >= N_DRIVE || !( ALLOWED_DRIVE_MASK & ( 1 << drive)) )
++	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;
+-
+-	if ((filp->f_mode & 3)  &&
+-	    UDRS->track == PROVEN_ABSENT )
+-		return -ENXIO;
+-
+ 	old_dev = UDRS->fd_device;
+ 	if (UDRS->fd_ref && old_dev != 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;
+@@ -2900,7 +3052,7 @@
+ 	if (floppy_grab_irq_and_dma())
+ 		return -EBUSY;
+ 
+-	if(filp->f_flags & O_EXCL)
++	if (filp->f_flags & O_EXCL)
+ 		UDRS->fd_ref = -1;
+ 	else
+ 		UDRS->fd_ref++;
+@@ -2916,30 +3068,21 @@
+ 	/* 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 && UDRS->track == PROVEN_ABSENT )
+-		RETERR(ENXIO);
+-
+-	if (user_reset_fdc(drive, FD_RESET_IF_NEEDED,0))
+-		RETERR(EIO);
+-
+ 	if (filp->f_mode & 3) {
+ 		UDRS->last_checked = 0;
+ 		check_disk_change(inode->i_rdev);
+-		if (test_bit(drive,&changed_floppies))
++		if (UTESTF(FD_DISK_CHANGED))
+ 			RETERR(ENXIO);
+ 	}
+-
+-	if (filp->f_mode && UDRS->track == PROVEN_ABSENT )
+-		RETERR(ENXIO);
+-
+-	if ((filp->f_mode & 2) && !(UDRS->flags & FD_DISK_WRITABLE))
++	if ((filp->f_mode & 2) && !(UTESTF(FD_DISK_WRITABLE)))
+ 		RETERR(EROFS);
+ 	return 0;
+ #undef RETERR
+@@ -2957,45 +3100,38 @@
+ 		return 0;
+ 	}
+ 
+-	if(test_bit(drive, &changed_floppies))
++	if (UTESTF(FD_DISK_CHANGED))
+ 		return 1;
+ 
+ 	if(UDRS->last_checked + UDP->checkfreq < jiffies){
+ 		lock_fdc(drive,0);
+-		start_motor();
+-		redo_fd_request();
++		poll_drive(0,0);
++		process_fd_request();
+ 	}
+-
+-	if(test_bit(drive, &changed_floppies))
+-		return 1;
+-	if(test_bit(drive, &fake_change))
++		
++	if(UTESTF(FD_DISK_CHANGED) ||
++	   test_bit(drive, &fake_change) ||
++	   (!TYPE(dev) && !current_type[drive]))
+ 		return 1;
+ 	return 0;
+ }
+ 
+-static struct cont_t poll_cont={
+-	success_and_wakeup,
+-	floppy_ready,
+-	generic_failure,
+-	generic_done };
+-
+-
+ /* revalidate the floppy disk, i.e. trigger format autodetection by reading
+- * the bootblock (block 0). "Autodetection" is also needed to check wether
++ * 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(dev_t dev)
+ {
++#define NO_GEOM (!current_type[drive] && !TYPE(dev))
+ 	struct buffer_head * bh;
+ 	int drive=DRIVE(dev);
+ 	int cf;
+ 
+-	cf = test_bit(drive, &changed_floppies);
+-	if(cf || test_bit(drive, &fake_change)){
++	if(UTESTF(FD_DISK_CHANGED) || test_bit(drive, &fake_change) || NO_GEOM){
+ 		lock_fdc(drive,0);
+-		cf = test_bit(drive, &changed_floppies);
+-		if(! (cf || test_bit(drive, &fake_change))){
+-			redo_fd_request(); /* already done by another thread */
++		cf = UTESTF(FD_DISK_CHANGED);
++		if(! (cf || test_bit(drive, &fake_change) || NO_GEOM)){
++			process_fd_request(); /*already done by another thread*/
+ 			return 0;
+ 		}
+ 		UDRS->maxblock = 0;
+@@ -3003,31 +3139,28 @@
+ 		if ( buffer_drive == drive)
+ 			buffer_track = -1;
+ 		clear_bit(drive, &fake_change);
+-		clear_bit(drive, &changed_floppies);
+-		if(cf){
++		UCLEARF(FD_DISK_CHANGED);
++		if(cf)
+ 			UDRS->generation++;
+-			if(!current_type[drive] && !TYPE(dev)){
+-				/* auto-sensing */
+-				if (!(bh = getblk(dev,0,1024))){
+-					redo_fd_request();
+-					return 1;
+-				}
+-				if ( bh && ! bh->b_uptodate)
+-					ll_rw_block(READ, 1, &bh);
+-				redo_fd_request();
+-				wait_on_buffer(bh);
+-				brelse(bh);
+-				return 0;
+-			} else {
+-				/* no auto-sense, just clear dcl */
+-				raw_cmd.flags=FD_RAW_NEED_SEEK|FD_RAW_NEED_DISK;
+-				raw_cmd.track=0;
+-				raw_cmd.cmd_count=0;
+-				cont = &poll_cont;
+-				wait_til_done(floppy_ready,0);
++		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;
+ 			}
+-		}
+-		redo_fd_request();
++			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;
+ }
+@@ -3094,17 +3227,145 @@
+ 	}
+ 	printk("FDC %d is a post-1991 82077\n",fdc);
+ 	return FDC_82077;	/* Revised 82077AA passes all the tests */
+-} /* fdc_init */
++} /* get_fdc_version */
+ 
+-void floppy_init(void)
++/* 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 parameter for cmos\n");
++		return;
++	}
++	current_drive = ints[1];
++	if (current_drive < 0 || current_drive >= 8 ){
++		DPRINT("bad drive for set_cmos\n");
++		return;
++	}
++	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 {
++	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)
++		return;
++	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;
++		}
++	}
++	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");
++	DPRINT("Read linux/drivers/block/README.fd\n");
++}
++
++#ifdef FD_MODULE
++static
++#endif
++int new_floppy_init(void)
++{
++	int i,drive;
++	int have_no_fdc=0;
++
+ 	sti();
+ 
+ 	if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
+ 		printk("Unable to get major %d for floppy\n",MAJOR_NR);
+-		return;
++		return -EBUSY;
+ 	}
+ 
+ 	for(i=0; i<256; i++)
+@@ -3114,55 +3375,63 @@
+ 			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;
+-	hardsect_size[MAJOR_NR] = fd_sectsizes;
+-	timer_table[FLOPPY_TIMER].fn = floppy_shutdown;
+-	timer_active &= ~(1 << FLOPPY_TIMER);
++	del_timer(&fd_timeout);
+ 	config_types();
+ 
+-	for(i = 0; i<256; i++)
+-		fd_sectsizes[i] = 1024;			/* patch for msdos driver */
+-
+-	fdc_state[0].address = 0x3f0;
++	fdc_state[0].address = FDC1;
++	fdc_state[0].dor = 0;
+ #if N_FDC > 1
+-	fdc_state[1].address = 0x370;
++	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 = 0;
++		FDCS->dor = 0x4;
+ 		FDCS->reset = 0;
+ 		FDCS->version = FDC_NONE;
+-		set_dor(fdc, ~0, 0xc );
++	}
++
++	if(floppy_grab_irq_and_dma()){
++		unregister_blkdev(MAJOR_NR,"fd");
++		return -EBUSY;
+ 	}
+ 
+ 	/* initialise drive state */
+-	for (i = 0; i < N_DRIVE ; i++) {
+-		current_drive = i;
+-		DRS->flags = FD_VERIFY | FD_DISK_NEWCHANGE;
+-		DRS->generation = 0;
+-		DRS->keep_data = 0;
+-		DRS->fd_ref = 0;
+-		DRS->fd_device = 0;
+-		DRWE->write_errors = 0;
+-		DRWE->first_error_sector = 0;
+-		DRWE->first_error_generation = 0;
+-		DRWE->last_error_sector = 0;
+-		DRWE->last_error_generation = 0;
+-		DRWE->badness = 0;
++	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;
++		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;
+ 	}
+ 
+-	floppy_grab_irq_and_dma();
+ 	for (i = 0 ; i < N_FDC ; i++) {
+ 		fdc = i;
++		if (FDCS->address == -1 )
++			continue;
+ 		FDCS->rawcmd = 2;
+-		if(user_reset_fdc(-1,FD_RESET_IF_NEEDED,0))
++		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)
++		if (FDCS->version == FDC_NONE){
++			FDCS->address = -1;
+ 			continue;
++		}
+ 
++		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.
+@@ -3174,6 +3443,15 @@
+ 	current_drive = 0;
+ 	floppy_release_irq_and_dma();
+ 	initialising=0;
++	if(have_no_fdc)
++		unregister_blkdev(MAJOR_NR,"fd");
++	return have_no_fdc;
++}
++
++/* stupid compatibility hack... */
++void floppy_init(void)
++{
++	new_floppy_init();
+ }
+ 
+ static int floppy_grab_irq_and_dma(void)
+@@ -3185,13 +3463,17 @@
+ 		return 0;
+ 	}
+ 	sti();
+-
++#ifdef FD_MODULE
++	MOD_INC_USE_COUNT;
++#endif
+ 	for(i=0; i< N_FDC; i++){
+-		fdc = i;
+-		reset_fdc_info(1);
+-		arm_set_dor(FDCS->dor);
++		if(FDCS->address != -1){	
++			fdc = i;
++			reset_fdc_info(1);
++			outb_p(FDCS->dor, FD_DOR);
++		}
+ 	}
+-	set_dor(0, ~0, 8); /* avoid immediate interrupt */
++	set_dor(0, ~0, 8);  /* avoid immediate interrupt */
+ 
+ 	if (request_irq(FLOPPY_IRQ, floppy_interrupt, SA_INTERRUPT, "floppy")) {
+ 		DPRINT1("Unable to grab IRQ%d for the floppy driver\n",
+@@ -3204,24 +3486,49 @@
+ 		free_irq(FLOPPY_IRQ);
+ 		return -1;
+ 	}
++	for(fdc = 0; fdc < N_FDC ; fdc++)
++		if(FDCS->address != -1)
++			outb_p(FDCS->dor, FD_DOR);
++	fdc = 0;
+ 	enable_irq(FLOPPY_IRQ);
+ 	return 0;
+ }
+ 
+ static void floppy_release_irq_and_dma(void)
+ {
+-	int i;
++#ifdef CONFIG_FLOPPY_SANITY
++	int drive;
++#endif
+ 	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);
+-	/* switch off dma gates */
+-	for(i=0; i< N_FDC; i++)
+-		set_dor(i, ~8, 0);
++
++	set_dor(0, ~0, 8);
++#if N_FDC > 1
++	set_dor(1, ~8, 0);
++#endif
++	floppy_enable_hlt();
++#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\n");
++	if (fd_timer.next)
++		printk("auxiliary floppy timer still active\n");
++	if(floppy_tq.sync)
++		printk("task queue still active\n");
++#endif
+ }
++
diff -r -u -N linux.orig/arch/arm/drivers/block/hdsrch.c linux.arm/arch/arm/drivers/block/hdsrch.c
--- linux.orig/arch/arm/drivers/block/hdsrch.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/block/hdsrch.c	Fri Oct 27 23:14:44 1995
@@ -0,0 +1,654 @@
+#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>
+
+#pragma no_check_stack
+
+#define RECSIZE 60
+
+#define MAX_BLK_FRAGS 64
+
+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;
+#if 0
+static long *pages=NULL;
+#endif
+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)
+        address-=0x200;
+    }
+    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;
+
+/*   p=strchr(path,':'); */
+/*   if(p!=NULL) */
+/*   { */
+/*     drive=p[1]-'0'; */
+/*     if(drive<4 || drive>5) */
+/*       return 0; */
+/*     p=strchr(p,'.'); */
+/*     if(p==NULL) */
+/*       return 0; */
+/*     path=p+1; */
+/*   } */
+  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;
+}
diff -r -u -N linux.orig/arch/arm/drivers/block/ll_rw_blk.c linux.arm/arch/arm/drivers/block/ll_rw_blk.c
--- linux.orig/arch/arm/drivers/block/ll_rw_blk.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/block/ll_rw_blk.c	Fri Oct 27 23:14:45 1995
@@ -0,0 +1,568 @@
+/*
+ *  linux/drivers/block/ll_rw_blk.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 1994,      Karl Keyte: Added support for disk statistics
+ */
+
+/*
+ * This handles all read/write requests to block devices
+ */
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/locks.h>
+#include <linux/mm.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include "blk.h"
+
+/*
+ * The request-struct contains all necessary data
+ * to load a nr of sectors into memory
+ */
+static struct request all_requests[NR_REQUEST];
+
+/*
+ * used to wait on when there are no free requests
+ */
+struct wait_queue * wait_for_request = NULL;
+
+/* This specifies how many sectors to read ahead on the disk.  */
+
+int read_ahead[MAX_BLKDEV] = {0, };
+
+/* blk_dev_struct is:
+ *	do_request-address
+ *	next-request
+ */
+struct blk_dev_struct blk_dev[MAX_BLKDEV] = {
+	{ NULL, NULL },		/* 0 no_dev */
+	{ NULL, NULL },		/* 1 dev mem */
+	{ NULL, NULL },		/* 2 dev fd */
+	{ NULL, NULL },		/* 3 dev ide0 or hd */
+	{ NULL, NULL },		/* 4 dev ttyx */
+	{ NULL, NULL },		/* 5 dev tty */
+	{ NULL, NULL },		/* 6 dev lp */
+	{ NULL, NULL },		/* 7 dev pipes */
+	{ NULL, NULL },		/* 8 dev sd */
+	{ NULL, NULL },		/* 9 dev st */
+	{ NULL, NULL },		/* 10 */
+	{ NULL, NULL },		/* 11 */
+	{ NULL, NULL },		/* 12 */
+	{ NULL, NULL },		/* 13 */
+	{ NULL, NULL },		/* 14 */
+	{ NULL, NULL },		/* 15 */
+	{ NULL, NULL },		/* 16 */
+	{ NULL, NULL },		/* 17 */
+	{ NULL, NULL },		/* 18 */
+	{ NULL, NULL },		/* 19 */
+	{ NULL, NULL },		/* 20 */
+	{ NULL, NULL },		/* 21 */
+	{ NULL, NULL }		/* 22 dev ide1 */
+};
+
+/*
+ * blk_size contains the size of all block-devices in units of 1024 byte
+ * sectors:
+ *
+ * blk_size[MAJOR][MINOR]
+ *
+ * if (!blk_size[MAJOR]) then no minor size checking is done.
+ */
+int * blk_size[MAX_BLKDEV] = { NULL, NULL, };
+
+/*
+ * blksize_size contains the size of all block-devices:
+ *
+ * blksize_size[MAJOR][MINOR]
+ *
+ * if (!blksize_size[MAJOR]) then 1024 bytes is assumed.
+ */
+int * blksize_size[MAX_BLKDEV] = { NULL, NULL, };
+
+/*
+ * hardsect_size contains the size of the hardware sector of a device.
+ *
+ * hardsect_size[MAJOR][MINOR]
+ *
+ * if (!hardsect_size[MAJOR])
+ *		then 512 bytes is assumed.
+ * else
+ *		sector_size is hardsect_size[MAJOR][MINOR]
+ * This is currently set by some scsi device and read by the msdos fs driver
+ * This might be a some uses later.
+ */
+int * hardsect_size[MAX_BLKDEV] = { NULL, NULL, };
+
+/*
+ * look for a free request in the first N entries.
+ * NOTE: interrupts must be disabled on the way in, and will still
+ *       be disabled on the way out.
+ */
+static inline struct request * get_request(int n, int dev)
+{
+	static struct request *prev_found = NULL, *prev_limit = NULL;
+	register struct request *req, *limit;
+
+	if (n <= 0)
+		panic("get_request(%d): impossible!\n", n);
+
+	limit = all_requests + n;
+	if (limit != prev_limit) {
+		prev_limit = limit;
+		prev_found = all_requests;
+	}
+	req = prev_found;
+	for (;;) {
+		req = ((req > all_requests) ? req : limit) - 1;
+		if (req->dev < 0)
+			break;
+		if (req == prev_found)
+			return NULL;
+	}
+	prev_found = req;
+	req->dev = dev;
+	return req;
+}
+
+/*
+ * wait until a free request in the first N entries is available.
+ * NOTE: interrupts must be disabled on the way in, and will still
+ *       be disabled on the way out.
+ */
+static inline struct request * get_request_wait(int n, int dev)
+{
+	register struct request *req;
+
+	while ((req = get_request(n, dev)) == NULL)
+		sleep_on(&wait_for_request);
+	return req;
+}
+
+/* RO fail safe mechanism */
+
+static long ro_bits[MAX_BLKDEV][8];
+
+int is_read_only(int dev)
+{
+	int minor,major;
+
+	major = MAJOR(dev);
+	minor = MINOR(dev);
+	if (major < 0 || major >= MAX_BLKDEV) return 0;
+	return ro_bits[major][minor >> 5] & (1 << (minor & 31));
+}
+
+void set_device_ro(int dev,int flag)
+{
+	int minor,major;
+
+	major = MAJOR(dev);
+	minor = MINOR(dev);
+	if (major < 0 || major >= MAX_BLKDEV) return;
+	if (flag) ro_bits[major][minor >> 5] |= 1 << (minor & 31);
+	else ro_bits[major][minor >> 5] &= ~(1 << (minor & 31));
+}
+
+/*
+ * add-request adds a request to the linked list.
+ * It disables interrupts so that it can muck with the
+ * request-lists in peace.
+ */
+static void add_request(struct blk_dev_struct * dev, struct request * req)
+{
+	struct request * tmp;
+	short		 disk_index;
+
+	switch (MAJOR(req->dev)) {
+		case SCSI_DISK_MAJOR:	disk_index = (MINOR(req->dev) & 0x0070) >> 4;
+					if (disk_index < 4)
+						kstat.dk_drive[disk_index]++;
+					break;
+		case HD_MAJOR:
+		case XT_DISK_MAJOR:	disk_index = (MINOR(req->dev) & 0x0040) >> 6;
+					kstat.dk_drive[disk_index]++;
+					break;
+		case IDE1_MAJOR:	disk_index = ((MINOR(req->dev) & 0x0040) >> 6) + 2;
+					kstat.dk_drive[disk_index]++;
+		default:		break;
+	}
+
+	req->next = NULL;
+	cli();
+	if (req->bh)
+		mark_buffer_clean(req->bh);
+	if (!(tmp = dev->current_request)) {
+		dev->current_request = req;
+		(dev->request_fn)();
+		sti();
+		return;
+	}
+	for ( ; tmp->next ; tmp = tmp->next) {
+		if ((IN_ORDER(tmp,req) ||
+		    !IN_ORDER(tmp,tmp->next)) &&
+		    IN_ORDER(req,tmp->next))
+			break;
+	}
+	req->next = tmp->next;
+	tmp->next = req;
+
+/* for SCSI devices, call request_fn unconditionally */
+	if (scsi_major(MAJOR(req->dev)))
+		(dev->request_fn)();
+
+	sti();
+}
+
+static void make_request(int major,int rw, struct buffer_head * bh)
+{
+	unsigned int sector, count;
+	struct request * req;
+	int rw_ahead, max_req;
+
+/* WRITEA/READA is special case - it is not really needed, so if the */
+/* buffer is locked, we just forget about it, else it's a normal read */
+	rw_ahead = (rw == READA || rw == WRITEA);
+	if (rw_ahead) {
+		if (bh->b_lock)
+			return;
+		if (rw == READA)
+			rw = READ;
+		else
+			rw = WRITE;
+	}
+	if (rw!=READ && rw!=WRITE) {
+		printk("Bad block dev command, must be R/W/RA/WA\n");
+		return;
+	}
+	count = bh->b_size >> 9;
+	sector = bh->b_blocknr * count;
+	if (blk_size[major])
+		if (blk_size[major][MINOR(bh->b_dev)] < (sector + count)>>1) {
+			bh->b_dirt = bh->b_uptodate = 0;
+			bh->b_req = 0;
+			return;
+		}
+	/* Uhhuh.. Nasty dead-lock possible here.. */
+	if (bh->b_lock)
+		return;
+	/* Maybe the above fixes it, and maybe it doesn't boot. Life is interesting */
+	lock_buffer(bh);
+	if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) {
+		unlock_buffer(bh);
+		return;
+	}
+
+/* we don't allow the write-requests to fill up the queue completely:
+ * we want some room for reads: they take precedence. The last third
+ * of the requests are only for reads.
+ */
+	max_req = (rw == READ) ? NR_REQUEST : ((NR_REQUEST*2)/3);
+
+/* big loop: look for a free request. */
+
+repeat:
+	cli();
+
+/* The scsi disk drivers and the IDE driver completely remove the request
+ * from the queue when they start processing an entry.  For this reason
+ * it is safe to continue to add links to the top entry for those devices.
+ */
+	if ((   major == IDE0_MAJOR	/* same as HD_MAJOR */
+	     || major == IDE1_MAJOR
+	     || major == FLOPPY_MAJOR
+	     || major == SCSI_DISK_MAJOR
+	     || major == SCSI_CDROM_MAJOR)
+	    && (req = blk_dev[major].current_request))
+	{
+#ifdef CONFIG_BLK_DEV_HD
+	        if (major == HD_MAJOR || major == FLOPPY_MAJOR)
+#else
+		if (major == FLOPPY_MAJOR)
+#endif CONFIG_BLK_DEV_HD
+			req = req->next;
+		while (req) {
+			if (req->dev == bh->b_dev &&
+			    !req->sem &&
+			    req->cmd == rw &&
+			    req->sector + req->nr_sectors == sector &&
+			    req->nr_sectors < 244)
+			{
+				req->bhtail->b_reqnext = bh;
+				req->bhtail = bh;
+				req->nr_sectors += count;
+				mark_buffer_clean(bh);
+				sti();
+				return;
+			}
+
+			if (req->dev == bh->b_dev &&
+			    !req->sem &&
+			    req->cmd == rw &&
+			    req->sector - count == sector &&
+			    req->nr_sectors < 244)
+			{
+			    	req->nr_sectors += count;
+			    	bh->b_reqnext = req->bh;
+			    	req->buffer = bh->b_data;
+			    	req->current_nr_sectors = count;
+			    	req->sector = sector;
+				mark_buffer_clean(bh);
+			    	req->bh = bh;
+			    	sti();
+			    	return;
+			}    
+
+			req = req->next;
+		}
+	}
+
+/* find an unused request. */
+	req = get_request(max_req, bh->b_dev);
+
+/* if no request available: if rw_ahead, forget it; otherwise try again. */
+	if (! req) {
+		if (rw_ahead) {
+			sti();
+			unlock_buffer(bh);
+			return;
+		}
+		sleep_on(&wait_for_request);
+		sti();
+		goto repeat;
+	}
+
+/* we found a request. */
+	sti();
+
+/* fill up the request-info, and add it to the queue */
+	req->cmd = rw;
+	req->errors = 0;
+	req->sector = sector;
+	req->nr_sectors = count;
+	req->current_nr_sectors = count;
+	req->buffer = bh->b_data;
+	req->sem = NULL;
+	req->bh = bh;
+	req->bhtail = bh;
+	req->next = NULL;
+	add_request(major+blk_dev,req);
+}
+
+void ll_rw_page(int rw, int dev, int page, char * buffer)
+{
+	struct request * req;
+	unsigned int major = MAJOR(dev);
+	struct semaphore sem = MUTEX_LOCKED;
+
+	if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
+		printk("Trying to read nonexistent block-device %04x (%d)\n",dev,page*8);
+		return;
+	}
+	if (rw!=READ && rw!=WRITE)
+		panic("Bad block dev command, must be R/W");
+	if (rw == WRITE && is_read_only(dev)) {
+		printk("Can't page to read-only device 0x%X\n",dev);
+		return;
+	}
+	cli();
+	req = get_request_wait(NR_REQUEST, dev);
+	sti();
+/* fill up the request-info, and add it to the queue */
+	req->cmd = rw;
+	req->errors = 0;
+	req->sector = page << (PAGE_SHIFT-9);
+	req->nr_sectors = 1 << (PAGE_SHIFT-9);
+	req->current_nr_sectors = 1 << (PAGE_SHIFT-9);
+	req->buffer = buffer;
+	req->sem = &sem;
+	req->bh = NULL;
+	req->next = NULL;
+	add_request(major+blk_dev,req);
+	down(&sem);
+}
+
+/* This function can be used to request a number of buffers from a block
+   device. Currently the only restriction is that all buffers must belong to
+   the same device */
+
+void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
+{
+	unsigned int major;
+	struct request plug;
+	int plugged;
+	int correct_size;
+	struct blk_dev_struct * dev;
+	int i;
+
+	/* Make sure that the first block contains something reasonable */
+	while (!*bh) {
+		bh++;
+		if (--nr <= 0)
+			return;
+	};
+
+	dev = NULL;
+	if ((major = MAJOR(bh[0]->b_dev)) < MAX_BLKDEV)
+		dev = blk_dev + major;
+	if (!dev || !dev->request_fn) {
+		printk(
+	"ll_rw_block: Trying to read nonexistent block-device %04lX (%ld)\n",
+		       (unsigned long) bh[0]->b_dev, bh[0]->b_blocknr);
+		goto sorry;
+	}
+
+	/* Determine correct block size for this device.  */
+	correct_size = BLOCK_SIZE;
+	if (blksize_size[major]) {
+		i = blksize_size[major][MINOR(bh[0]->b_dev)];
+		if (i)
+			correct_size = i;
+	}
+
+	/* Verify requested block sizes.  */
+	for (i = 0; i < nr; i++) {
+		if (bh[i] && bh[i]->b_size != correct_size) {
+			printk(
+			"ll_rw_block: only %d-char blocks implemented (%lu)\n",
+			       correct_size, bh[i]->b_size);
+			goto sorry;
+		}
+	}
+
+	if ((rw == WRITE || rw == WRITEA) && is_read_only(bh[0]->b_dev)) {
+		printk("Can't write to read-only device 0x%X\n",bh[0]->b_dev);
+		goto sorry;
+	}
+
+	/* If there are no pending requests for this device, then we insert
+	   a dummy request for that device.  This will prevent the request
+	   from starting until we have shoved all of the blocks into the
+	   queue, and then we let it rip.  */
+
+	plugged = 0;
+	cli();
+	if (!dev->current_request && nr > 1) {
+		dev->current_request = &plug;
+		plug.dev = -1;
+		plug.next = NULL;
+		plugged = 1;
+	}
+	sti();
+	for (i = 0; i < nr; i++) {
+		if (bh[i]) {
+			bh[i]->b_req = 1;
+			make_request(major, rw, bh[i]);
+			if (rw == READ || rw == READA)
+				kstat.pgpgin++;
+			else
+				kstat.pgpgout++;
+		}
+	}
+	if (plugged) {
+		cli();
+		dev->current_request = plug.next;
+		(dev->request_fn)();
+		sti();
+	}
+	return;
+
+      sorry:
+	for (i = 0; i < nr; i++) {
+		if (bh[i])
+			bh[i]->b_dirt = bh[i]->b_uptodate = 0;
+	}
+	return;
+}
+
+void ll_rw_swap_file(int rw, int dev, unsigned int *b, int nb, char *buf)
+{
+	int i;
+	int buffersize;
+	struct request * req;
+	unsigned int major = MAJOR(dev);
+	struct semaphore sem = MUTEX_LOCKED;
+
+	if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
+		printk("ll_rw_swap_file: trying to swap nonexistent block-device\n");
+		return;
+	}
+
+	if (rw!=READ && rw!=WRITE) {
+		printk("ll_rw_swap: bad block dev command, must be R/W");
+		return;
+	}
+	if (rw == WRITE && is_read_only(dev)) {
+		printk("Can't swap to read-only device 0x%X\n",dev);
+		return;
+	}
+	printk("ll_rw_swap: cannot swap to a swapfile!\n");
+
+	return;
+	buffersize = PAGE_SIZE / nb;
+
+	for (i=0; i<nb; i++, buf += buffersize)
+	{
+		cli();
+		req = get_request_wait(NR_REQUEST, dev);
+		sti();
+		req->cmd = rw;
+		req->errors = 0;
+		req->sector = (b[i] * buffersize) >> 9;
+		req->nr_sectors = buffersize >> 9;
+		req->current_nr_sectors = buffersize >> 9;
+		req->buffer = buf;
+		req->sem = &sem;
+		req->bh = NULL;
+		req->next = NULL;
+		add_request(major+blk_dev,req);
+		down(&sem);
+	}
+}
+
+long blk_dev_init(long mem_start, long mem_end)
+{
+	struct request * req;
+
+	req = all_requests + NR_REQUEST;
+	while (--req >= all_requests) {
+		req->dev = -1;
+		req->next = NULL;
+	}
+	memset(ro_bits,0,sizeof(ro_bits));
+#ifdef CONFIG_BLK_DEV_HD
+	mem_start = hd_init(mem_start,mem_end);
+#endif
+#ifdef CONFIG_BLK_DEV_IDE
+	mem_start = ide_init(mem_start,mem_end);
+#endif
+#ifdef CONFIG_BLK_DEV_XD
+	mem_start = xd_init(mem_start,mem_end);
+#endif
+#ifdef CONFIG_CDU31A
+	mem_start = cdu31a_init(mem_start,mem_end);
+#endif
+#ifdef CONFIG_CDU535
+	mem_start = sony535_init(mem_start,mem_end);
+#endif
+#ifdef CONFIG_MCD
+	mem_start = mcd_init(mem_start,mem_end);
+#endif
+#ifdef CONFIG_AZTCD
+        mem_start = aztcd_init(mem_start,mem_end);
+#endif
+#ifdef CONFIG_BLK_DEV_FD
+	floppy_init();
+#endif
+#ifdef CONFIG_SBPCD
+	mem_start = sbpcd_init(mem_start, mem_end);
+#endif CONFIG_SBPCD
+	if (ramdisk_size)
+		mem_start += rd_init(mem_start, ramdisk_size*1024);
+	return mem_start;
+}
diff -r -u -N linux.orig/arch/arm/drivers/block/ll_rw_blk.c.old linux.arm/arch/arm/drivers/block/ll_rw_blk.c.old
--- linux.orig/arch/arm/drivers/block/ll_rw_blk.c.old	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/block/ll_rw_blk.c.old	Fri Oct 27 23:14:50 1995
@@ -0,0 +1,519 @@
+/*
+ *  linux/drivers/block/ll_rw_blk.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 1994,      Karl Keyte: Added support for disk statistics
+ */
+
+/*
+ * This handles all read/write requests to block devices
+ */
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/locks.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include "blk.h"
+
+/*
+ * The request-struct contains all necessary data
+ * to load a nr of sectors into memory
+ */
+static struct request all_requests[NR_REQUEST];
+
+/*
+ * used to wait on when there are no free requests
+ */
+struct wait_queue * wait_for_request = NULL;
+
+/* This specifies how many sectors to read ahead on the disk.  */
+
+int read_ahead[MAX_BLKDEV] = {0, };
+
+/* blk_dev_struct is:
+ *	do_request-address
+ *	next-request
+ */
+struct blk_dev_struct blk_dev[MAX_BLKDEV] = {
+	{ NULL, NULL },		/* no_dev */
+	{ NULL, NULL },		/* dev mem */
+	{ NULL, NULL },		/* dev fd */
+	{ NULL, NULL },		/* dev hd */
+	{ NULL, NULL },		/* dev ttyx */
+	{ NULL, NULL },		/* dev tty */
+	{ NULL, NULL },		/* dev lp */
+	{ NULL, NULL },		/* dev pipes */
+	{ NULL, NULL },		/* dev sd */
+	{ NULL, NULL }		/* dev st */
+};
+
+/*
+ * blk_size contains the size of all block-devices in units of 1024 byte
+ * sectors:
+ *
+ * blk_size[MAJOR][MINOR]
+ *
+ * if (!blk_size[MAJOR]) then no minor size checking is done.
+ */
+int * blk_size[MAX_BLKDEV] = { NULL, NULL, };
+
+/*
+ * blksize_size contains the size of all block-devices:
+ *
+ * blksize_size[MAJOR][MINOR]
+ *
+ * if (!blksize_size[MAJOR]) then 1024 bytes is assumed.
+ */
+int * blksize_size[MAX_BLKDEV] = { NULL, NULL, };
+
+/*
+ * look for a free request in the first N entries.
+ * NOTE: interrupts must be disabled on the way in, and will still
+ *       be disabled on the way out.
+ */
+static inline struct request * get_request(int n, int dev)
+{
+	static struct request *prev_found = NULL, *prev_limit = NULL;
+	register struct request *req, *limit;
+
+	if (n <= 0)
+		panic("get_request(%d): impossible!\n", n);
+
+	limit = all_requests + n;
+	if (limit != prev_limit) {
+		prev_limit = limit;
+		prev_found = all_requests;
+	}
+	req = prev_found;
+	for (;;) {
+		req = ((req > all_requests) ? req : limit) - 1;
+		if (req->dev < 0)
+			break;
+		if (req == prev_found)
+			return NULL;
+	}
+	prev_found = req;
+	req->dev = dev;
+	return req;
+}
+
+/*
+ * wait until a free request in the first N entries is available.
+ * NOTE: interrupts must be disabled on the way in, and will still
+ *       be disabled on the way out.
+ */
+static inline struct request * get_request_wait(int n, int dev)
+{
+	register struct request *req;
+
+	while ((req = get_request(n, dev)) == NULL)
+		sleep_on(&wait_for_request);
+	return req;
+}
+
+/* RO fail safe mechanism */
+
+static long ro_bits[MAX_BLKDEV][8];
+
+int is_read_only(int dev)
+{
+	int minor,major;
+
+	major = MAJOR(dev);
+	minor = MINOR(dev);
+	if (major < 0 || major >= MAX_BLKDEV) return 0;
+	return ro_bits[major][minor >> 5] & (1 << (minor & 31));
+}
+
+void set_device_ro(int dev,int flag)
+{
+	int minor,major;
+
+	major = MAJOR(dev);
+	minor = MINOR(dev);
+	if (major < 0 || major >= MAX_BLKDEV) return;
+	if (flag) ro_bits[major][minor >> 5] |= 1 << (minor & 31);
+	else ro_bits[major][minor >> 5] &= ~(1 << (minor & 31));
+}
+
+/*
+ * add-request adds a request to the linked list.
+ * It disables interrupts so that it can muck with the
+ * request-lists in peace.
+ */
+static void add_request(struct blk_dev_struct * dev, struct request * req)
+{
+	struct request * tmp;
+	short		 disk_index;
+
+	switch (MAJOR(req->dev)) {
+		case SCSI_DISK_MAJOR:	disk_index = (MINOR(req->dev) & 0x0070) >> 4;
+					if (disk_index < 4)
+						kstat.dk_drive[disk_index]++;
+					break;
+		case HD_MAJOR:
+		case XT_DISK_MAJOR:	disk_index = (MINOR(req->dev) & 0x00C0) >> 6;
+					if (disk_index < 4)
+						kstat.dk_drive[disk_index]++;
+					break;
+		default:		break;
+	}
+
+	req->next = NULL;
+	cli();
+	if (req->bh)
+		mark_buffer_clean(req->bh);
+	if (!(tmp = dev->current_request)) {
+		dev->current_request = req;
+		(dev->request_fn)();
+		sti();
+		return;
+	}
+	for ( ; tmp->next ; tmp = tmp->next) {
+		if ((IN_ORDER(tmp,req) ||
+		    !IN_ORDER(tmp,tmp->next)) &&
+		    IN_ORDER(req,tmp->next))
+			break;
+	}
+	req->next = tmp->next;
+	tmp->next = req;
+
+/* for SCSI devices, call request_fn unconditionally */
+	if (scsi_major(MAJOR(req->dev)))
+		(dev->request_fn)();
+
+	sti();
+}
+
+static void make_request(int major,int rw, struct buffer_head * bh)
+{
+	unsigned int sector, count;
+	struct request * req;
+	int rw_ahead, max_req;
+
+/* WRITEA/READA is special case - it is not really needed, so if the */
+/* buffer is locked, we just forget about it, else it's a normal read */
+	rw_ahead = (rw == READA || rw == WRITEA);
+	if (rw_ahead) {
+		if (bh->b_lock)
+			return;
+		if (rw == READA)
+			rw = READ;
+		else
+			rw = WRITE;
+	}
+	if (rw!=READ && rw!=WRITE) {
+		printk("Bad block dev command, must be R/W/RA/WA\n");
+		return;
+	}
+	count = bh->b_size >> 9;
+	sector = bh->b_blocknr * count;
+	if (blk_size[major])
+		if (blk_size[major][MINOR(bh->b_dev)] < (sector + count)>>1) {
+			bh->b_dirt = bh->b_uptodate = 0;
+			bh->b_req = 0;
+			return;
+		}
+	lock_buffer(bh);
+	if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) {
+		unlock_buffer(bh);
+		return;
+	}
+
+/* we don't allow the write-requests to fill up the queue completely:
+ * we want some room for reads: they take precedence. The last third
+ * of the requests are only for reads.
+ */
+	max_req = (rw == READ) ? NR_REQUEST : ((NR_REQUEST*2)/3);
+
+/* big loop: look for a free request. */
+
+repeat:
+	cli();
+
+/* The scsi disk drivers completely remove the request from the queue when
+ * they start processing an entry.  For this reason it is safe to continue
+ * to add links to the top entry for scsi devices.
+ */
+	if ((major == HD_MAJOR
+	     || major == FLOPPY_MAJOR
+	     || major == SCSI_DISK_MAJOR
+	     || major == SCSI_CDROM_MAJOR)
+	    && (req = blk_dev[major].current_request))
+	{
+	        if (major == HD_MAJOR || major == FLOPPY_MAJOR)
+			req = req->next;
+		while (req) {
+			if (req->dev == bh->b_dev &&
+			    !req->sem &&
+			    req->cmd == rw &&
+			    req->sector + req->nr_sectors == sector &&
+			    req->nr_sectors < 244)
+			{
+				req->bhtail->b_reqnext = bh;
+				req->bhtail = bh;
+				req->nr_sectors += count;
+				mark_buffer_clean(bh);
+				sti();
+				return;
+			}
+
+			if (req->dev == bh->b_dev &&
+			    !req->sem &&
+			    req->cmd == rw &&
+			    req->sector - count == sector &&
+			    req->nr_sectors < 244)
+			{
+			    	req->nr_sectors += count;
+			    	bh->b_reqnext = req->bh;
+			    	req->buffer = bh->b_data;
+			    	req->current_nr_sectors = count;
+			    	req->sector = sector;
+				mark_buffer_clean(bh);
+			    	req->bh = bh;
+			    	sti();
+			    	return;
+			}
+
+			req = req->next;
+		}
+	}
+
+/* find an unused request. */
+	req = get_request(max_req, bh->b_dev);
+
+/* if no request available: if rw_ahead, forget it; otherwise try again. */
+	if (! req) {
+		if (rw_ahead) {
+			sti();
+			unlock_buffer(bh);
+			return;
+		}
+		sleep_on(&wait_for_request);
+		sti();
+		goto repeat;
+	}
+
+/* we found a request. */
+	sti();
+
+/* fill up the request-info, and add it to the queue */
+	req->cmd = rw;
+	req->errors = 0;
+	req->sector = sector;
+	req->nr_sectors = count;
+	req->current_nr_sectors = count;
+	req->buffer = bh->b_data;
+	req->sem = NULL;
+	req->bh = bh;
+	req->bhtail = bh;
+	req->next = NULL;
+	add_request(major+blk_dev,req);
+}
+
+void ll_rw_page(int rw, int dev, int page, char * buffer)
+{
+	struct request * req;
+	unsigned int major = MAJOR(dev);
+	struct semaphore sem = MUTEX_LOCKED;
+
+	if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
+		printk("Trying to read nonexistent block-device %04x (%d)\n",dev,page*8);
+		return;
+	}
+	if (rw!=READ && rw!=WRITE)
+		panic("Bad block dev command, must be R/W");
+	if (rw == WRITE && is_read_only(dev)) {
+		printk("Can't page to read-only device 0x%X\n",dev);
+		return;
+	}
+	cli();
+	req = get_request_wait(NR_REQUEST, dev);
+	sti();
+/* fill up the request-info, and add it to the queue */
+	req->cmd = rw;
+	req->errors = 0;
+	req->sector = page << (PAGE_SHIFT-9);
+	req->nr_sectors = 1 << (PAGE_SHIFT-9);
+	req->current_nr_sectors = 1 << (PAGE_SHIFT-9);
+	req->buffer = buffer;
+	req->sem = &sem;
+	req->bh = NULL;
+	req->next = NULL;
+	add_request(major+blk_dev,req);
+	down(&sem);
+}
+
+/* This function can be used to request a number of buffers from a block
+   device. Currently the only restriction is that all buffers must belong to
+   the same device */
+
+void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
+{
+	unsigned int major;
+	struct request plug;
+	int plugged;
+	int correct_size;
+	struct blk_dev_struct * dev;
+	int i;
+
+	/* Make sure that the first block contains something reasonable */
+	while (!*bh) {
+		bh++;
+		if (--nr <= 0)
+			return;
+	};
+
+	dev = NULL;
+	if ((major = MAJOR(bh[0]->b_dev)) < MAX_BLKDEV)
+		dev = blk_dev + major;
+	if (!dev || !dev->request_fn) {
+		printk(
+	"ll_rw_block: Trying to read nonexistent block-device %04lX (%ld)\n",
+		       (unsigned long) bh[0]->b_dev, bh[0]->b_blocknr);
+		goto sorry;
+	}
+
+	/* Determine correct block size for this device.  */
+	correct_size = BLOCK_SIZE;
+	if (blksize_size[major]) {
+		i = blksize_size[major][MINOR(bh[0]->b_dev)];
+		if (i)
+			correct_size = i;
+	}
+
+	/* Verify requested block sizes.  */
+	for (i = 0; i < nr; i++) {
+		if (bh[i] && bh[i]->b_size != correct_size) {
+			printk(
+			"ll_rw_block: only %d-char blocks implemented (%lu)\n",
+			       correct_size, bh[i]->b_size);
+			goto sorry;
+		}
+	}
+
+	if ((rw == WRITE || rw == WRITEA) && is_read_only(bh[0]->b_dev)) {
+		printk("Can't write to read-only device 0x%X\n",bh[0]->b_dev);
+		goto sorry;
+	}
+
+	/* If there are no pending requests for this device, then we insert
+	   a dummy request for that device.  This will prevent the request
+	   from starting until we have shoved all of the blocks into the
+	   queue, and then we let it rip.  */
+
+	plugged = 0;
+	cli();
+	if (!dev->current_request && nr > 1) {
+		dev->current_request = &plug;
+		plug.dev = -1;
+		plug.next = NULL;
+		plugged = 1;
+	}
+	sti();
+	for (i = 0; i < nr; i++) {
+		if (bh[i]) {
+			bh[i]->b_req = 1;
+			make_request(major, rw, bh[i]);
+			if (rw == READ || rw == READA)
+				kstat.pgpgin++;
+			else
+				kstat.pgpgout++;
+		}
+	}
+	if (plugged) {
+		cli();
+		dev->current_request = plug.next;
+		(dev->request_fn)();
+		sti();
+	}
+	return;
+
+      sorry:
+	for (i = 0; i < nr; i++) {
+		if (bh[i])
+			bh[i]->b_dirt = bh[i]->b_uptodate = 0;
+	}
+	return;
+}
+
+void ll_rw_swap_file(int rw, int dev, unsigned int *b, int nb, char *buf)
+{
+	int i;
+	int buffersize;
+	struct request * req;
+	unsigned int major = MAJOR(dev);
+	struct semaphore sem = MUTEX_LOCKED;
+
+	if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
+		printk("ll_rw_swap_file: trying to swap nonexistent block-device\n");
+		return;
+	}
+
+	if (rw!=READ && rw!=WRITE) {
+		printk("ll_rw_swap: bad block dev command, must be R/W");
+		return;
+	}
+	if (rw == WRITE && is_read_only(dev)) {
+		printk("Can't swap to read-only device 0x%X\n",dev);
+		return;
+	}
+	printk("ll_rw_swap: cannot swap to a swapfile!\n");
+	return;
+
+	buffersize = PAGE_SIZE / nb;
+
+	for (i=0; i<nb; i++, buf += buffersize)
+	{
+		cli();
+		req = get_request_wait(NR_REQUEST, dev);
+		sti();
+		req->cmd = rw;
+		req->errors = 0;
+		req->sector = (b[i] * buffersize) >> 9;
+		req->nr_sectors = buffersize >> 9;
+		req->current_nr_sectors = buffersize >> 9;
+		req->buffer = buf;
+		req->sem = &sem;
+		req->bh = NULL;
+		req->next = NULL;
+		add_request(major+blk_dev,req);
+		down(&sem);
+	}
+}
+
+long blk_dev_init(long mem_start, long mem_end)
+{
+	struct request * req;
+
+	req = all_requests + NR_REQUEST;
+	while (--req >= all_requests) {
+		req->dev = -1;
+		req->next = NULL;
+	}
+	memset(ro_bits,0,sizeof(ro_bits));
+#ifdef CONFIG_BLK_DEV_HD
+	mem_start = hd_init(mem_start,mem_end);
+#endif
+#ifdef CONFIG_BLK_DEV_XD
+	mem_start = xd_init(mem_start,mem_end);
+#endif
+#ifdef CONFIG_CDU31A
+	mem_start = cdu31a_init(mem_start,mem_end);
+#endif
+#ifdef CONFIG_MCD
+	mem_start = mcd_init(mem_start,mem_end);
+#endif
+#ifdef CONFIG_SBPCD
+	mem_start = sbpcd_init(mem_start, mem_end);
+#endif CONFIG_SBPCD
+	if (ramdisk_size)
+		mem_start += rd_init(mem_start, ramdisk_size*1024);
+	return mem_start;
+}
diff -r -u -N linux.orig/arch/arm/drivers/block/mfmhd.c linux.arm/arch/arm/drivers/block/mfmhd.c
--- linux.orig/arch/arm/drivers/block/mfmhd.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/block/mfmhd.c	Fri Oct 27 23:14:51 1995
@@ -0,0 +1,770 @@
+#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 -r -u -N linux.orig/arch/arm/drivers/block/ramdisk.c linux.arm/arch/arm/drivers/block/ramdisk.c
--- linux.orig/arch/arm/drivers/block/ramdisk.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/block/ramdisk.c	Fri Oct 27 23:14:45 1995
@@ -0,0 +1,234 @@
+/*
+ *  linux/kernel/blk_drv/ramdisk.c
+ *
+ *  Written by Theodore Ts'o, 12/2/91
+ *
+ * Modifications by Fred N. van Kempen to allow for bootable root
+ * disks (which are used in LINUX/Pro).  Also some cleanups.  03/03/93
+ */
+
+
+#include <linux/sched.h>
+#include <linux/minix_fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+
+#define MAJOR_NR  MEM_MAJOR
+#include "blk.h"
+
+#define RAMDISK_MINOR	1
+
+extern void wait_for_keypress(void);
+
+char	*rd_start;
+int	rd_length = 0;
+static int rd_blocksizes[2] = {0, 0};
+
+static void do_rd_request(void)
+{
+	int	len;
+	char	*addr;
+
+repeat:
+	INIT_REQUEST;
+	addr = rd_start + (CURRENT->sector << 9);
+	len = CURRENT->current_nr_sectors << 9;
+
+	if ((MINOR(CURRENT->dev) != RAMDISK_MINOR) ||
+	    (addr+len > rd_start+rd_length)) {
+		end_request(0);
+		goto repeat;
+	}
+	if (CURRENT-> cmd == WRITE) {
+		(void ) memcpy(addr,
+			      CURRENT->buffer,
+			      len);
+	} else if (CURRENT->cmd == READ) {
+		(void) memcpy(CURRENT->buffer, 
+			      addr,
+			      len);
+	} else
+		panic("RAMDISK: unknown RAM disk command !\n");
+	end_request(1);
+	goto repeat;
+}
+
+static struct file_operations rd_fops = {
+	NULL,			/* lseek - default */
+	block_read,		/* read - general block-dev read */
+	block_write,		/* write - general block-dev write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select */
+	NULL,			/* ioctl */
+	NULL,			/* mmap */
+	NULL,			/* no special open code */
+	NULL,			/* no special release code */
+	block_fsync		/* fsync */
+};
+
+/*
+ * Returns amount of memory which needs to be reserved.
+ */
+long rd_init(long mem_start, int length)
+{
+	int	i;
+	char	*cp;
+
+	if (register_blkdev(MEM_MAJOR,"rd",&rd_fops)) {
+		printk("RAMDISK: Unable to get major %d.\n", MEM_MAJOR);
+		return 0;
+	}
+	blk_dev[MEM_MAJOR].request_fn = DEVICE_REQUEST;
+	rd_start = (char *) mem_start;
+	rd_length = length;
+	cp = rd_start;
+	for (i=0; i < length; i++)
+		*cp++ = '\0';
+
+	for(i=0;i<2;i++) rd_blocksizes[i] = 1024;
+	blksize_size[MAJOR_NR] = rd_blocksizes;
+
+	return(length);
+}
+
+static void do_load(void)
+{
+	struct buffer_head *bh;
+	struct super_block {
+	  union
+	  {
+	    char minix [sizeof (struct minix_super_block)];
+	    char ext2 [sizeof (struct ext2_super_block)];
+	  } record;
+	} sb;
+	struct minix_super_block *minixsb =
+		(struct minix_super_block *)&sb;
+	struct ext2_super_block *ext2sb =
+		(struct ext2_super_block *)&sb;
+	int		block, tries;
+	int		i = 1;
+	int		nblocks;
+	char		*cp;
+	
+	/*
+	 * Check for a super block on the diskette.
+	 * The old-style boot/root diskettes had their RAM image
+	 * starting at block 512 of the boot diskette.  LINUX/Pro
+	 * uses the entire diskette as a file system, so in that
+	 * case, we have to look at block 0.  Be intelligent about
+	 * this, and check both... - FvK
+	 */
+	for (tries = 0; tries < 1000; tries += 512) {
+		block = tries;
+		bh = breada(ROOT_DEV,block+1,BLOCK_SIZE, 0,  PAGE_SIZE);
+		if (!bh) {
+			printk("RAMDISK: I/O error while looking for super block!\n");
+			return;
+		}
+
+		/* This is silly- why do we require it to be a MINIX FS? */
+		*((struct super_block *) &sb) =
+			*((struct super_block *) bh->b_data);
+		brelse(bh);
+
+
+		/* Try Minix */
+		nblocks = -1;
+		if (minixsb->s_magic == MINIX_SUPER_MAGIC ||
+		    minixsb->s_magic == MINIX_SUPER_MAGIC2) {
+			printk("RAMDISK: Minix filesystem found at block %d\n",
+				block);
+			nblocks = minixsb->s_nzones << minixsb->s_log_zone_size;
+		}
+
+		/* Try ext2 */
+		if (nblocks == -1 && (ext2sb->s_magic ==
+			EXT2_PRE_02B_MAGIC ||
+			ext2sb->s_magic == EXT2_SUPER_MAGIC))
+	        {
+			printk("RAMDISK: Ext2 filesystem found at block %d\n",
+				block);
+			nblocks = ext2sb->s_blocks_count;
+		}
+
+		if (nblocks == -1)
+		{
+			printk("RAMDISK: trying old-style RAM image.\n");
+			continue;
+		}
+
+		if (nblocks > (rd_length >> BLOCK_SIZE_BITS)) {
+			printk("RAMDISK: image too big! (%d/%d blocks)\n",
+					nblocks, rd_length >> BLOCK_SIZE_BITS);
+			return;
+		}
+		printk("RAMDISK: Loading %d blocks into RAM disk", nblocks);
+
+		/* We found an image file system.  Load it into core! */
+		cp = rd_start;
+		while (nblocks) {
+			if (nblocks > 2) 
+			        bh = breada(ROOT_DEV, block, BLOCK_SIZE, 0,  PAGE_SIZE);
+			else
+				bh = bread(ROOT_DEV, block, BLOCK_SIZE);
+			if (!bh) {
+				printk("RAMDISK: I/O error on block %d, aborting!\n", 
+				block);
+				return;
+			}
+			(void) memcpy(cp, bh->b_data, BLOCK_SIZE);
+			brelse(bh);
+			if (!(nblocks-- & 15)) printk(".");
+			cp += BLOCK_SIZE;
+			block++;
+			i++;
+		}
+		printk("\ndone\n");
+
+		/* We loaded the file system image.  Prepare for mounting it. */
+		ROOT_DEV = ((MEM_MAJOR << 8) | RAMDISK_MINOR);
+		return;
+	}
+}
+
+/*
+ * If the root device is the RAM disk, try to load it.
+ * In order to do this, the root device is originally set to the
+ * floppy, and we later change it to be RAM disk.
+ */
+void rd_load(void)
+{
+	struct inode inode;
+	struct file filp;
+
+	/* If no RAM disk specified, give up early. */
+	if (!rd_length)
+		return;
+	printk("RAMDISK: %d bytes, starting at 0x%x\n",
+			rd_length, (int) rd_start);
+
+	/* If we are doing a diskette boot, we might have to pre-load it. */
+	if (MAJOR(ROOT_DEV) != FLOPPY_MAJOR)
+		return;
+
+	/* for Slackware install disks */
+	printk(KERN_NOTICE "VFS: Insert ramdisk floppy and press ENTER\n");
+	wait_for_keypress();
+
+	memset(&filp, 0, sizeof(filp));
+	memset(&inode, 0, sizeof(inode));
+	inode.i_rdev = ROOT_DEV;
+	filp.f_mode = 1; /* read only */
+	filp.f_inode = &inode;
+	if(blkdev_open(&inode, &filp) == 0 ){
+		do_load();
+		if(filp.f_op && filp.f_op->release)
+			filp.f_op->release(&inode,&filp);
+	}
+}
diff -r -u -N linux.orig/arch/arm/drivers/char/Makefile linux.arm/arch/arm/drivers/char/Makefile
--- linux.orig/arch/arm/drivers/char/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/Makefile	Fri Oct 27 23:14:21 1995
@@ -0,0 +1,81 @@
+#
+# 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..
+#
+
+.c.s:
+	$(CC) $(CFLAGS) -S $<
+.s.o:
+	$(AS) -c -o $*.o $<
+.c.o:
+	$(CC) $(CFLAGS) -c $<
+
+OBJS  = tty_io.o n_tty.o console.o keyboard.o serial.o \
+	tty_ioctl.o pty.o vt.o mem.o \
+	defkeymap.o consolemap.o arcmouse.o ptr.o iic.o \
+	vc_screen.o
+# vesa_blank.o selection.o
+
+SRCS  = tty_io.c n_tty.c console.c keyboard.c serial.c \
+	tty_ioctl.c pty.c vt.c mem.c \
+	defkeymap.c consolemap.c arcmouse.c ptr.c iic.c \
+	vc_screen.c
+#vc_screen.c \
+#	defkeymap.c vesa_blank.c selection.c
+
+
+ifdef CONFIG_PRINTER
+OBJS := $(OBJS) lp.o
+SRCS := $(SRCS) lp.c
+else
+MODULES := $(MODULES) lp.o
+endif
+
+ifdef CONFIG_ARMMOUSE
+OBJS := $(OBJS) arcmouse.o
+SRCS := $(SRCS) arcmouse.c
+M = 1
+endif
+
+ifdef M
+OBJS := $(OBJS) mouse.o
+SRCS := $(SRCS) mouse.c
+endif
+
+all: char.a
+
+char.a: $(OBJS)
+	$(AR) rcs char.a $(OBJS)
+	sync	
+
+ifdef MODULES
+
+modules: $(MODULES)
+	(cd ../../modules;for i in $(MODULES); do ln -sf ../drivers/char/$$i .; done)
+
+else
+
+modules:
+
+endif
+
+dep:
+	$(CPP) -M $(SRCS) > .depend
+ifdef MODULES
+	$(CPP) -M -DMODULE $(MODULES:.o=.c) >> .depend
+endif
+
+dummy:
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff -r -u -N linux.orig/arch/arm/drivers/char/arcmouse.c linux.arm/arch/arm/drivers/char/arcmouse.c
--- linux.orig/arch/arm/drivers/char/arcmouse.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/arcmouse.c	Fri Oct 27 23:14:22 1995
@@ -0,0 +1,140 @@
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+
+#pragma no_check_stack
+
+char   mouse_buttons;
+int    mouse_dxpos;
+int    mouse_dypos;
+int    mouse_present;
+char   mouse_ready;
+char   mouse_active;
+struct wait_queue *mouse_wait;
+
+void arch_mouse_close(struct inode *inode,struct file *file)
+{
+  mouse_active=0;
+  mouse_ready =0;
+  return;
+}
+
+int arch_mouse_open(struct inode *inode,struct file *file)
+{
+  unsigned long flags;
+
+  if(!mouse_present)
+    return -EINVAL;
+
+  if(mouse_active)
+    return -EBUSY;
+
+  save_flags(flags);
+  cli();
+
+  mouse_active =1;
+  mouse_ready  =0;
+  mouse_dxpos  =0;
+  mouse_dypos  =0;
+  mouse_buttons=0;
+
+  restore_flags(flags);
+
+  return 0;
+}
+
+int arch_mouse_read(struct inode *inode,struct file *file,char *buffer,int count)
+{
+  unsigned long flags;
+  int dxpos,dypos,i,buttons;
+
+  if(count<3)
+    return -EINVAL;
+
+  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;
+}
+
+int arch_mouse_write(struct inode *inode,struct file *file,char *buffer,int count)
+{
+  return -EINVAL;
+}
+
+int arch_mouse_select(struct inode *inode,struct file *file,int sel_type,select_table *wait)
+{
+  if(sel_type!=SEL_IN)
+    return 0;
+
+  if(mouse_ready)
+    return 1;
+
+  select_wait(&mouse_wait,wait);
+
+  return 0;
+}
+
+struct file_operations arch_mouse_fops=
+{
+  NULL,			/* mouse_seek */
+  arch_mouse_read,
+  arch_mouse_write,
+  NULL,			/* mouse_readdir */
+  arch_mouse_select,
+  NULL,			/* mouse_ioctl */
+  NULL,			/* mouse_mmap */
+  arch_mouse_open,
+  arch_mouse_close
+};
+
+unsigned long arch_mouse_init(unsigned long kmem_start)
+{
+  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);
+  return kmem_start;
+}
diff -r -u -N linux.orig/arch/arm/drivers/char/console.c linux.arm/arch/arm/drivers/char/console.c
--- linux.orig/arch/arm/drivers/char/console.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/console.c	Fri Oct 27 23:14:30 1995
@@ -0,0 +1,2663 @@
+/*
+ * linux/arch/arm/drivers/char/console.c
+ */
+
+/*
+ *  arcscr.c
+ *
+ * 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)'
+ *
+ *	'long con_init(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 0xd00ff80
+ 
+#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 int conv_uni_to_pc(unsigned long ucs);
+
+/* 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 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 char * 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 */
+	0x100D,       /* Red */
+	0x10D0,       /* Green */
+	0x10DD,       /* Yellow */
+	0x1D00,       /* Blue */
+	0x1D0D,       /* Magenta */
+	0x1DD0,       /* Cyan */
+	0x1DDD,       /* 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, 0x1B, 0x63, 0x7B, 0x87, 0x9F, 0xE7, 0xFF,
+                                  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(NULL_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(NULL_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);
+	G0_charset	= saved_G0;
+	G1_charset	= saved_G1;
+	intensity	= s_intensity;
+	blink		= s_blink;
+	underline	= s_underline;
+	reverse		= s_reverse;
+	forecol		= s_forecol;
+	backcol		= s_backcol;
+	need_wrap	= 0;
+}
+
+enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
+	EShash, ESsetG0, ESsetG1, ESpercent, ESignore };
+
+static void reset_terminal(int currcons,int do_clear)
+{
+	top		= 0;
+	bottom		= video_num_lines;
+	vc_state	= ESnormal;
+	ques		= 0;
+	translate	= set_translate(NORM_MAP);
+	G0_charset	= NORM_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,
+		     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)
+				    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
+				    utf_count = 0;
+				continue;
+			}
+		    } else
+		      utf_count = 0;
+
+		    tc = conv_uni_to_pc(c);
+		    if (tc == -1 || tc == -2)
+		      continue;
+		    if (tc == -3 || tc == -4) { /* hashtable not valid */
+		    				/* or symbol not found */
+		        tc = (c <= 0xff) ? translate[c] : 040;
+		        ok = 0;
+		    } else
+		        ok = 1;
+		} else {	/* no utf */
+		    tc = translate[toggle_meta ? (c|0x80) : c];
+		    ok = 0;
+		}
+
+		/* 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!
+		 */
+		if (!ok && tc && (c >= 32 || (disp_ctrl && c !=0x1b)
+		|| !((CTRL_ACTION >> c) & 1)))
+		    ok = 1;
+
+		if (vc_state == ESnormal && ok) {
+			if (need_wrap) {
+				cr(currcons);
+				lf(currcons);
+      			}
+			if(decim)
+				insert_char(currcons);
+			charwrite(currcons, pos, translate[c]);
+			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 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 = NORM_MAP;
+				else if (c == 'U')
+					G0_charset = NULL_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 = NORM_MAP;
+				else if (c == 'U')
+					G1_charset = NULL_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);
+}
+
+/*
+ *  long con_init(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.
+ *
+ */
+long con_init(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;
+}
+
+/* == 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 -r -u -N linux.orig/arch/arm/drivers/char/consolemap.c linux.arm/arch/arm/drivers/char/consolemap.c
--- linux.orig/arch/arm/drivers/char/consolemap.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/consolemap.c	Fri Oct 27 23:14:30 1995
@@ -0,0 +1,286 @@
+/*
+ * consolemap.c
+ *
+ * Mapping from internal code (such as Latin-1 or Unicode or IBM PC code)
+ * to font positions.
+ *
+ * aeb, 950210
+ */
+
+#include <linux/kd.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <asm/segment.h>
+#include "consolemap.h"
+
+static unsigned char * translations[] = {
+/* 8-bit Latin-1 mapped to the PC character set: '\0' means non-printable */
+(unsigned char *)
+	"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+	"\0\0\0\0\0\0\0\0\0\0\376\0\0\0\0\0"
+	" !\"#$%&'()*+,-./0123456789:;<=>?"
+	"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
+	"`abcdefghijklmnopqrstuvwxyz{|}~\0"
+	"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+	"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+	"\377\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376"
+	"\370\361\375\376\376\346\024\371\376\376\247\257\254\253\376\250"
+	"\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376"
+	"\376\245\376\376\376\376\231\376\350\376\376\376\232\376\376\341"
+	"\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213"
+	"\376\244\225\242\223\376\224\366\355\227\243\226\201\376\376\230",
+/* vt100 graphics */
+(unsigned char *)
+	"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+	"\0\0\0\0\0\0\0\0\0\0\376\0\0\0\0\0"
+	" !\"#$%&'()*+,-./0123456789:;<=>?"
+	"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^ "
+	"\004\261\007\007\007\007\370\361\007\007\331\277\332\300\305\304"
+	"\304\304\137\137\303\264\301\302\263\363\362\343\330\234\007\0"
+	"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+	"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+	"\377\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376"
+	"\370\361\375\376\376\346\024\371\376\376\247\257\254\253\376\250"
+	"\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376"
+	"\376\245\376\376\376\376\231\376\376\376\376\376\232\376\376\341"
+	"\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213"
+	"\376\244\225\242\223\376\224\366\376\227\243\226\201\376\376\230",
+/* IBM graphics: minimal translations (BS, CR, LF, LL, SO, SI and ESC) */
+(unsigned char *)
+	"\000\001\002\003\004\005\006\007\000\011\000\013\000\000\000\000"
+	"\020\021\022\023\024\025\026\027\030\031\032\000\034\035\036\037"
+	"\040\041\042\043\044\045\046\047\050\051\052\053\054\055\056\057"
+	"\060\061\062\063\064\065\066\067\070\071\072\073\074\075\076\077"
+	"\100\101\102\103\104\105\106\107\110\111\112\113\114\115\116\117"
+	"\120\121\122\123\124\125\126\127\130\131\132\133\134\135\136\137"
+	"\140\141\142\143\144\145\146\147\150\151\152\153\154\155\156\157"
+	"\160\161\162\163\164\165\166\167\170\171\172\173\174\175\176\177"
+	"\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217"
+	"\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237"
+	"\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257"
+	"\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277"
+	"\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317"
+	"\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"
+	"\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357"
+	"\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377",
+ /* USER: customizable mappings, initialized as the previous one (IBM) */
+(unsigned char *)
+	"\000\001\002\003\004\005\006\007\010\011\000\013\000\000\016\017"
+	"\020\021\022\023\024\025\026\027\030\031\032\000\034\035\036\037"
+	"\040\041\042\043\044\045\046\047\050\051\052\053\054\055\056\057"
+	"\060\061\062\063\064\065\066\067\070\071\072\073\074\075\076\077"
+	"\100\101\102\103\104\105\106\107\110\111\112\113\114\115\116\117"
+	"\120\121\122\123\124\125\126\127\130\131\132\133\134\135\136\137"
+	"\140\141\142\143\144\145\146\147\150\151\152\153\154\155\156\157"
+	"\160\161\162\163\164\165\166\167\170\171\172\173\174\175\176\177"
+	"\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217"
+	"\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237"
+	"\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257"
+	"\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277"
+	"\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317"
+	"\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"
+	"\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357"
+	"\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377"
+};
+
+/* the above mappings are not invertible - this is just a best effort */
+static unsigned char * inv_translate = NULL;
+static unsigned char inv_norm_transl[E_TABSZ];
+static unsigned char * inverse_translations[4] = { NULL, NULL, NULL, NULL };
+
+static void set_inverse_transl(int i)
+{
+	int j;
+	unsigned char *p = translations[i];
+	unsigned char *q = inverse_translations[i];
+
+	if (!q) {
+		/* slightly messy to avoid calling kmalloc too early */
+		q = inverse_translations[i] = ((i == NORM_MAP)
+			? inv_norm_transl
+			: (unsigned char *) kmalloc(E_TABSZ, GFP_KERNEL));
+		if (!q)
+			return;
+	}
+	for (j=0; j<E_TABSZ; j++)
+		q[j] = 0;
+	for (j=0; j<E_TABSZ; j++)
+		if (q[p[j]] < 32)	/* prefer '-' above SHY etc. */
+			q[p[j]] = j;
+}
+
+unsigned char *set_translate(int m)
+{
+	if (!inverse_translations[m])
+		set_inverse_transl(m);
+	inv_translate = inverse_translations[m];
+	return translations[m];
+}
+
+/*
+ * Inverse translation is impossible for several reasons:
+ * 1. The translation maps are not 1-1
+ * 2. The text may have been written while a different translation map
+ *    was active
+ * Still, it is now possible to a certain extent to cut and paste non-ASCII.
+ */
+unsigned char inverse_translate(unsigned char c) {
+	return ((inv_translate && inv_translate[c]) ? inv_translate[c] : c);
+}
+
+/*
+ * Load customizable translation table
+ * arg points to a 256 byte translation table.
+ */
+int con_set_trans(char * arg)
+{
+	int i;
+	unsigned char *p = translations[USER_MAP];
+
+	i = verify_area(VERIFY_READ, (void *)arg, E_TABSZ);
+	if (i)
+		return i;
+
+	for (i=0; i<E_TABSZ ; i++)
+		p[i] = get_fs_byte(arg+i);
+	p[012] = p[014] = p[015] = p[033] = 0;
+	set_inverse_transl(USER_MAP);
+	return 0;
+}
+
+int con_get_trans(char * arg)
+{
+	int i;
+	unsigned char *p = translations[USER_MAP];
+
+	i = verify_area(VERIFY_WRITE, (void *)arg, E_TABSZ);
+	if (i)
+		return i;
+
+	for (i=0; i<E_TABSZ ; i++) put_fs_byte(p[i],arg+i);
+	return 0;
+}
+
+/*
+ * Unicode -> current font conversion 
+ *
+ * A font has at most 512 chars, usually 256.
+ * But one font position may represent several Unicode chars
+ * (and moreover, hashtables work best when they are not too full),
+ * so pick HASHSIZE somewhat larger than 512.
+ * Since there are likely to be long consecutive stretches
+ * (like U+0000 to U+00FF), HASHSTEP should not be too small.
+ * Searches longer than MAXHASHLEVEL steps are refused, unless
+ * requested explicitly.
+ *
+ * Note: no conversion tables are compiled in, so the user
+ * must supply an explicit mapping herself. See kbd-0.90 (or an
+ * earlier kernel version) for the default Unicode-to-PC mapping.
+ * Usually, the mapping will be loaded simultaneously with the font.
+ */
+
+#define HASHSIZE   641
+#define HASHSTEP   189		/* yields hashlevel = 3 initially */
+#define MAXHASHLEVEL 6
+static struct unipair hashtable[HASHSIZE];
+
+int hashtable_contents_valid = 0; 	/* cleared by setfont */
+
+static unsigned int hashsize;
+static unsigned int hashstep;
+static unsigned int hashlevel;
+static unsigned int maxhashlevel;
+
+void
+con_clear_unimap(struct unimapinit *ui) {
+	int i;
+
+	/* read advisory values for hash algorithm */
+	hashsize = ui->advised_hashsize;
+	if (hashsize < 256 || hashsize > HASHSIZE)
+	  hashsize = HASHSIZE;
+	hashstep = (ui->advised_hashstep % hashsize);
+	if (hashstep < 64)
+	  hashstep = HASHSTEP;
+	maxhashlevel = ui->advised_hashlevel;
+	if (!maxhashlevel)
+	  maxhashlevel = MAXHASHLEVEL;
+	if (maxhashlevel > hashsize)
+	  maxhashlevel = hashsize;
+
+	/* initialize */
+	hashlevel = 0;
+	for (i=0; i<hashsize; i++)
+	  hashtable[i].unicode = 0xffff;
+	hashtable_contents_valid = 1;
+}
+
+int
+con_set_unimap(ushort ct, struct unipair *list){
+	int i, lct;
+	ushort u, hu;
+	struct unimapinit hashdefaults = { 0, 0, 0 };
+
+	if (!hashtable_contents_valid)
+	  con_clear_unimap(&hashdefaults);
+	while(ct) {
+	    u = get_fs_word(&list->unicode);
+	    i = u % hashsize;
+	    lct = 1;
+	    while ((hu = hashtable[i].unicode) != 0xffff && hu != u) {
+		if (lct++ >=  maxhashlevel)
+		  return -ENOMEM;
+		i += hashstep;
+		if (i >= hashsize)
+		  i -= hashsize;
+	    }
+	    if (lct > hashlevel)
+	      hashlevel = lct;
+	    hashtable[i].unicode = u;
+	    hashtable[i].fontpos = get_fs_word(&list->fontpos);
+	    list++;
+	    ct--;
+	}
+	return 0;
+}
+
+int
+con_get_unimap(ushort ct, ushort *uct, struct unipair *list){
+	int i, ect;
+
+	ect = 0;
+	if (hashtable_contents_valid)
+	  for (i = 0; i<hashsize; i++)
+	    if (hashtable[i].unicode != 0xffff) {
+		if (ect++ < ct) {
+		    put_fs_word(hashtable[i].unicode, &list->unicode);
+		    put_fs_word(hashtable[i].fontpos, &list->fontpos);
+		    list++;
+		}
+	    }
+	put_fs_word(ect, uct);
+	return ((ect <= ct) ? 0 : -ENOMEM);
+}
+
+int
+conv_uni_to_pc(unsigned long ucs) {
+      int i, h;
+
+      if (!hashtable_contents_valid || ucs < 0x20)
+	return -3;
+      if (ucs == 0xffff || ucs == 0xfffe)
+	return -1;
+      if (ucs == 0xfeff || (ucs >= 0x200a && ucs <= 0x200f))
+	return -2;
+      
+      h = ucs % hashsize;
+      for (i = 0; i < hashlevel; i++) {
+	  if (hashtable[h].unicode == ucs)
+	    return hashtable[h].fontpos;
+	  if ((h += hashstep) >= hashsize)
+	    h -= hashsize;
+      }
+
+      return -4;		/* not found */
+}
diff -r -u -N linux.orig/arch/arm/drivers/char/consolemap.h linux.arm/arch/arm/drivers/char/consolemap.h
--- linux.orig/arch/arm/drivers/char/consolemap.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/consolemap.h	Fri Oct 27 23:14:39 1995
@@ -0,0 +1,13 @@
+/*
+ * consolemap.h
+ *
+ * Interface between console.c, selection.c  and consolemap.c
+ */
+#define NORM_MAP 0
+#define GRAF_MAP 1
+#define NULL_MAP 2
+#define USER_MAP 3
+
+extern int hashtable_contents_valid;
+extern unsigned char inverse_translate(unsigned char c);
+extern unsigned char *set_translate(int m);
diff -r -u -N linux.orig/arch/arm/drivers/char/defkeymap.c linux.arm/arch/arm/drivers/char/defkeymap.c
--- linux.orig/arch/arm/drivers/char/defkeymap.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/defkeymap.c	Fri Oct 27 23:14:20 1995
@@ -0,0 +1,331 @@
+#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 -r -u -N linux.orig/arch/arm/drivers/char/diacr.h linux.arm/arch/arm/drivers/char/diacr.h
--- linux.orig/arch/arm/drivers/char/diacr.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/diacr.h	Fri Oct 27 23:14:20 1995
@@ -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 -r -u -N linux.orig/arch/arm/drivers/char/iic.c linux.arm/arch/arm/drivers/char/iic.c
--- linux.orig/arch/arm/drivers/char/iic.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/iic.c	Fri Oct 27 23:14:20 1995
@@ -0,0 +1,102 @@
+#pragma no_check_stack
+#include <asm/system.h>
+
+#define DELAY udelay(10)
+
+/* extern volatile char *const ioc; */
+#define ioc ((volatile char *)0x03200000)
+
+static void iic_start(void)
+{
+  char out;
+
+  out=(ioc[0] & 0xFC) | 0xC0;
+
+  ioc[0]=out|3;  DELAY;
+  ioc[0]=out|2;  DELAY;
+}
+
+static void iic_stop(void)
+{
+  char out;
+
+  out=(ioc[0] & 0xFC) | 0xC0;
+
+  DELAY;  ioc[0]=out|2;
+  DELAY;  ioc[0]=out|3;
+}
+
+static int iic_sendbyte(char b)
+{
+  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 char iic_recvbyte(void)
+{
+  char out,in;
+  int i;
+
+  out=(ioc[0] & 0xFC) | 0xC0;
+
+  ioc[0]=out;
+  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(int addr,int loc,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]))
+        goto error;
+  }
+  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 -r -u -N linux.orig/arch/arm/drivers/char/kbd_kern.h linux.arm/arch/arm/drivers/char/kbd_kern.h
--- linux.orig/arch/arm/drivers/char/kbd_kern.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/kbd_kern.h	Fri Oct 27 23:14:23 1995
@@ -0,0 +1,127 @@
+#ifndef _KBD_KERN_H
+#define _KBD_KERN_H
+
+#include <linux/interrupt.h>
+#define set_leds() mark_bh(KEYBOARD_BH)
+
+#include <linux/keyboard.h>
+
+extern char *func_table[MAX_NR_FUNC];
+extern char func_buf[];
+extern char *funcbufptr;
+extern int funcbufsize, funcbufleft;
+
+/*
+ * kbd->xxx contains the VC-local things (flag settings etc..)
+ *
+ * Note: externally visible are LED_SCR, LED_NUM, LED_CAP defined in kd.h
+ *       The code in KDGETLED / KDSETLED depends on the internal and
+ *       external order being the same.
+ *
+ * Note: lockstate is used as index in the array key_map.
+ */
+struct kbd_struct {
+
+	unsigned char lockstate;
+/* 8 modifiers - the names do not have any meaning at all;
+   they can be associated to arbitrarily chosen keys */
+#define VC_SHIFTLOCK	KG_SHIFT	/* shift lock mode */
+#define VC_ALTGRLOCK	KG_ALTGR	/* altgr lock mode */
+#define VC_CTRLLOCK	KG_CTRL 	/* control lock mode */
+#define VC_ALTLOCK	KG_ALT  	/* alt lock mode */
+#define VC_SHIFTLLOCK	KG_SHIFTL	/* shiftl lock mode */
+#define VC_SHIFTRLOCK	KG_SHIFTR	/* shiftr lock mode */
+#define VC_CTRLLLOCK	KG_CTRLL 	/* ctrll lock mode */
+#define VC_CTRLRLOCK	KG_CTRLR 	/* ctrlr lock mode */
+
+	unsigned char ledmode:2; 	/* one 2-bit value */
+#define LED_SHOW_FLAGS 0        /* traditional state */
+#define LED_SHOW_IOCTL 1        /* only change leds upon ioctl */
+#define LED_SHOW_MEM 2          /* `heartbeat': peek into memory */
+
+	unsigned char ledflagstate:3;	/* flags, not lights */
+	unsigned char default_ledflagstate:3;
+#define VC_SCROLLOCK	0	/* scroll-lock mode */
+#define VC_NUMLOCK	1	/* numeric lock mode */
+#define VC_CAPSLOCK	2	/* capslock mode */
+
+	unsigned char kbdmode:2;	/* one 2-bit value */
+#define VC_XLATE	0	/* translate keycodes using keymap */
+#define VC_MEDIUMRAW	1	/* medium raw (keycode) mode */
+#define VC_RAW		2	/* raw (scancode) mode */
+#define VC_UNICODE	3	/* Unicode mode */
+
+	unsigned char modeflags:5;
+#define VC_APPLIC	0	/* application key mode */
+#define VC_CKMODE	1	/* cursor key mode */
+#define VC_REPEAT	2	/* keyboard repeat */
+#define VC_CRLF		3	/* 0 - enter sends CR, 1 - enter sends CRLF */
+#define VC_META		4	/* 0 - meta, 1 - meta=prefix with ESC */
+};
+
+extern struct kbd_struct kbd_table[];
+
+extern unsigned long kbd_init(unsigned long);
+
+extern unsigned char getledstate(void);
+extern void setledstate(struct kbd_struct *kbd, unsigned int led);
+#ifndef __ARM__
+extern inline int vc_kbd_mode(struct kbd_struct * kbd, int flag)
+{
+	return ((kbd->modeflags >> flag) & 1);
+}
+
+extern inline int vc_kbd_led(struct kbd_struct * kbd, int flag)
+{
+	return ((kbd->ledflagstate >> flag) & 1);
+}
+
+extern inline void set_vc_kbd_mode(struct kbd_struct * kbd, int flag)
+{
+	kbd->modeflags |= 1 << flag;
+}
+
+extern inline void set_vc_kbd_led(struct kbd_struct * kbd, int flag)
+{
+	kbd->ledflagstate |= 1 << flag;
+}
+
+extern inline void clr_vc_kbd_mode(struct kbd_struct * kbd, int flag)
+{
+	kbd->modeflags &= ~(1 << flag);
+}
+
+extern inline void clr_vc_kbd_led(struct kbd_struct * kbd, int flag)
+{
+	kbd->ledflagstate &= ~(1 << flag);
+}
+
+extern inline void chg_vc_kbd_lock(struct kbd_struct * kbd, int flag)
+{
+	kbd->lockstate ^= 1 << flag;
+}
+
+extern inline void chg_vc_kbd_mode(struct kbd_struct * kbd, int flag)
+{
+	kbd->modeflags ^= 1 << flag;
+}
+
+extern inline void chg_vc_kbd_led(struct kbd_struct * kbd, int flag)
+{
+	kbd->ledflagstate ^= 1 << flag;
+}
+#else
+#define vc_kbd_mode(kbd,flag)		(((kbd)->modeflags >> flag) & 1)
+#define vc_kbd_led(kbd,flag)		(((kbd)->ledflagstate >> flag) & 1)
+#define set_vc_kbd_mode(kbd,flag)	((kbd)->modeflags |= 1 << flag)
+#define set_vc_kbd_led(kbd,flag)	((kbd)->ledflagstate |= 1 << flag)
+#define clr_vc_kbd_mode(kbd,flag)	((kbd)->modeflags &= ~(1 << flag))
+#define clr_vc_kbd_led(kbd,flag)	((kbd)->ledflagstate &= ~(1 << flag))
+#define chg_vc_kbd_lock(kbd,flag)	((kbd)->lockstate ^= 1 << flag)
+#define chg_vc_kbd_mode(kbd,flag)	((kbd)->modeflags ^= 1 << flag)
+#define chg_vc_kbd_led(kbd,flag)	((kbd)->ledflagstate ^= 1 << flag)
+#endif
+
+#define U(x) ((x) ^ 0xf000)
+
+#endif
diff -r -u -N linux.orig/arch/arm/drivers/char/keyboard.c linux.arm/arch/arm/drivers/char/keyboard.c
--- linux.orig/arch/arm/drivers/char/keyboard.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/keyboard.c	Fri Oct 27 23:14:24 1995
@@ -0,0 +1,1062 @@
+#define IRQ_KEYBOARDRX 15
+#define IRQ_KEYBOARDTX 14
+
+#define VERSION 002
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/ptrace.h>
+
+#include <asm/irq.h>
+
+#include "kbd_kern.h"
+#include "diacr.h"
+#include "vt_kern.h"
+
+#define SIZE(x) (sizeof(x)/sizeof((x)[0]))
+
+/* Mouse stuff */
+
+extern struct wait_queue *mouse_wait;
+extern char		mouse_buttons;
+extern int		mouse_dxpos;
+extern int		mouse_dypos;
+extern char		mouse_ready;
+
+#define KBD_IRQ_RPT	1
+#define KBD_IRQ_KEY	2
+
+#ifndef KBD_DEFMODE
+#define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META))
+#endif
+
+#ifndef KBD_DEFLEDS
+#define KBD_DEFLEDS 0 /* VC_NUMLOCK */
+#endif
+
+#ifndef KBD_DEFLOCK
+#define KBD_DEFLOCK 0
+#endif
+
+#define KBD_SHIFT 	0x11
+#define KBD_CTRL  	0x22
+#define KBD_ALT   	0x44
+
+#define KBD_LSHIFT	0x01
+#define KBD_LCTRL	0x02
+#define KBD_LALT	0x04
+#define KBD_RSHIFT	0x10
+#define KBD_RCTRL	0x20
+#define KBD_RALT	0x40
+
+#define HRST 0xFF
+#define RAK1 0xFE
+#define RAK2 0xFD
+#define SMAK 0x33
+
+extern void poke_blanked_console (void);
+extern void ctrl_alt_del (void);
+extern void change_console (unsigned int console);
+static void process_key(int keycode, char up_flag, int repeattimeout);
+static unsigned char getleds(void);
+
+static unsigned char k_down[NR_SHIFT] = {0, };
+
+static int want_console    = -1;
+static int last_console    = 0; 		/* last used VC */
+static int dead_key_next   = 0;
+
+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 */
+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;
+
+static int kbd_rollover[3] = {-1,-1,-1};
+
+extern unsigned char	*ioc;
+static unsigned char	kbd_sendvala[4];
+static unsigned char	kbd_sendptri;
+static unsigned char	kbd_sendptro;
+static unsigned char    ledstate = 0xff;
+static unsigned char    kbd_repeatkey=-1;
+
+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_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_ignore, do_ignore, do_ignore, do_ignore
+};
+
+typedef void (*void_fnp)(void);
+typedef void (void_fn)(void);
+
+static void_fn enter, show_ptregs, send_intr, lastcons, caps_toggle,
+	num, hold, scroll_forw, scroll_back, boot_it, caps_on, compose,
+	SAK, decr_console, incr_console, show_stack/*, show_floppy*/;
+
+static void_fnp spec_fn_table[] = {
+	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/*,	show_floppy*/
+};
+
+/* 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
+};
+
+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;
+
+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 */
+}
+
+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
+}
+
+
+/* ----------------------------------------------------------------------------------------- */
+
+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(int keycode, char up_flag)
+{
+  char process=0;
+  if(keycode >= 0x70 && keycode <= 0x72)
+  {
+    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;
+    }
+    mouse_ready=1;
+    wake_up_interruptible(&mouse_wait);
+    mark_bh(KEYBOARD_BH);
+    return;
+  }
+
+  tty = ttytab[fg_console];
+  kbd = kbd_table + fg_console;
+
+  if(!up_flag)
+  {
+    if(kbd_rollover[0] != -1)
+    {
+      if(kbd_rollover[1] != -1)
+      {
+        if(kbd_rollover[2] != -1)
+          return;
+        kbd_rollover[2] = kbd_rollover[1];
+      }
+      kbd_rollover[1] = kbd_rollover[0];
+    }
+    kbd_rollover[0] = keycode;
+    process=1;
+  }
+  else
+  {
+    if(kbd_rollover[0] == keycode)
+      kbd_rollover[0] = -1;
+    if(kbd_rollover[1] == keycode)
+      kbd_rollover[1] = -1;
+    if(kbd_rollover[2] == keycode)
+      kbd_rollover[2] = -1;
+    process=1;
+  }
+
+  del_timer(&key_timer);
+  rep = 0;
+
+  if(kbd->kbdmode == VC_RAW)
+    put_queue(keycode|(up_flag?0x80:0));
+  else
+  {
+    if(process)
+      process_key(keycode, up_flag, HZ*300/1000);
+    mark_bh(KEYBOARD_BH);
+  }
+}
+
+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;
+  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;
+  static unsigned char kbd_state=0;
+  static unsigned char kbd_keyhigh=0;
+
+  keyval=ioc[4];
+
+  switch(kbd_state)
+  {
+    case 0:/* initial reset condition */
+      if(keyval == HRST)
+      {
+        kbd_reset();
+        kbd_sendval(RAK1);
+        kbd_state = 1;
+      }
+      else
+      {
+        kbd_sendval(HRST);
+        kbd_state = 0;
+      }
+      break;
+
+    case 1:/* Sent RAK1 */
+      if(keyval == RAK1)
+      {
+        kbd_sendval(RAK2);
+        kbd_state = 2;
+      }
+      else
+      {
+        kbd_sendval(HRST);
+        kbd_state = 0;
+      }
+      break;
+
+    case 2:/* Sent RAK2 */
+      if(keyval == RAK2)
+      {
+        kbd_sendval(SMAK);
+        kbd_state = 3;
+        ledstate  = 0xff;
+        mark_bh(KEYBOARD_BH);
+      }
+      else
+      {
+        kbd_sendval(HRST);
+        kbd_state = 0;
+      }
+      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(0x3F);
+            break;
+
+          case 0xD0:
+            kbd_keyhigh=keyval;
+            kbd_state = 5;
+            kbd_sendval(0x3F);
+            break;
+
+          default:
+            kbd_state = 0;
+            kbd_sendval(HRST);
+        }
+      }
+      else
+      {
+        kbd_mousedx=keyval&0x40?keyval|0x80:keyval;
+        kbd_state = 6;
+        kbd_sendval(0x3F);
+      }
+      break;
+
+    case 4:
+      if((keyval & 0xF0)!=0xC0)
+      {
+        kbd_state = 0;
+        kbd_sendval(HRST);
+      }
+      else
+      {
+        kbd_state = 3;
+        kbd_sendval(SMAK);
+        if(((kbd_keyhigh^keyval)&0xF0)==0)
+          kbd_key((keyval&0x0F)|((kbd_keyhigh<<4)&0xF0),0);
+      }
+      break;
+
+    case 5:
+      if((keyval & 0xF0)!=0xD0)
+      {
+        kbd_state = 0;
+        kbd_sendval(HRST);
+      }
+      else
+      {
+        kbd_state = 3;
+        kbd_sendval(SMAK);
+        if(((kbd_keyhigh^keyval)&0xF0)==0)
+          kbd_key((keyval&0x0F)|((kbd_keyhigh<<4)&0xF0),1);
+      }
+      break;
+
+    case 6:
+      if(keyval & 0x80)
+      {
+        kbd_state = 0;
+        kbd_sendval(HRST);
+      }
+      else
+      {
+        kbd_state = 3;
+        kbd_sendval(SMAK);
+        mouse_dypos+=(int)((char)(keyval&0x40?keyval|0x80:keyval));
+        mouse_dxpos+=(int)kbd_mousedx;
+        mouse_ready=1;
+        wake_up_interruptible(&mouse_wait);
+        mark_bh(KEYBOARD_BH);
+      }
+      break;
+  }
+}
+
+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);
+  }
+}
+
+static void process_key(int keycode, char up_flag, int repeattimeout)
+{
+  del_timer(&key_timer);
+
+  if(!up_flag)
+  {
+    kbd_repeatkey = keycode;
+    if(vc_kbd_mode(kbd, VC_REPEAT))
+    {
+      key_timer.expires = repeattimeout;
+      add_timer(&key_timer);
+    }
+  }
+  else
+    del_timer(&key_timer);
+
+  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;
+    int     shift_final = shift_state ^ kbd->lockstate;
+    u_short *key_map = key_maps[shift_final];
+
+    if(key_map != NULL)
+    {
+      keysym = key_map[keycode];
+      type   = KTYP(keysym);
+
+      if((type & 15) == KT_SHIFT)
+        kbd_rollover[0] = -1;
+
+      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);
+      }
+      else
+      {
+        if(!up_flag)
+          to_utf8(keysym);
+      }
+    }
+    else
+    {
+      keysym = U(plain_map[keycode]);
+      type   = KTYP(keysym);
+      if(type == KT_SHIFT)
+        (*key_handler[type])(keysym & 0xff, up_flag);
+    }
+  }
+}
+
+static void kbd_bh(void *unused)
+{
+  unsigned char leds;
+
+  tty = ttytab[fg_console];
+  kbd = kbd_table + fg_console;
+
+  if(rep)
+    process_key(kbd_repeatkey, 0, HZ*30/1000);
+  rep = 0;
+
+  if(want_console >= 0)
+  {
+    if(want_console != fg_console)
+    {
+      last_console = fg_console;
+      change_console(want_console);
+    }
+    want_console = -1;
+  }
+
+  leds = getleds();
+  if(leds != ledstate)
+  {
+    unsigned long flags;
+    ledstate = leds;
+#define LED_LKCAPS		1
+#define LED_LKNUM		2
+#define LED_LKSCRL		4
+    leds = ((leds & (1<<VC_SCROLLOCK))?LED_LKSCRL:0) | ((leds & (1<<VC_NUMLOCK))?LED_LKNUM:0) |
+            ((leds & (1<<VC_CAPSLOCK))?LED_LKCAPS:0);
+    save_flags(flags); cli(); intr_count -= 1;
+    kbd_sendval(leds);
+    intr_count += 1; restore_flags(flags);
+  }
+  poke_blanked_console();
+}
+
+/* ----------------------------------------------------------------------------------------- */
+
+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);
+}
+int debug_flag=0;
+static void show_ptregs(void)
+{ debug_flag ^= 1;
+  if(!pt_regs)
+    return;
+  printk("\n");
+}
+
+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;
+
+  if(tty->stopped)
+    start_tty(tty);
+  else
+    stop_tty(tty);
+}
+
+static void num(void)
+{
+  if(vc_kbd_mode(kbd,VC_APPLIC))
+  {
+    applkey('P', 1);
+    return;
+  }
+  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 || (tty->termios && I_IGNBRK(tty)))
+    return;
+  tty_insert_flip_char(tty, 0, TTY_BREAK);
+}
+
+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);
+}
+
+static void do_ignore(unsigned char value, char up_flag)
+{
+}
+
+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;
+
+  if(diacr)
+    value = handle_diacr(value);
+
+  if(dead_key_next)
+  {
+    dead_key_next = 0;
+    diacr = value;
+    return;
+  }
+if (debug_flag) printk("inserting into put_queue\n");
+  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)
+  {
+    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<sizeof(func_table) && func_table[value]!=NULL)
+    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));
+}
+
+void compute_shiftstate(void)
+{
+}
+
+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;
+  }
+}
+
+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);
+}
+
+/* --------------------------------------------------------------------------------------- *
+ * Led driver										   *
+ * --------------------------------------------------------------------------------------- */
+
+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 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;
+}
+
+/* --------------------------------------------------------------------------------------- *
+ *                                   Keyboard initialisation                               *
+ *    kbd_rx must be allocated first, then kbd_tx                                          *
+ * --------------------------------------------------------------------------------------- */
+
+unsigned long kbd_init(unsigned long kmem_start)
+{
+	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.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);
+	printk("%s keyboard driver installed (%s) v%d.%02d.\n", "A5000", "English", VERSION/100, VERSION%100);
+	return kmem_start;
+}
+
diff -r -u -N linux.orig/arch/arm/drivers/char/lp.c linux.arm/arch/arm/drivers/char/lp.c
--- linux.orig/arch/arm/drivers/char/lp.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/lp.c	Fri Oct 27 23:14:24 1995
@@ -0,0 +1,618 @@
+/*
+ * Copyright (C) 1992 by Jim Weigand and Linus Torvalds
+ * Copyright (C) 1992,1993 by Michael K. Johnson
+ * - Thanks much to Gunter Windau for pointing out to me where the error
+ *   checking ought to be.
+ * Copyright (C) 1993 by Nigel Gamble (added interrupt code)
+ * Copyright (C) 1994 by Alan Cox (Modularised it)
+ * LPCAREFUL, LPABORT, LPGETSTATUS added by Chris Metcalf, metcalf@lcs.mit.edu
+ */
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/lp.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+
+/* the BIOS manuals say there can be up to 4 lpt devices
+ * but I have not seen a board where the 4th address is listed
+ * if you have different hardware change the table below 
+ * please let me know if you have different equipment
+ * if you have more than 3 printers, remember to increase LP_NO
+ */
+struct lp_struct lp_table[] = {
+	{ 0x3bc, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
+	{ 0x378, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
+	{ 0x278, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
+}; 
+#define LP_NO 3
+
+/* Test if printer is ready (and optionally has no error conditions) */
+#define LP_READY(minor, status) \
+  ((LP_F(minor) & LP_CAREFUL) ? _LP_CAREFUL_READY(status) : (status & LP_PBUSY))
+#define LP_CAREFUL_READY(minor, status) \
+  ((LP_F(minor) & LP_CAREFUL) ? _LP_CAREFUL_READY(status) : 1)
+#define _LP_CAREFUL_READY(status) \
+   (status & (LP_PBUSY|LP_POUTPA|LP_PSELECD|LP_PERRORP)) == \
+      (LP_PBUSY|LP_PSELECD|LP_PERRORP) 
+
+/* Allow old versions of tunelp to continue to work */
+#define OLD_LPCHAR   0x0001
+#define OLD_LPTIME   0x0002
+#define OLD_LPABORT  0x0004
+#define OLD_LPSETIRQ 0x0005
+#define OLD_LPGETIRQ 0x0006
+#define OLD_LPWAIT   0x0008
+#define OLD_IOCTL_MAX 8
+
+/* 
+ * All my debugging code assumes that you debug with only one printer at
+ * a time. RWWH
+ */
+
+#undef LP_DEBUG
+
+static int lp_reset(int minor)
+{
+	int testvalue;
+	unsigned char command;
+
+	command = LP_PSELECP | LP_PINITP;
+
+	/* reset value */
+	outb_p(0, LP_C(minor));
+	for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
+		;
+	outb_p(command, LP_C(minor));
+	return LP_S(minor);
+}
+
+#ifdef LP_DEBUG
+static int lp_max_count = 1;
+#endif
+
+static int lp_char_polled(char lpchar, int minor)
+{
+	int status = 0, wait = 0;
+	unsigned long count  = 0; 
+
+	do {
+		status = LP_S(minor);
+		count ++;
+		if(need_resched)
+			schedule();
+	} while(!LP_READY(minor,status) && count < LP_CHAR(minor));
+
+	if (count == LP_CHAR(minor)) {
+		return 0;
+		/* we timed out, and the character was /not/ printed */
+	}
+#ifdef LP_DEBUG
+	if (count > lp_max_count) {
+		printk("lp success after %d counts.\n",count);
+		lp_max_count=count;
+	}
+#endif
+	outb_p(lpchar, LP_B(minor));
+	/* must wait before taking strobe high, and after taking strobe
+	   low, according spec.  Some printers need it, others don't. */
+	while(wait != LP_WAIT(minor)) wait++;
+        /* control port takes strobe high */
+	outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
+	while(wait) wait--;
+        /* take strobe low */
+	outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
+
+	return 1;
+}
+
+static int lp_char_interrupt(char lpchar, int minor)
+{
+	int wait = 0;
+	unsigned char status;
+
+
+	if (!((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)
+	|| !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)
+	|| !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)) {
+
+		if (!LP_CAREFUL_READY(minor, status))
+			return 0;
+		outb_p(lpchar, LP_B(minor));
+		/* must wait before taking strobe high, and after taking strobe
+		   low, according spec.  Some printers need it, others don't. */
+		while(wait != LP_WAIT(minor)) wait++;
+		/* control port takes strobe high */
+		outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
+		while(wait) wait--;
+		/* take strobe low */
+		outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
+		return 1;
+	}
+
+	return 0;
+}
+
+#ifdef LP_DEBUG
+	unsigned int lp_total_chars = 0;
+	unsigned int lp_last_call = 0;
+#endif
+
+static void lp_interrupt(int irq, struct pt_regs *regs)
+{
+	struct lp_struct *lp = &lp_table[0];
+	struct lp_struct *lp_end = &lp_table[LP_NO];
+
+	while (irq != lp->irq) {
+		if (++lp >= lp_end)
+			return;
+	}
+
+	wake_up(&lp->lp_wait_q);
+}
+
+static int lp_write_interrupt(struct inode * inode, struct file * file, char * buf, int count)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+	unsigned long copy_size;
+	unsigned long total_bytes_written = 0;
+	unsigned long bytes_written;
+	struct lp_struct *lp = &lp_table[minor];
+	unsigned char status;
+
+	do {
+		bytes_written = 0;
+		copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);
+		memcpy_fromfs(lp->lp_buffer, buf, copy_size);
+
+		while (copy_size) {
+			if (lp_char_interrupt(lp->lp_buffer[bytes_written], minor)) {
+				--copy_size;
+				++bytes_written;
+			} else {
+				int rc = total_bytes_written + bytes_written;
+				status = LP_S(minor);
+				if ((status & LP_POUTPA)) {
+					printk(KERN_INFO "lp%d out of paper\n", minor);
+					if (LP_F(minor) & LP_ABORT)
+						return rc?rc:-ENOSPC;
+				} else if (!(status & LP_PSELECD)) {
+					printk(KERN_INFO "lp%d off-line\n", minor);
+					if (LP_F(minor) & LP_ABORT)
+						return rc?rc:-EIO;
+				} else if (!(status & LP_PERRORP)) {
+					printk(KERN_ERR "lp%d printer error\n", minor);
+					if (LP_F(minor) & LP_ABORT)
+						return rc?rc:-EIO;
+				}
+				cli();
+				outb_p((LP_PSELECP|LP_PINITP|LP_PINTEN), (LP_C(minor)));
+				status = LP_S(minor);
+				if ((!(status & LP_PACK) || (status & LP_PBUSY))
+				  && LP_CAREFUL_READY(minor, status)) {
+					outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
+					sti();
+					continue;
+				}
+				current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
+				interruptible_sleep_on(&lp->lp_wait_q);
+				outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
+				sti();
+				if (current->signal & ~current->blocked) {
+					if (total_bytes_written + bytes_written)
+						return total_bytes_written + bytes_written;
+					else
+						return -EINTR;
+				}
+			}
+		}
+
+		total_bytes_written += bytes_written;
+		buf += bytes_written;
+		count -= bytes_written;
+
+	} while (count > 0);
+
+	return total_bytes_written;
+}
+
+static int lp_write_polled(struct inode * inode, struct file * file,
+			   char * buf, int count)
+{
+	int  retval;
+	unsigned int minor = MINOR(inode->i_rdev);
+	char c, *temp = buf;
+
+#ifdef LP_DEBUG
+	if (jiffies-lp_last_call > LP_TIME(minor)) {
+		lp_total_chars = 0;
+		lp_max_count = 1;
+	}
+	lp_last_call = jiffies;
+#endif
+
+	temp = buf;
+	while (count > 0) {
+		c = get_fs_byte(temp);
+		retval = lp_char_polled(c, minor);
+		/* only update counting vars if character was printed */
+		if (retval) { count--; temp++;
+#ifdef LP_DEBUG
+			lp_total_chars++;
+#endif
+		}
+		if (!retval) { /* if printer timed out */
+			int status = LP_S(minor);
+
+			if (status & LP_POUTPA) {
+				printk(KERN_INFO "lp%d out of paper\n", minor);
+				if(LP_F(minor) & LP_ABORT)
+					return temp-buf?temp-buf:-ENOSPC;
+				current->state = TASK_INTERRUPTIBLE;
+				current->timeout = jiffies + LP_TIMEOUT_POLLED;
+				schedule();
+			} else
+			if (!(status & LP_PSELECD)) {
+				printk(KERN_INFO "lp%d off-line\n", minor);
+				if(LP_F(minor) & LP_ABORT)
+					return temp-buf?temp-buf:-EIO;
+				current->state = TASK_INTERRUPTIBLE;
+				current->timeout = jiffies + LP_TIMEOUT_POLLED;
+				schedule();
+			} else
+	                /* not offline or out of paper. on fire? */
+			if (!(status & LP_PERRORP)) {
+				printk(KERN_ERR "lp%d reported invalid error status (on fire, eh?)\n", minor);
+				if(LP_F(minor) & LP_ABORT)
+					return temp-buf?temp-buf:-EIO;
+				current->state = TASK_INTERRUPTIBLE;
+				current->timeout = jiffies + LP_TIMEOUT_POLLED;
+				schedule();
+			}
+
+			/* check for signals before going to sleep */
+			if (current->signal & ~current->blocked) {
+				if (temp != buf)
+					return temp-buf;
+				else
+					return -EINTR;
+			}
+#ifdef LP_DEBUG
+			printk("lp sleeping at %d characters for %d jiffies\n",
+				lp_total_chars, LP_TIME(minor));
+			lp_total_chars=0;
+#endif
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + LP_TIME(minor);
+			schedule();
+		}
+	}
+	return temp-buf;
+}
+
+static int lp_write(struct inode * inode, struct file * file, char * buf, int count)
+{
+	if (LP_IRQ(MINOR(inode->i_rdev)))
+		return lp_write_interrupt(inode, file, buf, count);
+	else
+		return lp_write_polled(inode, file, buf, count);
+}
+
+static int lp_lseek(struct inode * inode, struct file * file,
+		    off_t offset, int origin)
+{
+	return -ESPIPE;
+}
+
+static int lp_open(struct inode * inode, struct file * file)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+	int ret;
+	unsigned int irq;
+
+	if (minor >= LP_NO)
+		return -ENODEV;
+	if ((LP_F(minor) & LP_EXIST) == 0)
+		return -ENODEV;
+	if (LP_F(minor) & LP_BUSY)
+		return -EBUSY;
+
+	MOD_INC_USE_COUNT;
+
+	/* If ABORTOPEN is set and the printer is offline or out of paper,
+	   we may still want to open it to perform ioctl()s.  Therefore we
+	   have commandeered O_NONBLOCK, even though it is being used in
+	   a non-standard manner.  This is strictly a Linux hack, and
+	   should most likely only ever be used by the tunelp application. */
+        if ((LP_F(minor) & LP_ABORTOPEN) && !(file->f_flags & O_NONBLOCK)) {
+		int status = LP_S(minor);
+		if (status & LP_POUTPA) {
+			printk(KERN_INFO "lp%d out of paper\n", minor);
+			MOD_DEC_USE_COUNT;
+			return -ENOSPC;
+		} else if (!(status & LP_PSELECD)) {
+			printk(KERN_INFO "lp%d off-line\n", minor);
+			MOD_DEC_USE_COUNT;
+			return -EIO;
+		} else if (!(status & LP_PERRORP)) {
+			printk(KERN_ERR "lp%d printer error\n", minor);
+			MOD_DEC_USE_COUNT;
+			return -EIO;
+		}
+	}
+
+	if ((irq = LP_IRQ(minor))) {
+		lp_table[minor].lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
+		if (!lp_table[minor].lp_buffer) {
+			MOD_DEC_USE_COUNT;
+			return -ENOMEM;
+		}
+
+		ret = request_irq(irq, lp_interrupt, SA_INTERRUPT, "printer");
+		if (ret) {
+			kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
+			lp_table[minor].lp_buffer = NULL;
+			printk("lp%d unable to use interrupt %d, error %d\n", minor, irq, ret);
+			MOD_DEC_USE_COUNT;
+			return ret;
+		}
+	}
+
+	LP_F(minor) |= LP_BUSY;
+	return 0;
+}
+
+static void lp_release(struct inode * inode, struct file * file)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+	unsigned int irq;
+
+	if ((irq = LP_IRQ(minor))) {
+		free_irq(irq);
+		kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
+		lp_table[minor].lp_buffer = NULL;
+	}
+
+	LP_F(minor) &= ~LP_BUSY;
+	MOD_DEC_USE_COUNT;
+}
+
+
+static int lp_ioctl(struct inode *inode, struct file *file,
+		    unsigned int cmd, unsigned long arg)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+	int retval = 0;
+
+#ifdef LP_DEBUG
+	printk("lp%d ioctl, cmd: 0x%x, arg: 0x%x\n", minor, cmd, arg);
+#endif
+	if (minor >= LP_NO)
+		return -ENODEV;
+	if ((LP_F(minor) & LP_EXIST) == 0)
+		return -ENODEV;
+	if (cmd <= OLD_IOCTL_MAX)
+		printk(KERN_NOTICE "lp%d: warning: obsolete ioctl %#x (perhaps you need a new tunelp)\n",
+		    minor, cmd);
+	switch ( cmd ) {
+		case OLD_LPTIME:
+		case LPTIME:
+			LP_TIME(minor) = arg;
+			break;
+		case OLD_LPCHAR:
+		case LPCHAR:
+			LP_CHAR(minor) = arg;
+			break;
+		case OLD_LPABORT:
+		case LPABORT:
+			if (arg)
+				LP_F(minor) |= LP_ABORT;
+			else
+				LP_F(minor) &= ~LP_ABORT;
+			break;
+		case LPABORTOPEN:
+			if (arg)
+				LP_F(minor) |= LP_ABORTOPEN;
+			else
+				LP_F(minor) &= ~LP_ABORTOPEN;
+			break;
+		case LPCAREFUL:
+			if (arg)
+				LP_F(minor) |= LP_CAREFUL;
+			else
+				LP_F(minor) &= ~LP_CAREFUL;
+			break;
+		case OLD_LPWAIT:
+		case LPWAIT:
+			LP_WAIT(minor) = arg;
+			break;
+		case OLD_LPSETIRQ:
+		case LPSETIRQ: {
+			int oldirq;
+			int newirq = arg;
+			struct lp_struct *lp = &lp_table[minor];
+
+			if (!suser())
+				return -EPERM;
+
+			oldirq = LP_IRQ(minor);
+
+			/* Allocate buffer now if we are going to need it */
+			if (!oldirq && newirq) {
+				lp->lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
+				if (!lp->lp_buffer)
+					return -ENOMEM;
+			}
+
+			if (oldirq) {
+				free_irq(oldirq);
+			}
+			if (newirq) {
+				/* Install new irq */
+				if ((retval = request_irq(newirq, lp_interrupt, SA_INTERRUPT, "printer"))) {
+					if (oldirq) {
+						/* restore old irq */
+						request_irq(oldirq, lp_interrupt, SA_INTERRUPT, "printer");
+					} else {
+						/* We don't need the buffer */
+						kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
+						lp->lp_buffer = NULL;
+					}
+					return retval;
+				}
+			}
+			if (oldirq && !newirq) {
+				/* We don't need the buffer */
+				kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
+				lp->lp_buffer = NULL;
+			}
+			LP_IRQ(minor) = newirq;
+			lp_reset(minor);
+			break;
+		}
+		case OLD_LPGETIRQ:
+			retval = LP_IRQ(minor);
+			break;
+		case LPGETIRQ:
+			retval = verify_area(VERIFY_WRITE, (void *) arg,
+			    sizeof(int));
+		    	if (retval)
+		    		return retval;
+			memcpy_tofs((int *) arg, &LP_IRQ(minor), sizeof(int));
+			break;
+		case LPGETSTATUS:
+			retval = verify_area(VERIFY_WRITE, (void *) arg,
+			    sizeof(int));
+		    	if (retval)
+		    		return retval;
+			else {
+				int status = LP_S(minor);
+				memcpy_tofs((int *) arg, &status, sizeof(int));
+			}
+			break;
+		case LPRESET:
+			lp_reset(minor);
+			break;
+		default:
+			retval = -EINVAL;
+	}
+	return retval;
+}
+
+
+static struct file_operations lp_fops = {
+	lp_lseek,
+	NULL,		/* lp_read */
+	lp_write,
+	NULL,		/* lp_readdir */
+	NULL,		/* lp_select */
+	lp_ioctl,
+	NULL,		/* lp_mmap */
+	lp_open,
+	lp_release
+};
+
+#ifndef MODULE
+
+long lp_init(long kmem_start)
+{
+	int offset = 0;
+	unsigned int testvalue = 0;
+	int count = 0;
+
+	if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
+		printk("unable to get major %d for line printer\n", LP_MAJOR);
+		return kmem_start;
+	}
+	/* take on all known port values */
+	for (offset = 0; offset < LP_NO; offset++) {
+		if (check_region(LP_B(offset), 3))
+			continue;
+		/* write to port & read back to check */
+		outb_p( LP_DUMMY, LP_B(offset));
+		for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
+			;
+		testvalue = inb_p(LP_B(offset));
+		if (testvalue == LP_DUMMY) {
+			LP_F(offset) |= LP_EXIST;
+			lp_reset(offset);
+			printk("lp%d at 0x%04x, ", offset,LP_B(offset));
+			request_region(LP_B(offset), 3, "lp");
+			if (LP_IRQ(offset))
+				printk("using IRQ%d\n", LP_IRQ(offset));
+			else
+				printk("using polling driver\n");
+			count++;
+		}
+	}
+	if (count == 0)
+		printk("lp_init: no lp devices found\n");
+	return kmem_start;
+}
+
+#else
+
+char kernel_version[]= UTS_RELEASE;
+
+int init_module(void)
+{
+	int offset = 0;
+	unsigned int testvalue = 0;
+	int count = 0;
+
+	if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
+		printk("unable to get major %d for line printer\n", LP_MAJOR);
+		return -EIO;
+	}
+	/* take on all known port values */
+	for (offset = 0; offset < LP_NO; offset++) {
+		/* write to port & read back to check */
+		outb_p( LP_DUMMY, LP_B(offset));
+		for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
+			;
+		testvalue = inb_p(LP_B(offset));
+		if (testvalue == LP_DUMMY) {
+			LP_F(offset) |= LP_EXIST;
+			lp_reset(offset);
+			printk("lp%d at 0x%04x, ", offset,LP_B(offset));
+			request_region(LP_B(offset),3,"lp");
+			if (LP_IRQ(offset))
+				printk("using IRQ%d\n", LP_IRQ(offset));
+			else
+				printk("using polling driver\n");
+			count++;
+		}
+	}
+	if (count == 0)
+		printk("lp_init: no lp devices found\n");
+	return 0;
+}
+
+void cleanup_module(void)
+{
+        int offset;
+	if(MOD_IN_USE)
+               printk("lp: busy - remove delayed\n");
+        else
+               unregister_chrdev(LP_MAJOR,"lp");
+	       for (offset = 0; offset < LP_NO; offset++) 
+			if(LP_F(offset) && LP_EXIST) 
+		 		release_region(LP_B(offset),3);
+}
+
+#endif
diff -r -u -N linux.orig/arch/arm/drivers/char/lp.c.orig linux.arm/arch/arm/drivers/char/lp.c.orig
--- linux.orig/arch/arm/drivers/char/lp.c.orig	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/lp.c.orig	Fri Oct 27 23:14:38 1995
@@ -0,0 +1,526 @@
+/*
+ * Copyright (C) 1992 by Jim Weigand and Linus Torvalds
+ * Copyright (C) 1992,1993 by Michael K. Johnson
+ * - Thanks much to Gunter Windau for pointing out to me where the error
+ *   checking ought to be.
+ * Copyright (C) 1993 by Nigel Gamble (added interrupt code)
+ * Copyright (C) 1994 by Alan Cox (Modularised it)
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/lp.h>
+#include <linux/malloc.h>
+
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+
+/* the BIOS manuals say there can be up to 4 lpt devices
+ * but I have not seen a board where the 4th address is listed
+ * if you have different hardware change the table below
+ * please let me know if you have different equipment
+ * if you have more than 3 printers, remember to increase LP_NO
+ */
+struct lp_struct lp_table[] = {
+	{ 0x3bc, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
+	{ 0x378, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
+	{ 0x278, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
+};
+#define LP_NO 3
+
+#ifdef MODULE
+#include <linux/module.h>
+#include "../../tools/version.h"
+#endif
+
+/*
+ * All my debugging code assumes that you debug with only one printer at
+ * a time. RWWH
+ */
+
+#undef LP_DEBUG
+
+static int lp_reset(int minor)
+{
+	int testvalue;
+	unsigned char command;
+
+	command = LP_PSELECP | LP_PINITP;
+
+	/* reset value */
+	outb_p(0, LP_C(minor));
+	for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
+		;
+	outb_p(command, LP_C(minor));
+	return LP_S(minor);
+}
+
+#ifdef LP_DEBUG
+static int lp_max_count = 1;
+#endif
+
+static int lp_char_polled(char lpchar, int minor)
+{
+	int status = 0, wait = 0;
+	unsigned long count  = 0;
+
+	do {
+		status = LP_S(minor);
+		count ++;
+		if(need_resched)
+			schedule();
+	} while(!(status & LP_PBUSY) && count < LP_CHAR(minor));
+
+	if (count == LP_CHAR(minor)) {
+		return 0;
+		/* we timed out, and the character was /not/ printed */
+	}
+#ifdef LP_DEBUG
+	if (count > lp_max_count) {
+		printk("lp success after %d counts.\n",count);
+		lp_max_count=count;
+	}
+#endif
+	outb_p(lpchar, LP_B(minor));
+	/* must wait before taking strobe high, and after taking strobe
+	   low, according spec.  Some printers need it, others don't. */
+	while(wait != LP_WAIT(minor)) wait++;
+        /* control port takes strobe high */
+	outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
+	while(wait) wait--;
+        /* take strobe low */
+	outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
+
+	return 1;
+}
+
+static int lp_char_interrupt(char lpchar, int minor)
+{
+	int wait = 0;
+	unsigned char status;
+
+
+	if (!((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)
+	|| !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)
+	|| !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)) {
+
+		outb_p(lpchar, LP_B(minor));
+		/* must wait before taking strobe high, and after taking strobe
+		   low, according spec.  Some printers need it, others don't. */
+		while(wait != LP_WAIT(minor)) wait++;
+		/* control port takes strobe high */
+		outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
+		while(wait) wait--;
+		/* take strobe low */
+		outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
+		return 1;
+	}
+
+	return 0;
+}
+
+#ifdef LP_DEBUG
+	unsigned int lp_total_chars = 0;
+	unsigned int lp_last_call = 0;
+#endif
+
+static void lp_interrupt(int irq)
+{
+	struct lp_struct *lp = &lp_table[0];
+	struct lp_struct *lp_end = &lp_table[LP_NO];
+
+	while (irq != lp->irq) {
+		if (++lp >= lp_end)
+			return;
+	}
+
+	wake_up(&lp->lp_wait_q);
+}
+
+static int lp_write_interrupt(struct inode * inode, struct file * file, char * buf, int count)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+	unsigned long copy_size;
+	unsigned long total_bytes_written = 0;
+	unsigned long bytes_written;
+	struct lp_struct *lp = &lp_table[minor];
+	unsigned char status;
+
+	do {
+		bytes_written = 0;
+		copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);
+		memcpy_fromfs(lp->lp_buffer, buf, copy_size);
+
+		while (copy_size) {
+			if (lp_char_interrupt(lp->lp_buffer[bytes_written], minor)) {
+				--copy_size;
+				++bytes_written;
+			} else {
+				if (!((status = LP_S(minor)) & LP_PERRORP)) {
+					int rc = total_bytes_written + bytes_written;
+
+					if ((status & LP_POUTPA)) {
+						printk("lp%d out of paper\n", minor);
+						if (!rc)
+							rc = -ENOSPC;
+					} else if (!(status & LP_PSELECD)) {
+						printk("lp%d off-line\n", minor);
+						if (!rc)
+							rc = -EIO;
+					} else {
+						printk("lp%d printer error\n", minor);
+						if (!rc)
+							rc = -EIO;
+					}
+					if(LP_F(minor) & LP_ABORT)
+						return rc;
+				}
+				cli();
+				outb_p((LP_PSELECP|LP_PINITP|LP_PINTEN), (LP_C(minor)));
+				status = LP_S(minor);
+				if (!(status & LP_PACK) || (status & LP_PBUSY)) {
+					outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
+					sti();
+					continue;
+				}
+				current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
+				interruptible_sleep_on(&lp->lp_wait_q);
+				outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
+				sti();
+				if (current->signal & ~current->blocked) {
+					if (total_bytes_written + bytes_written)
+						return total_bytes_written + bytes_written;
+					else
+						return -EINTR;
+				}
+			}
+		}
+
+		total_bytes_written += bytes_written;
+		buf += bytes_written;
+		count -= bytes_written;
+
+	} while (count > 0);
+
+	return total_bytes_written;
+}
+
+static int lp_write_polled(struct inode * inode, struct file * file,
+			   char * buf, int count)
+{
+	int  retval;
+	unsigned int minor = MINOR(inode->i_rdev);
+	char c, *temp = buf;
+
+#ifdef LP_DEBUG
+	if (jiffies-lp_last_call > LP_TIME(minor)) {
+		lp_total_chars = 0;
+		lp_max_count = 1;
+	}
+	lp_last_call = jiffies;
+#endif
+
+	temp = buf;
+	while (count > 0) {
+		c = get_fs_byte(temp);
+		retval = lp_char_polled(c, minor);
+		/* only update counting vars if character was printed */
+		if (retval) { count--; temp++;
+#ifdef LP_DEBUG
+			lp_total_chars++;
+#endif
+		}
+		if (!retval) { /* if printer timed out */
+			int status = LP_S(minor);
+
+			if (status & LP_POUTPA) {
+				printk("lp%d out of paper\n", minor);
+				if(LP_F(minor) & LP_ABORT)
+					return temp-buf?temp-buf:-ENOSPC;
+				current->state = TASK_INTERRUPTIBLE;
+				current->timeout = jiffies + LP_TIMEOUT_POLLED;
+				schedule();
+			} else
+			if (!(status & LP_PSELECD)) {
+				printk("lp%d off-line\n", minor);
+				if(LP_F(minor) & LP_ABORT)
+					return temp-buf?temp-buf:-EIO;
+				current->state = TASK_INTERRUPTIBLE;
+				current->timeout = jiffies + LP_TIMEOUT_POLLED;
+				schedule();
+			} else
+	                /* not offline or out of paper. on fire? */
+			if (!(status & LP_PERRORP)) {
+				printk("lp%d reported invalid error status (on fire, eh?)\n", minor);
+				if(LP_F(minor) & LP_ABORT)
+					return temp-buf?temp-buf:-EFAULT;
+				current->state = TASK_INTERRUPTIBLE;
+				current->timeout = jiffies + LP_TIMEOUT_POLLED;
+				schedule();
+			}
+
+			/* check for signals before going to sleep */
+			if (current->signal & ~current->blocked) {
+				if (temp != buf)
+					return temp-buf;
+				else
+					return -EINTR;
+			}
+#ifdef LP_DEBUG
+			printk("lp sleeping at %d characters for %d jiffies\n",
+				lp_total_chars, LP_TIME(minor));
+			lp_total_chars=0;
+#endif
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + LP_TIME(minor);
+			schedule();
+		}
+	}
+	return temp-buf;
+}
+
+static int lp_write(struct inode * inode, struct file * file, char * buf, int count)
+{
+	if (LP_IRQ(MINOR(inode->i_rdev)))
+		return lp_write_interrupt(inode, file, buf, count);
+	else
+		return lp_write_polled(inode, file, buf, count);
+}
+
+static int lp_lseek(struct inode * inode, struct file * file,
+		    off_t offset, int origin)
+{
+	return -ESPIPE;
+}
+
+static int lp_open(struct inode * inode, struct file * file)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+	int ret;
+	unsigned int irq;
+
+	if (minor >= LP_NO)
+		return -ENODEV;
+	if ((LP_F(minor) & LP_EXIST) == 0)
+		return -ENODEV;
+	if (LP_F(minor) & LP_BUSY)
+		return -EBUSY;
+
+	if ((irq = LP_IRQ(minor))) {
+		lp_table[minor].lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
+		if (!lp_table[minor].lp_buffer)
+			return -ENOMEM;
+
+		ret = request_irq(irq, lp_interrupt, SA_INTERRUPT, "printer");
+		if (ret) {
+			kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
+			lp_table[minor].lp_buffer = NULL;
+			printk("lp%d unable to use interrupt %d, error %d\n", minor, irq, ret);
+			return ret;
+		}
+	}
+
+	LP_F(minor) |= LP_BUSY;
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+	return 0;
+}
+
+static void lp_release(struct inode * inode, struct file * file)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+	unsigned int irq;
+
+	if ((irq = LP_IRQ(minor))) {
+		free_irq(irq);
+		kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
+		lp_table[minor].lp_buffer = NULL;
+	}
+
+	LP_F(minor) &= ~LP_BUSY;
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+
+static int lp_ioctl(struct inode *inode, struct file *file,
+		    unsigned int cmd, unsigned long arg)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+	int retval = 0;
+
+#ifdef LP_DEBUG
+	printk("lp%d ioctl, cmd: 0x%x, arg: 0x%x\n", minor, cmd, arg);
+#endif
+	if (minor >= LP_NO)
+		return -ENODEV;
+	if ((LP_F(minor) & LP_EXIST) == 0)
+		return -ENODEV;
+	switch ( cmd ) {
+		case LPTIME:
+			LP_TIME(minor) = arg;
+			break;
+		case LPCHAR:
+			LP_CHAR(minor) = arg;
+			break;
+		case LPABORT:
+			if (arg)
+				LP_F(minor) |= LP_ABORT;
+			else
+				LP_F(minor) &= ~LP_ABORT;
+			break;
+		case LPWAIT:
+			LP_WAIT(minor) = arg;
+			break;
+		case LPSETIRQ: {
+			int oldirq;
+			int newirq = arg;
+			struct lp_struct *lp = &lp_table[minor];
+
+			if (!suser())
+				return -EPERM;
+
+			oldirq = LP_IRQ(minor);
+
+			/* Allocate buffer now if we are going to need it */
+			if (!oldirq && newirq) {
+				lp->lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
+				if (!lp->lp_buffer)
+					return -ENOMEM;
+			}
+
+			if (oldirq) {
+				free_irq(oldirq);
+			}
+			if (newirq) {
+				/* Install new irq */
+				if ((retval = request_irq(newirq, lp_interrupt, SA_INTERRUPT, "printer"))) {
+					if (oldirq) {
+						/* restore old irq */
+						request_irq(oldirq, lp_interrupt, SA_INTERRUPT, "printer");
+					} else {
+						/* We don't need the buffer */
+						kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
+						lp->lp_buffer = NULL;
+					}
+					return retval;
+				}
+			}
+			if (oldirq && !newirq) {
+				/* We don't need the buffer */
+				kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
+				lp->lp_buffer = NULL;
+			}
+			LP_IRQ(minor) = newirq;
+			lp_reset(minor);
+			break;
+		}
+		case LPGETIRQ:
+			retval = LP_IRQ(minor);
+			break;
+		default:
+			retval = -EINVAL;
+	}
+	return retval;
+}
+
+
+static struct file_operations lp_fops = {
+	lp_lseek,
+	NULL,		/* lp_read */
+	lp_write,
+	NULL,		/* lp_readdir */
+	NULL,		/* lp_select */
+	lp_ioctl,
+	NULL,		/* lp_mmap */
+	lp_open,
+	lp_release
+};
+
+#ifndef MODULE
+
+long lp_init(long kmem_start)
+{
+	int offset = 0;
+	unsigned int testvalue = 0;
+	int count = 0;
+
+	if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
+		printk("unable to get major %d for line printer\n", LP_MAJOR);
+		return kmem_start;
+	}
+	/* take on all known port values */
+	for (offset = 0; offset < LP_NO; offset++) {
+		/* write to port & read back to check */
+		outb_p( LP_DUMMY, LP_B(offset));
+		for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
+			;
+		testvalue = inb_p(LP_B(offset));
+		if (testvalue == LP_DUMMY) {
+			LP_F(offset) |= LP_EXIST;
+			lp_reset(offset);
+			printk("lp_init: lp%d exists, ", offset);
+			if (LP_IRQ(offset))
+				printk("using IRQ%d\n", LP_IRQ(offset));
+			else
+				printk("using polling driver\n");
+			count++;
+		}
+	}
+	if (count == 0)
+		printk("lp_init: no lp devices found\n");
+	return kmem_start;
+}
+
+#else
+
+char kernel_version[]= UTS_RELEASE;
+
+int init_module(void)
+{
+	int offset = 0;
+	unsigned int testvalue = 0;
+	int count = 0;
+
+	if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
+		printk("unable to get major %d for line printer\n", LP_MAJOR);
+		return -EIO;
+	}
+	/* take on all known port values */
+	for (offset = 0; offset < LP_NO; offset++) {
+		/* write to port & read back to check */
+		outb_p( LP_DUMMY, LP_B(offset));
+		for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
+			;
+		testvalue = inb_p(LP_B(offset));
+		if (testvalue == LP_DUMMY) {
+			LP_F(offset) |= LP_EXIST;
+			lp_reset(offset);
+			printk("lp_init: lp%d exists, ", offset);
+			if (LP_IRQ(offset))
+				printk("using IRQ%d\n", LP_IRQ(offset));
+			else
+				printk("using polling driver\n");
+			count++;
+		}
+	}
+	if (count == 0)
+		printk("lp_init: no lp devices found\n");
+	return 0;
+}
+
+void cleanup_module(void)
+{
+       if(MOD_IN_USE)
+               printk("lp: busy - remove delayed\n");
+       else
+               unregister_chrdev(LP_MAJOR,"lp");
+}
+
+#endif
diff -r -u -N linux.orig/arch/arm/drivers/char/mem.c linux.arm/arch/arm/drivers/char/mem.c
--- linux.orig/arch/arm/drivers/char/mem.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/mem.c	Fri Oct 27 23:14:24 1995
@@ -0,0 +1,388 @@
+/*
+ *  linux/drivers/char/mem.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+#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 <asm/segment.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_SOUND
+extern long soundcard_init(long mem_start);
+#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,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;
+	int read;
+
+	if (count < 0)
+		return -EINVAL;
+	if (p >= high_memory)
+		return 0;
+	if (count > high_memory - p)
+		count = high_memory - p;
+	read = 0;
+	while (p < PAGE_SIZE && count > 0) {
+		put_fs_byte(0,buf);
+		buf++;
+		p++;
+		count--;
+		read++;
+	}
+	memcpy_tofs(buf,(void *) p,count);
+	read += count;
+	file->f_pos += read;
+	return read;
+}
+
+static int write_mem(struct inode * inode, struct file * file,char * buf, int count)
+{
+	unsigned long p = file->f_pos;
+	int written;
+
+	if (count < 0)
+		return -EINVAL;
+	if (p >= high_memory)
+		return 0;
+	if (count > high_memory - p)
+		count = high_memory - p;
+	written = 0;
+	while (p < PAGE_SIZE && count > 0) {
+		/* Hmm. Do something? */
+		buf++;
+		p++;
+		count--;
+		written++;
+	}
+	memcpy_fromfs((void *) p,buf,count);
+	written += count;
+	file->f_pos += written;
+	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;
+#ifndef __arm__
+	if (x86 > 3 && vma->vm_offset >= high_memory)
+		vma->vm_page_prot |= PAGE_PCD;
+#endif
+	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_fs_byte(inb(i),tmp);
+		i++;
+		tmp++;
+	}
+	file->f_pos = i;
+	return tmp-buf;
+}
+
+static int write_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) {
+		outb(get_fs_byte(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,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_fs_byte(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,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 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 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;
+		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 */
+};
+
+#ifdef CONFIG_FTAPE
+char* ftape_big_buffer;
+#endif
+
+long chr_dev_init(long mem_start, long mem_end)
+{
+	if (register_chrdev(MEM_MAJOR,"mem",&memory_fops))
+		printk("unable to get major %d for memory devs\n", MEM_MAJOR);
+	mem_start = tty_init(mem_start);
+#ifdef CONFIG_PRINTER
+	mem_start = lp_init(mem_start);
+#endif
+#ifdef CONFIG_ARMMOUSE
+	mem_start = mouse_init(mem_start);
+#endif
+/*#ifdef CONFIG_SOUND
+	mem_start = soundcard_init(mem_start);
+#endif*/
+#ifdef CONFIG_QIC02_TAPE
+	mem_start = qic02_tape_init(mem_start);
+#endif
+/*
+ *      Rude way to allocate kernel memory buffer for tape device
+ */
+#ifdef CONFIG_FTAPE
+        /* allocate NR_FTAPE_BUFFERS 32Kb buffers at aligned address */
+        ftape_big_buffer= (char*) ((mem_start + 0x7fff) & ~0x7fff);
+        printk( "ftape: allocated %d buffers aligned at: %p\n",
+               NR_FTAPE_BUFFERS, ftape_big_buffer);
+        mem_start = (long) ftape_big_buffer + NR_FTAPE_BUFFERS * 0x8000;
+#endif
+	return mem_start;
+}
diff -r -u -N linux.orig/arch/arm/drivers/char/mouse.c linux.arm/arch/arm/drivers/char/mouse.c
--- linux.orig/arch/arm/drivers/char/mouse.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/mouse.c	Fri Oct 27 23:14:24 1995
@@ -0,0 +1,68 @@
+/*
+ * linux/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.
+ */
+
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/mouse.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+
+/*
+ * note that you can remove any or all of the drivers by undefining
+ * the minor values in <linux/mouse.h>
+ */
+extern struct file_operations arch_mouse_fops;
+extern unsigned long arch_mouse_init(unsigned long);
+
+static int mouse_open(struct inode * inode, struct file * file)
+{
+	int minor = MINOR(inode->i_rdev);
+
+	switch (minor) {
+#ifdef CONFIG_ARMMOUSE
+		case 4:
+			file->f_op = &arch_mouse_fops;
+			break;
+#endif
+		default:
+			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 */
+};
+
+unsigned long mouse_init(unsigned long kmem_start)
+{
+#ifdef CONFIG_ARMMOUSE
+	kmem_start = arch_mouse_init(kmem_start);
+#endif
+	if (register_chrdev(MOUSE_MAJOR, "mouse", &mouse_fops))
+		printk("unable to get major %d for mouse devices\n",
+		       MOUSE_MAJOR);
+	return kmem_start;
+}
diff -r -u -N linux.orig/arch/arm/drivers/char/n_tty.c linux.arm/arch/arm/drivers/char/n_tty.c
--- linux.orig/arch/arm/drivers/char/n_tty.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/n_tty.c	Fri Oct 27 23:14:25 1995
@@ -0,0 +1,1021 @@
+/*
+ * 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.
+ */
+
+#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);
+}
+extern int debug_flag;
+static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
+{if (debug_flag) printk("received from queue: n_tty_receive_char");
+	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 (debug_flag) printk("trace 1\n");
+	/*
+	 * 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 (debug_flag) printk("trace 2\n");
+	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 (debug_flag) printk("trace 3\n");
+	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, unsigned char *cp,
+			      char *fp, int count)
+{
+	unsigned char *p;
+	char *f, flags = 0;
+	int	i;
+if (debug_flag) printk("n_tty_receive_buf\n");
+	if (!tty->read_buf)
+		return;
+if (debug_flag) printk("trace a\n");
+	if (tty->real_raw) {
+if (debug_flag) printk("trace b\n");
+		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 {
+if (debug_flag) printk("trace c\n");
+		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 (debug_flag) printk("trace d\n");
+	if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) {
+if (debug_flag) printk("trace e: %p %p\n", tty->fasync, tty->read_wait);
+		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->sigaction[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/32);
+
+		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((unsigned char *) 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_fs_byte(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_fs_byte(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_fs_byte(c, b++);
+					if (--nr)
+						continue;
+					break;
+				}
+				if (--tty->canon_data < 0) {
+					tty->canon_data = 0;
+				}
+				if (c != __DISABLED_CHAR) {
+					put_fs_byte(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,
+		      unsigned char * buf, unsigned int nr)
+{
+	struct wait_queue wait = { current, NULL };
+	int c;
+	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_fs_byte(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 -r -u -N linux.orig/arch/arm/drivers/char/n_tty.c.orig linux.arm/arch/arm/drivers/char/n_tty.c.orig
--- linux.orig/arch/arm/drivers/char/n_tty.c.orig	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/n_tty.c.orig	Fri Oct 27 23:14:38 1995
@@ -0,0 +1,1007 @@
+/*
+ * 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.
+ */
+
+#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) && L_IEXTEN(tty)) {
+		start_tty(tty);
+		return;
+	}
+
+	if (I_ISTRIP(tty))
+		c &= 0x7f;
+	if (I_IUCLC(tty) && L_IEXTEN(tty))
+		c=tolower(c);
+
+	/*
+	 * 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, unsigned char *cp,
+			      char *fp, int count)
+{
+	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, let as many
+	 * characters through as possible, so that the excess
+	 * characters can be "beeped".
+	 */
+	if (L_ICANON(tty))
+		return N_TTY_BUF_SIZE;
+
+	if (left > 0)
+		return left;
+	return 0;
+}
+
+int is_ignored(int sig)
+{
+	return ((current->blocked & (1<<(sig-1))) ||
+	        (current->sigaction[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/32);
+
+		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)
+{
+	tty_wait_until_sent(tty, 0);
+	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;
+	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)
+{
+	int c;
+	unsigned char *b = buf;
+	int minimum, time;
+	int retval = 0;
+	int size;
+	struct wait_queue wait = { current, NULL };
+
+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;
+printk("read_chan: suspending: tpgrp = %p, ttypgrp = %p\n",current->pgrp, tty->pgrp);
+			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_fs_byte(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_fs_byte(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_fs_byte(c, b++);
+					if (--nr)
+						continue;
+					break;
+				}
+				if (--tty->canon_data < 0) {
+					tty->canon_data = 0;
+				}
+				if (c != __DISABLED_CHAR) {
+					put_fs_byte(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,
+		      unsigned char * buf, unsigned int nr)
+{
+	int c;
+	unsigned char *b = buf;
+	int retval = 0;
+	struct wait_queue wait = { current, NULL };
+
+	/* 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_fs_byte(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 -r -u -N linux.orig/arch/arm/drivers/char/ptr.c linux.arm/arch/arm/drivers/char/ptr.c
--- linux.orig/arch/arm/drivers/char/ptr.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/ptr.c	Fri Oct 27 23:14:20 1995
@@ -0,0 +1,210 @@
+/*
+ * ptr.c
+ *
+ * Archimedes pointer driver
+ *
+ * By R.M.King
+ *
+ * This pointer driver provides a total of 5 possible pointers.
+ *
+ */
+#include <linux/errno.h>
+#include <linux/mm.h>
+
+#include <asm/segment.h>
+
+extern void vidc_write(int reg, int val);
+extern void memc_write(int reg, int val);
+
+/* Have to go into a separate file */
+#define N_PTRS 5
+
+#define ARC_MOUSE_ON		0x7A01
+#define ARC_MOUSE_OFF		0x7A02
+#define ARC_MOUSE_SETPOS	0x7A03
+#define ARC_MOUSE_SHAPE		0x7A04
+#define ARC_MOUSE_SELECT	0x7A05
+
+#define MAX_X 799
+#define MAX_Y 599
+
+struct pointer_define
+{
+  unsigned char no;
+  unsigned char x;	/* X size */
+  unsigned char y;	/* Y size */
+  unsigned char x_hot;	/* Hot spot X offset */
+  unsigned char y_hot;	/* Hot spot Y offset */
+  char ndata[1];
+};
+
+static unsigned char current_ptr=0;
+static unsigned long ptr_y=0;
+static unsigned long ptr_x=0;
+static int ptr_toupdate=0;
+static int ptr_displayed=0;
+
+static struct ptr_data
+{
+	unsigned char x_size;
+	unsigned char y_size;
+	unsigned char x_hot;
+	unsigned char y_hot;
+} data[N_PTRS];
+
+void ptr_update(void)
+{
+	long y;
+	y = ptr_y - (int)data[current_ptr].y_hot;
+	if(ptr_toupdate & 3)
+	{
+	 	long x,ry,ey;
+		x  = 195 + ptr_x - (int)data[current_ptr].x_hot;
+		ry = 23 + (y < 0 ? 0 : y);
+		ey = 23 + y + (int)data[current_ptr].y_size;
+
+		if(ey < 7)
+			ey=7;
+
+		vidc_write(0x98, (x  << 13));
+		vidc_write(0xB8, (ry << 14));
+		vidc_write(0xBC, (ey << 14));
+	}
+	if(ptr_toupdate & 3)
+	{
+		if(y < 0)
+			memc_write(3, (0x40100+0x300*current_ptr+8*(-y))>>2);
+		else
+			memc_write(3, (0x40100+0x300*current_ptr)>>2);
+	}
+
+	if(ptr_toupdate & 4)
+	{
+		vidc_write(0x98, 0);
+		vidc_write(0xB8, 0);
+		vidc_write(0xBC, 0x4000);
+	}
+	ptr_toupdate = 0;
+}
+
+int ptr_ioctl(struct tty_struct *tty, struct file * file,
+	unsigned int cmd, unsigned long arg)
+{
+	int i;
+	/* Check to make sure that this tty is local */
+
+	switch(cmd)
+	{
+		case ARC_MOUSE_ON:
+			ptr_displayed = 1;
+			ptr_toupdate |= 3;
+			return 0;
+
+		case ARC_MOUSE_OFF:
+			ptr_displayed = 0;
+			ptr_toupdate |= 4;
+			return 0;
+
+		case ARC_MOUSE_SETPOS:
+			i = verify_area(VERIFY_READ, (void *)arg, 8);
+			if(i)
+				return i;
+			ptr_x = get_fs_long((unsigned long *)arg);
+			ptr_y = get_fs_long(((unsigned long *)arg)+1);
+			if(ptr_x > MAX_X)
+			  ptr_x = MAX_X;
+			if(ptr_y > MAX_Y)
+			  ptr_y = MAX_Y;
+
+			ptr_toupdate |= ptr_displayed?3:0;
+			return 0;
+
+		case ARC_MOUSE_SHAPE:
+		{
+			struct pointer_define ptrdef;
+			int total_bytes,ptrbuf,y;
+			unsigned char ptr;
+
+			i = verify_area(VERIFY_READ, (void *)arg,
+					sizeof(struct pointer_define)-1);
+			if (i)
+				return i;
+
+			memcpy_fromfs((void *)&ptrdef, (void *)arg,
+				sizeof(struct pointer_define)-1);
+
+			total_bytes = ptrdef.x * ptrdef.y / 4;
+			ptr = ptrdef.no;
+
+			if(total_bytes > 0x300 || ptr > 4)
+				return -EINVAL;
+			i = verify_area(VERIFY_READ,
+				(void *)(arg + sizeof(struct pointer_define)-1), total_bytes);
+			if (i)
+				return i;
+
+			/* Hmm. Or shall I switch it off, update & switch on? */
+			if(ptr == current_ptr && ptr_displayed)
+				return -EBUSY;
+
+			data[ptr].x_size = ptrdef.x;
+			data[ptr].y_size = ptrdef.y;
+			data[ptr].x_hot  = ptrdef.x_hot;
+			data[ptr].y_hot  = ptrdef.y_hot;
+
+			ptrbuf = 0x100 + 0x300 * ptr;
+			memset((void *)ptrbuf, 0, 0x300);
+
+			for(y=0; y < ptrdef.y; y++)
+			{
+				memcpy_fromfs((void *)(ptrbuf + 8 * y),
+					(void *)(arg + 5 + y*ptrdef.x/4),
+					ptrdef.x / 4);
+			}
+			return 0;
+                }
+		case ARC_MOUSE_SELECT:
+			if(arg > 4)
+				return -EINVAL;
+
+			current_ptr = arg;
+			ptr_toupdate |= ptr_displayed?3:0;
+			return 0;
+	}
+	return -EINVAL;
+}
+
+void ptr_concontrol(int on_off, unsigned int pos)
+{
+  if(on_off)
+  {
+    if(!ptr_displayed)
+    {
+      int i;
+      current_ptr = 0;
+      ptr_displayed = 1;
+      data[0].x_size = 4;
+      data[0].y_size = 8;
+      data[0].x_hot  = 0;
+      data[0].y_hot  = 0;
+      for(i = 0x100; i < 0x300; i++)
+        *(unsigned char *)i=0x55 << (i & 1);
+    }
+
+    ptr_x = pos % (MAX_X+1);
+    ptr_y = pos / (MAX_X+1);
+
+    if(ptr_y > MAX_Y)
+      ptr_y = MAX_Y;
+
+    ptr_toupdate |= 3;
+  }
+  else
+  {
+    if(ptr_displayed)
+    {
+      ptr_displayed = 0;
+      ptr_toupdate |= 4;
+    }
+  }
+}
diff -r -u -N linux.orig/arch/arm/drivers/char/pty.c linux.arm/arch/arm/drivers/char/pty.c
--- linux.orig/arch/arm/drivers/char/pty.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/pty.c	Fri Oct 27 23:14:25 1995
@@ -0,0 +1,262 @@
+/*
+ *  linux/drivers/char/pty.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ *	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 <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,
+		       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;
+}
+
+long pty_init(long kmem_start)
+{
+	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 kmem_start;
+}
diff -r -u -N linux.orig/arch/arm/drivers/char/serial.c linux.arm/arch/arm/drivers/char/serial.c
--- linux.orig/arch/arm/drivers/char/serial.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/serial.c	Fri Oct 27 23:14:26 1995
@@ -0,0 +1,2691 @@
+/*
+ *  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:
+ *
+ *	long rs_init(long);
+ * 	int  rs_open(struct tty_struct * tty, struct file * filp)
+ *
+ *  Slight modifications for ARM
+ */
+
+#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/major.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
+#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,
+					dev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+	static const char *badmagic =
+		"Warning: bad magic number for serial struct (%d, %d) in %s\n";
+	static const char *badinfo =
+		"Warning: null async_struct for (%d, %d) in %s\n";
+
+	if (!info) {
+		printk(badinfo, MAJOR(device), MINOR(device), routine);
+		return 1;
+	}
+	if (info->magic != SERIAL_MAGIC) {
+		printk(badmagic, MAJOR(device), MINOR(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);
+	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*HZ) {
+		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 * HZ;
+	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 = 6000;	/* 60 seconds === a long time :-) */
+
+	info = IRQ_ports[irq];
+	if (!info) {
+		IRQ_timeout[irq] = 6000;
+		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;
+	}
+#ifdef __alpha__
+	info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2;
+	info->MCR_noint |= UART_MCR_OUT1 | UART_MCR_OUT2;
+#endif
+	if (info->irq == 0)
+		info->MCR = info->MCR_noint;
+	serial_outp(info, UART_MCR, info->MCR);
+	
+	/*
+	 * 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;
+	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 */
+	cval = cflag & (CSIZE | CSTOPB);
+	cval >>= 4;
+	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,
+		    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;
+	info->closing_wait = new_serial.closing_wait;
+
+	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_fs_long(result,(unsigned long *) 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_fs_long(result,(unsigned long *) 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_fs_long((unsigned long *) 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+10;
+	while (timeout >= jiffies)
+		;
+	
+	rs_triggered = 0;	/* Reset after letting things settle */
+
+	timeout = jiffies+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;
+	
+	multi->port1 = new_multi.port1;
+	multi->mask1 = new_multi.mask1;
+	multi->match1 = new_multi.match1;
+
+	multi->port2 = new_multi.port2;
+	multi->mask2 = new_multi.mask2;
+	multi->match2 = new_multi.match2;
+
+	multi->port3 = new_multi.port3;
+	multi->mask3 = new_multi.mask3;
+	multi->match3 = new_multi.match3;
+
+	multi->port4 = new_multi.port4;
+	multi->mask4 = new_multi.mask4;
+	multi->match4 = new_multi.match4;
+
+	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;
+	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.
+	 */
+#ifdef __alpha__
+	/*
+	 * I wonder what DEC did to the OUT1 and OUT2 lines?
+	 * clearing them results in endless interrupts.
+	 */
+	serial_outp(info, UART_MCR, 0x0c);
+#else
+	serial_outp(info, UART_MCR, 0x00);
+#endif
+	serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
+				     UART_FCR_CLEAR_XMIT));
+	(void)serial_in(info, UART_RX);
+	
+	restore_flags(flags);
+}
+
+/*
+ * The serial driver boot-time initialization code!
+ */
+long rs_init(long kmem_start)
+{
+	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 = 50;
+		info->closing_wait = 3000;
+		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 kmem_start;
+}
+
+/*
+ * 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 -r -u -N linux.orig/arch/arm/drivers/char/serial.c.orig linux.arm/arch/arm/drivers/char/serial.c.orig
--- linux.orig/arch/arm/drivers/char/serial.c.orig	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/serial.c.orig	Fri Oct 27 23:14:39 1995
@@ -0,0 +1,2478 @@
+/*
+ *  linux/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:
+ *
+ *	long rs_init(long);
+ * 	int  rs_open(struct tty_struct * tty, struct file * filp)
+ */
+
+#include <linux/config.h>
+#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/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/major.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.
+ */
+int ser_check=0;
+#define SERIAL_PARANOIA_CHECK
+#define CONFIG_SERIAL_NOPAUSE_IO
+#define SERIAL_DO_RESTART
+#define CONFIG_SERIAL_NEW_ISR
+
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
+
+#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 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 )
+
+/* Standard COM flags (except for COM4, because of the 8514 problem) */
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST )
+#define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF
+
+#define FOURPORT_FLAGS ASYNC_FOURPORT
+#define ACCENT_FLAGS 0
+#define BOCA_FLAGS 0
+#define HUB6_FLAGS 0
+
+/*
+ * The following define the access methods for the HUB6 card. All
+ * access is through two ports for all 24 possible chips. The card is
+ * selected through the high 2 bits, the port on that card with the
+ * "middle" 3 bits, and the register on that port with the bottom
+ * 3 bits.
+ *
+ * While the access port and interrupt is configurable, the default
+ * port locations are 0x302 for the port control register, and 0x303
+ * for the data read/write register. Normally, the interrupt is at irq3
+ * but can be anything from 3 to 7 inclusive. Note that using 3 will
+ * require disabling com2.
+ */
+
+#define C_P(card,port) (((card)<<6|(port)<<3) + 1)
+
+struct async_struct rs_table[] = {
+	/* UART CLK   PORT IRQ     FLAGS        */
+#ifdef __arm__
+	{ 0, BASE_BAUD, 0x3F8,10, STD_COM_FLAGS },
+	{ 0, BASE_BAUD, 0x2F8,10, STD_COM_FLAGS }
+#define PODULE
+#ifdef PODULE
+       ,{ 0, BASE_BAUD,     0, 0, STD_COM_FLAGS },
+	{ 0, BASE_BAUD,     0, 0, STD_COM_FLAGS },
+	{ 0, BASE_BAUD,     0, 0, STD_COM_FLAGS },
+	{ 0, BASE_BAUD,     0, 0, STD_COM_FLAGS },
+	{ 0, BASE_BAUD,     0, 0, STD_COM_FLAGS },
+	{ 0, BASE_BAUD,     0, 0, STD_COM_FLAGS },
+	{ 0, BASE_BAUD,     0, 0, STD_COM_FLAGS },
+	{ 0, BASE_BAUD,     0, 0, STD_COM_FLAGS }
+#endif
+#else
+	{ 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS },	/* ttyS0 */
+	{ 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS },	/* ttyS1 */
+	{ 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS },	/* ttyS2 */
+	{ 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS },	/* ttyS3 */
+
+	{ 0, BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, 	/* ttyS4 */
+	{ 0, BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS },	/* ttyS5 */
+	{ 0, BASE_BAUD, 0x1B0, 9, FOURPORT_FLAGS },	/* ttyS6 */
+	{ 0, BASE_BAUD, 0x1B8, 9, FOURPORT_FLAGS },	/* ttyS7 */
+
+	{ 0, BASE_BAUD, 0x2A0, 5, FOURPORT_FLAGS },	/* ttyS8 */
+	{ 0, BASE_BAUD, 0x2A8, 5, FOURPORT_FLAGS },	/* ttyS9 */
+	{ 0, BASE_BAUD, 0x2B0, 5, FOURPORT_FLAGS },	/* ttyS10 */
+	{ 0, BASE_BAUD, 0x2B8, 5, FOURPORT_FLAGS },	/* ttyS11 */
+
+	{ 0, BASE_BAUD, 0x330, 4, ACCENT_FLAGS },	/* ttyS12 */
+	{ 0, BASE_BAUD, 0x338, 4, ACCENT_FLAGS },	/* ttyS13 */
+	{ 0, BASE_BAUD, 0x000, 0, 0 },	/* ttyS14 (spare; user configurable) */
+	{ 0, BASE_BAUD, 0x000, 0, 0 },	/* ttyS15 (spare; user configurable) */
+
+	{ 0, BASE_BAUD, 0x100, 12, BOCA_FLAGS },	/* ttyS16 */
+	{ 0, BASE_BAUD, 0x108, 12, BOCA_FLAGS },	/* ttyS17 */
+	{ 0, BASE_BAUD, 0x110, 12, BOCA_FLAGS },	/* ttyS18 */
+	{ 0, BASE_BAUD, 0x118, 12, BOCA_FLAGS },	/* ttyS19 */
+	{ 0, BASE_BAUD, 0x120, 12, BOCA_FLAGS },	/* ttyS20 */
+	{ 0, BASE_BAUD, 0x128, 12, BOCA_FLAGS },	/* ttyS21 */
+	{ 0, BASE_BAUD, 0x130, 12, BOCA_FLAGS },	/* ttyS22 */
+	{ 0, BASE_BAUD, 0x138, 12, BOCA_FLAGS },	/* ttyS23 */
+	{ 0, BASE_BAUD, 0x140, 12, BOCA_FLAGS },	/* ttyS24 */
+	{ 0, BASE_BAUD, 0x148, 12, BOCA_FLAGS },	/* ttyS25 */
+	{ 0, BASE_BAUD, 0x150, 12, BOCA_FLAGS },	/* ttyS26 */
+	{ 0, BASE_BAUD, 0x158, 12, BOCA_FLAGS },	/* ttyS27 */
+	{ 0, BASE_BAUD, 0x160, 12, BOCA_FLAGS },	/* ttyS28 */
+	{ 0, BASE_BAUD, 0x168, 12, BOCA_FLAGS },	/* ttyS29 */
+	{ 0, BASE_BAUD, 0x170, 12, BOCA_FLAGS },	/* ttyS30 */
+	{ 0, BASE_BAUD, 0x178, 12, BOCA_FLAGS },	/* ttyS31 */
+
+/* You can have up to four HUB6's in the system, but I've only
+ * included two cards here for a total of twelve ports.
+ */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,0) },	/* ttyS32 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,1) },	/* ttyS33 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,2) },	/* ttyS34 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,3) },	/* ttyS35 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,4) },	/* ttyS36 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,5) },	/* ttyS37 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,0) },	/* ttyS32 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,1) },	/* ttyS33 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,2) },	/* ttyS34 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,3) },	/* ttyS35 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) },	/* ttyS36 */
+	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) },	/* ttyS37 */
+#endif
+};
+
+#define NR_PORTS	(sizeof(rs_table)/sizeof(struct async_struct))
+#ifdef PODULE
+int dser_sport = 2; /* This is the start of the serial port NULL entries */
+#endif
+
+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,
+					dev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+	static const char *badmagic =
+		"Warning: bad magic number for serial struct (%d, %d) in %s\n";
+	static const char *badinfo =
+		"Warning: null async_struct for (%d, %d) in %s\n";
+
+	if (!info) {
+		printk(badinfo, MAJOR(device), MINOR(device), routine);
+		return 1;
+	}
+	if (info->magic != SERIAL_MAGIC) {
+		printk(badmagic, MAJOR(device), MINOR(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)
+{
+	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;
+
+	do {
+		ch = serial_inp(info, UART_RX);
+		if (*status & (info->ignore_status_mask & ~UART_LSR_OE))/* RMK - stupid to ignore errors! */
+			continue;
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+			break;
+		tty->flip.count++;
+		if (*status & info->read_status_mask) {
+			if (*status & (UART_LSR_BI)) {
+				*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;
+		} else
+			*tty->flip.flag_buf_ptr++ = 0;
+		*tty->flip.char_buf_ptr++ = ch;
+	} while ((*status = serial_inp(info, UART_LSR)) & UART_LSR_DR);
+	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;
+#ifdef CONFIG_SERIAL_NEW_ISR
+		serial_out(info, UART_IER, info->IER);
+#endif
+		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;
+#ifdef CONFIG_SERIAL_NEW_ISR
+		serial_out(info, UART_IER, info->IER);
+#endif
+	}
+}
+
+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...\n", 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...\n");
+#endif
+			rs_sched_event(info, RS_EVENT_HANGUP);
+		}
+	}
+	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;
+#ifdef CONFIG_SERIAL_NEW_ISR
+				serial_out(info, UART_IER, info->IER);
+#endif
+				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;
+#ifdef CONFIG_SERIAL_NEW_ISR
+				serial_out(info, UART_IER, info->IER);
+#endif
+			}
+		}
+	}
+}
+
+#ifdef CONFIG_SERIAL_NEW_ISR
+/*
+ * This is the serial driver's generic interrupt routine
+ */
+static void rs_interrupt(int irq)
+{
+	int status;
+	struct async_struct * info;
+	int pass_counter = 0;
+	struct async_struct *end_mark = 0;
+
+#ifdef SERIAL_DEBUG_INTR
+	printk("rs_interrupt(%d)...", irq);
+#endif
+
+	info = IRQ_ports[irq];
+	if (!info)
+		return;
+
+	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);
+		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++ > 64) {
+#if 0
+				printk("rs loop break\n");
+#endif
+				break; 	/* Prevent infinite loops */
+			}
+			continue;
+		}
+	} while (end_mark != info);
+}
+
+/*
+ * This is the serial driver's interrupt routine for a single port
+ */
+static void rs_interrupt_single(int irq)
+{
+	int status;
+	int pass_counter = 0;
+	struct async_struct * info;
+
+#ifdef SERIAL_DEBUG_INTR
+	printk("rs_interrupt_single(%d)...", irq);
+#endif
+
+	info = IRQ_ports[irq];
+	if (!info || !info->tty)
+		return;
+
+	do {
+		status = serial_inp(info, UART_LSR);
+		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++ > 64) {
+#if 0
+			printk("rs_single loop break.\n");
+#endif
+			break;
+		}
+	} while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT));
+	info->last_active = jiffies;
+}
+
+#else /* CONFIG_SERIAL_NEW_ISR */
+
+/*
+ * This is the serial driver's generic interrupt routine
+ */
+static void rs_interrupt(int irq)
+{
+	int status;
+	struct async_struct * info;
+	int done = 1, pass_counter = 0;
+
+
+#ifdef SERIAL_DEBUG_INTR
+	printk("rs_interrupt(%d)...", irq);
+#endif
+
+	info = IRQ_ports[irq];
+	if (!info)
+		return;
+
+	while (1) {
+		if (!info->tty)
+			goto next;
+
+		serial_outp(info, UART_IER, 0);
+		status = serial_inp(info, UART_LSR);
+		if (status & UART_LSR_DR) {
+			receive_chars(info, &status);
+			done = 0;
+		}
+		check_modem_status(info);
+		if (status & UART_LSR_THRE)
+			transmit_chars(info, &done);
+
+	next:
+		info = info->next_port;
+		if (!info) {
+			info = IRQ_ports[irq];
+			if (done)
+				break;
+			done = 1;
+			if (pass_counter++ > 64) {
+#if 0
+				printk("rs loop break\n");
+#endif
+				break; 	/* Prevent infinite loops */
+			}
+		}
+	}
+
+	/*
+	 * Reset the IER registers; info is already set up from the
+	 * above while loop.
+	 */
+	do
+		serial_outp(info, UART_IER, info->IER);
+	while ((info = info->next_port) != NULL);
+}
+
+/*
+ * This is the serial driver's interrupt routine for a single port
+ */
+static void rs_interrupt_single(int irq)
+{
+	int status;
+	struct async_struct * info;
+
+
+#ifdef SERIAL_DEBUG_INTR
+	printk("rs_interrupt_single(%d)...", irq);
+#endif
+
+	info = IRQ_ports[irq];
+	if (!info || !info->tty)
+		return;
+
+	serial_outp(info, UART_IER, 0);
+	status = serial_inp(info, UART_LSR);
+	if (status & UART_LSR_DR)
+		receive_chars(info, &status);
+	check_modem_status(info);
+	if (status & UART_LSR_THRE)
+		transmit_chars(info, 0);
+
+	/*
+	 * Reset the IER register
+	 */
+	serial_outp(info, UART_IER, info->IER);
+}
+
+#endif /* CONFIG_SERIAL_NEW_ISR */
+
+/*
+ * -------------------------------------------------------------------
+ * 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_HANGUP, &info->event)) {
+		tty_hangup(tty);
+		wake_up_interruptible(&info->open_wait);
+		info->flags &= ~(ASYNC_NORMAL_ACTIVE|
+				 ASYNC_CALLOUT_ACTIVE);
+	}
+	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 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) >= 60*HZ) {
+		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);
+				rs_interrupt(i);
+			} else
+				rs_interrupt_single(i);
+			sti();
+		}
+	}
+	last_strobe = jiffies;
+	timer_table[RS_TIMER].expires = jiffies + 60 * HZ;
+	timer_active |= 1 << RS_TIMER;
+
+	if (IRQ_ports[0]) {
+		cli();
+		rs_interrupt(0);
+		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 = 6000;	/* 60 seconds === a long time :-) */
+
+	info = IRQ_ports[irq];
+	if (!info) {
+		IRQ_timeout[irq] = 6000;
+		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);
+
+	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 *) kmalloc(SERIAL_XMIT_SIZE,GFP_KERNEL);
+		if (!info->xmit_buf)
+			return -ENOMEM;
+	}
+
+	save_flags(flags); cli();
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("starting up ttys%d (irq %d)...\n", info->line, info->irq);
+#endif
+
+	/*
+	 * Clear the FIFO buffers and disable them
+	 * (they will be reenabled in change_speed())
+	 */
+	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);
+			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 (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;
+	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)....\n", 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) {
+		kfree_s(info->xmit_buf, SERIAL_XMIT_SIZE);
+		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 == 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 */
+	cval = cflag & (CSIZE | CSTOPB);
+	cval >>= 4;
+	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
+		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;
+	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;
+	if (I_IGNBRK(info->tty)) {
+		info->ignore_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;
+	}
+
+	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,
+		    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.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 > 31) || (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;
+
+	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;
+	}
+
+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;
+}
+
+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_fs_long(result,(unsigned long *) value);
+	return 0;
+}
+
+static int set_modem_info(struct async_struct * info, unsigned int cmd,
+			  unsigned int *value)
+{
+	unsigned int arg = get_fs_long((unsigned long *) 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+10;
+	while (timeout >= jiffies)
+		;
+
+	rs_triggered = 0;	/* Reset after letting things settle */
+
+	timeout = jiffies+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 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;
+
+	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 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;
+
+		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 (!(old_termios->c_cflag & CLOCAL) &&
+	    (tty->termios->c_cflag & CLOCAL))
+		wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * 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;
+	if (info->flags & ASYNC_INITIALIZED) {
+		tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */
+		/*
+		 * 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);
+	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;
+
+	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)
+{
+	int		retval;
+	int		do_clocal = 0;
+	struct wait_queue wait = { current, NULL };
+
+	/*
+	 * 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, then make the check up front
+	 * and then exit.
+	 */
+	if (filp->f_flags & O_NONBLOCK) {
+		if (info->flags & ASYNC_CALLOUT_ACTIVE)
+			return -EBUSY;
+		info->flags |= ASYNC_NORMAL_ACTIVE;
+		return 0;
+	}
+
+	if (info->normal_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 *) kmalloc(SERIAL_XMIT_SIZE,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...\n", 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.00 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;
+	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);
+
+	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:
+			info->type = PORT_16550A;
+			info->xmit_fifo_size = 16;
+			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;
+	}
+
+	/*
+	 * Reset the UART.
+	 */
+	serial_outp(info, UART_MCR, 0x00);
+	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!
+ */
+long rs_init(long kmem_start)
+{
+	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;
+	}
+
+	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 = 50;
+		info->x_char = 0;
+		info->event = 0;
+		info->count = 0;
+		info->blocked_open = 0;
+		info->tqueue.routine = do_softint;
+		info->tqueue.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;
+			default:
+				printk("\n");
+				break;
+		}
+	}
+	return kmem_start;
+}
+
+/*
+ * 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 -r -u -N linux.orig/arch/arm/drivers/char/tty_diff linux.arm/arch/arm/drivers/char/tty_diff
--- linux.orig/arch/arm/drivers/char/tty_diff	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/tty_diff	Fri Oct 27 23:14:40 1995
@@ -0,0 +1,605 @@
+--- tty_io.c	Fri Jul 14 00:07:27 1995
++++ ../../../../drivers/char/tty_io.c	Sun Sep 24 11:09:57 1995
+@@ -52,6 +52,7 @@
+ #include <linux/mm.h>
+ #include <linux/string.h>
+ #include <linux/malloc.h>
++#include <linux/config.h>
+ 
+ #include <asm/segment.h>
+ #include <asm/system.h>
+@@ -59,18 +60,27 @@
+ 
+ #include "kbd_kern.h"
+ #include "vt_kern.h"
++/* This to come */
++/*#include "selection.h"*/
+ 
+ #define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
+ #define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
+ 
+ #undef TTY_DEBUG_HANGUP
+ 
++/* These to go */
+ extern int set_selection(const int arg, struct tty_struct *tty);
+ extern int paste_selection(struct tty_struct *tty);
+ extern int sel_loadlut(const int arg);
+ extern int mouse_reporting(void);
+ extern int shift_state;
+ extern int do_screendump(int arg);
++#define TTY_PARANOIA_CHECK
++#define CHECK_TTY_COUNT
++
++extern void do_blank_screen(int nopowersave);
++extern void do_unblank_screen(void);
++/*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 */
+@@ -78,10 +80,12 @@
+ 
+ /*
+  * 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;
+ 
+@@ -123,8 +127,6 @@
+ 	return(_tty_name(tty, buf));
+ }
+ 
+-#define TTY_PARANOIA_CHECK
+-
+ inline int tty_paranoia_check(struct tty_struct *tty, dev_t device,
+ 			      const char *routine)
+ {
+@@ -146,18 +148,45 @@
+ 	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 (%d, %d) tty->count(%d) != #fd's(%d) in %s\n",
++		       MAJOR(tty->device), MINOR(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;
+ }
+ 
+@@ -175,6 +204,8 @@
+ 		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);
+@@ -313,6 +344,7 @@
+ 
+ 	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;
+@@ -352,18 +384,22 @@
+ 				       -i);
+ 		}
+ 	}
+-
+-	if (tty->session > 0) {
+-		kill_sl(tty->session,SIGHUP,1);
+-		kill_sl(tty->session,SIGCONT,1);
+-	}
+-	tty->flags = 0;
+-	tty->session = 0;
+-	tty->pgrp = -1;
++	
+  	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)
+@@ -406,13 +442,19 @@
+ 	struct tty_struct *tty = current->tty;
+ 	struct task_struct *p;
+ 
+-	if (!tty)
++	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;
+ 
+@@ -466,6 +508,7 @@
+                 return;
+         if (!vc_cons_allocated(new_console))
+                 return;
++	last_console = fg_console;
+ 
+ 	/*
+ 	 * If we're switching, we could be going from KD_GRAPHICS to
+@@ -511,11 +554,9 @@
+ 	if (old_vc_mode != vt_cons[new_console]->vc_mode)
+ 	{
+ 		if (vt_cons[new_console]->vc_mode == KD_TEXT)
+-			unblank_screen();
+-		else {
+-			timer_active &= ~(1<<BLANK_TIMER);
+-			blank_screen();
+-		}
++			do_unblank_screen();
++		else
++			do_blank_screen(1);
+ 	}
+ 
+ 	/*
+@@ -715,7 +756,7 @@
+ 	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;
++	struct tty_driver *driver;	
+ 	int retval;
+ 	int idx;
+ 
+@@ -733,6 +774,7 @@
+ 	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 &&
+@@ -772,9 +813,10 @@
+ 
+ 		if (!*o_tty_loc && !o_tty) {
+ 			dev_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,
+@@ -800,7 +841,7 @@
+ 			memset(o_ltp, 0, sizeof(struct termios));
+ 			goto repeat;
+ 		}
+-
++		
+ 	}
+ 	/* Now we have allocated all the structures: update all the pointers.. */
+ 	if (!*tp_loc) {
+@@ -826,8 +867,15 @@
+ 			}
+ 		}
+ 		tty = NULL;
+-	} else
++	} 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;
+@@ -887,12 +935,13 @@
+ 	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;
+@@ -955,7 +1004,7 @@
+ 		}
+ 	}
+ #endif
+-
++	
+ 	if (tty->driver.close)
+ 		tty->driver.close(tty, filp);
+ 	if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
+@@ -974,6 +1023,9 @@
+ 	if (tty->count)
+ 		return;
+ 
++	/*
++	 * We're committed; at this point, we must not block!
++	 */
+ 	if (o_tty) {
+ 		if (o_tty->count)
+ 			return;
+@@ -981,10 +1033,11 @@
+ 		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
+@@ -1007,7 +1060,12 @@
+ 		(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;
+@@ -1036,11 +1094,12 @@
+ 	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));
+ 	}
+ }
+ 
+@@ -1077,11 +1136,12 @@
+ 		noctty = 1;
+ 	}
+ 	minor = MINOR(device);
+-
++	
+ 	retval = init_dev(device, &tty);
+-	filp->private_data = 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;
+@@ -1118,6 +1178,7 @@
+ 	    !current->tty &&
+ 	    tty->session == 0) {
+ 		current->tty = tty;
++		current->tty_old_pgrp = 0;
+ 		tty->session = current->session;
+ 		tty->pgrp = current->pgrp;
+ 	}
+@@ -1190,13 +1251,14 @@
+ 		if (!tty->fasync && !tty->read_wait)
+ 			tty->minimum_to_wake = N_TTY_BUF_SIZE;
+ 	}
+-	return 0;
++	return 0;	
+ }
+ 
++#if 0
+ /*
+  * XXX does anyone use this anymore?!?
+  */
+-static int do_get_ps_info(int arg)
++static int do_get_ps_info(unsigned long arg)
+ {
+ 	struct tstruct {
+ 		int flag;
+@@ -1207,7 +1269,7 @@
+ 	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;
+@@ -1220,10 +1282,11 @@
+ 				put_fs_byte(*c++, d++);
+ 			put_fs_long(1, (unsigned long *)(ts->present+n));
+ 		}
+-		else
++		else	
+ 			put_fs_long(0, (unsigned long *)(ts->present+n));
+-	return(0);
++	return(0);			
+ }
++#endif
+ 
+ static int tty_ioctl(struct inode * inode, struct file * file,
+ 		     unsigned int cmd, unsigned long arg)
+@@ -1235,7 +1298,7 @@
+ 	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;
+@@ -1268,7 +1331,7 @@
+ 			retval = verify_area(VERIFY_READ, (void *) arg,
+ 					     sizeof (struct winsize));
+ 			if (retval)
+-				return retval;
++				return retval;			
+ 			memcpy_fromfs(&tmp_ws, (struct winsize *) arg,
+ 				      sizeof (struct winsize));
+ 			if (memcmp(&tmp_ws, &tty->winsize,
+@@ -1344,6 +1407,7 @@
+ 					return -EPERM;
+ 			}
+ 			current->tty = tty;
++			current->tty_old_pgrp = 0;
+ 			tty->session = current->session;
+ 			tty->pgrp = current->pgrp;
+ 			return 0;
+@@ -1389,39 +1453,61 @@
+ 			arg = get_fs_long((unsigned long *) 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 (get_fs_byte((char *)arg))
++			switch (retval = get_fs_byte((char *)arg))
+ 			{
+ 				case 0:
+-					return do_screendump(arg);
++				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:
+-					unblank_screen();
++					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_fs_byte(0, arg);
+-/* 					put_fs_byte(shift_state,arg); */
++			/*
++			 * 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_fs_byte(shift_state,arg);
+ 					return 0;
+ 				case 7:
+ 					put_fs_byte(mouse_reporting(),arg);
+ 					return 0;
+-				default:
++/*				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,
+@@ -1445,7 +1531,7 @@
+  * 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
+@@ -1461,7 +1547,7 @@
+ 	int session;
+ 	int		i;
+ 	struct file	*filp;
+-
++	
+ 	if (!tty)
+ 		return;
+ 	session  = tty->session;
+@@ -1520,7 +1606,7 @@
+ 	count = tty->flip.count;
+ 	tty->flip.count = 0;
+ 	sti();
+-
++	
+ #if 0
+ 	if (count > tty->max_flip_cnt)
+ 		tty->max_flip_cnt = count;
+@@ -1569,10 +1655,10 @@
+ 
+ 	if (!driver->put_char)
+ 		driver->put_char = tty_default_put_char;
+-
++	
+ 	driver->prev = 0;
+ 	driver->next = tty_drivers;
+-	tty_drivers->prev = driver;
++	if (tty_drivers) tty_drivers->prev = driver;
+ 	tty_drivers = driver;
+ 	return error;
+ }
+@@ -1585,31 +1671,32 @@
+ 	int	retval;
+ 	struct tty_driver *p;
+ 	int	found = 0;
+-	int	major_inuse = 0;
+-
+-	if (driver->refcount)
++	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)
+-			major_inuse++;
++			othername = p->name;
+ 	}
+ 
+-	if (!major_inuse) {
++	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 = driver->next->prev;
++		driver->next->prev = driver->prev;
+ 
+ 	return 0;
+ }
+@@ -1628,20 +1715,19 @@
+ 	(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
+ 
+ 	/*
+-	 * Set up the standard termios.  Individual tty drivers may
++	 * 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
++	 * set up the console device so that later boot sequences can 
+ 	 * inform about problems etc..
+ 	 */
+ 	return con_init(kmem_start);
+@@ -1662,6 +1748,10 @@
+ 
+ 	kmem_start = kbd_init(kmem_start);
+ 	kmem_start = rs_init(kmem_start);
++#ifdef CONFIG_CYCLADES
++	kmem_start = cy_init(kmem_start);
++#endif
+ 	kmem_start = pty_init(kmem_start);
++	kmem_start = vcs_init(kmem_start);
+ 	return kmem_start;
+ }
diff -r -u -N linux.orig/arch/arm/drivers/char/tty_io.c linux.arm/arch/arm/drivers/char/tty_io.c
--- linux.orig/arch/arm/drivers/char/tty_io.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/tty_io.c	Fri Oct 27 23:14:27 1995
@@ -0,0 +1,1767 @@
+/*
+ *  linux/drivers/char/tty_io.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ * '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
+ */
+
+#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 <linux/config.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include "kbd_kern.h"
+#include "vt_kern.h"
+/* This to come */
+/*#include "selection.h"*/
+
+#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
+#define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
+
+#undef TTY_DEBUG_HANGUP
+
+/* These to go */
+extern int set_selection(const int arg, struct tty_struct *tty);
+extern int paste_selection(struct tty_struct *tty);
+extern int sel_loadlut(const int arg);
+extern int mouse_reporting(void);
+extern int shift_state;
+extern int do_screendump(int arg);
+#define TTY_PARANOIA_CHECK
+#define CHECK_TTY_COUNT
+
+extern void do_blank_screen(int nopowersave);
+extern void do_unblank_screen(void);
+/*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 *, 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);
+
+#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, dev_t device,
+			      const char *routine)
+{
+#ifdef TTY_PARANOIA_CHECK
+	static const char *badmagic =
+		"Warning: bad magic number for tty struct (%d, %d) in %s\n";
+	static const char *badtty =
+		"Warning: null TTY for (%d, %d) in %s\n";
+
+	if (!tty) {
+		printk(badtty, MAJOR(device), MINOR(device), routine);
+		return 1;
+	}
+	if (tty->magic != TTY_MAGIC) {
+		printk(badmagic, MAJOR(device), MINOR(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 (%d, %d) tty->count(%d) != #fd's(%d) in %s\n",
+		       MAJOR(tty->device), MINOR(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(dev_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, 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;
+}
+
+/*
+ * 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);
+	}
+
+	/*
+	 * 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, 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,(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(dev_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) {
+			dev_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 (%d, %d)\n",
+		       MAJOR(tty->device), MINOR(tty->device));
+		return;
+	}
+	if (tty != tty->driver.table[idx]) {
+		printk("release_dev: driver.table[%d] not tty for (%d, %d)\n",
+		       idx, MAJOR(tty->device), MINOR(tty->device));
+		return;
+	}
+	if (tp != tty->driver.termios[idx]) {
+		printk("release_dev: driver.termios[%d] not termios for (%d, %d)\n",
+		       idx, MAJOR(tty->device), MINOR(tty->device));
+		return;
+	}
+	if (ltp != tty->driver.termios_locked[idx]) {
+		printk("release_dev: driver.termios_locked[%d] not termios_locked for (%d, %d)\n",
+		       idx, MAJOR(tty->device), MINOR(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 (%d, %d)\n",
+			       idx, MAJOR(tty->device), MINOR(tty->device));
+			return;
+		}
+		if (o_tp != tty->driver.other->termios[idx]) {
+			printk("release_dev: other->termios[%d] not o_termios for (%d, %d)\n",
+			       idx, MAJOR(tty->device), MINOR(tty->device));
+			return;
+		}
+		if (o_ltp != tty->driver.other->termios_locked[idx]) {
+			printk("release_dev: other->termios_locked[%d] not o_termios_locked for (%d, %d)\n",
+			       idx, MAJOR(tty->device), MINOR(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;
+	dev_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;
+}
+
+static int tty_fasync(struct inode * inode, struct file * filp, int on)
+{
+	struct tty_struct * tty;
+	struct fasync_struct *fa, *prev;
+
+	tty = (struct tty_struct *)filp->private_data;
+	if (tty_paranoia_check(tty, inode->i_rdev, "tty_fasync"))
+		return 0;
+
+	for (fa = tty->fasync, prev = 0; fa; prev= fa, fa = 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;
+		fa->fa_next = tty->fasync;
+		tty->fasync = fa;
+		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 (!fa)
+			return 0;
+		if (prev)
+			prev->fa_next = fa->fa_next;
+		else
+			tty->fasync = fa->fa_next;
+		kfree_s(fa, sizeof(struct fasync_struct));
+		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_fs_byte(*c++, d++);
+			put_fs_long(1, (unsigned long *)(ts->present+n));
+		}
+		else	
+			put_fs_long(0, (unsigned long *)(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_fs_byte((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(long));
+			if (retval)
+				return retval;
+			arg = get_fs_long((unsigned long *) 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_fs_long(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_fs_long((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 (unsigned long));
+			if (retval)
+				return retval;
+			put_fs_long(tty->ldisc.num, (unsigned long *) arg);
+			return 0;
+		case TIOCSETD:
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			arg = get_fs_long((unsigned long *) 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_fs_byte((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_fs_byte(shift_state,arg);
+					return 0;
+				case 7:
+					put_fs_byte(mouse_reporting(),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;
+	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..
+ */
+long tty_init(long kmem_start)
+{
+	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);
+
+	kmem_start = kbd_init(kmem_start);
+	kmem_start = rs_init(kmem_start);
+#ifdef CONFIG_CYCLADES
+	kmem_start = cy_init(kmem_start);
+#endif
+	kmem_start = pty_init(kmem_start);
+	kmem_start = vcs_init(kmem_start);
+	return kmem_start;
+}
diff -r -u -N linux.orig/arch/arm/drivers/char/tty_io.c.orig linux.arm/arch/arm/drivers/char/tty_io.c.orig
--- linux.orig/arch/arm/drivers/char/tty_io.c.orig	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/tty_io.c.orig	Fri Oct 27 23:14:41 1995
@@ -0,0 +1,1667 @@
+/*
+ *  linux/drivers/char/tty_io.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ * '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
+ */
+
+#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 "kbd_kern.h"
+#include "vt_kern.h"
+
+#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
+#define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
+
+#undef TTY_DEBUG_HANGUP
+
+extern int set_selection(const int arg, struct tty_struct *tty);
+extern int paste_selection(struct tty_struct *tty);
+extern int sel_loadlut(const int arg);
+extern int mouse_reporting(void);
+extern int shift_state;
+extern int do_screendump(int 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,
+ * redirect is the pseudo-tty that console output
+ * is redirected to if asked by TIOCCONS.
+ */
+int fg_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 *, 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);
+
+#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));
+}
+
+#define TTY_PARANOIA_CHECK
+
+inline int tty_paranoia_check(struct tty_struct *tty, dev_t device,
+			      const char *routine)
+{
+#ifdef TTY_PARANOIA_CHECK
+	static const char *badmagic =
+		"Warning: bad magic number for tty struct (%d, %d) in %s\n";
+	static const char *badtty =
+		"Warning: null TTY for (%d, %d) in %s\n";
+
+	if (!tty) {
+		printk(badtty, MAJOR(device), MINOR(device), routine);
+		return 1;
+	}
+	if (tty->magic != TTY_MAGIC) {
+		printk(badmagic, MAJOR(device), MINOR(device), routine);
+		return 1;
+	}
+#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;
+
+	/* 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(dev_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, 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;
+	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);
+		}
+	}
+
+	if (tty->session > 0) {
+		kill_sl(tty->session,SIGHUP,1);
+		kill_sl(tty->session,SIGCONT,1);
+	}
+	tty->flags = 0;
+	tty->session = 0;
+	tty->pgrp = -1;
+ 	for_each_task(p) {
+		if (p->tty == tty)
+			p->tty = NULL;
+	}
+	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)
+		return;
+
+	if (tty->pgrp > 0) {
+		kill_pg(tty->pgrp, SIGHUP, priv);
+		kill_pg(tty->pgrp, SIGCONT, priv);
+	}
+	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;
+}
+
+/*
+ * 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;
+
+	/*
+	 * 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)
+			unblank_screen();
+		else {
+			timer_active &= ~(1<<BLANK_TIMER);
+			blank_screen();
+		}
+	}
+
+	/*
+	 * 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, 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,(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(dev_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) {
+			dev_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
+		(*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;
+
+	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 (%d, %d)\n",
+		       MAJOR(tty->device), MINOR(tty->device));
+		return;
+	}
+	if (tty != tty->driver.table[idx]) {
+		printk("release_dev: driver.table[%d] not tty for (%d, %d)\n",
+		       idx, MAJOR(tty->device), MINOR(tty->device));
+		return;
+	}
+	if (tp != tty->driver.termios[idx]) {
+		printk("release_dev: driver.termios[%d] not termios for (%d, %d)\n",
+		       idx, MAJOR(tty->device), MINOR(tty->device));
+		return;
+	}
+	if (ltp != tty->driver.termios_locked[idx]) {
+		printk("release_dev: driver.termios_locked[%d] not termios_locked for (%d, %d)\n",
+		       idx, MAJOR(tty->device), MINOR(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 (%d, %d)\n",
+			       idx, MAJOR(tty->device), MINOR(tty->device));
+			return;
+		}
+		if (o_tp != tty->driver.other->termios[idx]) {
+			printk("release_dev: other->termios[%d] not o_termios for (%d, %d)\n",
+			       idx, MAJOR(tty->device), MINOR(tty->device));
+			return;
+		}
+		if (o_ltp != tty->driver.other->termios_locked[idx]) {
+			printk("release_dev: other->termios_locked[%d] not o_termios_locked for (%d, %d)\n",
+			       idx, MAJOR(tty->device), MINOR(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;
+
+	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
+
+	/*
+	 * 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;
+
+	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));
+	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;
+	dev_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);
+	filp->private_data = tty;
+	if (retval)
+		return retval;
+	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;
+		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;
+}
+
+static int tty_fasync(struct inode * inode, struct file * filp, int on)
+{
+	struct tty_struct * tty;
+	struct fasync_struct *fa, *prev;
+
+	tty = (struct tty_struct *)filp->private_data;
+	if (tty_paranoia_check(tty, inode->i_rdev, "tty_fasync"))
+		return 0;
+
+	for (fa = tty->fasync, prev = 0; fa; prev= fa, fa = 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;
+		fa->fa_next = tty->fasync;
+		tty->fasync = fa;
+		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 (!fa)
+			return 0;
+		if (prev)
+			prev->fa_next = fa->fa_next;
+		else
+			tty->fasync = fa->fa_next;
+		kfree_s(fa, sizeof(struct fasync_struct));
+		if (!tty->fasync && !tty->read_wait)
+			tty->minimum_to_wake = N_TTY_BUF_SIZE;
+	}
+	return 0;
+}
+
+/*
+ * XXX does anyone use this anymore?!?
+ */
+static int do_get_ps_info(int 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_fs_byte(*c++, d++);
+			put_fs_long(1, (unsigned long *)(ts->present+n));
+		}
+		else
+			put_fs_long(0, (unsigned long *)(ts->present+n));
+	return(0);
+}
+
+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_fs_byte((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(long));
+			if (retval)
+				return retval;
+			arg = get_fs_long((unsigned long *) 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;
+			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_fs_long(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_fs_long((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 (unsigned long));
+			if (retval)
+				return retval;
+			put_fs_long(tty->ldisc.num, (unsigned long *) arg);
+			return 0;
+		case TIOCSETD:
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			arg = get_fs_long((unsigned long *) arg);
+			return tty_set_ldisc(tty, arg);
+		case TIOCLINUX:
+			retval = verify_area(VERIFY_READ, (void *) arg, 1);
+			if (retval)
+				return retval;
+			switch (get_fs_byte((char *)arg))
+			{
+				case 0:
+					return do_screendump(arg);
+				case 1:
+					printk("Deprecated TIOCLINUX (1) ioctl\n");
+					return do_get_ps_info(arg);
+				case 2:
+					return set_selection(arg, tty);
+				case 3:
+					return paste_selection(tty);
+				case 4:
+					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_fs_byte(0, arg);
+/* 					put_fs_byte(shift_state,arg); */
+					return 0;
+				case 7:
+					put_fs_byte(mouse_reporting(),arg);
+					return 0;
+				default:
+					return -EINVAL;
+			}
+		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;
+	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;
+	int	major_inuse = 0;
+
+	if (driver->refcount)
+		return -EBUSY;
+
+	for (p = tty_drivers; p; p = p->next) {
+		if (p == driver)
+			found++;
+		else if (p->major == driver->major)
+			major_inuse++;
+	}
+
+	if (!major_inuse) {
+		retval = unregister_chrdev(driver->major, driver->name);
+		if (retval)
+			return retval;
+	}
+
+	if (driver->prev)
+		driver->prev->next = driver->next;
+	else
+		tty_drivers = driver->next;
+
+	if (driver->next)
+		driver->next = driver->next->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..
+ */
+long tty_init(long kmem_start)
+{
+	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);
+
+	kmem_start = kbd_init(kmem_start);
+	kmem_start = rs_init(kmem_start);
+	kmem_start = pty_init(kmem_start);
+	return kmem_start;
+}
diff -r -u -N linux.orig/arch/arm/drivers/char/tty_ioctl.c linux.arm/arch/arm/drivers/char/tty_ioctl.c
--- linux.orig/arch/arm/drivers/char/tty_ioctl.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/tty_ioctl.c	Fri Oct 27 23:14:27 1995
@@ -0,0 +1,374 @@
+/*
+ *  linux/drivers/char/tty_ioctl.c
+ *
+ *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
+ *
+ * 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 -r -u -N linux.orig/arch/arm/drivers/char/vc_screen.c linux.arm/arch/arm/drivers/char/vc_screen.c
--- linux.orig/arch/arm/drivers/char/vc_screen.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/vc_screen.c	Fri Oct 27 23:14:41 1995
@@ -0,0 +1,226 @@
+/*
+ * linux/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
+ */
+
+#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, char *buf, int count)
+{
+	unsigned long p = file->f_pos;
+	unsigned int cons = MINOR(inode->i_rdev);
+	int viewed, attr, size, written;
+	char *buf0;
+	unsigned long *org, d;
+
+	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 */
+};
+
+long vcs_init(long kmem_start)
+{
+	if (register_chrdev(VCS_MAJOR, "vcs", &vcs_fops))
+		printk("unable to get major %d for vcs device", VCS_MAJOR);
+	return kmem_start;
+}
diff -r -u -N linux.orig/arch/arm/drivers/char/vt.c linux.arm/arch/arm/drivers/char/vt.c
--- linux.orig/arch/arm/drivers/char/vt.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/vt.c	Fri Oct 27 23:14:21 1995
@@ -0,0 +1,920 @@
+/*
+ *  linux/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 and EGA/VGA font from console.c
+ */
+extern int con_set_trans(char * table);
+extern int con_get_trans(char * 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 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.
+ */
+void
+kd_mksound2(unsigned int count, unsigned int vol, unsigned int ticks)
+{
+	int freq;
+	
+	if(count)
+		freq = 1193180/count;
+	else
+		freq = 0;
+	mk_beep(freq, vol, ticks);
+}
+
+void
+kd_mksound(unsigned int count, unsigned int ticks)
+{
+	kd_mksound2(count, 64, 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] >> 16) & 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_fs_byte(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_fs_long(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_fs_long(ucval, (unsigned long *) 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_fs_long(ucval, (unsigned long *) 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_fs_long((int *) &a->scancode);
+		kc = getkeycode(sc);
+		if (kc < 0)
+			return kc;
+		put_fs_long(kc, (int *) &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_fs_long((int *) &a->scancode);
+		kc = get_fs_long((int *) &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_fs_byte((char *) &a->kb_index)) >= NR_KEYS)
+			return -EINVAL;
+		if ((s = get_fs_byte((char *) &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_fs_word(val, (short *) &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_fs_byte((char *) &a->kb_index)) >= NR_KEYS)
+			return -EINVAL;
+		if ((s = get_fs_byte((char *) &a->kb_table)) >= MAX_NR_KEYMAPS)
+			return -EINVAL;
+		v = get_fs_word(&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_fs_byte(&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_fs_byte(*p, q++);
+		put_fs_byte(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_fs_byte(&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_fs_byte(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_fs_byte(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_fs_long(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_fs_long(&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_fs_byte(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_fs_byte(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_fs_byte(&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_fs_byte(&vtmode->waitv);
+		vt_cons[console]->vt_mode.relsig = get_fs_word(&vtmode->relsig);
+		vt_cons[console]->vt_mode.acqsig = get_fs_word(&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_fs_byte(vt_cons[console]->vt_mode.mode, &vtmode->mode);
+		put_fs_byte(vt_cons[console]->vt_mode.waitv, &vtmode->waitv);
+		put_fs_word(vt_cons[console]->vt_mode.relsig, &vtmode->relsig);
+		put_fs_word(vt_cons[console]->vt_mode.acqsig, &vtmode->acqsig);
+		put_fs_word(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_fs_word(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_fs_word(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_fs_long(i < MAX_NR_CONSOLES ? (i+1) : -1,
+			    (unsigned long *)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_fs_word(&vtsizes->v_rows);
+		cc = get_fs_word(&vtsizes->v_cols);
+		return vc_resize(ll, cc);
+	}
+	
+	case VT_GETPALETTE:
+	{
+		unsigned long pix, num, *val;
+		
+	        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;
+
+	        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:
+		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((char *)arg);
+
+	case GIO_SCRNMAP:
+		return con_get_trans((char *)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 -r -u -N linux.orig/arch/arm/drivers/char/vt_kern.h linux.arm/arch/arm/drivers/char/vt_kern.h
--- linux.orig/arch/arm/drivers/char/vt_kern.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/char/vt_kern.h	Fri Oct 27 23:14:27 1995
@@ -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 -r -u -N linux.orig/arch/arm/drivers/net/Makefile linux.arm/arch/arm/drivers/net/Makefile
--- linux.orig/arch/arm/drivers/net/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/Makefile	Fri Oct 27 23:15:04 1995
@@ -0,0 +1,105 @@
+# File: drivers/net/Makefile
+#
+# Makefile for the Linux network (ethercard) device drivers.
+#
+
+# This will go away in some future future: hidden configuration files
+# are difficult for users to deal with.
+include CONFIG
+
+# Build MODULES by appending to this string for every driver below
+#MODULES :=
+
+NETDRV_OBJS := Space.o auto_irq.o net_init.o loopback.o
+CFLAGS :=$(CFLAGS) -I../../../../net/inet
+CPP :=$(CPP) -I../../../../net/inet
+
+.c.o:
+	$(CC) $(CFLAGS) -c $<
+
+
+# The point of the makefile...
+all: net.a
+
+Space.o: Space.c ../../../../include/linux/autoconf.h
+	$(CC) $(CFLAGS) $(OPTS) -c $<
+
+net_init.o: ../../../../include/linux/autoconf.h
+
+ifdef CONFIG_ETHER3
+NETDRV_OBJS := $(NETDRV_OBJS) nq8005.o
+else
+MODULES := $(MODULES) nq8005.o
+endif
+
+ifdef CONFIG_PLIP
+NETDRV_OBJS := $(NETDRV_OBJS) plip.o
+else
+MODULES := $(MODULES) plip.o
+endif
+plip.o:	plip.c CONFIG
+	$(CC) $(CPPFLAGS) $(CFLAGS) $(PLIP_OPTS) -c $<
+
+ifdef CONFIG_PPP
+NETDRV_OBJS := $(NETDRV_OBJS) ppp.o
+CONFIG_SLHC = CONFIG_SLHC
+else
+MODULES := $(MODULES) ppp.o
+endif
+
+ifdef CONFIG_SLIP
+NETDRV_OBJS := $(NETDRV_OBJS) slip.o
+CONFIG_SLHC = CONFIG_SLHC
+else
+MODULES := $(MODULES) slip.o
+endif
+slip.o:	slip.c CONFIG
+	$(CC) $(CPPFLAGS) $(CFLAGS) -c $<
+
+ifdef CONFIG_DUMMY
+NETDRV_OBJS := $(NETDRV_OBJS) dummy.o
+else
+MODULES := $(MODULES) dummy.o
+endif
+dummy.o: dummy.c CONFIG
+	$(CC) $(CPPFLAGS) $(CFLAGS) -c $<
+
+ifdef CONFIG_SLHC
+NETDRV_OBJS := $(NETDRV_OBJS) slhc.o
+else
+MODULES := slhc.o $(MODULES)
+endif
+
+net.a: $(NETDRV_OBJS)
+	rm -f net.a
+	$(AR) rcs net.a $(NETDRV_OBJS)
+
+clean:
+	rm -f core *.o *.a *.s
+
+dep:
+	$(CPP) -M $(NETDRV_OBJS:.o=.c) > .depend
+ifdef MODULES
+	$(CPP) -M -DMODULE $(MODULES:.o=.c) >> .depend
+endif
+
+tar:
+
+ifdef MODULES
+
+modules: $(MODULES)
+	echo $(MODULES) > ../../../../modules/NET_MODULES
+	cd ../../../../modules; \
+		for i in $(MODULES); do ln -sf ../arch/arm/drivers/net/$$i .; done
+
+else
+
+modules:
+
+endif
+
+# include a dependency file if one exists
+
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff -r -u -N linux.orig/arch/arm/drivers/net/Space.c linux.arm/arch/arm/drivers/net/Space.c
--- linux.orig/arch/arm/drivers/net/Space.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/Space.c	Fri Oct 27 23:15:03 1995
@@ -0,0 +1,202 @@
+/*
+ * 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>
+
+#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 ether3_probe(struct device *dev);
+
+static int
+ethif_probe(struct device *dev)
+{
+    short base_addr = dev->base_addr;
+
+    if (base_addr < 0  ||  base_addr == 1)
+	return 1;		/* ENXIO */
+
+    if (1
+#ifdef CONFIG_ETHER3
+        && ether3_probe(dev)
+#endif
+	&& 1 ) {
+	return 1;	/* -ENODEV or -EAGAIN would be more accurate. */
+    }
+    return 0;
+}
+
+/* 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. */
+
+static struct device eth3_dev = {
+    "eth3", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, NEXT_DEV, ethif_probe };
+static struct device eth2_dev = {
+    "eth2", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, &eth3_dev, ethif_probe };
+static struct device eth1_dev = {
+    "eth1", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, &eth2_dev, ethif_probe };
+
+static struct device eth0_dev = {
+    "eth0", 0, 0, 0, 0, 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", 0, 0, 0, 0, 0x278, 2, 0, 0, 0, NEXT_DEV, plip_init, };
+    static struct device plip1_dev = {
+	"plip1", 0, 0, 0, 0, 0x378, 7, 0, 0, 0, &plip2_dev, plip_init, };
+    static struct device plip0_dev = {
+	"plip0", 0, 0, 0, 0, 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)
+    extern int slip_init(struct device *);
+    
+#ifdef SL_SLIP_LOTS
+
+    static struct device slip15_dev={"sl15",0,0,0,0,15,0,0,0,0,NEXT_DEV,slip_init};
+    static struct device slip14_dev={"sl14",0,0,0,0,14,0,0,0,0,&slip15_dev,slip_init};
+    static struct device slip13_dev={"sl13",0,0,0,0,13,0,0,0,0,&slip14_dev,slip_init};
+    static struct device slip12_dev={"sl12",0,0,0,0,12,0,0,0,0,&slip13_dev,slip_init};
+    static struct device slip11_dev={"sl11",0,0,0,0,11,0,0,0,0,&slip12_dev,slip_init};
+    static struct device slip10_dev={"sl10",0,0,0,0,10,0,0,0,0,&slip11_dev,slip_init};
+    static struct device slip9_dev={"sl9",0,0,0,0,9,0,0,0,0,&slip10_dev,slip_init};
+    static struct device slip8_dev={"sl8",0,0,0,0,8,0,0,0,0,&slip9_dev,slip_init};
+    static struct device slip7_dev={"sl7",0,0,0,0,7,0,0,0,0,&slip8_dev,slip_init};
+    static struct device slip6_dev={"sl6",0,0,0,0,6,0,0,0,0,&slip7_dev,slip_init};
+    static struct device slip5_dev={"sl5",0,0,0,0,5,0,0,0,0,&slip6_dev,slip_init};
+    static struct device slip4_dev={"sl4",0,0,0,0,4,0,0,0,0,&slip5_dev,slip_init};
+#   undef	NEXT_DEV
+#   define	NEXT_DEV	(&slip4_dev)
+#endif	/* SL_SLIP_LOTS */
+    
+    static struct device slip3_dev = {
+	"sl3",			/* Internal SLIP driver, channel 3	*/
+	0x0,			/* recv memory end			*/
+	0x0,			/* recv memory start			*/
+	0x0,			/* memory end				*/
+	0x0,			/* memory start				*/
+	0x3,			/* base I/O address			*/
+	0,			/* IRQ					*/
+	0, 0, 0,		/* flags				*/
+	NEXT_DEV,		/* next device				*/
+	slip_init		/* slip_init should set up the rest	*/
+    };
+    static struct device slip2_dev = {
+	"sl2",			/* Internal SLIP driver, channel 2	*/
+	0x0,			/* recv memory end			*/
+	0x0,			/* recv memory start			*/
+	0x0,			/* memory end				*/
+	0x0,			/* memory start				*/
+	0x2,			/* base I/O address			*/
+	0,			/* IRQ					*/
+	0, 0, 0,		/* flags				*/
+	&slip3_dev,		/* next device				*/
+	slip_init		/* slip_init should set up the rest	*/
+    };
+    static struct device slip1_dev = {
+	"sl1",			/* Internal SLIP driver, channel 1	*/
+	0x0,			/* recv memory end			*/
+	0x0,			/* recv memory start			*/
+	0x0,			/* memory end				*/
+	0x0,			/* memory start				*/
+	0x1,			/* base I/O address			*/
+	0,			/* IRQ					*/
+	0, 0, 0,		/* flags				*/
+	&slip2_dev,		/* next device				*/
+	slip_init		/* slip_init should set up the rest	*/
+    };
+    static struct device slip0_dev = {
+	"sl0",			/* Internal SLIP driver, channel 0	*/
+	0x0,			/* recv memory end			*/
+	0x0,			/* recv memory start			*/
+	0x0,			/* memory end				*/
+	0x0,			/* memory start				*/
+	0x0,			/* base I/O address			*/
+	0,			/* IRQ					*/
+	0, 0, 0,		/* flags				*/
+	&slip1_dev,		/* next device				*/
+	slip_init		/* slip_init should set up the rest	*/
+    };
+#   undef	NEXT_DEV
+#   define	NEXT_DEV	(&slip0_dev)
+#endif	/* SLIP */
+  
+#if defined(CONFIG_PPP)
+extern int ppp_init(struct device *);
+static struct device ppp3_dev = {
+    "ppp3", 0x0, 0x0, 0x0, 0x0, 3, 0, 0, 0, 0, NEXT_DEV,  ppp_init, };
+static struct device ppp2_dev = {
+    "ppp2", 0x0, 0x0, 0x0, 0x0, 2, 0, 0, 0, 0, &ppp3_dev, ppp_init, };
+static struct device ppp1_dev = {
+    "ppp1", 0x0, 0x0, 0x0, 0x0, 1, 0, 0, 0, 0, &ppp2_dev, ppp_init, };
+static struct device ppp0_dev = {
+    "ppp0", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, &ppp1_dev, ppp_init, };
+#undef NEXT_DEV
+#define NEXT_DEV (&ppp0_dev)
+#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
+
+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 -r -u -N linux.orig/arch/arm/drivers/net/auto_irq.c linux.arm/arch/arm/drivers/net/auto_irq.c
--- linux.orig/arch/arm/drivers/net/auto_irq.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/auto_irq.c	Fri Oct 27 23:14:59 1995
@@ -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 */};
+
+int irqs_busy = 0x2147;		/* The set of fixed IRQs (keyboard, timer, etc) */
+int irqs_used = 0x0001;		/* The set of fixed IRQs sometimes enabled. */
+int irqs_reserved = 0x0000;	/* An advisory "reserved" table. */
+int irqs_shared = 0x0000;	/* IRQ lines "shared" among conforming cards.*/
+
+static volatile int irq_number;	/* The latest irq number we actually found. */
+static volatile int irq_bitmap; /* The irqs we actually found. */
+static int irq_handled;		/* The irq lines we have a handler on. */
+
+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 -r -u -N linux.orig/arch/arm/drivers/net/dummy.c linux.arm/arch/arm/drivers/net/dummy.c
--- linux.orig/arch/arm/drivers/net/dummy.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/dummy.c	Fri Oct 27 23:14:59 1995
@@ -0,0 +1,175 @@
+/* dummy.c: a dummy net driver
+
+	The purpose of this driver is to provide a device to point a
+	route through, but not to actually transmit packets.
+
+	Why?  If you have a machine whose only connection is an occasional
+	PPP/SLIP/PLIP link, you can only connect to your own hostname
+	when the link is up.  Otherwise you have to use localhost.
+	This isn't very consistent.
+
+	One solution is to set up a dummy link using PPP/SLIP/PLIP,
+	but this seems (to me) too much overhead for too little gain.
+	This driver provides a small alternative. Thus you can do
+	
+	[when not running slip]
+		ifconfig dummy slip.addr.ess.here up
+	[to go to slip]
+		ifconfig dummy down
+		dip whatever
+
+	This was written by looking at Donald Becker's skeleton driver
+	and the loopback driver.  I then threw away anything that didn't
+	apply!	Thanks to Alan Cox for the key clue on what to do with
+	misguided packets.
+
+			Nick Holloway, 27th May 1994
+	[I tweaked this explanation a little but thats all]
+			Alan Cox, 30th May 1994
+*/
+
+/* To have statistics (just packets sent) define this */
+#undef DUMMY_STATS
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#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>
+
+static int dummy_xmit(struct sk_buff *skb, struct device *dev);
+#ifdef DUMMY_STATS
+static struct enet_statistics *dummy_get_stats(struct device *dev);
+#endif
+
+#ifdef MODULE
+static int dummy_open(struct device *dev)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int dummy_close(struct device *dev)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+#endif
+
+
+int dummy_init(struct device *dev)
+{
+/* I commented this out as bootup is noisy enough anyway and this driver
+   seems pretty reliable 8) 8) 8) */
+/*	printk ( KERN_INFO "Dummy net driver (94/05/27 v1.0)\n" ); */
+
+	/* Initialize the device structure. */
+	dev->hard_start_xmit	= dummy_xmit;
+
+#if DUMMY_STATS
+	dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
+	memset(dev->priv, 0, sizeof(struct enet_statistics));
+	dev->get_stats		= dummy_get_stats;
+#endif
+#ifdef MODULE
+	dev->open = &dummy_open;
+	dev->stop = &dummy_close;
+#endif
+
+	/* Fill in the fields of the device structure with ethernet-generic values. */
+	ether_setup(dev);
+	dev->flags |= IFF_NOARP;
+
+	return 0;
+}
+
+static int
+dummy_xmit(struct sk_buff *skb, struct device *dev)
+{
+#if DUMMY_STATS
+	struct enet_statistics *stats;
+#endif
+
+	if (skb == NULL || dev == NULL)
+		return 0;
+
+	dev_kfree_skb(skb, FREE_WRITE);
+
+#if DUMMY_STATS
+	stats = (struct enet_statistics *)dev->priv;
+	stats->tx_packets++;
+#endif
+
+	return 0;
+}
+
+#if DUMMY_STATS
+static struct enet_statistics *
+dummy_get_stats(struct device *dev)
+{
+	struct enet_statistics *stats = (struct enet_statistics*) dev->priv;
+	return stats;
+}
+#endif
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+
+static int dummy_probe(struct device *dev)
+{
+	dummy_init(dev);
+	return 0;
+}
+
+static struct device dev_dummy = {
+	"dummy0\0   ", 
+		0, 0, 0, 0,
+	 	0x0, 0,
+	 	0, 0, 0, NULL, dummy_probe };
+
+int init_module(void)
+{
+	/* Find a name for this unit */
+	int ct= 1;
+	
+	while(dev_get(dev_dummy.name)!=NULL && ct<100)
+	{
+		sprintf(dev_dummy.name,"dummy%d",ct);
+		ct++;
+	}
+	
+	if (register_netdev(&dev_dummy) != 0)
+		return -EIO;
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	if (MOD_IN_USE)
+		printk("dummy: device busy, remove delayed\n");
+	else
+	{
+		unregister_netdev(&dev_dummy);
+	}
+}
+#endif /* MODULE */
diff -r -u -N linux.orig/arch/arm/drivers/net/ether3.c linux.arm/arch/arm/drivers/net/ether3.c
--- linux.orig/arch/arm/drivers/net/ether3.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/ether3.c	Fri Oct 27 23:15:07 1995
@@ -0,0 +1,860 @@
+/*
+ * linux/drivers/net/ether3.c
+ *
+ * SEEQ nq8005 ethernet driver
+ *
+ */
+
+#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>
+
+#include "ether3.h"
+
+#define FUNC_PROLOGUE \
+	struct arc_net *an = (struct arc_net *)dev->priv
+
+static unsigned int net_debug = NET_DEBUG;
+
+#define tx_done(dev) 0
+/* ------------------------------------------------------------------------------- */
+static char *version = "ether3 ethernet driver (c) 1995 R.M.King V1.00\n";
+
+#define BUS_16 16
+#define BUS_8  8
+
+extern int inswb(int reg, void *buffer, int len);
+extern int outswb(int reg, void *buffer, int len);
+
+unsigned char def_eth_addr[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
+
+static int ether3_prods[] = { 0x00A4 };
+static int ether3_manus[] = { 0x0011 };
+
+/* --------------------------------------------------------------------------- */
+
+static void check_dev(struct device *dev, char *s)
+{
+	struct device *dev2;
+
+	dev2 = dev->next;
+	
+	while (dev2 && dev2 != dev)
+		dev2 = dev2->next;
+
+	if (dev2 == dev)
+		printk("eek! - %s\n", s);
+}
+
+/*
+ * set one of the receive address registers in the chip
+ */
+
+static void
+ether3_setrxaddr(struct device *dev, int address, unsigned char *addr)
+{
+	FUNC_PROLOGUE;
+	unsigned long flags;
+	int stat, i;
+
+	save_flags(flags);
+	cli();
+
+	stat = inw(REG_STATUS);
+	if(stat & STAT_RXON)
+		outw(an->regs.command | CMD_RXOFF, REG_COMMAND);
+
+	outw(an->regs.config1 | address, REG_CONFIG1);
+	for(i=0; i<6; i++)
+		outb(addr[i], REG_BUFWIN);
+
+	if(stat & STAT_RXON)
+		outw(an->regs.command | CMD_RXON, REG_COMMAND);
+
+	restore_flags(flags);
+}
+
+/*
+ * 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(an->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+		outw(an->regs.command | CMD_FIFOWRITE, REG_COMMAND);
+		while((inw(REG_STATUS) & STAT_FIFOEMPTY) == 0)
+			if(!timeout--)
+			{
+				printk("settxlim_16 broken\n");
+				an->broken = 1;
+				return;
+			}
+		outw(an->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(an->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+		outw(an->regs.command | CMD_FIFOWRITE, REG_COMMAND);
+		while((inw(REG_STATUS) & STAT_FIFOEMPTY) == 0)
+			if(!timeout--)
+			{
+				printk("setrxlim_16 broken\n");
+				an->broken = 1;
+				return;
+			}
+		outw(start, REG_DMAADDR);
+		outw(an->regs.command | CMD_FIFOREAD, REG_COMMAND);
+	}
+	if(length != 0)
+		inswb(REG_BUFWIN, data, length);
+}
+
+/* --------------------------------------------------------------------------- */
+
+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_1(struct device *dev)
+{
+	outb(0x80, REG_CONFIG2);
+	outb(0, REG_COMMAND);
+
+	outb(1, REG_CONFIG1);
+	if(inb(REG_CONFIG1+1)==1 && inb(REG_CONFIG1)==0)
+		return BUS_8;
+	if(inw(REG_CONFIG1)==0x101)
+		return BUS_16;
+	return 0;
+}
+
+static int
+ether3_init_2(struct device *dev)
+{
+	FUNC_PROLOGUE;
+	unsigned long a=0;
+	int i;
+
+	ether3_setrxaddr(dev, CFG1_BUFSELSTAT0, dev->dev_addr);
+
+	an->regs.config1 = CFG1_RECVSPECBROAD|CFG1_RECVCOMPSTAT0|CFG1_DMABURST8;
+	an->regs.config2 = CFG2_CTRLO|CFG2_RECVCRC|CFG2_ERRENCRC;
+
+	outw(an->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
+	outw((TX_END>>8) - 1, REG_BUFWIN);
+	outw(an->regs.recvptr	, REG_RECVPTR);
+	outw(an->regs.transmitptr	, REG_TRANSMITPTR);
+	outw(an->regs.recvptr >> 8, REG_RECVEND);
+	outw(an->regs.config2	, REG_CONFIG2);
+	outw(an->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+	ether3_writebuffer(dev, &a, an->regs.transmitptr, 4);
+	outw(an->regs.command	, REG_COMMAND);
+
+	i = ether3_ramtest(dev, 0x5A);
+	if(i)
+		return i;
+	return ether3_ramtest(dev, 0x1E);
+}
+
+/* This is the real probe routine. */
+
+static int
+ether3_probe1(struct device *dev)
+{
+	static unsigned version_printed = 0;
+	struct arc_net *an;
+	int i;
+
+	if(!dev->priv)
+		dev->priv = kmalloc(sizeof(struct arc_net), GFP_KERNEL);
+
+	if(!dev->priv)
+		return 1;
+
+	an = (struct arc_net *) dev->priv;
+	memset(an, 0, sizeof(struct arc_net));
+	an->bus_type    = BUS_16;
+	an->broken	= 0;
+
+	if((an->bus_type=ether3_init_1(dev))==0)
+	{
+		kfree(dev->priv);
+		return 1;
+	}
+
+	if (net_debug  &&  version_printed++ == 0)
+		printk(version);
+
+	printk("%s: ether3 found (bus width %d), ", dev->name, an->bus_type);
+
+	/* Retrive and print the ethernet address. */
+	for (i = 0; i < 6; i++)
+		printk(i==0?" %2.2x":i==5?":%2.2x\n":":%2.2x", dev->dev_addr[i]);
+
+	if(ether3_init_2(dev))
+	{
+		kfree(dev->priv);
+		return 1;
+	}
+
+	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);
+		return -EAGAIN;
+	}
+
+	irq2dev_map[dev->irq] = dev;
+#endif
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+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);
+}
+int
+ether3_probe(struct device *dev)
+{
+#ifndef MODULE
+	struct expansion_card *ec;
+	unsigned char *eth_addr;
+	int base_addr;
+
+	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);
+#endif
+	if(ether3_probe1(dev) == 0)
+		return 0;
+	return ENODEV;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static void
+ether3_init_for_open(struct device *dev)
+{
+	FUNC_PROLOGUE;
+	unsigned long a=0;
+
+	an->regs.command = 0;
+	outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND);
+	while(inw(REG_STATUS) & (STAT_RXON|STAT_TXON));
+
+	ether3_setrxaddr(dev, CFG1_BUFSELSTAT0, dev->dev_addr);
+	an->regs.transmitptr = 0;	/* address that transmitter has processed to */
+	an->txinsert = 0;		/* address of next available packet for tx */
+	an->txed = 0;			/* transmitted length index */
+	an->txto = 0;			/* to transmit length index */
+	an->tx_ends[0] = 0;
+	an->use = 0;
+
+	an->regs.config2 |= CFG2_CTRLO;
+
+	outw(an->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
+	outw((TX_END>>8) - 1, REG_BUFWIN);
+
+	an->regs.recvptr	= RX_START;
+	outw(an->regs.recvptr	, REG_RECVPTR);
+	outw(an->regs.recvptr>>8, REG_RECVEND);
+
+	outw(0, REG_TRANSMITPTR);
+	outw(an->regs.config2 , REG_CONFIG2);
+	outw(an->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+
+	ether3_writebuffer(dev, &a, an->regs.transmitptr, 4);
+
+
+	an->regs.command = CMD_ENINTRX|CMD_ENINTTX;
+	outw(an->regs.command | CMD_RXON, REG_COMMAND);
+}
+
+/* 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")) {
+		kfree(dev->priv);
+		return -EAGAIN;
+	}
+
+	irq2dev_map[dev->irq] = dev;
+#endif
+
+	MOD_INC_USE_COUNT;
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+	return 0;
+}
+
+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, %s?\n", dev->name,
+			   tx_done(dev) ? "IRQ conflict" : "network cable problem");
+		/* Try to restart the adaptor. */
+		dev->tbusy = 0;
+		an->use = 0;
+		an->regs.config2 |= CFG2_CTRLO;
+		outw(an->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 {
+		short length = ETH_ZLEN < (skb->len-2) ? (skb->len-2) : ETH_ZLEN;
+		unsigned char *buf = skb->data;
+		unsigned long flags;
+		unsigned long thisinfo, nextinfo=0;
+		int thisdata, thisstart, nextpacket;
+
+		if(an->broken)
+		{
+		  dev_kfree_skb(skb, FREE_WRITE);
+		  an->stats.tx_dropped ++;
+		  return 1;
+		}
+
+		thisstart = an->txinsert;
+		thisdata = thisstart + 4;
+		if(thisdata >= TX_END)
+			thisdata -= TX_END;
+		nextpacket = thisdata + length;
+		if(nextpacket >= TX_END)
+			nextpacket -= TX_END;
+		an->txinsert = nextpacket;
+		an->tx_ends[an->txto++] = nextpacket;
+		if(an->txto >= MAX_TXED)
+		  an->txto = 0;
+
+		thisinfo = 0x00E80000 | ((nextpacket >> 8) & 0xff) | ((nextpacket << 8) & 0xff00);
+
+		save_flags(flags);
+		cli();
+		an->use++;
+		an->regs.config2 &= ~CFG2_CTRLO;
+		outw(an->regs.config2 , REG_CONFIG2);
+
+		ether3_writebuffer(dev, buf + 2, thisdata, length&1?(length+1):length);
+		ether3_writebuffer(dev, &nextinfo, -1, 4);
+		ether3_writebuffer(dev, &thisinfo, thisstart, 4);
+		restore_flags(flags);
+
+		if((inw(REG_STATUS) & STAT_TXON)==0) {
+		  outw(thisstart, REG_TRANSMITPTR);
+		  outw(an->regs.command | CMD_TXON, REG_COMMAND);
+		}
+		dev->trans_start = jiffies;
+	}
+	dev_kfree_skb (skb, FREE_WRITE);
+
+	/* You might need to clean up and record Tx statistics here. */
+
+	return 0;
+}
+
+/* The typical workload of the driver:
+   Handle the network interface interrupts. */
+static void
+ether3_interrupt(int irq, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)(irq2dev_map[irq]);
+	struct arc_net *an = (struct arc_net *)dev->priv;
+	int status, boguscount = 0, 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;
+	}
+	dev->interrupt = 1;
+
+	do {
+		status = inw(REG_STATUS);
+		if (status & STAT_INTRX) {
+			/* Got a packet(s). */
+			ether3_rx(dev);
+			done = 1;
+		}
+		if (status & STAT_INTTX) {
+			ether3_tx(dev);
+			done = 1;
+		}
+		outw((status & (STAT_INTRX|STAT_INTTX|STAT_INTBUFWIN))
+		  | an->regs.command, REG_COMMAND);
+	} while (++boguscount < 20 && !done) ;
+
+	dev->interrupt = 0;
+
+#if NET_DEBUG > 1
+	if(net_debug & DEBUG_INT)
+		     printk("ether3_interrupt: done\n", irq);
+#endif
+}
+
+/* We have a good packet(s), get it/them out of the buffers. */
+static void
+ether3_rx(struct device *dev)
+{
+	FUNC_PROLOGUE;
+	struct sk_buff *skb=NULL;
+	unsigned int end;
+	unsigned int length;
+
+	an->use++;
+	if(an->use == 1)
+	{
+		an->regs.config2 &= ~CFG2_CTRLO;
+		outw(an->regs.config2 , REG_CONFIG2);
+	}
+	do {
+		unsigned char buffer[20];
+		ether3_readbuffer(dev, buffer, an->regs.recvptr, 20);
+
+		if(buffer[0] == 0 || (buffer[3] & 0x80)==0)
+			goto rx_fin;
+#if NET_DEBUG > 1
+		if(net_debug & DEBUG_RX) {
+		{
+			int i;
+			printk("ether3_rx:", an->regs.recvptr);
+			for(i=3; i<18; i++)
+				printk("%02X ",(unsigned int)buffer[i]);
+			printk("\n");
+		}
+#endif
+
+		end = (buffer[0]<<8) | buffer[1];
+
+		if((buffer[2] & 0xC0) != 0x40)
+		  /* no data follows */
+		  goto done;
+
+		if(buffer[3] & 15) {
+			an->stats.rx_errors++;
+			if(buffer[3] & 1) an->stats.rx_fifo_errors ++;
+			if(buffer[3] & 2) an->stats.rx_crc_errors ++;
+			if(buffer[3] & 4) an->stats.rx_fifo_errors ++;
+			if(buffer[3] & 8) an->stats.rx_length_errors ++;
+			goto done;
+		}
+
+		if(end > an->regs.recvptr)
+			length = end - an->regs.recvptr;
+		else
+			length = (end - RX_START) + (RX_END - an->regs.recvptr);
+
+		skb = alloc_skb(length + 4, GFP_ATOMIC);
+		if(skb == NULL) {
+			printk("%s: memory squeeze, dropping packet.\n", dev->name);
+			an->stats.rx_dropped++;
+			an->regs.recvptr = end;
+			outw(an->regs.recvptr >> 8, REG_RECVEND);
+			goto rx_fin;
+		}
+
+		skb->len = length - 4;
+		skb->dev = dev;
+		memcpy(skb->data + 2, buffer + 4, 20-4);
+		ether3_readbuffer(dev, skb->data+18, -1, length-20);
+	done:
+		an->regs.recvptr = end;
+		outw(an->regs.recvptr >> 8, REG_RECVEND);
+		if(skb) {
+			netif_rx(skb);
+			skb=NULL;
+			an->stats.rx_packets++;
+		}
+	}
+	while(inw(REG_STATUS) & STAT_RXON);
+
+	outw(an->regs.recvptr, REG_RECVPTR);
+	outw(an->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. */
+rx_fin:
+	if(an->use)
+	{
+		an->use--;
+		if(an->use == 0)
+		{
+			an->regs.config2 |= CFG2_CTRLO;
+			outw(an->regs.config2 , REG_CONFIG2);
+		}
+	}
+}
+
+/* ----------------------------------------------------------------------------- */
+
+static void
+ether3_tx(struct device *dev)
+{
+	FUNC_PROLOGUE;
+	unsigned char buffer[4];
+
+	if(an->regs.transmitptr + 4 > TX_END) {
+		int len = TX_END - an->regs.transmitptr;
+		ether3_readbuffer(dev, buffer, an->regs.transmitptr, len);
+		ether3_readbuffer(dev, buffer+len, 0, 4 - len);
+	}
+	else
+		ether3_readbuffer(dev, buffer, an->regs.transmitptr, 4);
+
+	if(!(buffer[3] & 0x80))
+		return;
+
+	if(buffer[3] & 7) {
+		if(buffer[3] & 5) an->stats.tx_errors ++;
+		if(buffer[3] & 4) an->stats.collisions += 16;
+		if(buffer[3] & 2) an->stats.collisions += (buffer[3] & 0x78)>>3;
+		if(buffer[3] & 1) an->stats.tx_fifo_errors ++;
+	}
+	if(!(buffer[3] & 5))
+		an->stats.tx_packets++;
+
+	an->regs.transmitptr = an->tx_ends[an->txed++];
+	if(an->txed >= MAX_TXED)
+		an->txed = 0;
+	if(an->regs.transmitptr >= TX_END)
+		an->regs.transmitptr -= TX_END;
+/* if(an->regs.transmitptr != ((buffer[0]<<8)|buffer[1])) */
+/* printk("%p:%p\n",an->regs.transmitptr, (buffer[2]<<24)|(buffer[3]<<16)|(buffer[0]<<8)|buffer[1]); */
+	dev->tbusy = 0;
+	mark_bh(NET_BH);	/* Inform upper layers. */
+	if(an->use)
+	{
+		an->use--;
+		if(an->use == 0)
+		{
+			an->regs.config2 |= CFG2_CTRLO;
+			outw(an->regs.config2 , REG_CONFIG2);
+		}
+	}
+}
+
+/* The inverse routine to net_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);
+	an->regs.command = 0;
+	while(inw(REG_STATUS) & (STAT_RXON|STAT_TXON));
+	ether3_init_1(dev);
+
+	restore_flags(flags);
+
+	/* Flush the Tx */
+#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 &an->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;
+
+	if(num_addrs == -1)
+		an->regs.config1 |= CFG1_RECVPROMISC;
+	else
+		an->regs.config1 = (an->regs.config1 & ~CFG1_RECVPROMISC)
+					| CFG1_RECVSPECBROAD;
+
+	outw(an->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+}
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+static struct device ether3 = {
+	"        ",
+	0, 0, 0, 0,
+	0, 0,
+	0, 0, 0, NULL, ether3_probe
+};
+
+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_probe;
+		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("%s: device busy, remove delayed\n", ether3.name);
+	} 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 -r -u -N linux.orig/arch/arm/drivers/net/ether3.h linux.arm/arch/arm/drivers/net/ether3.h
--- linux.orig/arch/arm/drivers/net/ether3.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/ether3.h	Fri Oct 27 23:15:07 1995
@@ -0,0 +1,153 @@
+/*
+ * 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 1
+#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)
+
+
+#define TX_END          0x6000
+#define RX_START        0x6000
+#define RX_LEN          0xA000
+#define RX_END          0x10000
+#define MAX_TXED	360
+
+struct arc_net
+{
+	int bus_type;
+	struct
+	{
+	  int command;
+	  int config1;
+	  int config2;
+	  int transmitptr;
+	  int recvptr;
+	} regs;
+	int txinsert;
+	struct enet_statistics stats;
+	int txed;
+	int txto;
+	int tx_ends[MAX_TXED];
+	int broken;
+	int use;
+};
+
+extern struct device *init_etherdev(struct device *dev, int sizeof_private,
+			unsigned long *mem_startp);
+
+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);
+static void	ether3_tx(struct device *dev);
+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 -r -u -N linux.orig/arch/arm/drivers/net/loopback.c linux.arm/arch/arm/drivers/net/loopback.c
--- linux.orig/arch/arm/drivers/net/loopback.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/loopback.c	Fri Oct 27 23:14:59 1995
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ *
+ *		Pseudo-driver for the loopback interface.
+ *
+ * Version:	@(#)loopback.c	1.0.4b	08/16/93
+ *
+ * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
+ *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *		Donald Becker, <becker@cesdis.gsfc.nasa.gov>
+ *
+ *		Alan Cox	:	Fixed oddments for NET3.014
+ *
+ *		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/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/in.h>
+#include <linux/if_ether.h>	/* For the statistics structure. */
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+
+static int
+loopback_xmit(struct sk_buff *skb, struct device *dev)
+{
+  struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
+  int done;
+
+  if (skb == NULL || dev == NULL) return(0);
+
+  cli();
+  if (dev->tbusy != 0) {
+	sti();
+	stats->tx_errors++;
+	return(1);
+  }
+  dev->tbusy = 1;
+  sti();
+  
+  /* FIXME: Optimise so buffers with skb->free=1 are not copied but
+     instead are lobbed from tx queue to rx queue */
+
+  done = dev_rint(skb->data, skb->len, 0, dev);
+  dev_kfree_skb(skb, FREE_WRITE);
+
+  while (done != 1) {
+	done = dev_rint(NULL, 0, 0, dev);
+  }
+  stats->tx_packets++;
+
+  dev->tbusy = 0;
+
+  if (!intr_count && (bh_active & bh_mask)) {
+	start_bh_atomic();
+	do_bottom_half();
+	end_bh_atomic();
+  }
+
+  return(0);
+}
+
+static struct enet_statistics *
+get_stats(struct device *dev)
+{
+    return (struct enet_statistics *)dev->priv;
+}
+
+static int loopback_open(struct device *dev)
+{
+	dev->flags|=IFF_LOOPBACK;
+	return 0;
+}
+
+/* Initialize the rest of the LOOPBACK device. */
+int
+loopback_init(struct device *dev)
+{
+  int i;
+
+  dev->mtu		= 2000;			/* MTU			*/
+  dev->tbusy		= 0;
+  dev->hard_start_xmit	= loopback_xmit;
+  dev->open		= NULL;
+#if 1
+  dev->hard_header	= eth_header;
+  dev->hard_header_len	= ETH_HLEN;		/* 14			*/
+  dev->addr_len		= ETH_ALEN;		/* 6			*/
+  dev->type		= ARPHRD_ETHER;		/* 0x0001		*/
+  dev->type_trans	= eth_type_trans;
+  dev->rebuild_header	= eth_rebuild_header;
+  dev->open		= loopback_open;
+#else
+  dev->hard_header_length = 0;
+  dev->addr_len		= 0;
+  dev->type		= 0;			/* loopback_type (0)	*/
+  dev->hard_header	= NULL;
+  dev->type_trans	= NULL;
+  dev->rebuild_header	= NULL;
+#endif
+
+  /* New-style flags. */
+  dev->flags		= IFF_LOOPBACK|IFF_BROADCAST;
+  dev->family		= AF_INET;
+#ifdef CONFIG_INET    
+  dev->pa_addr		= in_aton("127.0.0.1");
+  dev->pa_brdaddr	= in_aton("127.255.255.255");
+  dev->pa_mask		= in_aton("255.0.0.0");
+  dev->pa_alen		= sizeof(unsigned long);
+#endif  
+  dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
+  memset(dev->priv, 0, sizeof(struct enet_statistics));
+  dev->get_stats = get_stats;
+
+  /* Fill in the generic fields of the device structure. */
+  for (i = 0; i < DEV_NUMBUFFS; i++)
+	skb_queue_head_init(&dev->buffs[i]);
+  
+  return(0);
+};
diff -r -u -N linux.orig/arch/arm/drivers/net/net_init.c linux.arm/arch/arm/drivers/net/net_init.c
--- linux.orig/arch/arm/drivers/net/net_init.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/net_init.c	Fri Oct 27 23:14:59 1995
@@ -0,0 +1,320 @@
+/* netdrv_init.c: Initialization for network devices. */
+/*
+	Written 1993,1994,1995 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 file contains the initialization for the "pl14+" style ethernet
+	drivers.  It should eventually replace most of drivers/net/Space.c.
+	It's primary advantage is that it's able to allocate low-memory buffers.
+	A secondary advantage is that the dangerous NE*000 netcards can reserve
+	their I/O port region before the SCSI probes start.
+
+	Modifications/additions by Bjorn Ekwall <bj0rn@blox.se>:
+		ethdev_index[MAX_ETH_CARDS]
+		register_netdev() / unregister_netdev()
+*/
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/malloc.h>
+#include <linux/if_ether.h>
+#include <linux/string.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+/* The network devices currently exist only in the socket namespace, so these
+   entries are unused.  The only ones that make sense are
+    open	start the ethercard
+    close	stop  the ethercard
+    ioctl	To get statistics, perhaps set the interface port (AUI, BNC, etc.)
+   One can also imagine getting raw packets using
+    read & write
+   but this is probably better handled by a raw packet socket.
+
+   Given that almost all of these functions are handled in the current
+   socket-based scheme, putting ethercard devices in /dev/ seems pointless.
+   
+   [Removed all support for /dev network devices. When someone adds
+    streams then by magic we get them, but otherwise they are un-needed
+	and a space waste]
+*/
+
+/* The list of used and available "eth" slots (for "eth0", "eth1", etc.) */
+#define MAX_ETH_CARDS 16 /* same as the number if irq's in irq2dev[] */
+static struct device *ethdev_index[MAX_ETH_CARDS];
+
+unsigned long lance_init(unsigned long mem_start, unsigned long mem_end);
+unsigned long pi_init(unsigned long mem_start, unsigned long mem_end);
+unsigned long apricot_init(unsigned long mem_start, unsigned long mem_end);
+unsigned long dec21040_init(unsigned long mem_start, unsigned long mem_end);
+
+/*
+  net_dev_init() is our network device initialization routine.
+  It's called from init/main.c with the start and end of free memory,
+  and returns the new start of free memory.
+  */
+
+unsigned long net_dev_init (unsigned long mem_start, unsigned long mem_end)
+{
+
+	/* Network device initialization for devices that must allocate
+	   low-memory or contiguous DMA buffers.
+	   */
+#if defined(CONFIG_LANCE)
+	mem_start = lance_init(mem_start, mem_end);
+#endif
+#if defined(CONFIG_PI)
+	mem_start = pi_init(mem_start, mem_end);
+#endif	
+#if defined(CONFIG_DEC_ELCP)
+	mem_start = dec21040_init(mem_start, mem_end);
+#endif	
+	return mem_start;
+}
+
+/* Fill in the fields of the device structure with ethernet-generic values.
+
+   If no device structure is passed, a new one is constructed, complete with
+   a SIZEOF_PRIVATE private data area.
+
+   If an empty string area is passed as dev->name, or a new structure is made,
+   a new name string is constructed.  The passed string area should be 8 bytes
+   long.
+ */
+
+struct device *
+init_etherdev(struct device *dev, int sizeof_priv, unsigned long *mem_startp)
+{
+	int new_device = 0;
+	int i;
+
+	/* Use an existing correctly named device in Space.c:dev_base. */
+	if (dev == NULL) {
+		int alloc_size = sizeof(struct device) + sizeof("eth%d  ")
+			+ sizeof_priv + 3;
+		struct device *cur_dev;
+		char pname[8];		/* Putative name for the device.  */
+
+		for (i = 0; i < MAX_ETH_CARDS; ++i)
+			if (ethdev_index[i] == NULL) {
+				sprintf(pname, "eth%d", i);
+				for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next)
+					if (strcmp(pname, cur_dev->name) == 0) {
+						dev = cur_dev;
+						dev->init = NULL;
+						sizeof_priv = (sizeof_priv + 3) & ~3;
+						if (mem_startp && *mem_startp ) {
+							dev->priv = (void*) *mem_startp;
+							*mem_startp += sizeof_priv;
+						} else
+							dev->priv = kmalloc(sizeof_priv, GFP_KERNEL);
+						memset(dev->priv, 0, sizeof_priv);
+						goto found;
+					}
+			}
+
+		alloc_size &= ~3;		/* Round to dword boundary. */
+
+		if (mem_startp && *mem_startp ) {
+			dev = (struct device *)*mem_startp;
+			*mem_startp += alloc_size;
+		} else
+			dev = (struct device *)kmalloc(alloc_size, GFP_KERNEL);
+		memset(dev, 0, alloc_size);
+		if (sizeof_priv)
+			dev->priv = (void *) (dev + 1);
+		dev->name = sizeof_priv + (char *)(dev + 1);
+		new_device = 1;
+	}
+
+	found:						/* From the double loop above. */
+
+	if (dev->name &&
+		((dev->name[0] == '\0') || (dev->name[0] == ' '))) {
+		for (i = 0; i < MAX_ETH_CARDS; ++i)
+			if (ethdev_index[i] == NULL) {
+				sprintf(dev->name, "eth%d", i);
+				ethdev_index[i] = dev;
+				break;
+			}
+	}
+
+	ether_setup(dev); 	/* Hmmm, should this be called here? */
+	
+	if (new_device) {
+		/* Append the device to the device queue. */
+		struct device **old_devp = &dev_base;
+		while ((*old_devp)->next)
+			old_devp = & (*old_devp)->next;
+		(*old_devp)->next = dev;
+		dev->next = 0;
+	}
+	return dev;
+}
+
+void ether_setup(struct device *dev)
+{
+	int i;
+	/* Fill in the fields of the device structure with ethernet-generic values.
+	   This should be in a common file instead of per-driver.  */
+	for (i = 0; i < DEV_NUMBUFFS; i++)
+		skb_queue_head_init(&dev->buffs[i]);
+
+	/* register boot-defined "eth" devices */
+	if (dev->name && (strncmp(dev->name, "eth", 3) == 0)) {
+		i = simple_strtoul(dev->name + 3, NULL, 0);
+		if (ethdev_index[i] == NULL) {
+			ethdev_index[i] = dev;
+		}
+		else if (dev != ethdev_index[i]) {
+			/* Really shouldn't happen! */
+			printk("ether_setup: Ouch! Someone else took %s\n",
+				dev->name);
+		}
+	}
+
+	dev->hard_header	= eth_header;
+	dev->rebuild_header = eth_rebuild_header;
+	dev->type_trans = eth_type_trans;
+
+	dev->type		= ARPHRD_ETHER;
+#ifndef __arm__
+	dev->hard_header_len = ETH_HLEN;
+#else
+	dev->hard_header_len = (ETH_HLEN | 3) + 1;
+#endif
+	dev->mtu		= 1500; /* eth_mtu */
+	dev->addr_len	= ETH_ALEN;
+	for (i = 0; i < ETH_ALEN; i++) {
+		dev->broadcast[i]=0xff;
+	}
+
+	/* New-style flags. */
+	dev->flags		= IFF_BROADCAST|IFF_MULTICAST;
+	dev->family		= AF_INET;
+	dev->pa_addr	= 0;
+	dev->pa_brdaddr = 0;
+	dev->pa_mask	= 0;
+	dev->pa_alen	= sizeof(unsigned long);
+}
+
+int ether_config(struct device *dev, struct ifmap *map)
+{
+	if (map->mem_start != (u_long)(-1))
+		dev->mem_start = map->mem_start;
+	if (map->mem_end != (u_long)(-1))
+		dev->mem_end = map->mem_end;
+	if (map->base_addr != (u_short)(-1))
+		dev->base_addr = map->base_addr;
+	if (map->irq != (u_char)(-1))
+		dev->irq = map->irq;
+	if (map->dma != (u_char)(-1))
+		dev->dma = map->dma;
+	if (map->port != (u_char)(-1))
+		dev->if_port = map->port;
+	return 0;
+}
+
+int register_netdev(struct device *dev)
+{
+	struct device *d = dev_base;
+	unsigned long flags;
+	int i=MAX_ETH_CARDS;
+
+	save_flags(flags);
+	cli();
+
+	if (dev && dev->init) {
+		if (dev->name &&
+			((dev->name[0] == '\0') || (dev->name[0] == ' '))) {
+			for (i = 0; i < MAX_ETH_CARDS; ++i)
+				if (ethdev_index[i] == NULL) {
+					sprintf(dev->name, "eth%d", i);
+					printk("loading device '%s'...\n", dev->name);
+					ethdev_index[i] = dev;
+					break;
+				}
+		}
+
+		if (dev->init(dev) != 0) {
+		    if (i < MAX_ETH_CARDS) ethdev_index[i] = NULL;
+			restore_flags(flags);
+			return -EIO;
+		}
+
+		/* Add device to end of chain */
+		if (dev_base) {
+			while (d->next)
+				d = d->next;
+			d->next = dev;
+		}
+		else
+			dev_base = dev;
+		dev->next = NULL;
+	}
+	restore_flags(flags);
+	return 0;
+}
+
+void unregister_netdev(struct device *dev)
+{
+	struct device *d = dev_base;
+	unsigned long flags;
+	int i;
+
+	save_flags(flags);
+	cli();
+
+	printk("unregister_netdev: device ");
+
+	if (dev == NULL) {
+		printk("was NULL\n");
+		restore_flags(flags);
+		return;
+	}
+	/* else */
+	if (dev->start)
+		printk("'%s' busy\n", dev->name);
+	else {
+		if (dev_base == dev)
+			dev_base = dev->next;
+		else {
+			while (d && (d->next != dev))
+				d = d->next;
+
+			if (d && (d->next == dev)) {
+				d->next = dev->next;
+				printk("'%s' unlinked\n", dev->name);
+			}
+			else {
+				printk("'%s' not found\n", dev->name);
+				restore_flags(flags);
+				return;
+			}
+		}
+		for (i = 0; i < MAX_ETH_CARDS; ++i) {
+			if (ethdev_index[i] == dev) {
+				ethdev_index[i] = NULL;
+				break;
+			}
+		}
+	}
+	restore_flags(flags);
+}
+
+
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c net_init.c"
+ *  version-control: t
+ *  kept-new-versions: 5
+ *  tab-width: 4
+ * End:
+ */
diff -r -u -N linux.orig/arch/arm/drivers/net/nq8005.c linux.arm/arch/arm/drivers/net/nq8005.c
--- linux.orig/arch/arm/drivers/net/nq8005.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/nq8005.c	Fri Oct 27 23:15:00 1995
@@ -0,0 +1,860 @@
+/*
+ * linux/drivers/net/ether3.c
+ *
+ * SEEQ nq8005 ethernet driver
+ *
+ */
+
+#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>
+
+#include "ether3.h"
+
+#define FUNC_PROLOGUE \
+	struct arc_net *an = (struct arc_net *)dev->priv
+
+static unsigned int net_debug = NET_DEBUG;
+
+#define tx_done(dev) 0
+/* ------------------------------------------------------------------------------- */
+static char *version = "ether3 ethernet driver (c) 1995 R.M.King V1.00\n";
+
+#define BUS_16 16
+#define BUS_8  8
+
+extern int inswb(int reg, void *buffer, int len);
+extern int outswb(int reg, void *buffer, int len);
+
+unsigned char def_eth_addr[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
+
+static int ether3_prods[] = { 0x00A4 };
+static int ether3_manus[] = { 0x0011 };
+
+/* --------------------------------------------------------------------------- */
+
+static void check_dev(struct device *dev, char *s)
+{
+	struct device *dev2;
+
+	dev2 = dev->next;
+	
+	while (dev2 && dev2 != dev)
+		dev2 = dev2->next;
+
+	if (dev2 == dev)
+		printk("eek! - %s\n", s);
+}
+
+/*
+ * set one of the receive address registers in the chip
+ */
+
+static void
+ether3_setrxaddr(struct device *dev, int address, unsigned char *addr)
+{
+	FUNC_PROLOGUE;
+	unsigned long flags;
+	int stat, i;
+
+	save_flags(flags);
+	cli();
+
+	stat = inw(REG_STATUS);
+	if(stat & STAT_RXON)
+		outw(an->regs.command | CMD_RXOFF, REG_COMMAND);
+
+	outw(an->regs.config1 | address, REG_CONFIG1);
+	for(i=0; i<6; i++)
+		outb(addr[i], REG_BUFWIN);
+
+	if(stat & STAT_RXON)
+		outw(an->regs.command | CMD_RXON, REG_COMMAND);
+
+	restore_flags(flags);
+}
+
+/*
+ * 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(an->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+		outw(an->regs.command | CMD_FIFOWRITE, REG_COMMAND);
+		while((inw(REG_STATUS) & STAT_FIFOEMPTY) == 0)
+			if(!timeout--)
+			{
+				printk("settxlim_16 broken\n");
+				an->broken = 1;
+				return;
+			}
+		outw(an->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(an->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+		outw(an->regs.command | CMD_FIFOWRITE, REG_COMMAND);
+		while((inw(REG_STATUS) & STAT_FIFOEMPTY) == 0)
+			if(!timeout--)
+			{
+				printk("setrxlim_16 broken\n");
+				an->broken = 1;
+				return;
+			}
+		outw(start, REG_DMAADDR);
+		outw(an->regs.command | CMD_FIFOREAD, REG_COMMAND);
+	}
+	if(length != 0)
+		inswb(REG_BUFWIN, data, length);
+}
+
+/* --------------------------------------------------------------------------- */
+
+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_1(struct device *dev)
+{
+	outb(0x80, REG_CONFIG2);
+	outb(0, REG_COMMAND);
+
+	outb(1, REG_CONFIG1);
+	if(inb(REG_CONFIG1+1)==1 && inb(REG_CONFIG1)==0)
+		return BUS_8;
+	if(inw(REG_CONFIG1)==0x101)
+		return BUS_16;
+	return 0;
+}
+
+static int
+ether3_init_2(struct device *dev)
+{
+	FUNC_PROLOGUE;
+	unsigned long a=0;
+	int i;
+
+	ether3_setrxaddr(dev, CFG1_BUFSELSTAT0, dev->dev_addr);
+
+	an->regs.config1 = CFG1_RECVSPECBROAD|CFG1_RECVCOMPSTAT0|CFG1_DMABURST8;
+	an->regs.config2 = CFG2_CTRLO|CFG2_RECVCRC|CFG2_ERRENCRC;
+
+	outw(an->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
+	outw((TX_END>>8) - 1, REG_BUFWIN);
+	outw(an->regs.recvptr	, REG_RECVPTR);
+	outw(an->regs.transmitptr	, REG_TRANSMITPTR);
+	outw(an->regs.recvptr >> 8, REG_RECVEND);
+	outw(an->regs.config2	, REG_CONFIG2);
+	outw(an->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+	ether3_writebuffer(dev, &a, an->regs.transmitptr, 4);
+	outw(an->regs.command	, REG_COMMAND);
+
+	i = ether3_ramtest(dev, 0x5A);
+	if(i)
+		return i;
+	return ether3_ramtest(dev, 0x1E);
+}
+
+/* This is the real probe routine. */
+
+static int
+ether3_probe1(struct device *dev)
+{
+	static unsigned version_printed = 0;
+	struct arc_net *an;
+	int i;
+
+	if(!dev->priv)
+		dev->priv = kmalloc(sizeof(struct arc_net), GFP_KERNEL);
+
+	if(!dev->priv)
+		return 1;
+
+	an = (struct arc_net *) dev->priv;
+	memset(an, 0, sizeof(struct arc_net));
+	an->bus_type    = BUS_16;
+	an->broken	= 0;
+
+	if((an->bus_type=ether3_init_1(dev))==0)
+	{
+		kfree(dev->priv);
+		return 1;
+	}
+
+	if (net_debug  &&  version_printed++ == 0)
+		printk(version);
+
+	printk("%s: ether3 found (bus width %d), ", dev->name, an->bus_type);
+
+	/* Retrive and print the ethernet address. */
+	for (i = 0; i < 6; i++)
+		printk(i==0?" %2.2x":i==5?":%2.2x\n":":%2.2x", dev->dev_addr[i]);
+
+	if(ether3_init_2(dev))
+	{
+		kfree(dev->priv);
+		return 1;
+	}
+
+	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);
+		return -EAGAIN;
+	}
+
+	irq2dev_map[dev->irq] = dev;
+#endif
+	return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
+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);
+}
+int
+ether3_probe(struct device *dev)
+{
+#ifndef MODULE
+	struct expansion_card *ec;
+	unsigned char *eth_addr;
+	int base_addr;
+
+	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);
+#endif
+	if(ether3_probe1(dev) == 0)
+		return 0;
+	return ENODEV;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static void
+ether3_init_for_open(struct device *dev)
+{
+	FUNC_PROLOGUE;
+	unsigned long a=0;
+
+	an->regs.command = 0;
+	outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND);
+	while(inw(REG_STATUS) & (STAT_RXON|STAT_TXON));
+
+	ether3_setrxaddr(dev, CFG1_BUFSELSTAT0, dev->dev_addr);
+	an->regs.transmitptr = 0;	/* address that transmitter has processed to */
+	an->txinsert = 0;		/* address of next available packet for tx */
+	an->txed = 0;			/* transmitted length index */
+	an->txto = 0;			/* to transmit length index */
+	an->tx_ends[0] = 0;
+	an->use = 0;
+
+	an->regs.config2 |= CFG2_CTRLO;
+
+	outw(an->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
+	outw((TX_END>>8) - 1, REG_BUFWIN);
+
+	an->regs.recvptr	= RX_START;
+	outw(an->regs.recvptr	, REG_RECVPTR);
+	outw(an->regs.recvptr>>8, REG_RECVEND);
+
+	outw(0, REG_TRANSMITPTR);
+	outw(an->regs.config2 , REG_CONFIG2);
+	outw(an->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+
+	ether3_writebuffer(dev, &a, an->regs.transmitptr, 4);
+
+
+	an->regs.command = CMD_ENINTRX|CMD_ENINTTX;
+	outw(an->regs.command | CMD_RXON, REG_COMMAND);
+}
+
+/* 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")) {
+		kfree(dev->priv);
+		return -EAGAIN;
+	}
+
+	irq2dev_map[dev->irq] = dev;
+#endif
+
+	MOD_INC_USE_COUNT;
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+	return 0;
+}
+
+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, %s?\n", dev->name,
+			   tx_done(dev) ? "IRQ conflict" : "network cable problem");
+		/* Try to restart the adaptor. */
+		dev->tbusy = 0;
+		an->use = 0;
+		an->regs.config2 |= CFG2_CTRLO;
+		outw(an->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 {
+		short length = ETH_ZLEN < (skb->len-2) ? (skb->len-2) : ETH_ZLEN;
+		unsigned char *buf = skb->data;
+		unsigned long flags;
+		unsigned long thisinfo, nextinfo=0;
+		int thisdata, thisstart, nextpacket;
+
+		if(an->broken)
+		{
+		  dev_kfree_skb(skb, FREE_WRITE);
+		  an->stats.tx_dropped ++;
+		  return 1;
+		}
+
+		thisstart = an->txinsert;
+		thisdata = thisstart + 4;
+		if(thisdata >= TX_END)
+			thisdata -= TX_END;
+		nextpacket = thisdata + length;
+		if(nextpacket >= TX_END)
+			nextpacket -= TX_END;
+		an->txinsert = nextpacket;
+		an->tx_ends[an->txto++] = nextpacket;
+		if(an->txto >= MAX_TXED)
+		  an->txto = 0;
+
+		thisinfo = 0x00E80000 | ((nextpacket >> 8) & 0xff) | ((nextpacket << 8) & 0xff00);
+
+		save_flags(flags);
+		cli();
+		an->use++;
+		an->regs.config2 &= ~CFG2_CTRLO;
+		outw(an->regs.config2 , REG_CONFIG2);
+
+		ether3_writebuffer(dev, buf + 2, thisdata, length&1?(length+1):length);
+		ether3_writebuffer(dev, &nextinfo, -1, 4);
+		ether3_writebuffer(dev, &thisinfo, thisstart, 4);
+		restore_flags(flags);
+
+		if((inw(REG_STATUS) & STAT_TXON)==0) {
+		  outw(thisstart, REG_TRANSMITPTR);
+		  outw(an->regs.command | CMD_TXON, REG_COMMAND);
+		}
+		dev->trans_start = jiffies;
+	}
+	dev_kfree_skb (skb, FREE_WRITE);
+
+	/* You might need to clean up and record Tx statistics here. */
+
+	return 0;
+}
+
+/* The typical workload of the driver:
+   Handle the network interface interrupts. */
+static void
+ether3_interrupt(int irq, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)(irq2dev_map[irq]);
+	struct arc_net *an = (struct arc_net *)dev->priv;
+	int status, boguscount = 0, 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;
+	}
+	dev->interrupt = 1;
+
+	do {
+		status = inw(REG_STATUS);
+		if (status & STAT_INTRX) {
+			/* Got a packet(s). */
+			ether3_rx(dev);
+			done = 1;
+		}
+		if (status & STAT_INTTX) {
+			ether3_tx(dev);
+			done = 1;
+		}
+		outw((status & (STAT_INTRX|STAT_INTTX|STAT_INTBUFWIN))
+		  | an->regs.command, REG_COMMAND);
+	} while (++boguscount < 20 && !done) ;
+
+	dev->interrupt = 0;
+
+#if NET_DEBUG > 1
+	if(net_debug & DEBUG_INT)
+		     printk("ether3_interrupt: done\n", irq);
+#endif
+}
+
+/* We have a good packet(s), get it/them out of the buffers. */
+static void
+ether3_rx(struct device *dev)
+{
+	FUNC_PROLOGUE;
+	struct sk_buff *skb=NULL;
+	unsigned int end;
+	unsigned int length;
+
+	an->use++;
+	if(an->use == 1)
+	{
+		an->regs.config2 &= ~CFG2_CTRLO;
+		outw(an->regs.config2 , REG_CONFIG2);
+	}
+	do {
+		unsigned char buffer[20];
+		ether3_readbuffer(dev, buffer, an->regs.recvptr, 20);
+
+		if(buffer[0] == 0 || (buffer[3] & 0x80)==0)
+			goto rx_fin;
+#if NET_DEBUG > 1
+		if(net_debug & DEBUG_RX) {
+		{
+			int i;
+			printk("ether3_rx:", an->regs.recvptr);
+			for(i=3; i<18; i++)
+				printk("%02X ",(unsigned int)buffer[i]);
+			printk("\n");
+		}
+#endif
+
+		end = (buffer[0]<<8) | buffer[1];
+
+		if((buffer[2] & 0xC0) != 0x40)
+		  /* no data follows */
+		  goto done;
+
+		if(buffer[3] & 15) {
+			an->stats.rx_errors++;
+			if(buffer[3] & 1) an->stats.rx_fifo_errors ++;
+			if(buffer[3] & 2) an->stats.rx_crc_errors ++;
+			if(buffer[3] & 4) an->stats.rx_fifo_errors ++;
+			if(buffer[3] & 8) an->stats.rx_length_errors ++;
+			goto done;
+		}
+
+		if(end > an->regs.recvptr)
+			length = end - an->regs.recvptr;
+		else
+			length = (end - RX_START) + (RX_END - an->regs.recvptr);
+
+		skb = alloc_skb(length + 4, GFP_ATOMIC);
+		if(skb == NULL) {
+			printk("%s: memory squeeze, dropping packet.\n", dev->name);
+			an->stats.rx_dropped++;
+			an->regs.recvptr = end;
+			outw(an->regs.recvptr >> 8, REG_RECVEND);
+			goto rx_fin;
+		}
+
+		skb->len = length - 4;
+		skb->dev = dev;
+		memcpy(skb->data + 2, buffer + 4, 20-4);
+		ether3_readbuffer(dev, skb->data+18, -1, length-20);
+	done:
+		an->regs.recvptr = end;
+		outw(an->regs.recvptr >> 8, REG_RECVEND);
+		if(skb) {
+			netif_rx(skb);
+			skb=NULL;
+			an->stats.rx_packets++;
+		}
+	}
+	while(inw(REG_STATUS) & STAT_RXON);
+
+	outw(an->regs.recvptr, REG_RECVPTR);
+	outw(an->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. */
+rx_fin:
+	if(an->use)
+	{
+		an->use--;
+		if(an->use == 0)
+		{
+			an->regs.config2 |= CFG2_CTRLO;
+			outw(an->regs.config2 , REG_CONFIG2);
+		}
+	}
+}
+
+/* ----------------------------------------------------------------------------- */
+
+static void
+ether3_tx(struct device *dev)
+{
+	FUNC_PROLOGUE;
+	unsigned char buffer[4];
+
+	if(an->regs.transmitptr + 4 > TX_END) {
+		int len = TX_END - an->regs.transmitptr;
+		ether3_readbuffer(dev, buffer, an->regs.transmitptr, len);
+		ether3_readbuffer(dev, buffer+len, 0, 4 - len);
+	}
+	else
+		ether3_readbuffer(dev, buffer, an->regs.transmitptr, 4);
+
+	if(!(buffer[3] & 0x80))
+		return;
+
+	if(buffer[3] & 7) {
+		if(buffer[3] & 5) an->stats.tx_errors ++;
+		if(buffer[3] & 4) an->stats.collisions += 16;
+		if(buffer[3] & 2) an->stats.collisions += (buffer[3] & 0x78)>>3;
+		if(buffer[3] & 1) an->stats.tx_fifo_errors ++;
+	}
+	if(!(buffer[3] & 5))
+		an->stats.tx_packets++;
+
+	an->regs.transmitptr = an->tx_ends[an->txed++];
+	if(an->txed >= MAX_TXED)
+		an->txed = 0;
+	if(an->regs.transmitptr >= TX_END)
+		an->regs.transmitptr -= TX_END;
+/* if(an->regs.transmitptr != ((buffer[0]<<8)|buffer[1])) */
+/* printk("%p:%p\n",an->regs.transmitptr, (buffer[2]<<24)|(buffer[3]<<16)|(buffer[0]<<8)|buffer[1]); */
+	dev->tbusy = 0;
+	mark_bh(NET_BH);	/* Inform upper layers. */
+	if(an->use)
+	{
+		an->use--;
+		if(an->use == 0)
+		{
+			an->regs.config2 |= CFG2_CTRLO;
+			outw(an->regs.config2 , REG_CONFIG2);
+		}
+	}
+}
+
+/* The inverse routine to net_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);
+	an->regs.command = 0;
+	while(inw(REG_STATUS) & (STAT_RXON|STAT_TXON));
+	ether3_init_1(dev);
+
+	restore_flags(flags);
+
+	/* Flush the Tx */
+#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 &an->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;
+
+	if(num_addrs == -1)
+		an->regs.config1 |= CFG1_RECVPROMISC;
+	else
+		an->regs.config1 = (an->regs.config1 & ~CFG1_RECVPROMISC)
+					| CFG1_RECVSPECBROAD;
+
+	outw(an->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
+}
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+static struct device ether3 = {
+	"        ",
+	0, 0, 0, 0,
+	0, 0,
+	0, 0, 0, NULL, ether3_probe
+};
+
+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_probe;
+		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("%s: device busy, remove delayed\n", ether3.name);
+	} 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 -r -u -N linux.orig/arch/arm/drivers/net/plip.c linux.arm/arch/arm/drivers/net/plip.c
--- linux.orig/arch/arm/drivers/net/plip.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/plip.c	Fri Oct 27 23:15:01 1995
@@ -0,0 +1,1104 @@
+/* $Id: plip.c,v 1.12 1995/02/11 10:26:05 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.
+ *
+ *		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.
+ */
+
+static char *version = "NET3 PLIP version 2.0 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
+*/
+
+#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 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;
+	int (*orig_rebuild_header)(void *eth, struct device *dev,
+				   unsigned long raddr, struct sk_buff *skb);
+};
+
+/* Entry point of PLIP driver.
+   Probe the hardware, and register/initialize the driver. */
+int
+plip_init(struct device *dev)
+{
+	struct net_local *nl;
+
+	/* Check region before the probe */
+	if (check_region(PAR_DATA(dev), 3) < 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) {
+		printk("using assigned IRQ %d.\n", dev->irq);
+	} else {
+		int irq = 0;
+#ifdef MODULE
+		/* dev->irq==0 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 > 0) {
+			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), 3, 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)
+		return EAGAIN;
+	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;
+
+	/* 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
+			     && 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;
+	outb(0x00, 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;
+		return OK;
+
+	case PLIP_NB_2:
+	}
+}
+
+/* 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;
+		outb(0x01, 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));
+				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 || rcv->length.h < 8) {
+			printk("%s: bogus packet size %d.\n", dev->name, rcv->length.h);
+			return ERROR;
+		}
+		/* Malloc up new buffer. */
+		rcv->skb = alloc_skb(rcv->length.h, GFP_ATOMIC);
+		if (rcv->skb == NULL) {
+			printk("%s: Memory squeeze.\n", dev->name);
+			return ERROR;
+		}
+		rcv->skb->len = 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. */
+		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. */
+		outb (0x00, 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));
+			enable_irq(dev->irq);
+			return OK;
+		} else {
+			nl->connection = PLIP_CN_NONE;
+			sti();
+			outb(PAR_INTR_ON, PAR_CONTROL(dev));
+			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;
+	}
+}
+
+/* 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:
+		/* Trigger remote rx interrupt. */
+		outb(0x08, 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) {
+				outb(0x00, data_addr);
+				return TIMEOUT;
+			}
+		}
+
+	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 */
+		outb (0x00, 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));
+		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));
+	if ((status & 0xf8) == 0x80) {
+		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));
+		enable_irq(dev->irq);
+		mark_bh(NET_BH);
+	} else {
+		nl->is_deferred = 1;
+		queue_task(&nl->deferred, &tq_timer);
+	}
+
+	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;
+
+	if (dev == NULL) {
+		printk ("plip_interrupt: irq %d for unknown device.\n", irq);
+		return;
+	}
+
+	if (dev->interrupt)
+		return;
+
+	c0 = inb(PAR_STATUS(dev));
+	if ((c0 & 0xf8) != 0xc0) {
+		if (net_debug > 1)
+			printk("%s: spurious interrupt\n", dev->name);
+		return;
+	}
+	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) {
+		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 == 0) {
+		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. */
+	outb (0x00, PAR_DATA(dev));
+
+	/* Enable rx interrupt. */
+	outb(PAR_INTR_ON, PAR_CONTROL(dev));
+
+	/* 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;
+
+	/* 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();
+	outb(0x00, 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;
+
+static struct device dev_plip0 = 
+{
+	"plip0" /*"plip"*/,
+	0, 0, 0, 0,		/* memory */
+	0x3BC, 5,		/* base, irq */
+	0, 0, 0, NULL, plip_init 
+};
+
+static struct device dev_plip1 = 
+{
+	"plip1" /*"plip"*/,
+	0, 0, 0, 0,		/* memory */
+	0x378, 7,		/* base, irq */
+	0, 0, 0, NULL, plip_init 
+};
+
+static struct device dev_plip2 = 
+{
+	"plip2" /*"plip"*/,
+	0, 0, 0, 0,		/* memory */
+	0x278, 2,		/* base, irq */
+	0, 0, 0, NULL, plip_init 
+};
+
+int
+init_module(void)
+{
+	int devices=0;
+
+	if (register_netdev(&dev_plip0) != 0)
+		devices++;
+	if (register_netdev(&dev_plip1) != 0)
+		devices++;
+	if (register_netdev(&dev_plip2) != 0)
+		devices++;
+	if (devices == 0)
+		return -EIO;
+	return 0;
+}
+
+void
+cleanup_module(void)
+{
+	if (dev_plip0.priv) {
+		unregister_netdev(&dev_plip0);
+		release_region(PAR_DATA(&dev_plip0), 3);
+		kfree_s(dev_plip0.priv, sizeof(struct net_local));
+		dev_plip0.priv = NULL;
+	}
+	if (dev_plip1.priv) {
+		unregister_netdev(&dev_plip1);
+		release_region(PAR_DATA(&dev_plip1), 3);
+		kfree_s(dev_plip1.priv, sizeof(struct net_local));
+		dev_plip1.priv = NULL;
+	}
+	if (dev_plip2.priv) {
+		unregister_netdev(&dev_plip2);
+		release_region(PAR_DATA(&dev_plip2), 3);
+		kfree_s(dev_plip2.priv, sizeof(struct net_local));
+		dev_plip2.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 -r -u -N linux.orig/arch/arm/drivers/net/ppp.c linux.arm/arch/arm/drivers/net/ppp.c
--- linux.orig/arch/arm/drivers/net/ppp.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/ppp.c	Fri Oct 27 23:15:02 1995
@@ -0,0 +1,2126 @@
+/*
+   PPP for Linux
+*/
+
+/*
+   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.):
+
+   NET02D	      -	Define if using Net-2-Debugged in kernels earlier
+   			than v1.1.4.
+
+   NEW_TTY_DRIVERS    -	Define if using new Ted Ts'o's alpha TTY drivers
+   			from tsx-11.mit.edu. From Ted Ts'o.
+
+   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 not defined then the leading
+			flag is always sent.  
+*/
+
+/* #define NET02D				-* */
+#define NEW_TTY_DRIVERS				/* */
+#define OPTIMIZE_FLAG_TIME  ((HZ * 3)/2)	/* */
+#define CHECK_CHARACTERS
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#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>
+
+#ifdef NET02D				/* v1.1.4 net code and earlier */
+#include <dev.h>
+#include <skbuff.h>
+#include <inet.h>
+#define	skb_queue_head_init(buf)	*(buf) = NULL
+#else					/* v1.1.5 and later */
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/inet.h>
+#endif
+
+#include <linux/ppp.h>
+
+#include <linux/ip.h>
+#include <linux/tcp.h>
+
+#include "slhc.h"
+
+#include <linux/if_arp.h>
+#ifndef ARPHRD_PPP
+#define ARPHRD_PPP 0
+#endif
+
+#define PRINTK(p) printk p ;
+#define ASSERT(p) if (!p) PRINTK ((KERN_CRIT "assertion failed: " # p))
+#define PRINTKN(n,p) {if (ppp_debug >= n) PRINTK (p)}
+#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) (unsigned char) (c)) < 0x20) && \
+			ppp->recv_async_map & (1 << (c)))
+
+#define bset(p,b)	((p)[(b) >> 5] |= (1 << ((b) & 0x1f)))
+
+int ppp_debug = 2;
+int ppp_debug_netpackets = 0;
+
+/* Define this string only once for all macro invocations */
+static char ppp_warning[] = KERN_WARNING "PPP: ALERT! not INUSE! %d\n";
+
+int ppp_init(struct device *);
+static void ppp_init_ctrl_blk(struct ppp *);
+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 void ppp_kick_tty(struct ppp *);
+
+#ifdef NEW_TTY_DRIVERS
+#define ppp_find(tty) ((struct ppp *) tty->disc_data)
+#else
+static void ppp_output_done(void *);
+static void ppp_unesc(struct ppp *ppp, unsigned char *c, int n);
+static struct ppp *ppp_find(struct tty_struct *);
+#endif
+
+static void ppp_doframe(struct ppp *);
+static int ppp_do_ip(struct ppp *, unsigned short, unsigned char *, int);
+static int ppp_us_queue(struct ppp *, unsigned short, unsigned char *, int);
+static int ppp_xmit(struct sk_buff *, struct device *);
+static unsigned short ppp_type_trans(struct sk_buff *, struct device *);
+
+#ifdef NET02D
+static int ppp_header(unsigned char *buff, struct device *dev,
+		      unsigned short type, unsigned long daddr,
+		      unsigned long saddr, unsigned len);
+static int ppp_rebuild_header(void *buff, struct device *dev);
+static void ppp_add_arp(unsigned long addr, struct sk_buff *skb,
+			struct device *dev);
+#else
+static int ppp_header(unsigned char *, struct device *, unsigned short,
+		      void *, void *, unsigned, struct sk_buff *);
+static int ppp_rebuild_header(void *, struct device *, unsigned long,
+			      struct sk_buff *);
+#endif
+
+static struct enet_statistics *ppp_get_stats (struct device *);
+static struct ppp *ppp_alloc(void);
+static int ppp_lock(struct ppp *);
+static void ppp_unlock(struct ppp *);
+static void ppp_add_fcs(struct ppp *);
+static int ppp_check_fcs(struct ppp *);
+static void ppp_print_buffer(const char *,char *,int,int);
+
+static int ppp_read(struct tty_struct *, struct file *, unsigned char *,
+		    unsigned int);
+static int ppp_write(struct tty_struct *, struct file *, unsigned char *,
+		     unsigned int);
+static int ppp_ioctl(struct tty_struct *, struct file *, unsigned int,
+		     unsigned long);
+static int ppp_select(struct tty_struct *tty, struct inode * inode,
+		      struct file * filp, int sel_type, select_table * wait);
+static int ppp_open(struct tty_struct *);
+static void ppp_close(struct tty_struct *);
+
+#ifdef NEW_TTY_DRIVERS
+static int ppp_receive_room(struct tty_struct *tty);
+static void ppp_receive_buf(struct tty_struct *tty, unsigned char *cp,
+			    char *fp, int count);
+static void ppp_write_wakeup(struct tty_struct *tty);
+#else
+static void ppp_tty_input_ready(struct tty_struct *);
+#endif
+
+/* FCS table from RFC1331 */
+
+static unsigned short fcstab[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
+  };
+
+struct tty_ldisc ppp_ldisc;
+
+static struct ppp ppp_ctrl[PPP_NRUNIT];
+
+/*************************************************************
+ * INITIALIZATION
+ *************************************************************/
+
+static int first_time = 1;
+
+/* called at boot time for each ppp device */
+
+int
+ppp_init(struct device *dev)
+{
+  struct ppp *ppp;
+  int i;
+
+  ppp = &ppp_ctrl[dev->base_addr];
+
+  if (first_time) {
+    first_time = 0;
+
+    printk (KERN_INFO "PPP: version %s (%d channels)"
+#ifdef NET02D
+	   " NET02D"
+#endif
+#ifdef NEW_TTY_DRIVERS
+	   " NEW_TTY_DRIVERS"
+#endif
+#ifdef OPTIMIZE_FLAG_TIME
+	   " OPTIMIZE_FLAGS"
+#endif
+	   "\n", PPP_VERSION, PPP_NRUNIT);
+
+    printk (KERN_INFO
+	   "TCP compression code copyright 1989 Regents of the "
+	   "University of California\n");
+
+    (void) memset(&ppp_ldisc, 0, sizeof(ppp_ldisc));
+    ppp_ldisc.open    = ppp_open;
+    ppp_ldisc.close   = ppp_close;
+    ppp_ldisc.read    = ppp_read;
+    ppp_ldisc.write   = ppp_write;
+    ppp_ldisc.ioctl   = ppp_ioctl;
+    ppp_ldisc.select  = ppp_select;
+
+#ifdef NEW_TTY_DRIVERS
+    ppp_ldisc.magic       = TTY_LDISC_MAGIC;
+    ppp_ldisc.receive_room = ppp_receive_room;
+    ppp_ldisc.receive_buf = ppp_receive_buf;
+    ppp_ldisc.write_wakeup = ppp_write_wakeup;
+#else
+    ppp_ldisc.handler     = ppp_tty_input_ready;
+#endif
+
+    if ((i = tty_register_ldisc(N_PPP, &ppp_ldisc)) == 0)
+      printk(KERN_INFO "PPP line discipline registered.\n");
+    else
+      printk(KERN_ERR "error registering line discipline: %d\n", i);
+  }
+
+  /* initialize PPP control block */
+  ppp_init_ctrl_blk (ppp);
+  ppp->inuse = 0;
+  ppp->line  = dev->base_addr;
+  ppp->tty   = NULL;
+  ppp->dev   = dev;
+
+  /* clear statistics */
+  memset (&ppp->stats, '\0', sizeof (struct ppp_stats));
+
+  /* device INFO */
+  dev->mtu             = PPP_MTU;
+  dev->hard_start_xmit = ppp_xmit;
+  dev->open            = ppp_dev_open;
+  dev->stop            = ppp_dev_close;
+  dev->get_stats       = ppp_get_stats;
+  dev->hard_header     = ppp_header;
+  dev->type_trans      = ppp_type_trans;
+  dev->rebuild_header  = ppp_rebuild_header;
+  dev->hard_header_len = 0;
+  dev->addr_len        = 0;
+  dev->type            = ARPHRD_PPP;
+
+#ifdef NET02D
+  dev->add_arp         = ppp_add_arp;
+  dev->queue_xmit      = dev_queue_xmit;
+#else
+  dev->do_ioctl        = ppp_dev_ioctl;
+#endif
+
+  for (i = 0; i < DEV_NUMBUFFS; i++)
+    skb_queue_head_init(&dev->buffs[i]);  /* = NULL if NET02D */
+
+  /* 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    = sizeof(unsigned long);
+
+  return 0;
+}
+
+static void
+ppp_init_ctrl_blk(struct ppp *ppp)
+{
+  ppp->magic		= PPP_MAGIC;
+  ppp->sending		= 0;
+  ppp->toss		= 0xFE;
+  ppp->escape		= 0;
+
+  ppp->flags		= 0;
+  ppp->mtu		= PPP_MTU;
+  ppp->mru		= PPP_MRU;
+  ppp->fcs		= 0;
+
+  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->slcomp		= NULL;
+  ppp->rbuff		= NULL;
+  ppp->xbuff		= NULL;
+  ppp->cbuff		= NULL;
+
+  ppp->rhead		= NULL;
+  ppp->rend		= NULL;
+  ppp->rcount		= 0;
+  ppp->xhead		= NULL;
+  ppp->xtail		= NULL;
+
+  ppp->us_rbuff		= NULL;
+  ppp->us_rbuff_end	= NULL;
+  ppp->us_rbuff_head	= NULL;
+  ppp->us_rbuff_tail	= NULL;
+  ppp->read_wait	= NULL;
+  ppp->write_wait	= NULL;
+  ppp->us_rbuff_lock	= 0;
+  ppp->inp_sig		= 0;
+  ppp->inp_sig_pid	= 0;
+
+#ifdef OPTIMIZE_FLAG_TIME /* ensure flag will always be sent first time */
+  ppp->last_xmit	= jiffies - OPTIMIZE_FLAG_TIME;
+#else
+  ppp->last_xmit	= 0;
+#endif
+
+  /* clear statistics */
+  memset (&ppp->stats, '\0', sizeof (struct ppp_stats));
+
+  /* Reset the demand dial information */
+  ppp->ddinfo.ip_sjiffies  =
+  ppp->ddinfo.ip_rjiffies  =
+  ppp->ddinfo.nip_sjiffies =
+  ppp->ddinfo.nip_rjiffies = jiffies;
+}
+
+/*
+ * 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 void
+ppp_changedmtu (struct ppp *ppp, int new_mtu, int new_mru)
+{
+  struct device *dev;
+  unsigned char *new_rbuff, *new_xbuff, *new_cbuff;
+  unsigned char *old_rbuff, *old_xbuff, *old_cbuff;
+  int mtu, mru;
+/*
+ *  Allocate the buffer from the kernel for the data
+ */
+  dev = ppp->dev;
+  mru = new_mru;
+  mtu = new_mtu;
+
+  /* RFC 1331, section 7.2 says the minimum value is 1500 bytes */
+  if (mru < PPP_MRU)
+    mru = PPP_MRU;
+
+  mtu = (mtu * 2) + 20;
+  mru = (mru * 2) + 20;
+
+  PRINTKN (2,(KERN_INFO "ppp: channel %s mtu = %d, mru = %d\n",
+	      dev->name, new_mtu, new_mru));
+	
+  new_xbuff = (unsigned char *) kmalloc(mtu + 4, GFP_ATOMIC);
+  new_rbuff = (unsigned char *) kmalloc(mru + 4, GFP_ATOMIC);
+  new_cbuff = (unsigned char *) kmalloc(mru + 4, GFP_ATOMIC);
+/*
+ *  If the buffers failed to allocate then complain.
+ */
+  if (new_xbuff == NULL || new_rbuff == NULL || new_cbuff == NULL)
+    {
+      PRINTKN (2,(KERN_ERR "ppp: failed to allocate new buffers\n"));
+/*
+ *  Release new buffer pointers if the updates were not performed
+ */
+      if (new_rbuff != NULL)
+	kfree (new_rbuff);
+
+      if (new_xbuff != NULL)
+	kfree (new_xbuff);
+
+      if (new_cbuff != NULL)
+	kfree (new_cbuff);
+    }
+/*
+ *  Update the pointers to the new buffer structures.
+ */
+  else
+    {
+      cli();
+      old_xbuff       = ppp->xbuff;
+      old_rbuff       = ppp->rbuff;
+      old_cbuff       = ppp->cbuff;
+
+      ppp->xbuff      = new_xbuff;
+      ppp->rbuff      = new_rbuff;
+      ppp->cbuff      = new_cbuff;
+
+      dev->mem_start  = (unsigned long) new_xbuff;
+      dev->mem_end    = (unsigned long) (dev->mem_start + mtu);
+
+      dev->rmem_start = (unsigned long) new_rbuff;
+      ppp->rend       = (unsigned char *)
+      dev->rmem_end   = (unsigned long) (dev->rmem_start + mru);
+
+      ppp->rhead      = new_rbuff;
+/*
+ *  Update the parameters for the new buffer sizes
+ */
+      ppp->toss		= 0xFE;
+      ppp->escape	= 0;
+      ppp->sending	= 0;
+      ppp->rcount	= 0;
+
+      ppp->mru		= new_mru;
+
+      ppp->mtu		=
+      dev->mtu		= new_mtu;
+
+      sti();
+/*
+ *  Release old buffer pointers
+ */
+      if (old_rbuff != NULL)
+	kfree (old_rbuff);
+
+      if (old_xbuff != NULL)
+	kfree (old_xbuff);
+
+      if (old_cbuff != NULL)
+	kfree (old_cbuff);
+    }
+}
+
+/* called when we abandon the PPP line discipline */
+
+static void
+ppp_release(struct ppp *ppp)
+{
+#ifdef NEW_TTY_DRIVERS
+  if (ppp->tty != NULL && ppp->tty->disc_data == ppp)
+    ppp->tty->disc_data = NULL; /* Break the tty->ppp link */
+#endif
+
+  if (ppp->dev) {
+    dev_close (ppp->dev);
+    ppp->dev->flags = 0;
+  }
+
+  kfree (ppp->xbuff);
+  kfree (ppp->cbuff);
+  kfree (ppp->rbuff);
+  kfree (ppp->us_rbuff);
+
+  ppp->xbuff    =
+  ppp->cbuff    =
+  ppp->rbuff    =
+  ppp->us_rbuff = NULL;
+
+  if (ppp->slcomp) {
+    slhc_free(ppp->slcomp);
+    ppp->slcomp = NULL;
+  }
+
+  ppp->inuse = 0;
+  ppp->tty   = NULL;
+}
+
+static void
+ppp_close(struct tty_struct *tty)
+{
+  struct ppp *ppp = ppp_find(tty);
+
+  if (ppp == NULL || ppp->magic != PPP_MAGIC) {
+    PRINTKN (1,(KERN_WARNING "ppp: trying to close unopened tty!\n"));
+  } else {
+    CHECK_PPP_VOID();
+    ppp_release (ppp);
+
+    PRINTKN (2,(KERN_INFO "ppp: channel %s closing.\n", ppp->dev->name));
+  }
+}
+
+/* called when PPP line discipline is selected on a tty */
+static int
+ppp_open(struct tty_struct *tty)
+{
+  struct ppp *ppp = ppp_find(tty);
+
+  if (ppp) {
+    PRINTKN (1,(KERN_ERR "ppp_open: gack! tty already associated to %s!\n",
+		ppp->magic == PPP_MAGIC ? ppp->dev->name : "unknown"));
+    return -EEXIST;
+  }
+
+  ppp = ppp_alloc();
+  if (ppp == NULL) {
+    PRINTKN (1,(KERN_ERR "ppp_open: couldn't allocate ppp channel\n"));
+    return -ENFILE;
+  }
+
+  /* make sure the channel is actually open */
+  ppp_init_ctrl_blk (ppp);
+
+  ppp->tty = tty;
+
+#ifdef NEW_TTY_DRIVERS
+  tty->disc_data = ppp;
+  if (tty->driver.flush_buffer)
+    tty->driver.flush_buffer(tty);
+  if (tty->ldisc.flush_buffer)
+    tty->ldisc.flush_buffer(tty);
+#else
+  tty_read_flush (tty);
+  tty_write_flush (tty);
+#endif
+
+  if ((ppp->slcomp = slhc_init(16, 16)) == NULL) {
+    PRINTKN (1,(KERN_ERR "ppp: no space for compression buffers!\n"));
+    ppp_release (ppp);
+    return -ENOMEM;
+  }
+
+  /* Define the buffers for operation */
+  ppp_changedmtu (ppp, ppp->dev->mtu, ppp->mru);
+  if (ppp->rbuff == NULL) {
+    ppp_release (ppp);
+    return -ENOMEM;
+  }
+
+  /* Allocate a user-level receive buffer */
+  ppp->us_rbuff = (unsigned char *) kmalloc (RBUFSIZE, GFP_KERNEL);
+  if (ppp->us_rbuff == NULL) {
+    PRINTKN (1,(KERN_ERR "ppp: no space for user receive buffer\n"));
+    ppp_release (ppp);
+    return -ENOMEM;
+  }
+
+  ppp->us_rbuff_head =
+  ppp->us_rbuff_tail = ppp->us_rbuff;
+  ppp->us_rbuff_end  = ppp->us_rbuff + RBUFSIZE;
+
+  PRINTKN (2,(KERN_INFO "ppp: channel %s open\n", ppp->dev->name));
+
+#ifdef MODULE
+  MOD_INC_USE_COUNT;
+#endif
+
+  return (ppp->line);
+}
+
+/* called when ppp interface goes "up".  here this just means we start
+   passing IP packets */
+static int
+ppp_dev_open(struct device *dev)
+{
+  struct ppp *ppp = &ppp_ctrl[dev->base_addr];
+
+  /* reset POINTOPOINT every time, since dev_close zaps it! */
+  dev->flags |= IFF_POINTOPOINT;
+
+  if (ppp->tty == NULL) {
+    PRINTKN (1,(KERN_ERR "ppp: %s not connected to a TTY! can't go open!\n",
+		dev->name));
+    return -ENXIO;
+  }
+
+  PRINTKN (2,(KERN_INFO "ppp: channel %s going up for IP packets!\n",
+	      dev->name));
+
+  CHECK_PPP(-ENXIO);
+  return 0;
+}
+
+static int
+ppp_dev_close(struct device *dev)
+{
+  struct ppp *ppp = &ppp_ctrl[dev->base_addr];
+
+  if (ppp->tty == NULL) {
+    PRINTKN (1,(KERN_ERR "ppp: %s not connected to a TTY! can't go down!\n",
+		dev->name));
+    return -ENXIO;
+  }
+
+  PRINTKN (2,(KERN_INFO "ppp: channel %s going down for IP packets!\n",
+	      dev->name));
+  CHECK_PPP(-ENXIO);
+#ifdef MODULE
+  MOD_DEC_USE_COUNT;
+#endif
+  return 0;
+}
+
+#ifndef NET02D
+static int ppp_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+  struct ppp *ppp = &ppp_ctrl[dev->base_addr];
+  int    error;
+
+  struct stats
+  {
+    struct ppp_stats  ppp_stats;
+    struct slcompress slhc;
+  } *result;
+
+  error = verify_area (VERIFY_READ,
+		       ifr->ifr_ifru.ifru_data,
+		       sizeof (struct stats));
+
+  if (error == 0) {
+    result = (struct stats *) ifr->ifr_ifru.ifru_data;
+
+    memcpy_tofs (&result->ppp_stats, &ppp->stats, sizeof (struct ppp_stats));
+    if (ppp->slcomp)
+      memcpy_tofs (&result->slhc,    ppp->slcomp, sizeof (struct slcompress));
+  }
+
+  return error;
+}
+#endif
+
+/*************************************************************
+ * TTY OUTPUT
+ *    The following function delivers a fully-formed PPP
+ *    frame in ppp->xbuff to the TTY for output.
+ *************************************************************/
+
+#ifdef NEW_TTY_DRIVERS
+static inline void
+#else
+static void
+#endif
+ppp_output_done (void *ppp)
+{
+  /* unlock the transmitter queue */
+  ppp_unlock ((struct ppp *) ppp);
+
+  /* If the device is still up then enable the transmitter of the
+     next frame. */
+  if (((struct ppp *) ppp)->dev->flags & IFF_UP)
+#ifndef NET02D
+    mark_bh (NET_BH);
+#else
+    dev_tint (((struct ppp *) ppp)->dev);
+#endif
+
+  /* enable any blocked process pending transmission */
+  wake_up_interruptible (&((struct ppp *) ppp)->write_wait);
+}
+
+#ifndef NEW_TTY_DRIVERS
+static void
+ppp_kick_tty (struct ppp *ppp)
+{
+  register int count = ppp->xhead - ppp->xbuff;
+  register int answer;
+
+  ppp->stats.sbytes += count;
+
+  answer = tty_write_data (ppp->tty,
+			   ppp->xbuff,
+			   count,
+			   ppp_output_done,
+			   (void *) ppp);
+
+  if (answer == 0)
+    ppp_output_done (ppp);   /* Should not happen */
+  else
+    if (answer < 0) {
+      ppp->stats.serrors++;
+      ppp_output_done (ppp); /* unlock the transmitter */
+    }
+}
+
+#else
+
+static void
+ppp_kick_tty (struct ppp *ppp)
+{
+	register int count, actual;
+	
+	count = ppp->xhead - ppp->xbuff;
+	
+	actual = ppp->tty->driver.write(ppp->tty, 0, ppp->xbuff, count);
+	ppp->stats.sbytes += actual;
+	if (actual == count) {
+		ppp_output_done(ppp);
+	} else {
+		ppp->xtail = ppp->xbuff + actual;
+		ppp->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+	}
+}
+
+static void ppp_write_wakeup(struct tty_struct *tty)
+{
+	register int count, actual;
+	struct ppp *ppp = ppp_find(tty);
+
+	if (!ppp || ppp->magic != PPP_MAGIC) {
+		PRINTKN (1,
+			 (KERN_ERR "PPP: write_wakeup called but couldn't "
+			  "find PPP struct.\n"));
+		return;
+	}
+
+	if (!ppp->xtail)
+		return;
+
+	cli();
+	if (ppp->flags & SC_XMIT_BUSY) {
+		sti();
+		return;
+	}
+	ppp->flags |= SC_XMIT_BUSY;
+	sti();
+	
+	count = ppp->xhead - ppp->xtail;
+	
+	actual = tty->driver.write(tty, 0, ppp->xtail, count);
+	ppp->stats.sbytes += actual;
+	if (actual == count) {
+		ppp->xtail = 0;
+		tty->flags &= ~TTY_DO_WRITE_WAKEUP;
+
+		ppp_output_done(ppp);
+	} else {
+		ppp->xtail += actual;
+	}
+	ppp->flags &= ~SC_XMIT_BUSY;
+}
+#endif
+
+/*************************************************************
+ * 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.
+ *************************************************************/
+
+/* stuff a single character into the receive buffer */
+
+static inline void
+ppp_enqueue(struct ppp *ppp, unsigned char c)
+{
+  unsigned long flags;
+
+  save_flags(flags);
+  cli();
+  if (ppp->rhead < ppp->rend) {
+    *ppp->rhead = c;
+    ppp->rhead++;
+    ppp->rcount++;
+  } else
+    ppp->stats.roverrun++;
+  restore_flags(flags);
+}
+
+#ifdef CHECK_CHARACTERS
+static unsigned paritytab[8] = {
+    0x96696996, 0x69969669, 0x69969669, 0x96696996,
+    0x69969669, 0x96696996, 0x96696996, 0x69969669
+};
+#endif
+
+#ifndef NEW_TTY_DRIVERS
+static void
+ppp_dump_inqueue(struct tty_struct *tty)
+{
+  int  head = tty->read_q.head,
+       tail = tty->read_q.tail,
+       i, count;
+  char buffer[8];
+
+  PRINTK ((KERN_DEBUG "INQUEUE: head %d tail %d imode %x:\n", head, tail, 
+	   (unsigned int) tty->termios->c_iflag))
+
+  i     = tail;
+  count = 0;
+
+  while (i != head) {
+    buffer [count] = tty->read_q.buf[i];
+    if (++count == 8) {
+      ppp_print_buffer (NULL, buffer, 8, KERNEL_DS);
+      count = 0;
+    }
+    i = (i + 1) & (TTY_BUF_SIZE - 1);
+  }
+  ppp_print_buffer (NULL, buffer, count, KERNEL_DS);
+}
+
+/* called by lower levels of TTY driver when data becomes available.
+   all incoming data comes through this function. */
+
+void ppp_tty_input_ready(struct tty_struct *tty)
+{
+  struct ppp *ppp = ppp_find(tty);
+  int n, error;
+  unsigned char buff[128];
+
+/*  PRINTK( (KERN_DEBUG "PPP: handler called.\n") ) */
+  if (!ppp || ppp->magic != PPP_MAGIC) {
+    PRINTKN (1,
+	     (KERN_ERR "PPP: handler called but couldn't find PPP struct.\n"));
+    return;
+  }
+
+  CHECK_PPP_VOID();
+
+  /* ZZZ */
+  if (ppp_debug >= 5)
+    ppp_dump_inqueue(ppp->tty);
+
+  do {
+    n = tty_read_raw_data(tty, buff, 128);
+    if ( n == 0 )		/* nothing there */
+      break;
+
+    if (ppp_debug >= 5)
+      ppp_print_buffer ("receive buffer", buff, n > 0 ? n : -n, KERNEL_DS);
+
+    if ( n < 0 ) {
+      /* Last character is error flag.
+	 Process the previous characters, then set toss flag. */
+      n = (-n) - 1;
+      error = buff[n];
+    } else error = 0;
+    ppp->stats.rbytes += n;
+    ppp_unesc(ppp,buff,n);
+    if (error)
+      ppp->toss = error;
+  } while (1);
+}
+
+/* recover frame by undoing PPP escape mechanism;
+   copies N chars of input data from C into PPP->rbuff
+   calls ppp_doframe to dispose of any frames it finds
+*/
+
+static void
+ppp_unesc(struct ppp *ppp, unsigned char *c, int n)
+{
+  int i;
+
+  for (i = 0; i < n; i++, c++) {
+    PRINTKN (6,(KERN_DEBUG "(%x)", (unsigned int) *c));
+
+#ifdef CHECK_CHARACTERS
+    if (*c & 0x80)
+	sc->sc_flags |= SC_RCV_B7_1;
+    else
+	sc->sc_flags |= SC_RCV_B7_0;
+
+    if (paritytab[*c >> 5] & (1 << (*c & 0x1F)))
+	sc->sc_flags |= SC_RCV_ODDP;
+    else
+	sc->sc_flags |= SC_RCV_EVNP;
+#endif
+
+    switch (*c) {
+    case PPP_ESC:		/* PPP_ESC: invert 0x20 in next character */
+      ppp->escape = PPP_TRANS;
+      break;
+
+    case PPP_FLAG:		/* PPP_FLAG: end of frame */
+      if (ppp->escape)		/* PPP_ESC just before PPP_FLAG is illegal */
+	ppp->toss = 0xFF;
+
+      if ((ppp->toss & 0x80) == 0)
+	ppp_doframe(ppp);	/* pass frame on to next layers */
+
+      ppp->rcount = 0;
+      ppp->rhead  = ppp->rbuff;
+      ppp->escape = 0;
+      ppp->toss   = 0;
+      break;
+
+    default:			/* regular character */
+      if (!in_rmap (ppp, *c)) {
+	if (ppp->toss == 0)
+	  ppp_enqueue (ppp, *c ^ ppp->escape);
+	ppp->escape = 0;
+      }
+      break;
+    }
+  }
+}
+
+#else
+static int ppp_receive_room(struct tty_struct *tty)
+{
+	return 65536;  /* We can handle an infinite amount of data. :-) */
+}
+
+
+static void ppp_receive_buf(struct tty_struct *tty, unsigned char *cp,
+			    char *fp, int count)
+{
+  register struct ppp *ppp = ppp_find (tty);
+  unsigned char c;
+ 
+/*  PRINTK( ("PPP: handler called.\n") ); */
+
+  if (!ppp || ppp->magic != PPP_MAGIC) {
+    PRINTKN (1,("PPP: handler called but couldn't find "
+		"PPP struct.\n"));
+    return;
+  }
+
+  CHECK_PPP_VOID();
+ 
+  if (ppp_debug >= 5) {
+    ppp_print_buffer ("receive buffer", cp, count, KERNEL_DS);
+  }
+
+  ppp->stats.rbytes += count;
+ 
+  while (count-- > 0) {
+    c = *cp++;
+
+    if (fp) {
+      if (*fp && ppp->toss == 0)
+	ppp->toss = *fp;
+      fp++;
+    }
+
+#ifdef CHECK_CHARACTERS
+    if (c & 0x80)
+	ppp->flags |= SC_RCV_B7_1;
+    else
+	ppp->flags |= SC_RCV_B7_0;
+
+    if (paritytab[c >> 5] & (1 << (c & 0x1F)))
+	ppp->flags |= SC_RCV_ODDP;
+    else
+	ppp->flags |= SC_RCV_EVNP;
+#endif
+
+    switch (c) {
+    case PPP_ESC:		/* PPP_ESC: invert 0x20 in next character */
+      ppp->escape = PPP_TRANS;
+      break;
+
+    case PPP_FLAG:		/* PPP_FLAG: end of frame */
+      if (ppp->escape)		/* PPP_ESC just before PPP_FLAG is "cancel"*/
+	ppp->toss = 0xFF;
+
+      if ((ppp->toss & 0x80) == 0)
+	ppp_doframe(ppp);	/* pass frame on to next layers */
+
+      ppp->rcount = 0;
+      ppp->rhead  = ppp->rbuff;
+      ppp->escape = 0;
+      ppp->toss   = 0;
+      break;
+
+    default:			/* regular character */
+      if (!in_rmap (ppp, c)) {
+	if (ppp->toss == 0)
+	  ppp_enqueue (ppp, c ^ ppp->escape);
+	ppp->escape = 0;
+      }
+    }
+  }
+}
+#endif
+
+/* on entry, a received frame is in ppp->rbuff
+   check it and dispose as appropriate */
+static void
+ppp_doframe(struct ppp *ppp)
+{
+  u_char *c = ppp->rbuff;
+  u_short proto;
+  int count = ppp->rcount;
+
+  /* forget it if we've already noticed an error */
+  if (ppp->toss) {
+    PRINTKN (1, (KERN_WARNING "ppp_toss: tossing frame, reason = %d\n",
+		 ppp->toss));
+    slhc_toss (ppp->slcomp);
+    ppp->stats.rerrors++;
+    return;
+  }
+
+  /* do this before printing buffer to avoid generating copious output */
+  if (count == 0)
+    return;
+
+  if (ppp_debug >= 3)
+    ppp_print_buffer ("receive frame", c, count, KERNEL_DS);
+
+  if (count < 4) {
+    PRINTKN (1,(KERN_WARNING "ppp: got runt ppp frame, %d chars\n", count));
+    slhc_toss (ppp->slcomp);
+    ppp->stats.runts++;
+    return;
+  }
+
+  /* check PPP error detection field */
+  if (!ppp_check_fcs(ppp)) {
+    PRINTKN (1,(KERN_WARNING "ppp: frame with bad fcs\n"));
+    slhc_toss (ppp->slcomp);
+    ppp->stats.rerrors++;
+    return;
+  }
+
+  count -= 2;			/* ignore last two characters */
+
+  /* now we have a good frame */
+  /* figure out the protocol field */
+  if ((c[0] == PPP_ADDRESS) && (c[1] == PPP_CONTROL)) {
+    c = c + 2;			/* ADDR/CTRL not compressed, so skip */
+    count -= 2;
+  }
+
+  proto = (u_short) *c++;		/* PROTO compressed */
+  if (proto & 1) {
+    count--;
+  } else {
+    proto = (proto << 8) | (u_short) *c++; /* PROTO uncompressed */
+    count -= 2;
+  }
+
+  /* Send the frame to the network if the ppp device is up */
+  if ((ppp->dev->flags & IFF_UP) && ppp_do_ip(ppp, proto, c, count)) {
+    ppp->ddinfo.ip_rjiffies = jiffies;
+    return;
+  }
+
+  /* If we got here, it has to go to a user process doing a read,
+     so queue it.
+
+     User process expects to get whole frame (for some reason), so
+     use count+2 so as to include FCS field. */
+
+  if (ppp_us_queue (ppp, proto, c, count+2)) {
+    ppp->ddinfo.nip_rjiffies = jiffies;
+    ppp->stats.rothers++;
+    return;
+  }
+
+  /* couldn't cope. */
+  PRINTKN (1,(KERN_WARNING
+	      "ppp: dropping packet on the floor: nobody could take it.\n"));
+  slhc_toss (ppp->slcomp);
+  ppp->stats.tossed++;
+}
+
+/* Examine packet at C, attempt to pass up to net layer. 
+   PROTO is the protocol field from the PPP frame.
+   Return 1 if could handle it, 0 otherwise.  */
+
+static int
+ppp_do_ip (struct ppp *ppp, unsigned short proto, unsigned char *c,
+	  int count)
+{
+  int flags, done;
+
+  PRINTKN (4,(KERN_DEBUG "ppp_do_ip: proto %x len %d first byte %x\n",
+	      (int) proto, count, c[0]));
+
+  if (ppp_debug_netpackets) {
+    PRINTK (("KERN_DEBUG %s <-- proto %x len %d\n", ppp->dev->name,
+	     (int) proto, count));
+  }
+    
+  if (proto == PROTO_IP) {
+    ppp->stats.runcomp++;
+    goto sendit;
+  }
+
+  if ((proto == PROTO_VJCOMP) && !(ppp->flags & SC_REJ_COMP_TCP)) {
+    /* get space for uncompressing the header */
+    done = 0;
+    save_flags (flags);
+    cli();
+    if ((ppp->rhead + 80) < ppp->rend) {
+      ppp->rhead += 80;
+      ppp->rcount += 80;
+      done = 1;
+    }
+    restore_flags(flags);
+
+    if (! done)	{
+      PRINTKN (1,(KERN_NOTICE
+		  "ppp: no space to decompress VJ compressed TCP header.\n"));
+      ppp->stats.roverrun++;
+      slhc_toss (ppp->slcomp);
+      return 1;
+    }
+
+    count = slhc_uncompress(ppp->slcomp, c, count);
+    if (count <= 0) {
+      ppp->stats.rerrors++;
+      PRINTKN (1,(KERN_NOTICE "ppp: error in VJ decompression\n"));
+      slhc_toss (ppp->slcomp);
+      return 1;
+    }
+    ppp->stats.rcomp++;
+    goto sendit;
+  }
+  
+  if ((proto == PROTO_VJUNCOMP) && !(ppp->flags & SC_REJ_COMP_TCP)) {
+    if (slhc_remember(ppp->slcomp, c, count) <= 0) {
+      ppp->stats.rerrors++;
+      PRINTKN (1,(KERN_NOTICE "ppp: error in VJ memorizing\n"));
+      slhc_toss (ppp->slcomp);
+      return 1;
+    }
+    ppp->stats.runcomp++;
+    goto sendit;
+  }
+
+  /* not ours */
+  return 0;
+
+ sendit:
+  if (ppp_debug_netpackets) {
+    struct iphdr *iph = (struct iphdr *) c;
+    PRINTK ((KERN_INFO "%s <--    src %lx dst %lx len %d\n", ppp->dev->name, 
+	     iph->saddr, iph->daddr, count))
+  }
+
+  /* receive the frame through the network software */
+  (void) dev_rint (c, count, 0, ppp->dev);
+  return 1;
+}
+
+/* stuff packet at BUF, length LEN, into the us_rbuff buffer
+   prepend PROTO information */
+
+#define PUTC(c,label) *ppp->us_rbuff_head++ = c; \
+                if (ppp->us_rbuff_head == ppp->us_rbuff_end) \
+                     ppp->us_rbuff_head = ppp->us_rbuff; \
+                if (ppp->us_rbuff_head == ppp->us_rbuff_tail) \
+                     goto label;
+#define GETC(c) c = *ppp->us_rbuff_tail++; \
+                if (ppp->us_rbuff_tail == ppp->us_rbuff_end) \
+                     ppp->us_rbuff_tail = ppp->us_rbuff;
+
+static int
+ppp_us_queue(struct ppp *ppp, unsigned short proto, 
+	     unsigned char *buf, int len)
+{
+  int totlen;
+  unsigned char *saved_head;
+
+  totlen = len+2;		/* including protocol */
+
+  if (set_bit(1, &ppp->us_rbuff_lock)) {
+    PRINTKN (1, (KERN_NOTICE "ppp_us_queue: can't get lock\n"));
+    return 0;
+  }
+  saved_head = ppp->us_rbuff_head;
+
+  PUTC((totlen & 0xff00) >> 8, failure);
+  PUTC(totlen & 0x00ff, failure);
+  PUTC((proto & 0xff00) >> 8, failure);
+  PUTC(proto & 0x00ff, failure);
+
+  while (len-- > 0) {
+    PUTC(*buf++, failure);
+  }
+
+  PRINTKN (3, (KERN_INFO "ppp: successfully queued %d bytes\n", totlen));
+  clear_bit(1, &ppp->us_rbuff_lock);
+  wake_up_interruptible (&ppp->read_wait);
+
+#ifdef NEW_TTY_DRIVERS
+  kill_fasync(ppp->tty->fasync, SIGIO);
+#endif
+
+  if (ppp->inp_sig && ppp->inp_sig_pid)
+    if (kill_proc (ppp->inp_sig_pid, ppp->inp_sig, 1) != 0) {
+      /* process is gone */
+      PRINTKN (2,(KERN_NOTICE
+		  "ppp: process that requested notification is gone\n"));
+      ppp->inp_sig = 0;
+      ppp->inp_sig_pid = 0;
+    }
+  return 1;
+
+ failure:
+  ppp->us_rbuff_head = saved_head;
+  clear_bit(1, &ppp->us_rbuff_lock);
+
+  PRINTKN (1, (KERN_NOTICE "ppp_us_queue: ran out of buffer space.\n"));
+
+  return 0;
+}
+
+/*************************************************************
+ * 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_read(struct tty_struct *tty, struct file *file, unsigned char *buf, unsigned int nr)
+{
+  struct ppp *ppp = ppp_find(tty);
+  unsigned char c;
+  int len, i;
+
+  if (!ppp || ppp->magic != PPP_MAGIC) {
+    PRINTKN (1,(KERN_ERR "ppp_read: cannot find ppp channel\n"));
+    return -EIO;
+  }
+
+  CHECK_PPP(-ENXIO);
+
+  PRINTKN (4,(KERN_DEBUG "ppp_read: called %x num %u\n",
+	      (unsigned int) buf,
+	      nr));
+
+  do {
+    /* try to acquire read lock */
+    if (set_bit(0, &ppp->us_rbuff_lock) == 0) {
+      /* got lock */
+      if (ppp->us_rbuff_head == ppp->us_rbuff_tail) {
+	/* no data */
+	PRINTKN (4,(KERN_DEBUG "ppp_read: no data\n"));
+	clear_bit(0, &ppp->us_rbuff_lock);
+        if (ppp->inp_sig) {
+	  PRINTKN (4,(KERN_DEBUG "ppp_read: EWOULDBLOCK\n"));
+	  return -EWOULDBLOCK;
+        } else goto wait;
+      }
+
+      /* reset the time of the last read operation */
+      ppp->ddinfo.nip_rjiffies = jiffies;
+
+      GETC (c); len = c << 8; GETC (c); len += c;
+
+      PRINTKN (4,(KERN_DEBUG "ppp_read: len = %d\n", len));
+
+      if (len + 2 > nr) {
+	/* frame too big; can't copy it, but do update us_rbuff_head */
+	PRINTKN (1,(KERN_DEBUG
+		    "ppp: read of %u bytes too small for %d frame\n",
+		    nr, len+2));
+	ppp->us_rbuff_head += len;
+	if (ppp->us_rbuff_head > ppp->us_rbuff_end)
+	  ppp->us_rbuff_head += - (ppp->us_rbuff_end - ppp->us_rbuff);
+	clear_bit(0, &ppp->us_rbuff_lock);
+	wake_up_interruptible (&ppp->read_wait);
+	ppp->stats.rgiants++;
+	return -EOVERFLOW;		/* ZZZ; HACK! */
+      } else {
+	/* have the space: copy the packet, faking the first two bytes */
+	put_fs_byte (PPP_ADDRESS, buf++);
+	put_fs_byte (PPP_CONTROL, buf++);
+	i = len;
+	while (i-- > 0) {
+	  GETC (c);
+	  put_fs_byte (c, buf++);
+	}
+      }
+
+      clear_bit(0, &ppp->us_rbuff_lock);
+      PRINTKN (3,(KERN_DEBUG "ppp_read: passing %d bytes up\n", len + 2));
+      ppp->stats.rothers++;
+      return len + 2;
+    }
+
+    /* need to wait */
+  wait:
+    current->timeout = 0;
+    PRINTKN (3,(KERN_DEBUG "ppp_read: sleeping\n"));
+    interruptible_sleep_on (&ppp->read_wait);
+    if (current->signal & ~current->blocked)
+      return -EINTR;
+  } while (1);
+}
+
+/* stuff a character into the transmit buffer, using PPP's way of escaping
+   special characters.
+   also, update ppp->fcs to take account of new character */
+static inline void
+ppp_stuff_char(struct ppp *ppp, unsigned char c)
+{
+  int curpt = ppp->xhead - ppp->xbuff;
+  if ((curpt < 0) || (curpt > 3000)) {
+    PRINTK ((KERN_DEBUG "ppp_stuff_char: %x %x %d\n",
+	     (unsigned int) ppp->xbuff, (unsigned int) ppp->xhead, curpt))
+  }
+  if (in_xmap (ppp, c)) {
+    *ppp->xhead++ = PPP_ESC;
+    *ppp->xhead++ = c ^ PPP_TRANS;
+  } else
+    *ppp->xhead++ = c;
+  ppp->fcs = (ppp->fcs >> 8) ^ fcstab[(ppp->fcs ^ c) & 0xff];
+}
+
+/* write a frame with NR chars from BUF to TTY
+   we have to put the FCS field on ourselves
+*/
+
+static int
+ppp_write(struct tty_struct *tty, struct file *file, unsigned char *buf, unsigned int nr)
+{
+  struct ppp *ppp = ppp_find(tty);
+  int i;
+
+  if (!ppp || ppp->magic != PPP_MAGIC) {
+    PRINTKN (1,(KERN_ERR "ppp_write: cannot find ppp unit\n"));
+    return -EIO;
+  }
+
+  CHECK_PPP(-ENXIO);
+  
+  if (ppp->mtu != ppp->dev->mtu)	/* Someone has been ifconfigging */
+    ppp_changedmtu (ppp, ppp->dev->mtu, ppp->mru);
+
+  if (nr > ppp->mtu) {
+    PRINTKN (1,(KERN_WARNING
+		"ppp_write: truncating user packet from %u to mtu %d\n",
+		nr, ppp->mtu));
+    nr = ppp->mtu;
+  }
+
+  if (ppp_debug >= 3)
+    ppp_print_buffer ("write frame", buf, nr, USER_DS);
+
+  /* lock this PPP unit so we will be the only writer;
+     sleep if necessary */
+  while ((ppp->sending == 1) || !ppp_lock(ppp)) {
+    current->timeout = 0;
+    PRINTKN (3,(KERN_DEBUG "ppp_write: sleeping\n"));
+    interruptible_sleep_on(&ppp->write_wait);
+    if (current->signal & ~current->blocked)
+      return -EINTR;
+  }
+
+  /* OK, locked.  Stuff the given bytes into the buffer. */
+
+  PRINTKN(4,(KERN_DEBUG "ppp_write: acquired write lock\n"));
+  ppp->xhead = ppp->xbuff;
+
+#ifdef OPTIMIZE_FLAG_TIME
+  if (jiffies - ppp->last_xmit > OPTIMIZE_FLAG_TIME)
+    *ppp->xhead++ = PPP_FLAG;
+  ppp->last_xmit = jiffies;
+#else      
+  *ppp->xhead++ = PPP_FLAG;
+#endif
+
+  ppp->fcs = PPP_FCS_INIT;
+  i = nr;
+  while (i-- > 0)
+    ppp_stuff_char(ppp,get_fs_byte(buf++));
+
+  ppp_add_fcs(ppp);		/* concatenate FCS at end */
+
+  *ppp->xhead++ = PPP_FLAG;
+  
+  /* reset the time of the last write operation */
+  ppp->ddinfo.nip_sjiffies = jiffies;
+
+  if (ppp_debug >= 6)
+    ppp_print_buffer ("xmit buffer", ppp->xbuff, ppp->xhead - ppp->xbuff, KERNEL_DS);
+  else {
+    PRINTKN (4,(KERN_DEBUG
+		"ppp_write: writing %d chars\n", ppp->xhead - ppp->xbuff));
+  }
+
+  /* packet is ready-to-go */
+  ++ppp->stats.sothers;
+  ppp_kick_tty(ppp);
+
+  return((int)nr);
+}
+ 
+static int
+ppp_ioctl(struct tty_struct *tty, struct file *file, unsigned int i,
+	  unsigned long l)
+{
+  struct ppp *ppp = ppp_find(tty);
+  register int temp_i = 0;
+  int error;
+
+  if (!ppp || ppp->magic != PPP_MAGIC) {
+    PRINTK ((KERN_ERR "ppp_ioctl: can't find PPP block from tty!\n"))
+    return -EBADF;
+  }
+
+  CHECK_PPP(-ENXIO);
+
+  /* This must be root user */
+  if (!suser())
+    return -EPERM;
+
+  switch (i) {
+  case PPPIOCSMRU:
+    error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
+    if (error == 0) {
+      temp_i = (int) get_fs_long (l);
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: set mru to %d\n", temp_i));
+      if (ppp->mru != temp_i)
+	ppp_changedmtu (ppp, ppp->dev->mtu, temp_i);
+    }
+    break;
+
+  case PPPIOCGFLAGS:
+    error = verify_area (VERIFY_WRITE, (void *) l, 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_fs_long ((long) temp_i, l);
+      PRINTKN (3,(KERN_DEBUG "ppp_ioctl: get flags: addr %lx flags %x\n",
+		  l,
+		  temp_i));
+    }
+    break;
+
+  case PPPIOCSFLAGS:
+    error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
+    if (error == 0) {
+      temp_i      = (int) get_fs_long (l);
+      ppp->flags ^= ((ppp->flags ^ temp_i) & SC_MASK);
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: set flags to %x\n", temp_i));
+    }
+    break;
+
+  case PPPIOCGASYNCMAP:
+    error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i));
+    if (error == 0) {
+      put_fs_long (ppp->xmit_async_map[0], l);
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: get asyncmap: addr %lx asyncmap %lx\n",
+		  l, ppp->xmit_async_map[0]));
+    }
+    break;
+
+  case PPPIOCSASYNCMAP:
+    error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
+    if (error == 0) {
+      ppp->xmit_async_map[0] = get_fs_long (l);
+      bset (ppp->xmit_async_map, PPP_FLAG);
+      bset (ppp->xmit_async_map, PPP_ESC);
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: set xmit asyncmap %lx\n",
+		  ppp->xmit_async_map[0]));
+    }
+    break;
+
+  case PPPIOCRASYNCMAP:
+    error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
+    if (error == 0) {
+      ppp->recv_async_map = get_fs_long (l);
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: set recv asyncmap %lx\n",
+		  ppp->recv_async_map));
+    }
+    break;
+
+  case PPPIOCGUNIT:
+    error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i));
+    if (error == 0) {
+      put_fs_long (ppp->dev->base_addr, l);
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: get unit: %ld", ppp->dev->base_addr));
+    }
+    break;
+
+  case PPPIOCSINPSIG:
+    error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
+    if (error == 0) {
+      ppp->inp_sig     = (int) get_fs_long (l);
+      ppp->inp_sig_pid = current->pid;
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: set input signal %d\n", ppp->inp_sig));
+    }
+    break;
+
+  case PPPIOCSDEBUG:
+    error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
+    if (error == 0) {
+      ppp_debug = (int) get_fs_long (l);
+      ppp_debug_netpackets = (ppp_debug & 0xff00) >> 8;
+      ppp_debug &= 0xff;
+      PRINTKN (1, (KERN_INFO "ppp_ioctl: set debug level %d, netpacket %d\n", 
+		   ppp_debug, ppp_debug_netpackets));
+    }
+    break;
+
+  case PPPIOCGDEBUG:
+    error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i));
+    if (error == 0) {
+      put_fs_long ((long) (ppp_debug | (ppp_debug_netpackets << 8)), l);
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: get debug level %d\n", 
+		  ppp_debug | (ppp_debug_netpackets << 8)));
+    }
+    break;
+
+  case PPPIOCGSTAT:
+    error = verify_area (VERIFY_WRITE, (void *) l, sizeof (struct ppp_stats));
+    if (error == 0) {
+      memcpy_tofs ((void *) l, &ppp->stats, sizeof (struct ppp_stats));
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: read statistics\n"));
+    }
+    break;
+
+  case PPPIOCGTIME:
+    error = verify_area (VERIFY_WRITE, (void *) l, sizeof (struct ppp_ddinfo));
+    if (error == 0) {
+      struct ppp_ddinfo cur_ddinfo;
+      unsigned long cur_jiffies = jiffies;
+
+      /* change absolute times to relative times. */
+      cur_ddinfo.ip_sjiffies  = cur_jiffies - ppp->ddinfo.ip_sjiffies;
+      cur_ddinfo.ip_rjiffies  = cur_jiffies - ppp->ddinfo.ip_rjiffies;
+      cur_ddinfo.nip_sjiffies = cur_jiffies - ppp->ddinfo.nip_sjiffies;
+      cur_ddinfo.nip_rjiffies = cur_jiffies - ppp->ddinfo.nip_rjiffies;
+      
+      memcpy_tofs ((void *) l, &cur_ddinfo, sizeof (struct ppp_ddinfo));
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: read demand dial info\n"));
+    }
+    break;
+
+  case PPPIOCGXASYNCMAP:
+    error = verify_area (VERIFY_WRITE,
+			 (void *) l,
+			 sizeof (ppp->xmit_async_map));
+    if (error == 0) {
+      memcpy_tofs ((void *) l,
+		   ppp->xmit_async_map,
+		   sizeof (ppp->xmit_async_map));
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: get xasyncmap: addr %lx\n", l));
+    }
+    break;
+
+  case PPPIOCSXASYNCMAP:
+    error = verify_area (VERIFY_READ, (void *) l,
+			 sizeof (ppp->xmit_async_map));
+    if (error == 0) {
+      unsigned long temp_tbl [8];
+
+      memcpy_fromfs (temp_tbl, (void *) l, sizeof (ppp->xmit_async_map));
+      temp_tbl[1]  =  0x00000000; /* must not escape 0x20 - 0x3f */
+      temp_tbl[2] &= ~0x40000000; /* must not escape 0x5e        */
+      temp_tbl[3] |=  0x60000000; /* must escape 0x7d and 0x7e   */
+
+      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));
+	PRINTKN (3,(KERN_INFO "ppp_ioctl: set xasyncmap\n"));
+      }
+    }
+    break;
+
+  case PPPIOCSMAXCID:
+    error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
+    if (error == 0) {
+      temp_i = (int) get_fs_long (l) + 1;
+      PRINTKN (3,(KERN_INFO "ppp_ioctl: set maxcid to %d\n", temp_i));
+      if (ppp->slcomp != NULL)
+	slhc_free (ppp->slcomp);
+
+      ppp->slcomp = slhc_init (temp_i, temp_i);
+
+      if (ppp->slcomp == NULL) {
+	PRINTKN (1,(KERN_ERR "ppp: no space for compression buffers!\n"));
+	ppp_release (ppp);
+	error = -ENOMEM;
+      }
+    }
+    break;
+
+#ifdef NEW_TTY_DRIVERS
+    /* Allow stty to read, but not set, the serial port */
+  case TCGETS:
+  case TCGETA:
+    error = n_tty_ioctl(tty, file, i, l);
+    break;
+#endif
+
+/*
+ *  All other ioctl() events will come here.
+ */
+
+  default:
+    PRINTKN (1,(KERN_ERR "ppp_ioctl: invalid ioctl: %x, addr %lx\n",
+		i,
+		l));
+#ifdef NEW_TTY_DRIVERS
+    error = -ENOIOCTLCMD;
+#else
+    error = -EINVAL;
+#endif
+    break;
+  }
+  return error;
+}
+
+static int
+ppp_select (struct tty_struct *tty, struct inode * inode,
+	    struct file * filp, int sel_type, select_table * wait)
+{
+  struct ppp *ppp = ppp_find (tty);
+  
+  if (!ppp || ppp->magic != PPP_MAGIC) {
+    PRINTK ((KERN_ERR "ppp_select: can't find PPP block from tty!\n"))
+    return -EBADF;
+  }
+  
+  /* If the PPP protocol is no longer active, return false */
+  CHECK_PPP (0);
+  
+  /* Process the request based upon the type desired */
+  switch (sel_type) {
+  case SEL_IN:
+    if (set_bit(0, &ppp->us_rbuff_lock) == 0) {
+      /* Test for the presence of data in the queue */
+      if (ppp->us_rbuff_head != ppp->us_rbuff_tail) {
+	clear_bit (0, &ppp->us_rbuff_lock);
+	return 1;
+      }
+      clear_bit (0, &ppp->us_rbuff_lock);
+    } /* fall through */
+
+  case SEL_EX:
+    /* Is there a pending error condition? */
+    if (tty->packet && tty->link->ctrl_status)
+      return 1;
+    
+    /* closed? */
+    if (tty->flags & (1 << TTY_SLAVE_CLOSED))
+      return 1;
+    
+    /* If the tty is disconnected, then this is an exception too */
+    if (tty_hung_up_p(filp))
+      return 1;
+
+    select_wait (&ppp->read_wait, wait);
+    break;
+    
+  case SEL_OUT:
+    if (ppp_lock (ppp)) {
+      if (ppp->sending == 0) {
+	ppp_unlock (ppp);
+	return 1;
+      }
+      ppp_unlock (ppp);
+    }
+    select_wait (&ppp->write_wait, wait);
+    break;
+  }
+  return 0;
+}
+
+/*************************************************************
+ * 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...).
+ *************************************************************/
+
+int
+ppp_xmit(struct sk_buff *skb, struct device *dev)
+{
+  struct tty_struct *tty;
+  struct ppp *ppp;
+  unsigned char *p;
+  unsigned short proto;
+  int len;
+
+  /* just a little sanity check. */
+  if (skb == NULL) {
+    PRINTKN(3,(KERN_WARNING "ppp_xmit: null packet!\n"));
+    return 0;
+  }
+
+  /* Get pointers to the various components */
+  ppp   = &ppp_ctrl[dev->base_addr];
+  tty   = ppp->tty;
+  p     = (unsigned char *) (skb + 1);
+  len   = skb->len;
+  proto = PROTO_IP;
+
+  PRINTKN(4,(KERN_DEBUG "ppp_xmit [%s]: skb %lX busy %d\n", dev->name, 
+	     (unsigned long int) skb, ppp->sending));
+
+  /* avoid race conditions when the link fails */
+  if (!ppp->inuse) {
+    dev_kfree_skb(skb, FREE_WRITE);
+    dev_close (dev);
+    return 0;
+  }
+
+  if (tty == NULL) {
+    PRINTKN(1,(KERN_ERR "ppp_xmit: %s not connected to a TTY!\n", dev->name));
+    goto done;
+  }
+
+  if (!(dev->flags & IFF_UP)) {
+    PRINTKN(1,(KERN_WARNING
+	       "ppp_xmit: packet sent on interface %s, which is down for IP\n",
+	       dev->name));
+    goto done;
+  }
+
+  /* get length from IP header as per Alan Cox bugfix for slip.c */
+  if (len < sizeof(struct iphdr)) {
+    PRINTKN(0,(KERN_ERR "ppp_xmit: given runt packet, ignoring\n"));
+    goto done;
+  }
+  len = ntohs( ((struct iphdr *)(skb->data)) -> tot_len );
+
+  /* If doing demand dial then divert the first frame to pppd. */
+  if (ppp->flags & SC_IP_DOWN) {
+    if (ppp->flags & SC_IP_FLUSH == 0) {
+      if (ppp_us_queue (ppp, proto, p, len))
+	ppp->flags |= SC_IP_FLUSH;
+    }
+    goto done;
+  }
+
+  /* Attempt to acquire send lock */
+  if (ppp->sending || !ppp_lock(ppp)) {
+    PRINTKN(3,(KERN_WARNING "ppp_xmit: busy\n"));
+    ppp->stats.sbusy++;
+    return 1;
+  }
+
+  ppp->xhead = ppp->xbuff;
+
+  /* try to compress, if VJ compression mode is on */
+  if (ppp->flags & SC_COMP_TCP) {
+    len = slhc_compress(ppp->slcomp, p, len, ppp->cbuff, &p, 
+			!(ppp->flags & SC_NO_TCP_CCID));
+    if (p[0] & SL_TYPE_COMPRESSED_TCP)
+      proto = PROTO_VJCOMP;
+    else {
+      if (p[0] >= SL_TYPE_UNCOMPRESSED_TCP) {
+	proto = PROTO_VJUNCOMP;
+	p[0] = (p[0] & 0x0f) | 0x40; 
+      }
+    }
+  }
+
+  /* increment appropriate counter */
+  if (proto == PROTO_VJCOMP)
+    ++ppp->stats.scomp;
+  else
+    ++ppp->stats.suncomp;
+      
+  if (ppp_debug_netpackets) {
+    struct iphdr *iph = (struct iphdr *) (skb + 1);
+    PRINTK ((KERN_DEBUG "%s ==> proto %x len %d src %x dst %x proto %d\n",
+	    dev->name, (int) proto, (int) len, (int) iph->saddr,
+	    (int) iph->daddr, (int) iph->protocol))
+  }
+
+  /* start of frame:   FLAG  ALL_STATIONS  CONTROL  <protohi> <protolo> */
+#ifdef OPTIMIZE_FLAG_TIME
+  if (jiffies - ppp->last_xmit > OPTIMIZE_FLAG_TIME)
+    *ppp->xhead++ = PPP_FLAG;
+  ppp->last_xmit = jiffies;
+#else      
+  *ppp->xhead++ = PPP_FLAG;
+#endif
+
+  ppp->fcs = PPP_FCS_INIT;
+  if (!(ppp->flags & SC_COMP_AC)) { 
+    ppp_stuff_char(ppp, PPP_ADDRESS);
+    ppp_stuff_char(ppp, PPP_CONTROL);
+  }
+
+  if (!(ppp->flags & SC_COMP_PROT) || (proto & 0xff00))
+    ppp_stuff_char(ppp, proto>>8);
+  ppp_stuff_char(ppp, proto&0xff);
+
+  /* data part */
+  while (len-- > 0)
+    ppp_stuff_char(ppp, *p++);
+
+  /* fcs and flag */
+  ppp_add_fcs(ppp);
+  *ppp->xhead++ = PPP_FLAG;
+
+  /* update the time for demand dial function */
+  ppp->ddinfo.ip_sjiffies = jiffies;
+
+  /* send it! */
+  if (ppp_debug >= 6)
+    ppp_print_buffer ("xmit buffer", ppp->xbuff, ppp->xhead - ppp->xbuff, KERNEL_DS);
+  else {
+    PRINTKN (4,(KERN_DEBUG
+		"ppp_write: writing %d chars\n", ppp->xhead - ppp->xbuff));
+  }
+
+  ppp_kick_tty(ppp);
+
+ done:
+  dev_kfree_skb(skb, FREE_WRITE);
+  return 0;
+}
+  
+static unsigned short
+ppp_type_trans (struct sk_buff *skb, struct device *dev)
+{
+  return(htons(ETH_P_IP));
+}
+
+#ifdef NET02D
+static int
+ppp_header(unsigned char *buff, struct device *dev, unsigned short type,
+	   unsigned long daddr, unsigned long saddr, unsigned len)
+{
+  return(0);
+}
+
+static int
+ppp_rebuild_header(void *buff, struct device *dev)
+{
+  return(0);
+}
+
+static void
+ppp_add_arp(unsigned long addr, struct sk_buff *skb, struct device *dev)
+{
+}
+
+#else
+
+static int
+ppp_header(unsigned char *buff, struct device *dev, unsigned short type,
+	   void *daddr, void *saddr, unsigned len, struct sk_buff *skb)
+{
+  return(0);
+}
+
+static int
+ppp_rebuild_header(void *buff, struct device *dev, unsigned long raddr,
+		   struct sk_buff *skb)
+{
+  return(0);
+}
+#endif
+
+static struct enet_statistics *
+ppp_get_stats (struct device *dev)
+{
+  struct ppp *ppp = &ppp_ctrl[dev->base_addr];
+  static struct enet_statistics ppp_stats;
+
+  ppp_stats.rx_packets = ppp->stats.rcomp + ppp->stats.runcomp;
+  ppp_stats.rx_errors = ppp->stats.rerrors;
+  ppp_stats.rx_dropped = ppp->stats.tossed;
+  ppp_stats.rx_fifo_errors = 0;
+  ppp_stats.rx_length_errors = ppp->stats.runts;
+  ppp_stats.rx_over_errors = ppp->stats.roverrun;
+  ppp_stats.rx_crc_errors = 0;
+  ppp_stats.rx_frame_errors = 0;
+  ppp_stats.tx_packets = ppp->stats.scomp + ppp->stats.suncomp;
+  ppp_stats.tx_errors = ppp->stats.serrors;
+  ppp_stats.tx_dropped = 0;
+  ppp_stats.tx_fifo_errors = 0;
+  ppp_stats.collisions = ppp->stats.sbusy;
+  ppp_stats.tx_carrier_errors = 0;
+  ppp_stats.tx_aborted_errors = 0;
+  ppp_stats.tx_window_errors = 0;
+  ppp_stats.tx_heartbeat_errors = 0;
+
+  PRINTKN (3, (KERN_INFO "ppp_get_stats called"));
+  return &ppp_stats;
+}
+
+/*************************************************************
+ * UTILITIES
+ *    Miscellany called by various functions above.
+ *************************************************************/
+
+#ifndef NEW_TTY_DRIVERS
+/* find a PPP channel given a TTY */
+struct ppp *
+ppp_find(struct tty_struct *tty)
+{
+  int i;
+  for (i = 0; i < PPP_NRUNIT; i++)
+    if (ppp_ctrl[i].inuse && (ppp_ctrl[i].tty == tty)) return &ppp_ctrl[i];
+
+  return NULL;
+}
+#endif
+
+/* allocate a PPP channel */
+static struct ppp *
+ppp_alloc(void)
+{
+  int i;
+  for (i = 0; i < PPP_NRUNIT; i++)
+    if (!set_bit(0, &ppp_ctrl[i].inuse)) return &ppp_ctrl[i];
+
+  return NULL;
+}
+
+/* marks a PPP interface 'busy'.  user processes will wait, if
+   they try to write, and the network code will refrain from sending
+   return nonzero if succeeded in acquiring lock
+*/
+
+static int
+ppp_lock(struct ppp *ppp)
+{
+  int flags, locked;
+  save_flags(flags);
+  cli();
+  locked = ppp->sending;
+  ppp->sending = 1;
+  if (ppp->dev->flags & IFF_UP)
+    ppp->dev->tbusy = 1;
+  restore_flags(flags);
+  return locked == 0;
+}
+
+static void
+ppp_unlock(struct ppp *ppp)
+{
+  int flags;
+  save_flags(flags);
+  cli();
+  ppp->sending = 0;
+  if (ppp->dev->flags & IFF_UP)
+    ppp->dev->tbusy = 0;
+  restore_flags(flags);
+}
+
+/* FCS support functions */
+
+static void
+ppp_add_fcs(struct ppp *ppp)
+{
+  unsigned short fcs = ppp->fcs;
+
+  fcs ^= 0xffff;
+  ppp_stuff_char(ppp, fcs & 0x00ff);
+  ppp_stuff_char(ppp, (fcs & 0xff00) >> 8);
+  ASSERT (ppp->fcs == PPP_FCS_GOOD);
+  PRINTKN (4,(KERN_DEBUG "ppp_add_fcs: fcs is %lx\n",
+	      (long) (unsigned long) fcs));
+}
+
+static int
+ppp_check_fcs(struct ppp *ppp)
+{
+  unsigned short fcs = PPP_FCS_INIT, msgfcs;
+  unsigned char *c = ppp->rbuff;
+  int i;
+
+  for (i = 0; i < ppp->rcount - 2; i++, c++)
+    fcs = (fcs >> 8) ^ fcstab[(fcs ^ *c) & 0xff];
+
+  fcs ^= 0xffff;
+  msgfcs = (c[1] << 8) + c[0];
+  PRINTKN (4,(KERN_INFO "ppp_check_fcs: got %lx want %lx\n",
+	      (unsigned long) msgfcs, (unsigned long) fcs));
+  return fcs == msgfcs;
+}
+
+static char hex[] = "0123456789ABCDEF";
+
+static inline void ppp_print_hex (register char *out, char *in, int count)
+{
+  register unsigned char next_ch;
+
+  while (count-- > 0) {
+    next_ch = (unsigned char) get_fs_byte (in);
+
+    *out++  = hex[(next_ch >> 4) & 0x0F];
+    *out++  = hex[next_ch        & 0x0F];
+    ++out;
+    ++in;
+  }
+}
+
+static inline void ppp_print_char (register char *out, char *in, int count)
+{
+  register unsigned char next_ch;
+
+  while (count-- > 0) {
+    next_ch = (unsigned char) get_fs_byte (in);
+
+    if (next_ch < 0x20 || next_ch > 0x7e)
+      *out++ = '.';
+    else {
+      *out++ = next_ch;
+      if (next_ch == '%')	/* printk/syslogd has a bug !! */
+	*out++ = '%';
+    }
+    ++in;
+  }
+  *out = '\0';
+}
+
+static void ppp_print_buffer(const char *name, char *buf, int count, int seg)
+{
+  char line [44];
+  int  old_fs = get_fs();
+
+  set_fs (seg);
+
+  if (name != NULL)
+    PRINTK ((KERN_DEBUG "ppp: %s, count = %d\n", name, count));
+
+  while (count > 8) {
+    memset         (line, ' ', sizeof (line));
+    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, ' ', sizeof (line));
+    ppp_print_hex  (line, buf, count);
+    ppp_print_char (&line[8 * 3], buf, count);
+    PRINTK ((KERN_DEBUG "%s\n", line));
+  }
+
+  set_fs (old_fs);
+}
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+
+static struct device dev_ppp[PPP_NRUNIT] = {
+	{
+		"ppp0",		/* ppp */
+		0, 0, 0, 0,	/* memory */
+		0, 0,		/* base, irq */
+		0, 0, 0, NULL, ppp_init,
+	}
+	, { "ppp1" , 0, 0, 0, 0,  1, 0, 0, 0, 0, NULL, ppp_init }
+	, { "ppp2" , 0, 0, 0, 0,  2, 0, 0, 0, 0, NULL, ppp_init }
+	, { "ppp3" , 0, 0, 0, 0,  3, 0, 0, 0, 0, NULL, ppp_init }
+
+#ifdef PPP_PPP_LOTS
+	, { "ppp4" , 0, 0, 0, 0,  4, 0, 0, 0, 0, NULL, ppp_init }
+	, { "ppp5" , 0, 0, 0, 0,  5, 0, 0, 0, 0, NULL, ppp_init }
+	, { "ppp6" , 0, 0, 0, 0,  6, 0, 0, 0, 0, NULL, ppp_init }
+	, { "ppp7" , 0, 0, 0, 0,  7, 0, 0, 0, 0, NULL, ppp_init }
+	, { "ppp8" , 0, 0, 0, 0,  8, 0, 0, 0, 0, NULL, ppp_init }
+	, { "ppp9" , 0, 0, 0, 0,  9, 0, 0, 0, 0, NULL, ppp_init }
+	, { "ppp10" , 0, 0, 0, 0, 10, 0, 0, 0, 0, NULL, ppp_init }
+	, { "ppp11" , 0, 0, 0, 0, 11, 0, 0, 0, 0, NULL, ppp_init }
+	, { "ppp12" , 0, 0, 0, 0, 12, 0, 0, 0, 0, NULL, ppp_init }
+	, { "ppp13" , 0, 0, 0, 0, 13, 0, 0, 0, 0, NULL, ppp_init }
+	, { "ppp14" , 0, 0, 0, 0, 14, 0, 0, 0, 0, NULL, ppp_init }
+	, { "ppp15" , 0, 0, 0, 0, 15, 0, 0, 0, 0, NULL, ppp_init }
+#endif
+};
+
+int
+init_module(void)
+{
+	int err;
+	int i;
+
+	for (i = 0; i < PPP_NRUNIT; i++)  {
+		if ((err = register_netdev(&dev_ppp[i])))  {
+			if (err == -EEXIST)  {
+				printk("PPP: devices already present. Module not loaded.\n");
+			}
+			return err;
+		}
+	}
+	return 0;
+}
+
+void
+cleanup_module(void)
+{
+	int i;
+
+	if (MOD_IN_USE)  {
+		printk("PPP: device busy, remove delayed\n");
+		return;
+	}
+	for (i = 0; i < PPP_NRUNIT; i++)  {
+		unregister_netdev(&dev_ppp[i]);
+	}
+	if ((i = tty_register_ldisc(N_PPP, NULL)))  {
+		printk("PPP: can't unregister line discipline (err = %d)\n", i);
+	}
+}
+
+#endif
diff -r -u -N linux.orig/arch/arm/drivers/net/slhc.c linux.arm/arch/arm/drivers/net/slhc.c
--- linux.orig/arch/arm/drivers/net/slhc.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/slhc.c	Fri Oct 27 23:15:05 1995
@@ -0,0 +1,747 @@
+/*
+ * Routines to compress and uncompress tcp packets (for transmission
+ * over low speed serial lines).
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *	Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ *	- Initial distribution.
+ *
+ *
+ * modified for KA9Q Internet Software Package by
+ * Katie Stevens (dkstevens@ucdavis.edu)
+ * University of California, Davis
+ * Computing Services
+ *	- 01-31-90	initial adaptation (from 1.19)
+ *	PPP.05	02-15-90 [ks]
+ *	PPP.08	05-02-90 [ks]	use PPP protocol field to signal compression
+ *	PPP.15	09-90	 [ks]	improve mbuf handling
+ *	PPP.16	11-02	 [karn]	substantially rewritten to use NOS facilities
+ *
+ *	- Feb 1991	Bill_Simpson@um.cc.umich.edu
+ *			variable number of conversation slots
+ *			allow zero or one slots
+ *			separate routines
+ *			status display
+ *	- Jul 1994	Dmitry Gorodchanin
+ *			Fixes for memory leaks.
+ *      - Oct 1994      Dmitry Gorodchanin
+ *                      Modularization.
+ *	- Jan 1995	Bjorn Ekwall
+ *			Use ip_fast_csum from ip.h
+ *
+ *
+ *	This module is a difficult issue. It's clearly inet code but it's also clearly
+ *	driver code belonging close to PPP and SLIP
+ */
+
+#include <linux/config.h>
+#ifdef CONFIG_INET
+/* Entire module is for IP only */
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/termios.h>
+#include <linux/in.h>
+#include <linux/fcntl.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include "ip.h"
+#include "protocol.h"
+#include "icmp.h"
+#include "tcp.h"
+#include <linux/skbuff.h>
+#include "sock.h"
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <linux/mm.h>
+#include "slhc.h"
+
+int last_retran;
+
+static unsigned char *encode(unsigned char *cp, unsigned short n);
+static long decode(unsigned char **cpp);
+static unsigned char * put16(unsigned char *cp, unsigned short x);
+static unsigned short pull16(unsigned char **cpp);
+
+/* Initialize compression data structure
+ *	slots must be in range 0 to 255 (zero meaning no compression)
+ */
+struct slcompress *
+slhc_init(int rslots, int tslots)
+{
+	register short i;
+	register struct cstate *ts;
+	struct slcompress *comp;
+
+	comp = (struct slcompress *)kmalloc(sizeof(struct slcompress),
+					    GFP_KERNEL);
+	if (! comp)
+		return NULL;
+
+	memset(comp, 0, sizeof(struct slcompress));
+
+	if ( rslots > 0  &&  rslots < 256 ) {
+		comp->rstate =
+		  (struct cstate *)kmalloc(rslots * sizeof(struct cstate),
+					   GFP_KERNEL);
+		if (! comp->rstate)
+		{
+			kfree((unsigned char *)comp);
+			return NULL;
+		}
+		memset(comp->rstate, 0, rslots * sizeof(struct cstate));
+		comp->rslot_limit = rslots - 1;
+	}
+
+	if ( tslots > 0  &&  tslots < 256 ) {
+		comp->tstate =
+		  (struct cstate *)kmalloc(tslots * sizeof(struct cstate),
+					   GFP_KERNEL);
+		if (! comp->tstate)
+		{
+			kfree((unsigned char *)comp->rstate);
+			kfree((unsigned char *)comp);
+			return NULL;
+		}
+		memset(comp->tstate, 0, rslots * sizeof(struct cstate));
+		comp->tslot_limit = tslots - 1;
+	}
+
+	comp->xmit_oldest = 0;
+	comp->xmit_current = 255;
+	comp->recv_current = 255;
+	/*
+	 * don't accept any packets with implicit index until we get
+	 * one with an explicit index.  Otherwise the uncompress code
+	 * will try to use connection 255, which is almost certainly
+	 * out of range
+	 */
+	comp->flags |= SLF_TOSS;
+
+	if ( tslots > 0 ) {
+		ts = comp->tstate;
+		for(i = comp->tslot_limit; i > 0; --i){
+			ts[i].cs_this = i;
+			ts[i].next = &(ts[i - 1]);
+		}
+		ts[0].next = &(ts[comp->tslot_limit]);
+		ts[0].cs_this = 0;
+	}
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+	return comp;
+}
+
+
+/* Free a compression data structure */
+void
+slhc_free(struct slcompress *comp)
+{
+	if ( comp == NULLSLCOMPR )
+		return;
+
+	if ( comp->rstate != NULLSLSTATE )
+		kfree( comp->rstate );
+
+	if ( comp->tstate != NULLSLSTATE )
+		kfree( comp->tstate );
+
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+	kfree( comp );
+}
+
+
+/* Put a short in host order into a char array in network order */
+static inline unsigned char *
+put16(unsigned char *cp, unsigned short x)
+{
+	*cp++ = x >> 8;
+	*cp++ = x;
+
+	return cp;
+}
+
+
+/* Encode a number */
+unsigned char *
+encode(unsigned char *cp, unsigned short n)
+{
+	if(n >= 256 || n == 0){
+		*cp++ = 0;
+		cp = put16(cp,n);
+	} else {
+		*cp++ = n;
+	}
+	return cp;
+}
+
+/* Pull a 16-bit integer in host order from buffer in network byte order */
+static unsigned short
+pull16(unsigned char **cpp)
+{
+	short rval;
+
+	rval = *(*cpp)++;
+	rval <<= 8;
+	rval |= *(*cpp)++;
+	return rval;
+}
+
+/* Decode a number */
+long
+decode(unsigned char **cpp)
+{
+	register int x;
+
+	x = *(*cpp)++;
+	if(x == 0){
+		return pull16(cpp) & 0xffff;	/* pull16 returns -1 on error */
+	} else {
+		return x & 0xff;		/* -1 if PULLCHAR returned error */
+	}
+}
+
+/*
+ * icp and isize are the original packet.
+ * ocp is a place to put a copy if necessary.
+ * cpp is initially a pointer to icp.  If the copy is used,
+ *    change it to ocp.
+ */
+
+int
+slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
+	unsigned char *ocp, unsigned char **cpp, int compress_cid)
+{
+	register struct cstate *ocs = &(comp->tstate[comp->xmit_oldest]);
+	register struct cstate *lcs = ocs;
+	register struct cstate *cs = lcs->next;
+	register unsigned long deltaS, deltaA;
+	register short changes = 0;
+	int hlen;
+	unsigned char new_seq[16];
+	register unsigned char *cp = new_seq;
+	struct iphdr *ip;
+	struct tcphdr *th, *oth;
+
+	ip = (struct iphdr *) icp;
+
+	/* Bail if this packet isn't TCP, or is an IP fragment */
+	if(ip->protocol != IPPROTO_TCP || (ntohs(ip->frag_off) & 0x1fff) ||
+				       (ip->frag_off & 32)){
+		/* Send as regular IP */
+		if(ip->protocol != IPPROTO_TCP)
+			comp->sls_o_nontcp++;
+		else
+			comp->sls_o_tcp++;
+		return isize;
+	}
+	/* Extract TCP header */
+
+	th = (struct tcphdr *)(((unsigned char *)ip) + ip->ihl*4);
+	hlen = ip->ihl*4 + th->doff*4;
+
+	/*  Bail if the TCP packet isn't `compressible' (i.e., ACK isn't set or
+	 *  some other control bit is set).
+	 */
+	if(th->syn || th->fin || th->rst ||
+	    ! (th->ack)){
+		/* TCP connection stuff; send as regular IP */
+		comp->sls_o_tcp++;
+		return isize;
+	}
+	/*
+	 * Packet is compressible -- we're going to send either a
+	 * COMPRESSED_TCP or UNCOMPRESSED_TCP packet.  Either way,
+	 * we need to locate (or create) the connection state.
+	 *
+	 * States are kept in a circularly linked list with
+	 * xmit_oldest pointing to the end of the list.  The
+	 * list is kept in lru order by moving a state to the
+	 * head of the list whenever it is referenced.  Since
+	 * the list is short and, empirically, the connection
+	 * we want is almost always near the front, we locate
+	 * states via linear search.  If we don't find a state
+	 * for the datagram, the oldest state is (re-)used.
+	 */
+	for ( ; ; ) {
+		if( ip->saddr == cs->cs_ip.saddr
+		 && ip->daddr == cs->cs_ip.daddr
+		 && th->source == cs->cs_tcp.source
+		 && th->dest == cs->cs_tcp.dest)
+			goto found;
+
+		/* if current equal oldest, at end of list */
+		if ( cs == ocs )
+			break;
+		lcs = cs;
+		cs = cs->next;
+		comp->sls_o_searches++;
+	};
+	/*
+	 * Didn't find it -- re-use oldest cstate.  Send an
+	 * uncompressed packet that tells the other side what
+	 * connection number we're using for this conversation.
+	 *
+	 * Note that since the state list is circular, the oldest
+	 * state points to the newest and we only need to set
+	 * xmit_oldest to update the lru linkage.
+	 */
+	comp->sls_o_misses++;
+	comp->xmit_oldest = lcs->cs_this;
+	goto uncompressed;
+
+found:
+	/*
+	 * Found it -- move to the front on the connection list.
+	 */
+	if(lcs == ocs) {
+ 		/* found at most recently used */
+	} else if (cs == ocs) {
+		/* found at least recently used */
+		comp->xmit_oldest = lcs->cs_this;
+	} else {
+		/* more than 2 elements */
+		lcs->next = cs->next;
+		cs->next = ocs->next;
+		ocs->next = cs;
+	}
+
+	/*
+	 * Make sure that only what we expect to change changed.
+	 * Check the following:
+	 * IP protocol version, header length & type of service.
+	 * The "Don't fragment" bit.
+	 * The time-to-live field.
+	 * The TCP header length.
+	 * IP options, if any.
+	 * TCP options, if any.
+	 * If any of these things are different between the previous &
+	 * current datagram, we send the current datagram `uncompressed'.
+	 */
+	oth = &cs->cs_tcp;
+
+	if(last_retran
+	 || ip->version != cs->cs_ip.version || ip->ihl != cs->cs_ip.ihl
+	 || ip->tos != cs->cs_ip.tos
+	 || (ip->frag_off & 64) != (cs->cs_ip.frag_off & 64)
+	 || ip->ttl != cs->cs_ip.ttl
+	 || th->doff != cs->cs_tcp.doff
+	 || (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0)
+	 || (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4 != 0))){
+		goto uncompressed;
+	}
+
+	/*
+	 * Figure out which of the changing fields changed.  The
+	 * receiver expects changes in the order: urgent, window,
+	 * ack, seq (the order minimizes the number of temporaries
+	 * needed in this section of code).
+	 */
+	if(th->urg){
+		deltaS = ntohs(th->urg_ptr);
+		cp = encode(cp,deltaS);
+		changes |= NEW_U;
+	} else if(th->urg_ptr != oth->urg_ptr){
+		/* argh! URG not set but urp changed -- a sensible
+		 * implementation should never do this but RFC793
+		 * doesn't prohibit the change so we have to deal
+		 * with it. */
+		goto uncompressed;
+	}
+	if((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0){
+		cp = encode(cp,deltaS);
+		changes |= NEW_W;
+	}
+	if((deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L){
+		if(deltaA > 0x0000ffff)
+			goto uncompressed;
+		cp = encode(cp,deltaA);
+		changes |= NEW_A;
+	}
+	if((deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L){
+		if(deltaS > 0x0000ffff)
+			goto uncompressed;
+		cp = encode(cp,deltaS);
+		changes |= NEW_S;
+	}
+
+	switch(changes){
+	case 0:	/* Nothing changed. If this packet contains data and the
+		 * last one didn't, this is probably a data packet following
+		 * an ack (normal on an interactive connection) and we send
+		 * it compressed.  Otherwise it's probably a retransmit,
+		 * retransmitted ack or window probe.  Send it uncompressed
+		 * in case the other side missed the compressed version.
+		 */
+		if(ip->tot_len != cs->cs_ip.tot_len &&
+		   ntohs(cs->cs_ip.tot_len) == hlen)
+			break;
+		goto uncompressed;
+		break;
+	case SPECIAL_I:
+	case SPECIAL_D:
+		/* actual changes match one of our special case encodings --
+		 * send packet uncompressed.
+		 */
+		goto uncompressed;
+	case NEW_S|NEW_A:
+		if(deltaS == deltaA &&
+		    deltaS == ntohs(cs->cs_ip.tot_len) - hlen){
+			/* special case for echoed terminal traffic */
+			changes = SPECIAL_I;
+			cp = new_seq;
+		}
+		break;
+	case NEW_S:
+		if(deltaS == ntohs(cs->cs_ip.tot_len) - hlen){
+			/* special case for data xfer */
+			changes = SPECIAL_D;
+			cp = new_seq;
+		}
+		break;
+	}
+	deltaS = ntohs(ip->id) - ntohs(cs->cs_ip.id);
+	if(deltaS != 1){
+		cp = encode(cp,deltaS);
+		changes |= NEW_I;
+	}
+	if(th->psh)
+		changes |= TCP_PUSH_BIT;
+	/* Grab the cksum before we overwrite it below.  Then update our
+	 * state with this packet's header.
+	 */
+	deltaA = ntohs(th->check);
+	memcpy(&cs->cs_ip,ip,20);
+	memcpy(&cs->cs_tcp,th,20);
+	/* We want to use the original packet as our compressed packet.
+	 * (cp - new_seq) is the number of bytes we need for compressed
+	 * sequence numbers.  In addition we need one byte for the change
+	 * mask, one for the connection id and two for the tcp checksum.
+	 * So, (cp - new_seq) + 4 bytes of header are needed.
+	 */
+	deltaS = cp - new_seq;
+	if(compress_cid == 0 || comp->xmit_current != cs->cs_this){
+		cp = ocp;
+		*cpp = ocp;
+		*cp++ = changes | NEW_C;
+		*cp++ = cs->cs_this;
+		comp->xmit_current = cs->cs_this;
+	} else {
+		cp = ocp;
+		*cpp = ocp;
+		*cp++ = changes;
+	}
+	cp = put16(cp,(short)deltaA);	/* Write TCP checksum */
+/* deltaS is now the size of the change section of the compressed header */
+	memcpy(cp,new_seq,deltaS);	/* Write list of deltas */
+	memcpy(cp+deltaS,icp+hlen,isize-hlen);
+	comp->sls_o_compressed++;
+	ocp[0] |= SL_TYPE_COMPRESSED_TCP;
+	return isize - hlen + deltaS + (cp - ocp);
+
+	/* Update connection state cs & send uncompressed packet (i.e.,
+	 * a regular ip/tcp packet but with the 'conversation id' we hope
+	 * to use on future compressed packets in the protocol field).
+	 */
+uncompressed:
+	memcpy(&cs->cs_ip,ip,20);
+	memcpy(&cs->cs_tcp,th,20);
+	if (ip->ihl > 5)
+	  memcpy(cs->cs_ipopt, ip+1, ((ip->ihl) - 5) * 4);
+	if (th->doff > 5)
+	  memcpy(cs->cs_tcpopt, th+1, ((th->doff) - 5) * 4);
+	comp->xmit_current = cs->cs_this;
+	comp->sls_o_uncompressed++;
+	memcpy(ocp, icp, isize);
+	*cpp = ocp;
+	ocp[9] = cs->cs_this;
+	ocp[0] |= SL_TYPE_UNCOMPRESSED_TCP;
+	return isize;
+}
+
+
+int
+slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)
+{
+	register int changes;
+	long x;
+	register struct tcphdr *thp;
+	register struct iphdr *ip;
+	register struct cstate *cs;
+	int len, hdrlen;
+	unsigned char *cp = icp;
+
+	/* We've got a compressed packet; read the change byte */
+	comp->sls_i_compressed++;
+	if(isize < 3){
+		comp->sls_i_error++;
+		return 0;
+	}
+	changes = *cp++;
+	if(changes & NEW_C){
+		/* Make sure the state index is in range, then grab the state.
+		 * If we have a good state index, clear the 'discard' flag.
+		 */
+		x = *cp++;	/* Read conn index */
+		if(x < 0 || x > comp->rslot_limit)
+			goto bad;
+
+		comp->flags &=~ SLF_TOSS;
+		comp->recv_current = x;
+	} else {
+		/* this packet has an implicit state index.  If we've
+		 * had a line error since the last time we got an
+		 * explicit state index, we have to toss the packet. */
+		if(comp->flags & SLF_TOSS){
+			comp->sls_i_tossed++;
+			return 0;
+		}
+	}
+	cs = &comp->rstate[comp->recv_current];
+	thp = &cs->cs_tcp;
+	ip = &cs->cs_ip;
+
+	if((x = pull16(&cp)) == -1) {	/* Read the TCP checksum */
+		goto bad;
+        }
+	thp->check = htons(x);
+
+	thp->psh = (changes & TCP_PUSH_BIT) ? 1 : 0;
+/*
+ * we can use the same number for the length of the saved header and
+ * the current one, because the packet wouldn't have been sent
+ * as compressed unless the options were the same as the previous one
+ */
+
+	hdrlen = ip->ihl * 4 + thp->doff * 4;
+
+	switch(changes & SPECIALS_MASK){
+	case SPECIAL_I:		/* Echoed terminal traffic */
+		{
+		register short i;
+		i = ntohs(ip->tot_len) - hdrlen;
+		thp->ack_seq = htonl( ntohl(thp->ack_seq) + i);
+		thp->seq = htonl( ntohl(thp->seq) + i);
+		}
+		break;
+
+	case SPECIAL_D:			/* Unidirectional data */
+		thp->seq = htonl( ntohl(thp->seq) +
+				  ntohs(ip->tot_len) - hdrlen);
+		break;
+
+	default:
+		if(changes & NEW_U){
+			thp->urg = 1;
+			if((x = decode(&cp)) == -1) {
+				goto bad;
+			}
+			thp->urg_ptr = htons(x);
+		} else
+			thp->urg = 0;
+		if(changes & NEW_W){
+			if((x = decode(&cp)) == -1) {
+				goto bad;
+			}
+			thp->window = htons( ntohs(thp->window) + x);
+		}
+		if(changes & NEW_A){
+			if((x = decode(&cp)) == -1) {
+				goto bad;
+			}
+			thp->ack_seq = htonl( ntohl(thp->ack_seq) + x);
+		}
+		if(changes & NEW_S){
+			if((x = decode(&cp)) == -1) {
+				goto bad;
+			}
+			thp->seq = htonl( ntohl(thp->seq) + x);
+		}
+		break;
+	}
+	if(changes & NEW_I){
+		if((x = decode(&cp)) == -1) {
+			goto bad;
+		}
+		ip->id = htons (ntohs (ip->id) + x);
+	} else
+		ip->id = htons (ntohs (ip->id) + 1);
+
+	/*
+	 * At this point, cp points to the first byte of data in the
+	 * packet.  Put the reconstructed TCP and IP headers back on the
+	 * packet.  Recalculate IP checksum (but not TCP checksum).
+	 */
+
+	len = isize - (cp - icp);
+	if (len < 0)
+		goto bad;
+	len += hdrlen;
+	ip->tot_len = htons(len);
+	ip->check = 0;
+
+	memmove(icp + hdrlen, cp, len - hdrlen);
+
+	cp = icp;
+	memcpy(cp, ip, 20);
+	cp += 20;
+
+	if (ip->ihl > 5) {
+	  memcpy(cp, cs->cs_ipopt, ((ip->ihl) - 5) * 4);
+	  cp += ((ip->ihl) - 5) * 4;
+	}
+
+	((struct iphdr *)icp)->check = ip_fast_csum(icp, ((struct iphdr*)icp)->ihl);
+
+	memcpy(cp, thp, 20);
+	cp += 20;
+
+	if (thp->doff > 5) {
+	  memcpy(cp, cs->cs_tcpopt, ((thp->doff) - 5) * 4);
+	  cp += ((thp->doff) - 5) * 4;
+	}
+
+	return len;
+bad:
+	comp->sls_i_error++;
+	return slhc_toss( comp );
+}
+
+
+int
+slhc_remember(struct slcompress *comp, unsigned char *icp, int isize)
+{
+	register struct cstate *cs;
+	short ip_len;
+	struct iphdr *ip;
+	struct tcphdr *thp;
+
+	unsigned char index;
+
+	if(isize < 20) {
+		/* The packet is shorter than a legal IP header */
+		comp->sls_i_runt++;
+		return slhc_toss( comp );
+	}
+	/* Sneak a peek at the IP header's IHL field to find its length */
+	ip_len = (icp[0] & 0xf) << 2;
+	if(ip_len < 20){
+		/* The IP header length field is too small */
+		comp->sls_i_runt++;
+		return slhc_toss( comp );
+	}
+	index = icp[9];
+	icp[9] = IPPROTO_TCP;
+	ip = (struct iphdr *) icp;
+
+	if (ip_fast_csum(icp, ip->ihl)) {
+		/* Bad IP header checksum; discard */
+		comp->sls_i_badcheck++;
+		return slhc_toss( comp );
+	}
+	thp = (struct tcphdr *)(((unsigned char *)ip) + ip->ihl*4);
+	if(index > comp->rslot_limit) {
+		comp->sls_i_error++;
+		return slhc_toss(comp);
+	}
+
+	/* Update local state */
+	cs = &comp->rstate[comp->recv_current = index];
+	comp->flags &=~ SLF_TOSS;
+	memcpy(&cs->cs_ip,ip,20);
+	memcpy(&cs->cs_tcp,thp,20);
+	if (ip->ihl > 5)
+	  memcpy(cs->cs_ipopt, ip+1, ((ip->ihl) - 5) * 4);
+	if (thp->doff > 5)
+	  memcpy(cs->cs_tcpopt, thp+1, ((thp->doff) - 5) * 4);
+	cs->cs_hsize = ip->ihl*2 + thp->doff*2;
+	/* Put headers back on packet
+	 * Neither header checksum is recalculated
+	 */
+	comp->sls_i_uncompressed++;
+	return isize;
+}
+
+
+int
+slhc_toss(struct slcompress *comp)
+{
+	if ( comp == NULLSLCOMPR )
+		return 0;
+
+	comp->flags |= SLF_TOSS;
+	return 0;
+}
+
+
+void slhc_i_status(struct slcompress *comp)
+{
+	if (comp != NULLSLCOMPR) {
+		printk("\t%ld Cmp, %ld Uncmp, %ld Bad, %ld Tossed\n",
+			comp->sls_i_compressed,
+			comp->sls_i_uncompressed,
+			comp->sls_i_error,
+			comp->sls_i_tossed);
+	}
+}
+
+
+void slhc_o_status(struct slcompress *comp)
+{
+	if (comp != NULLSLCOMPR) {
+		printk("\t%ld Cmp, %ld Uncmp, %ld AsIs, %ld NotTCP\n",
+			comp->sls_o_compressed,
+			comp->sls_o_uncompressed,
+			comp->sls_o_tcp,
+			comp->sls_o_nontcp);
+		printk("\t%10ld Searches, %10ld Misses\n",
+			comp->sls_o_searches,
+			comp->sls_o_misses);
+	}
+}
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+
+int init_module(void)
+{
+	printk("CSLIP: code copyright 1989 Regents of the University of California\n");
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	if (MOD_IN_USE)  {
+		printk("CSLIP: module in use, remove delayed");
+	}
+	return;
+}
+#endif /* MODULE */
+#endif /* CONFIG_INET */
diff -r -u -N linux.orig/arch/arm/drivers/net/slhc.h linux.arm/arch/arm/drivers/net/slhc.h
--- linux.orig/arch/arm/drivers/net/slhc.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/slhc.h	Fri Oct 27 23:15:03 1995
@@ -0,0 +1,187 @@
+#ifndef _SLHC_H
+#define _SLHC_H
+/*
+ * Definitions for tcp compression routines.
+ *
+ * $Header: slcompress.h,v 1.10 89/12/31 08:53:02 van Exp $
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *	Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ *	- Initial distribution.
+ *
+ *
+ * modified for KA9Q Internet Software Package by
+ * Katie Stevens (dkstevens@ucdavis.edu)
+ * University of California, Davis
+ * Computing Services
+ *	- 01-31-90	initial adaptation
+ *
+ *	- Feb 1991	Bill_Simpson@um.cc.umich.edu
+ *			variable number of conversation slots
+ *			allow zero or one slots
+ *			separate routines
+ *			status display
+ */
+
+/*
+ * Compressed packet format:
+ *
+ * The first octet contains the packet type (top 3 bits), TCP
+ * 'push' bit, and flags that indicate which of the 4 TCP sequence
+ * numbers have changed (bottom 5 bits).  The next octet is a
+ * conversation number that associates a saved IP/TCP header with
+ * the compressed packet.  The next two octets are the TCP checksum
+ * from the original datagram.  The next 0 to 15 octets are
+ * sequence number changes, one change per bit set in the header
+ * (there may be no changes and there are two special cases where
+ * the receiver implicitly knows what changed -- see below).
+ *
+ * There are 5 numbers which can change (they are always inserted
+ * in the following order): TCP urgent pointer, window,
+ * acknowledgment, sequence number and IP ID.  (The urgent pointer
+ * is different from the others in that its value is sent, not the
+ * change in value.)  Since typical use of SLIP links is biased
+ * toward small packets (see comments on MTU/MSS below), changes
+ * use a variable length coding with one octet for numbers in the
+ * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
+ * range 256 - 65535 or 0.  (If the change in sequence number or
+ * ack is more than 65535, an uncompressed packet is sent.)
+ */
+
+/*
+ * Packet types (must not conflict with IP protocol version)
+ *
+ * The top nibble of the first octet is the packet type.  There are
+ * three possible types: IP (not proto TCP or tcp with one of the
+ * control flags set); uncompressed TCP (a normal IP/TCP packet but
+ * with the 8-bit protocol field replaced by an 8-bit connection id --
+ * this type of packet syncs the sender & receiver); and compressed
+ * TCP (described above).
+ *
+ * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
+ * is logically part of the 4-bit "changes" field that follows.  Top
+ * three bits are actual packet type.  For backward compatibility
+ * and in the interest of conserving bits, numbers are chosen so the
+ * IP protocol version number (4) which normally appears in this nibble
+ * means "IP packet".
+ */
+
+/* SLIP compression masks for len/vers byte */
+#define SL_TYPE_IP 0x40
+#define SL_TYPE_UNCOMPRESSED_TCP 0x70
+#define SL_TYPE_COMPRESSED_TCP 0x80
+#define SL_TYPE_ERROR 0x00
+
+/* Bits in first octet of compressed packet */
+#define NEW_C	0x40	/* flag bits for what changed in a packet */
+#define NEW_I	0x20
+#define NEW_S	0x08
+#define NEW_A	0x04
+#define NEW_W	0x02
+#define NEW_U	0x01
+
+/* reserved, special-case values of above */
+#define SPECIAL_I (NEW_S|NEW_W|NEW_U)		/* echoed interactive traffic */
+#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U)	/* unidirectional data */
+#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
+
+#define TCP_PUSH_BIT 0x10
+
+/*
+ * data type and sizes conversion assumptions:
+ *
+ *	VJ code		KA9Q style	generic
+ *	u_char		byte_t		unsigned char	 8 bits
+ *	u_short		int16		unsigned short	16 bits
+ *	u_int		int16		unsigned short	16 bits
+ *	u_long		unsigned long	unsigned long	32 bits
+ *	int		int32		long		32 bits
+ */
+
+typedef unsigned char byte_t;
+typedef unsigned long int32;
+
+/*
+ * "state" data for each active tcp conversation on the wire.  This is
+ * basically a copy of the entire IP/TCP header from the last packet
+ * we saw from the conversation together with a small identifier
+ * the transmit & receive ends of the line use to locate saved header.
+ */
+struct cstate {
+	byte_t	cs_this;	/* connection id number (xmit) */
+	struct cstate *next;	/* next in ring (xmit) */
+	struct iphdr cs_ip;	/* ip/tcp hdr from most recent packet */
+	struct tcphdr cs_tcp;
+	unsigned char cs_ipopt[64];
+	unsigned char cs_tcpopt[64];
+	int cs_hsize;
+};
+#define NULLSLSTATE	(struct cstate *)0
+
+/*
+ * all the state data for one serial line (we need one of these per line).
+ */
+struct slcompress {
+	struct cstate *tstate;	/* transmit connection states (array)*/
+	struct cstate *rstate;	/* receive connection states (array)*/
+
+	byte_t tslot_limit;	/* highest transmit slot id (0-l)*/
+	byte_t rslot_limit;	/* highest receive slot id (0-l)*/
+
+	byte_t xmit_oldest;	/* oldest xmit in ring */
+	byte_t xmit_current;	/* most recent xmit id */
+	byte_t recv_current;	/* most recent rcvd id */
+
+	byte_t flags;
+#define SLF_TOSS	0x01	/* tossing rcvd frames until id received */
+
+	int32 sls_o_nontcp;	/* outbound non-TCP packets */
+	int32 sls_o_tcp;	/* outbound TCP packets */
+	int32 sls_o_uncompressed;	/* outbound uncompressed packets */
+	int32 sls_o_compressed;	/* outbound compressed packets */
+	int32 sls_o_searches;	/* searches for connection state */
+	int32 sls_o_misses;	/* times couldn't find conn. state */
+
+	int32 sls_i_uncompressed;	/* inbound uncompressed packets */
+	int32 sls_i_compressed;	/* inbound compressed packets */
+	int32 sls_i_error;	/* inbound error packets */
+	int32 sls_i_tossed;	/* inbound packets tossed because of error */
+
+	int32 sls_i_runt;
+	int32 sls_i_badcheck;
+};
+#define NULLSLCOMPR	(struct slcompress *)0
+
+#define __ARGS(x) x
+
+/* In slhc.c: */
+struct slcompress *slhc_init __ARGS((int rslots, int tslots));
+void slhc_free __ARGS((struct slcompress *comp));
+
+int slhc_compress __ARGS((struct slcompress *comp, unsigned char *icp,
+			  int isize, unsigned char *ocp, unsigned char **cpp,
+			  int compress_cid));
+int slhc_uncompress __ARGS((struct slcompress *comp, unsigned char *icp,
+			    int isize));
+int slhc_remember __ARGS((struct slcompress *comp, unsigned char *icp,
+			  int isize));
+int slhc_toss __ARGS((struct slcompress *comp));
+
+void slhc_i_status __ARGS((struct slcompress *comp));
+void slhc_o_status __ARGS((struct slcompress *comp));
+
+#endif	/* _SLHC_H */
diff -r -u -N linux.orig/arch/arm/drivers/net/slip.c linux.arm/arch/arm/drivers/net/slip.c
--- linux.orig/arch/arm/drivers/net/slip.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/slip.c	Fri Oct 27 23:15:03 1995
@@ -0,0 +1,1212 @@
+/*
+ * slip.c	This module implements the SLIP protocol for kernel-based
+ *		devices like TTY.  It interfaces between a raw TTY, and the
+ *		kernel's INET protocol layers (via DDI).
+ *
+ * Version:	@(#)slip.c	0.8.3	12/24/94
+ *
+ * Authors:	Laurence Culhane, <loz@holmes.demon.co.uk>
+ *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ *
+ * Fixes:
+ *		Alan Cox	: 	Sanity checks and avoid tx overruns.
+ *					Has a new sl->mtu field.
+ *		Alan Cox	: 	Found cause of overrun. ifconfig sl0 mtu upwards.
+ *					Driver now spots this and grows/shrinks its buffers(hack!).
+ *					Memory leak if you run out of memory setting up a slip driver fixed.
+ *		Matt Dillon	:	Printable slip (borrowed from NET2E)
+ *	Pauline Middelink	:	Slip driver fixes.
+ *		Alan Cox	:	Honours the old SL_COMPRESSED flag
+ *		Alan Cox	:	KISS AX.25 and AXUI IP support
+ *		Michael Riepe	:	Automatic CSLIP recognition added
+ *		Charles Hedrick :	CSLIP header length problem fix.
+ *		Alan Cox	:	Corrected non-IP cases of the above.
+ *		Alan Cox	:	Now uses hardware type as per FvK.
+ *		Alan Cox	:	Default to 192.168.0.0 (RFC 1597)
+ *		A.N.Kuznetsov	:	dev_tint() recursion fix.
+ *	Dmitry Gorodchanin	:	SLIP memory leaks
+ *      Dmitry Gorodchanin      :       Code cleanup. Reduce tty driver
+ *                                      buffering from 4096 to 256 bytes.
+ *                                      Improving SLIP response time.
+ *                                      CONFIG_SLIP_MODE_SLIP6.
+ *                                      ifconfig sl? up & down now works correctly.
+ *					Modularization.
+ *              Alan Cox        :       Oops - fix AX.25 buffer lengths
+ *      Dmitry Gorodchanin      :       Even more cleanups. Preserve CSLIP
+ *                                      statistics. Include CSLIP code only
+ *                                      if it really needed.
+ *		Alan Cox	:	Free slhc buffers in the right place.
+ *
+ *
+ *
+ *	FIXME:	This driver still makes some IP'ish assumptions. It should build cleanly KISS TNC only without
+ *	CONFIG_INET defined.
+ *      I hope now it is fixed ;)
+ */
+
+#define SL_CHECK_TRANSMIT
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+/* Undef this, if you don't need 6bit encapsulation code in the driver */
+#define CONFIG_SLIP_MODE_SLIP6
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#ifdef CONFIG_AX25
+#include "ax25.h"
+#endif
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include "slip.h"
+#ifdef CONFIG_INET
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include "slhc.h"
+#endif
+
+#ifdef MODULE
+#define SLIP_VERSION    "0.8.3-NET3.019-NEWTTY-MODULAR"
+#else
+#define	SLIP_VERSION	"0.8.3-NET3.019-NEWTTY"
+#endif
+
+
+static struct slip	sl_ctrl[SL_NRUNIT];
+static struct tty_ldisc	sl_ldisc;
+static int		already = 0;
+
+static int slip_esc(unsigned char *p, unsigned char *d, int len);
+static void slip_unesc(struct slip *sl, unsigned char c);
+#ifdef CONFIG_SLIP_MODE_SLIP6
+static int slip_esc6(unsigned char *p, unsigned char *d, int len);
+static void slip_unesc6(struct slip *sl, unsigned char c);
+#endif
+
+
+/* Find a free SLIP channel, and link in this `tty' line. */
+static inline struct slip *
+sl_alloc(void)
+{
+	struct slip *sl;
+	int i;
+
+	for (i = 0; i < SL_NRUNIT; i++) {
+		sl = &sl_ctrl[i];
+		if (!set_bit(SLF_INUSE, &sl->flags)) {
+			return sl;
+		}
+	}
+	return NULL;
+}
+
+
+/* Free a SLIP channel. */
+static inline void
+sl_free(struct slip *sl)
+{
+	/* Free all SLIP frame buffers. */
+	if (sl->rbuff)  {
+		kfree(sl->rbuff);
+	}
+	sl->rbuff = NULL;
+	if (sl->xbuff)  {
+		kfree(sl->xbuff);
+	}
+	sl->xbuff = NULL;
+#ifdef SL_INCLUDE_CSLIP
+	/* Save CSLIP statistics */
+	if (sl->slcomp)  {
+		sl->rx_compressed += sl->slcomp->sls_i_compressed;
+		sl->rx_dropped    += sl->slcomp->sls_i_tossed;
+		sl->tx_compressed += sl->slcomp->sls_o_compressed;
+		sl->tx_misses     += sl->slcomp->sls_o_misses;
+	}
+	if (sl->cbuff)  {
+		kfree(sl->cbuff);
+	}
+	sl->cbuff = NULL;
+	if(sl->slcomp)
+		slhc_free(sl->slcomp);
+	sl->slcomp = NULL;
+#endif
+
+	if (!clear_bit(SLF_INUSE, &sl->flags)) {
+		printk("%s: sl_free for already free unit.\n", sl->dev->name);
+	}
+}
+
+/* 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 void sl_changedmtu(struct slip *sl)
+{
+	struct device *dev = sl->dev;
+	unsigned char *xbuff, *rbuff, *oxbuff, *orbuff;
+#ifdef SL_INCLUDE_CSLIP
+	unsigned char *cbuff, *ocbuff;
+#endif
+	int len;
+	unsigned long flags;
+
+	len = dev->mtu * 2;
+/*
+ * allow for arrival of larger UDP packets, even if we say not to
+ * also fixes a bug in which SunOS sends 512-byte packets even with
+ * an MSS of 128
+ */
+	if (len < 576 * 2)  {
+		len = 576 * 2;
+	}
+
+	xbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
+	rbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
+#ifdef SL_INCLUDE_CSLIP
+	cbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
+#endif
+
+#ifdef SL_INCLUDE_CSLIP
+	if (xbuff == NULL || rbuff == NULL || cbuff == NULL)  {
+#else
+	if (xbuff == NULL || rbuff == NULL)  {
+#endif
+		printk("%s: unable to grow slip buffers, MTU change cancelled.\n",
+		       sl->dev->name);
+		dev->mtu = sl->mtu;
+		if (xbuff != NULL)  {
+			kfree(xbuff);
+		}
+		if (rbuff != NULL)  {
+			kfree(rbuff);
+		}
+#ifdef SL_INCLUDE_CSLIP
+		if (cbuff != NULL)  {
+			kfree(cbuff);
+		}
+#endif
+		return;
+	}
+
+	save_flags(flags); cli();
+
+	oxbuff    = sl->xbuff;
+	sl->xbuff = xbuff;
+	orbuff    = sl->rbuff;
+	sl->rbuff = rbuff;
+#ifdef SL_INCLUDE_CSLIP
+	ocbuff    = sl->cbuff;
+	sl->cbuff = cbuff;
+#endif
+	if (sl->xleft)  {
+		if (sl->xleft <= len)  {
+			memcpy(sl->xbuff, sl->xhead, sl->xleft);
+		} else  {
+			sl->xleft = 0;
+			sl->tx_dropped++;
+		}
+	}
+	sl->xhead = sl->xbuff;
+
+	if (sl->rcount)  {
+		if (sl->rcount <= len) {
+			memcpy(sl->rbuff, orbuff, sl->rcount);
+		} else  {
+			sl->rcount = 0;
+			sl->rx_over_errors++;
+			set_bit(SLF_ERROR, &sl->flags);
+		}
+	}
+#ifdef CONFIG_AX25
+	sl->mtu      = dev->mtu + 73;
+#else
+	sl->mtu      = dev->mtu;
+#endif
+	sl->buffsize = len;
+
+	restore_flags(flags);
+
+	if (oxbuff != NULL)   {
+		kfree(oxbuff);
+	}
+	if (orbuff != NULL)    {
+		kfree(orbuff);
+	}
+#ifdef SL_INCLUDE_CSLIP
+	if (ocbuff != NULL)  {
+		kfree(ocbuff);
+	}
+#endif
+}
+
+
+/* Set the "sending" flag.  This must be atomic, hence the ASM. */
+static inline void
+sl_lock(struct slip *sl)
+{
+	if (set_bit(0, (void *) &sl->dev->tbusy))  {
+		printk("%s: trying to lock already locked device!\n", sl->dev->name);
+        }
+}
+
+
+/* Clear the "sending" flag.  This must be atomic, hence the ASM. */
+static inline void
+sl_unlock(struct slip *sl)
+{
+	if (!clear_bit(0, (void *)&sl->dev->tbusy))  {
+		printk("%s: trying to unlock already unlocked device!\n", sl->dev->name);
+        }
+}
+
+/* Send one completely decapsulated IP datagram to the IP layer. */
+static void
+sl_bump(struct slip *sl)
+{
+	struct sk_buff *skb;
+	int count;
+
+	count = sl->rcount;
+#ifdef SL_INCLUDE_CSLIP
+	if (sl->mode & (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) {
+		unsigned char c;
+		if ((c = sl->rbuff[0]) & SL_TYPE_COMPRESSED_TCP) {
+			/* ignore compressed packets when CSLIP is off */
+			if (!(sl->mode & SL_MODE_CSLIP)) {
+				printk("%s: compressed packet ignored\n", sl->dev->name);
+				return;
+			}
+			/* make sure we've reserved enough space for uncompress to use */
+			if (count + 80 > sl->buffsize) {
+				sl->rx_over_errors++;
+				return;
+			}
+			count = slhc_uncompress(sl->slcomp, sl->rbuff, count);
+			if (count <= 0) {
+				return;
+			}
+		} else if (c >= SL_TYPE_UNCOMPRESSED_TCP) {
+			if (!(sl->mode & SL_MODE_CSLIP)) {
+				/* turn on header compression */
+				sl->mode |= SL_MODE_CSLIP;
+				sl->mode &= ~SL_MODE_ADAPTIVE;
+				printk("%s: header compression turned on\n", sl->dev->name);
+			}
+			sl->rbuff[0] &= 0x4f;
+			if (slhc_remember(sl->slcomp, sl->rbuff, count) <= 0) {
+				return;
+			}
+		}
+	}
+#endif  /* SL_INCLUDE_CSLIP */
+
+	skb = alloc_skb(count, GFP_ATOMIC);
+	if (skb == NULL)  {
+		printk("%s: memory squeeze, dropping packet.\n", sl->dev->name);
+		sl->rx_dropped++;
+		return;
+	}
+	skb->len = count;
+	skb->dev = sl->dev;
+	memcpy(skb->data, sl->rbuff, count);
+	netif_rx(skb);
+	sl->rx_packets++;
+}
+
+/* Encapsulate one IP datagram and stuff into a TTY queue. */
+static void
+sl_encaps(struct slip *sl, unsigned char *icp, int len)
+{
+	unsigned char *p;
+	int actual, count;
+
+
+#ifdef CONFIG_AX25
+	if (sl->mtu != sl->dev->mtu + 73) {	/* Someone has been ifconfigging */
+#else
+	if (sl->mtu != sl->dev->mtu) {	/* Someone has been ifconfigging */
+#endif
+		sl_changedmtu(sl);
+	}
+
+	if (len > sl->mtu) {		/* Sigh, shouldn't occur BUT ... */
+		len = sl->mtu;
+		printk ("%s: truncating oversized transmit packet!\n", sl->dev->name);
+		sl->tx_dropped++;
+		sl_unlock(sl);
+		return;
+	}
+
+	p = icp;
+#ifdef SL_INCLUDE_CSLIP
+	if (sl->mode & SL_MODE_CSLIP)  {
+		len = slhc_compress(sl->slcomp, p, len, sl->cbuff, &p, 1);
+	}
+#endif
+#ifdef CONFIG_SLIP_MODE_SLIP6
+	if(sl->mode & SL_MODE_SLIP6)
+		count = slip_esc6(p, (unsigned char *) sl->xbuff, len);
+	else
+#endif
+		count = slip_esc(p, (unsigned char *) sl->xbuff, len);
+
+	/* Order of next two lines is *very* important.
+	 * When we are sending a little amount of data,
+	 * the transfer may be completed inside driver.write()
+	 * routine, because it's running with interrupts enabled.
+	 * In this case we *never* got WRITE_WAKEUP event,
+	 * if we did not request it before write operation.
+	 *       14 Oct 1994  Dmitry Gorodchanin.
+	 */
+	sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+	actual = sl->tty->driver.write(sl->tty, 0, sl->xbuff, count);
+#ifdef SL_CHECK_TRANSMIT
+	sl->dev->trans_start = jiffies;
+#endif
+	sl->xleft = count - actual;
+	sl->xhead = sl->xbuff + actual;
+}
+
+/*
+ * Called by the driver when there's room for more data.  If we have
+ * more packets to send, we send them here.
+ */
+static void slip_write_wakeup(struct tty_struct *tty)
+{
+	int actual;
+	struct slip *sl = (struct slip *) tty->disc_data;
+
+	/* First make sure we're connected. */
+	if (!sl || sl->magic != SLIP_MAGIC || !sl->dev->start) {
+		return;
+	}
+
+	if (sl->xleft <= 0)  {
+		/* Now serial buffer is almost free & we can start
+		 * transmission of another packet */
+		sl->tx_packets++;
+		tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+		sl_unlock(sl);
+		mark_bh(NET_BH);
+		return;
+	}
+
+	actual = tty->driver.write(tty, 0, sl->xhead, sl->xleft);
+	sl->xleft -= actual;
+	sl->xhead += actual;
+}
+
+/* Encapsulate an IP datagram and kick it into a TTY queue. */
+static int
+sl_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct slip *sl = &sl_ctrl[dev->base_addr];
+
+	if (!dev->start)  {
+		printk("%s: xmit call when iface is down\n", dev->name);
+		return 1;
+	}
+	/*
+	 * If we are busy already- too bad.  We ought to be able
+	 * to queue things at this point, to allow for a little
+	 * frame buffer.  Oh well...
+	 * -----------------------------------------------------
+	 * I hate queues in SLIP driver. May be it's efficient,
+	 * but for me latency is more important. ;)
+	 * So, no queues !
+	 *        14 Oct 1994  Dmitry Gorodchanin.
+	 */
+	if (dev->tbusy) {
+		/* May be we must check transmitter timeout here ?
+		 *      14 Oct 1994 Dmitry Gorodchanin.
+		 */
+#ifdef SL_CHECK_TRANSMIT
+		if (jiffies - dev->trans_start  < 20 * HZ)  {
+			/* 20 sec timeout not reached */
+			return 1;
+		}
+		printk("%s: transmit timed out, %s?\n", dev->name,
+		       (sl->tty->driver.chars_in_buffer(sl->tty) || sl->xleft) ?
+		       "bad line quality" : "driver error");
+		sl->xleft = 0;
+		sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+		sl_unlock(sl);
+#else
+		return 1;
+#endif
+	}
+
+	/* We were not busy, so we are now... :-) */
+	if (skb != NULL) {
+		sl_lock(sl);
+		sl_encaps(sl, skb->data, skb->len);
+		dev_kfree_skb(skb, FREE_WRITE);
+	}
+	return 0;
+}
+
+
+/* Return the frame type ID.  This is normally IP but maybe be AX.25. */
+static unsigned short
+sl_type_trans (struct sk_buff *skb, struct device *dev)
+{
+#ifdef CONFIG_AX25
+	struct slip *sl = &sl_ctrl[dev->base_addr];
+
+	if (sl->mode & SL_MODE_AX25)  {
+		return htons(ETH_P_AX25);
+	}
+#endif
+	return htons(ETH_P_IP);
+}
+
+
+/* Fill in the MAC-level header. Not used by SLIP. */
+static int
+sl_header(unsigned char *buff, struct device *dev, unsigned short type,
+	  void *daddr, void *saddr, unsigned len, struct sk_buff *skb)
+{
+#ifdef CONFIG_AX25
+#ifdef CONFIG_INET
+	struct slip *sl = &sl_ctrl[dev->base_addr];
+
+	if ((sl->mode & SL_MODE_AX25) && type != htons(ETH_P_AX25))  {
+		return ax25_encapsulate(buff, dev, type, daddr, saddr, len, skb);
+	}
+#endif
+#endif
+	return 0;
+}
+
+
+/* Rebuild the MAC-level header.  Not used by SLIP. */
+static int
+sl_rebuild_header(void *buff, struct device *dev, unsigned long raddr,
+		  struct sk_buff *skb)
+{
+#ifdef CONFIG_AX25
+#ifdef CONFIG_INET
+	struct slip *sl = &sl_ctrl[dev->base_addr];
+
+	if (sl->mode & SL_MODE_AX25)  {
+		return ax25_rebuild_header(buff, dev, raddr, skb);
+	}
+#endif
+#endif
+	return 0;
+}
+
+
+/* Open the low-level part of the SLIP channel. Easy! */
+static int
+sl_open(struct device *dev)
+{
+	struct slip *sl = &sl_ctrl[dev->base_addr];
+	unsigned long len;
+
+	if (sl->tty == NULL) {
+		return -ENODEV;
+	}
+
+	/*
+	 * Allocate the SLIP frame buffers:
+	 *
+	 * rbuff	Receive buffer.
+	 * xbuff	Transmit buffer.
+	 * cbuff        Temporary compression buffer.
+	 */
+	len = dev->mtu * 2;
+	/*
+	 * allow for arrival of larger UDP packets, even if we say not to
+	 * also fixes a bug in which SunOS sends 512-byte packets even with
+	 * an MSS of 128
+	 */
+	if (len < 576 * 2)  {
+		len = 576 * 2;
+	}
+	sl->rbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
+	if (sl->rbuff == NULL)   {
+		goto norbuff;
+	}
+	sl->xbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
+	if (sl->xbuff == NULL)   {
+		goto noxbuff;
+	}
+#ifdef SL_INCLUDE_CSLIP
+	sl->cbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
+	if (sl->cbuff == NULL)   {
+		goto nocbuff;
+	}
+	sl->slcomp = slhc_init(16, 16);
+	if (sl->slcomp == NULL)  {
+		goto noslcomp;
+	}
+#endif
+
+#ifdef CONFIG_AX25
+	sl->mtu	     = dev->mtu + 73;
+#else
+	sl->mtu	     = dev->mtu;
+#endif
+	sl->buffsize = len;
+	sl->rcount   = 0;
+	sl->xleft    = 0;
+#ifdef CONFIG_SLIP_MODE_SLIP6
+	sl->xdata    = 0;
+	sl->xbits    = 0;
+#endif
+	sl->flags   &= (1 << SLF_INUSE);      /* Clear ESCAPE & ERROR flags */
+
+	/* Needed because address '0' is special */
+	if (dev->pa_addr == 0)  {
+		dev->pa_addr=ntohl(0xC0A80001);
+	}
+	dev->tbusy  = 0;
+/*	dev->flags |= IFF_UP; */
+	dev->start  = 1;
+
+	return 0;
+
+	/* Cleanup */
+#ifdef SL_INCLUDE_CSLIP
+noslcomp:
+	kfree(sl->cbuff);
+nocbuff:
+#endif
+	kfree(sl->xbuff);
+noxbuff:
+	kfree(sl->rbuff);
+norbuff:
+	return -ENOMEM;
+}
+
+
+/* Close the low-level part of the SLIP channel. Easy! */
+static int
+sl_close(struct device *dev)
+{
+	struct slip *sl = &sl_ctrl[dev->base_addr];
+
+	if (sl->tty == NULL) {
+		return -EBUSY;
+	}
+	sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+	dev->tbusy = 1;
+	dev->start = 0;
+	
+/*	dev->flags &= ~IFF_UP; */
+
+	return 0;
+}
+
+static int
+slip_receive_room(struct tty_struct *tty)
+{
+	return 65536;  /* We can handle an infinite amount of data. :-) */
+}
+
+/*
+ * Handle the 'receiver data ready' interrupt.
+ * This function is called by the 'tty_io' module in the kernel when
+ * a block of SLIP data has been received, which can now be decapsulated
+ * and sent on to some IP layer for further processing.
+ */
+static void
+slip_receive_buf(struct tty_struct *tty, unsigned char *cp, char *fp, int count)
+{
+	struct slip *sl = (struct slip *) tty->disc_data;
+
+	if (!sl || sl->magic != SLIP_MAGIC || !sl->dev->start)
+		return;
+
+	/*
+	 * Argh! mtu change time! - costs us the packet part received
+	 * at the change
+	 */
+#ifdef CONFIG_AX25
+	if (sl->mtu != sl->dev->mtu + 73)  {
+#else
+	if (sl->mtu != sl->dev->mtu)  {
+#endif
+		sl_changedmtu(sl);
+	}
+
+	/* Read the characters out of the buffer */
+	while (count--) {
+		if (fp && *fp++) {
+			if (!set_bit(SLF_ERROR, &sl->flags))  {
+				sl->rx_errors++;
+			}
+			cp++;
+			continue;
+		}
+#ifdef CONFIG_SLIP_MODE_SLIP6
+		if (sl->mode & SL_MODE_SLIP6)
+			slip_unesc6(sl, *cp++);
+		else
+#endif
+			slip_unesc(sl, *cp++);
+	}
+}
+
+/*
+ * Open the high-level part of the SLIP channel.
+ * This function is called by the TTY module when the
+ * SLIP line discipline is called for.  Because we are
+ * sure the tty line exists, we only have to link it to
+ * a free SLIP channel...
+ */
+static int
+slip_open(struct tty_struct *tty)
+{
+	struct slip *sl = (struct slip *) tty->disc_data;
+	int err;
+
+	/* First make sure we're not already connected. */
+	if (sl && sl->magic == SLIP_MAGIC) {
+		return -EEXIST;
+	}
+
+	/* OK.  Find a free SLIP channel to use. */
+	if ((sl = sl_alloc()) == NULL) {
+		return -ENFILE;
+	}
+
+	sl->tty = tty;
+	tty->disc_data = sl;
+	if (tty->driver.flush_buffer)  {
+		tty->driver.flush_buffer(tty);
+	}
+	if (tty->ldisc.flush_buffer)  {
+		tty->ldisc.flush_buffer(tty);
+	}
+
+	/* Restore default settings */
+	sl->mode      = SL_MODE_DEFAULT;
+	sl->dev->type = ARPHRD_SLIP + sl->mode;
+#ifdef CONFIG_AX25	
+	if (sl->dev->type == 260) {	/* KISS */
+		sl->dev->type = ARPHRD_AX25;
+	}
+#endif	
+	/* Perform the low-level SLIP initialization. */
+	if ((err = sl_open(sl->dev)))  {
+		return err;
+	}
+	
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+
+	/* Done.  We have linked the TTY line to a channel. */
+	return sl->dev->base_addr;
+}
+
+
+/*
+ * Close down a SLIP channel.
+ * This means flushing out any pending queues, and then restoring the
+ * TTY line discipline to what it was before it got hooked to SLIP
+ * (which usually is TTY again).
+ */
+static void
+slip_close(struct tty_struct *tty)
+{
+	struct slip *sl = (struct slip *) tty->disc_data;
+
+	/* First make sure we're connected. */
+	if (!sl || sl->magic != SLIP_MAGIC) {
+		return;
+	}
+
+	(void) dev_close(sl->dev);
+	
+	tty->disc_data = 0;
+	sl->tty = NULL;
+	sl_free(sl);
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+
+static struct enet_statistics *
+sl_get_stats(struct device *dev)
+{
+	static struct enet_statistics stats;
+	struct slip *sl = &sl_ctrl[dev->base_addr];
+#ifdef SL_INCLUDE_CSLIP
+	struct slcompress *comp;
+#endif
+
+	memset(&stats, 0, sizeof(struct enet_statistics));
+
+	stats.rx_packets     = sl->rx_packets;
+	stats.tx_packets     = sl->tx_packets;
+	stats.rx_dropped     = sl->rx_dropped;
+	stats.tx_dropped     = sl->tx_dropped;
+	stats.tx_errors      = sl->tx_errors;
+	stats.rx_errors      = sl->rx_errors;
+	stats.rx_over_errors = sl->rx_over_errors;
+#ifdef SL_INCLUDE_CSLIP
+	stats.rx_fifo_errors = sl->rx_compressed;
+	stats.tx_fifo_errors = sl->tx_compressed;
+	stats.collisions     = sl->tx_misses;
+	comp = sl->slcomp;
+	if (comp) {
+		stats.rx_fifo_errors += comp->sls_i_compressed;
+		stats.rx_dropped     += comp->sls_i_tossed;
+		stats.tx_fifo_errors += comp->sls_o_compressed;
+		stats.collisions     += comp->sls_o_misses;
+	}
+#endif /* CONFIG_INET */
+	return (&stats);
+}
+
+
+ /************************************************************************
+  *			STANDARD SLIP ENCAPSULATION		  	 *
+  ************************************************************************/
+
+int
+slip_esc(unsigned char *s, unsigned char *d, int len)
+{
+	unsigned char *ptr = d;
+	unsigned char c;
+
+	/*
+	 * Send an initial END character to flush out any
+	 * data that may have accumulated in the receiver
+	 * due to line noise.
+	 */
+
+	*ptr++ = END;
+
+	/*
+	 * For each byte in the packet, send the appropriate
+	 * character sequence, according to the SLIP protocol.
+	 */
+
+	while (len-- > 0) {
+		switch(c = *s++) {
+		 case END:
+			*ptr++ = ESC;
+			*ptr++ = ESC_END;
+			break;
+		 case ESC:
+			*ptr++ = ESC;
+			*ptr++ = ESC_ESC;
+			break;
+		 default:
+			*ptr++ = c;
+			break;
+		}
+	}
+	*ptr++ = END;
+	return (ptr - d);
+}
+
+static void
+slip_unesc(struct slip *sl, unsigned char s)
+{
+
+	switch(s) {
+	 case END:
+		if (!clear_bit(SLF_ERROR, &sl->flags) && (sl->rcount > 2))  {
+			sl_bump(sl);
+		}
+		clear_bit(SLF_ESCAPE, &sl->flags);
+		sl->rcount = 0;
+		return;
+
+	 case ESC:
+		set_bit(SLF_ESCAPE, &sl->flags);
+		return;
+	 case ESC_ESC:
+		if (clear_bit(SLF_ESCAPE, &sl->flags))  {
+			s = ESC;
+		}
+		break;
+	 case ESC_END:
+		if (clear_bit(SLF_ESCAPE, &sl->flags))  {
+			s = END;
+		}
+		break;
+	}
+	if (!test_bit(SLF_ERROR, &sl->flags))  {
+		if (sl->rcount < sl->buffsize)  {
+			sl->rbuff[sl->rcount++] = s;
+			return;
+		}
+		sl->rx_over_errors++;
+		set_bit(SLF_ERROR, &sl->flags);
+	}
+}
+
+
+#ifdef CONFIG_SLIP_MODE_SLIP6
+/************************************************************************
+ *			 6 BIT SLIP ENCAPSULATION			*
+ ************************************************************************/
+
+int
+slip_esc6(unsigned char *s, unsigned char *d, int len)
+{
+	unsigned char *ptr = d;
+	unsigned char c;
+	int i;
+	unsigned short v = 0;
+	short bits = 0;
+
+	/*
+	 * Send an initial END character to flush out any
+	 * data that may have accumulated in the receiver
+	 * due to line noise.
+	 */
+
+	*ptr++ = 0x70;
+
+	/*
+	 * Encode the packet into printable ascii characters
+	 */
+
+	for (i = 0; i < len; ++i) {
+		v = (v << 8) | s[i];
+		bits += 8;
+		while (bits >= 6) {
+			bits -= 6;
+			c = 0x30 + ((v >> bits) & 0x3F);
+			*ptr++ = c;
+		}
+	}
+	if (bits) {
+		c = 0x30 + ((v << (6 - bits)) & 0x3F);
+		*ptr++ = c;
+	}
+	*ptr++ = 0x70;
+	return ptr - d;
+}
+
+void
+slip_unesc6(struct slip *sl, unsigned char s)
+{
+	unsigned char c;
+
+	if (s == 0x70) {
+		if (!clear_bit(SLF_ERROR, &sl->flags) && (sl->rcount > 2))  {
+			sl_bump(sl);
+		}
+		sl->rcount = 0;
+		sl->xbits = 0;
+		sl->xdata = 0;
+ 	} else if (s >= 0x30 && s < 0x70) {
+		sl->xdata = (sl->xdata << 6) | ((s - 0x30) & 0x3F);
+		sl->xbits += 6;
+		if (sl->xbits >= 8) {
+			sl->xbits -= 8;
+			c = (unsigned char)(sl->xdata >> sl->xbits);
+			if (!test_bit(SLF_ERROR, &sl->flags))  {
+				if (sl->rcount < sl->buffsize)  {
+					sl->rbuff[sl->rcount++] = c;
+					return;
+				}
+				sl->rx_over_errors++;
+				set_bit(SLF_ERROR, &sl->flags);
+			}
+		}
+ 	}
+}
+#endif /* CONFIG_SLIP_MODE_SLIP6 */
+
+#ifdef CONFIG_AX25
+int
+sl_set_mac_address(struct device *dev, void *addr)
+{
+	int err;
+
+	err = verify_area(VERIFY_READ, addr, 7);
+	if (err)  {
+		return err;
+	}
+
+	memcpy_fromfs(dev->dev_addr, addr, 7);	/* addr is an AX.25 shifted ASCII mac address */
+
+	return 0;
+}
+
+static int
+sl_set_dev_mac_address(struct device *dev, void *addr)
+{
+	memcpy(dev->dev_addr, addr, 7);
+	return 0;
+}
+#endif /* CONFIG_AX25 */
+
+
+/* Perform I/O control on an active SLIP channel. */
+static int
+slip_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
+{
+	struct slip *sl = (struct slip *) tty->disc_data;
+	int err;
+	unsigned int tmp;
+
+	/* First make sure we're connected. */
+	if (!sl || sl->magic != SLIP_MAGIC) {
+		return -EINVAL;
+	}
+
+	switch(cmd) {
+	 case SIOCGIFNAME:
+		err = verify_area(VERIFY_WRITE, arg, 16);
+		if (err)  {
+			return -err;
+		}
+		memcpy_tofs(arg, sl->dev->name, strlen(sl->dev->name) + 1);
+		return 0;
+
+	case SIOCGIFENCAP:
+		err = verify_area(VERIFY_WRITE, arg, sizeof(long));
+		if (err)  {
+			return -err;
+		}
+		put_fs_long(sl->mode, (long *)arg);
+		return 0;
+
+	case SIOCSIFENCAP:
+		err = verify_area(VERIFY_READ, arg, sizeof(long));
+		if (err)  {
+			return -err;
+		}
+		tmp = get_fs_long((long *)arg);
+#ifndef SL_INCLUDE_CSLIP
+		if (tmp & (SL_MODE_CSLIP|SL_MODE_ADAPTIVE))  {
+			return -EINVAL;
+		}
+#else
+		if ((tmp & (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) ==
+		    (SL_MODE_ADAPTIVE | SL_MODE_CSLIP))  {
+			/* return -EINVAL; */
+			tmp &= ~SL_MODE_ADAPTIVE;
+		}
+#endif
+#ifndef CONFIG_SLIP_MODE_SLIP6
+		if (tmp & SL_MODE_SLIP6)  {
+			return -EINVAL;
+		}
+#endif
+#ifndef CONFIG_AX25
+		if (tmp & SL_MODE_AX25)  {
+			return -EINVAL;
+		}
+#else
+		if (tmp & SL_MODE_AX25)	{
+			sl->dev->addr_len=7;	/* sizeof an AX.25 addr */
+			sl->dev->hard_header_len=17;	/* We don't do digipeaters */
+		} else	{
+			sl->dev->addr_len=0;	/* No mac addr in slip mode */
+			sl->dev->hard_header_len=0;
+		}
+#endif
+		sl->mode = tmp;
+		sl->dev->type = ARPHRD_SLIP+sl->mode;
+#ifdef CONFIG_AX25		
+		if (sl->dev->type == 260)  {
+			sl->dev->type = ARPHRD_AX25;
+		}
+#endif		
+		return 0;
+
+	 case SIOCSIFHWADDR:
+#ifdef CONFIG_AX25
+		return sl_set_mac_address(sl->dev, arg);
+#else
+		return -EINVAL;
+#endif
+
+	/* Allow stty to read, but not set, the serial port */
+	case TCGETS:
+	case TCGETA:
+		return n_tty_ioctl(tty, (struct file *) file, cmd, (unsigned long) arg);
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+static int sl_open_dev(struct device *dev)
+{
+	struct slip *sl = &sl_ctrl[dev->base_addr];
+	if(sl->tty==NULL)
+		return -ENODEV;
+	return 0;
+}
+
+/* Initialize the SLIP driver.  Called by DDI. */
+int
+slip_init(struct device *dev)
+{
+	struct slip *sl = &sl_ctrl[dev->base_addr];
+	int i;
+#ifdef CONFIG_AX25
+	static char ax25_bcast[7] =
+		{'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1};
+	static char ax25_test[7] =
+		{'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1};
+#endif
+
+	if (already++ == 0) {
+		printk("SLIP: version %s (%d channels) %s\n",
+		       SLIP_VERSION, SL_NRUNIT,
+#ifdef CONFIG_SLIP_MODE_SLIP6
+		       "(6 bit encapsulation enabled)"
+#else
+		       ""
+#endif
+		       );
+#if defined(SL_INCLUDE_CSLIP) && !defined(MODULE)
+		printk("CSLIP: code copyright 1989 Regents of the University of California\n");
+#endif
+#ifdef CONFIG_AX25
+		printk("AX25: KISS encapsulation enabled\n");
+#endif
+		/* Fill in our LDISC request block. */
+		memset(&sl_ldisc, 0, sizeof(sl_ldisc));
+		sl_ldisc.magic	= TTY_LDISC_MAGIC;
+		sl_ldisc.flags	= 0;
+		sl_ldisc.open	= slip_open;
+		sl_ldisc.close	= slip_close;
+		sl_ldisc.read	= NULL;
+		sl_ldisc.write	= NULL;
+		sl_ldisc.ioctl	= (int (*)(struct tty_struct *, struct file *,
+					   unsigned int, unsigned long)) slip_ioctl;
+		sl_ldisc.select = NULL;
+		sl_ldisc.receive_buf = slip_receive_buf;
+		sl_ldisc.receive_room = slip_receive_room;
+		sl_ldisc.write_wakeup = slip_write_wakeup;
+		if ((i = tty_register_ldisc(N_SLIP, &sl_ldisc)) != 0)  {
+			printk("SLIP: can't register line discipline (err = %d)\n", i);
+		}
+	}
+
+	/* Set up the "SLIP Control Block". (And clear statistics) */
+	
+	memset(sl, 0, sizeof (struct slip));
+	sl->magic  = SLIP_MAGIC;
+	sl->dev	   = dev;
+	
+	/* Finish setting up the DEVICE info. */
+	dev->mtu		= SL_MTU;
+	dev->hard_start_xmit	= sl_xmit;
+	dev->open		= sl_open_dev;
+	dev->stop		= sl_close;
+	dev->hard_header	= sl_header;
+	dev->type_trans	        = sl_type_trans;
+	dev->get_stats	        = sl_get_stats;
+#ifdef HAVE_SET_MAC_ADDR
+#ifdef CONFIG_AX25
+	dev->set_mac_address    = sl_set_dev_mac_address;
+#endif
+#endif
+	dev->hard_header_len	= 0;
+	dev->addr_len		= 0;
+	dev->type		= ARPHRD_SLIP + SL_MODE_DEFAULT;
+#ifdef CONFIG_AX25
+	if (sl->dev->type == 260)  {
+		sl->dev->type = ARPHRD_AX25;
+	}
+	memcpy(dev->broadcast, ax25_bcast, 7); /* Only activated in AX.25 mode */
+	memcpy(dev->dev_addr, ax25_test, 7);   /*    ""      ""       ""    "" */
+#endif
+	dev->rebuild_header	= sl_rebuild_header;
+
+	for (i = 0; i < DEV_NUMBUFFS; i++)  {
+		skb_queue_head_init(&dev->buffs[i]);
+	}
+
+	/* New-style flags. */
+	dev->flags		= 0;
+	dev->family		= AF_INET;
+	dev->pa_addr		= 0;
+	dev->pa_brdaddr	        = 0;
+	dev->pa_mask		= 0;
+	dev->pa_alen		= sizeof(unsigned long);
+
+	return 0;
+}
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+
+static struct device dev_slip[SL_NRUNIT] =  {
+	{
+		"sl0",		/* slip */
+		0, 0, 0, 0,	/* memory */
+		0, 0,		/* base, irq */
+		0, 0, 0, NULL, slip_init,
+	},
+	{ "sl1" , 0, 0, 0, 0,  1, 0, 0, 0, 0, NULL, slip_init },
+	{ "sl2" , 0, 0, 0, 0,  2, 0, 0, 0, 0, NULL, slip_init },
+	{ "sl3" , 0, 0, 0, 0,  3, 0, 0, 0, 0, NULL, slip_init },
+#ifdef SL_SLIP_LOTS
+	{ "sl4" , 0, 0, 0, 0,  4, 0, 0, 0, 0, NULL, slip_init },
+	{ "sl5" , 0, 0, 0, 0,  5, 0, 0, 0, 0, NULL, slip_init },
+	{ "sl6" , 0, 0, 0, 0,  6, 0, 0, 0, 0, NULL, slip_init },
+	{ "sl7" , 0, 0, 0, 0,  7, 0, 0, 0, 0, NULL, slip_init },
+	{ "sl8" , 0, 0, 0, 0,  8, 0, 0, 0, 0, NULL, slip_init },
+	{ "sl9" , 0, 0, 0, 0,  9, 0, 0, 0, 0, NULL, slip_init },
+	{ "sl10", 0, 0, 0, 0, 10, 0, 0, 0, 0, NULL, slip_init },
+	{ "sl11", 0, 0, 0, 0, 11, 0, 0, 0, 0, NULL, slip_init },
+	{ "sl12", 0, 0, 0, 0, 12, 0, 0, 0, 0, NULL, slip_init },
+	{ "sl13", 0, 0, 0, 0, 13, 0, 0, 0, 0, NULL, slip_init },
+	{ "sl14", 0, 0, 0, 0, 14, 0, 0, 0, 0, NULL, slip_init },
+	{ "sl15", 0, 0, 0, 0, 15, 0, 0, 0, 0, NULL, slip_init },
+#endif /* SL_SLIP_LOTS */
+};
+
+int
+init_module(void)
+{
+	int err;
+	int i;
+
+	for (i = 0; i < SL_NRUNIT; i++)  {
+		if ((err = register_netdev(&dev_slip[i])))  {
+			if (err == -EEXIST)  {
+				printk("SLIP: devices already present. Module not loaded.\n");
+			}
+			return err;
+		}
+	}
+	return 0;
+}
+
+void
+cleanup_module(void)
+{
+	int i;
+
+	if (MOD_IN_USE)  {
+		printk("SLIP: device busy, remove delayed\n");
+		return;
+	}
+	for (i = 0; i < SL_NRUNIT; i++)  {
+		unregister_netdev(&dev_slip[i]);
+	}
+	if ((i = tty_register_ldisc(N_SLIP, NULL)))  {
+		printk("SLIP: can't unregister line discipline (err = %d)\n", i);
+	}
+	already = 0;
+}
+#endif /* MODULE */
diff -r -u -N linux.orig/arch/arm/drivers/net/slip.h linux.arm/arch/arm/drivers/net/slip.h
--- linux.orig/arch/arm/drivers/net/slip.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/net/slip.h	Fri Oct 27 23:15:02 1995
@@ -0,0 +1,108 @@
+/*
+ * slip.h	Define the SLIP device driver interface and constants.
+ *
+ * NOTE:	THIS FILE WILL BE MOVED TO THE LINUX INCLUDE DIRECTORY
+ *		AS SOON AS POSSIBLE!
+ *
+ * Version:	@(#)slip.h	1.2.0	03/28/93
+ *
+ * Fixes:
+ *		Alan Cox	: 	Added slip mtu field.
+ *		Matt Dillon	:	Printable slip (borrowed from net2e)
+ *		Alan Cox	:	Added SL_SLIP_LOTS
+ *	Dmitry Gorodchanin	:	A lot of changes in the 'struct slip'
+ *	Dmitry Gorodchanin	:	Added CSLIP statistics.
+ *
+ * Author:	Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ */
+#ifndef _LINUX_SLIP_H
+#define _LINUX_SLIP_H
+
+#include <linux/config.h>
+
+#if defined(CONFIG_INET) && defined(CONFIG_SLIP_COMPRESSED)
+# define SL_INCLUDE_CSLIP
+#endif
+
+#ifdef SL_INCLUDE_CSLIP
+# define SL_MODE_DEFAULT SL_MODE_ADAPTIVE
+#else
+# define SL_MODE_DEFAULT SL_MODE_SLIP
+#endif
+
+/* SLIP configuration. */
+#ifndef SL_SLIP_LOTS
+#define SL_NRUNIT	4		/* number of SLIP channels	*/
+#else
+#define SL_NRUNIT	16
+#endif
+#define SL_MTU		296		/* 296; I am used to 600- FvK	*/
+
+/* SLIP protocol characters. */
+#define END             0300		/* indicates end of frame	*/
+#define ESC             0333		/* indicates byte stuffing	*/
+#define ESC_END         0334		/* ESC ESC_END means END 'data'	*/
+#define ESC_ESC         0335		/* ESC ESC_ESC means ESC 'data'	*/
+
+
+struct slip {
+  int			magic;
+
+  /* Various fields. */
+  struct tty_struct	*tty;		/* ptr to TTY structure		*/
+  struct device		*dev;		/* easy for intr handling	*/
+#ifdef SL_INCLUDE_CSLIP
+  struct slcompress	*slcomp;	/* for header compression 	*/
+  unsigned char		*cbuff;		/* compression buffer		*/
+#endif
+
+  /* These are pointers to the malloc()ed frame buffers. */
+  unsigned char		*rbuff;		/* receiver buffer		*/
+  int                   rcount;         /* received chars counter       */
+  unsigned char		*xbuff;		/* transmitter buffer		*/
+  unsigned char         *xhead;         /* pointer to next byte to XMIT */
+  int                   xleft;          /* bytes left in XMIT queue     */
+
+  /* SLIP interface statistics. */
+  unsigned long		rx_packets;	/* inbound frames counter	*/
+  unsigned long         tx_packets;     /* outbound frames counter      */
+  unsigned long         rx_errors;      /* Parity, etc. errors          */
+  unsigned long         tx_errors;      /* Planned stuff                */
+  unsigned long         rx_dropped;     /* No memory for skb            */
+  unsigned long         tx_dropped;     /* When MTU change              */
+  unsigned long         rx_over_errors; /* Frame bigger then SLIP buf.  */
+#ifdef SL_INCLUDE_CSLIP
+  unsigned long		tx_compressed;
+  unsigned long		rx_compressed;
+  unsigned long		tx_misses;
+#endif
+  /* Detailed SLIP statistics. */
+
+  int			mtu;		/* Our mtu (to spot changes!)   */
+  int                   buffsize;       /* Max buffers sizes            */
+
+#ifdef CONFIG_SLIP_MODE_SLIP6
+  int			xdata, xbits;	/* 6 bit slip controls 		*/
+#endif
+
+  unsigned char		flags;		/* Flag values/ mode etc	*/
+#define SLF_INUSE	0		/* Channel in use               */
+#define SLF_ESCAPE	1               /* ESC received                 */
+#define SLF_ERROR	2               /* Parity, etc. error           */
+
+  unsigned char		mode;		/* SLIP mode			*/
+#define SL_MODE_SLIP	0
+#define SL_MODE_CSLIP	1
+#define SL_MODE_SLIP6	2		/* Matt Dillon's printable slip */
+#define SL_MODE_CSLIP6	(SL_MODE_SLIP6|SL_MODE_CSLIP)
+#define SL_MODE_AX25	4
+#define SL_MODE_ADAPTIVE 8
+};
+
+
+
+#define SLIP_MAGIC 0x5302
+
+extern int slip_init(struct device *dev);
+
+#endif	/* _LINUX_SLIP.H */
diff -r -u -N linux.orig/arch/arm/drivers/podules/dserport.c linux.arm/arch/arm/drivers/podules/dserport.c
--- linux.orig/arch/arm/drivers/podules/dserport.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/podules/dserport.c	Fri Oct 27 23:14:53 1995
@@ -0,0 +1,30 @@
+/*
+ * dser.c
+ *
+ * The serial port dual serial port card (Issue 2)
+ *
+ * Initialisation of kernel to use ports
+ */
+
+#include <linux/config.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/podules/podules.h>
+#include "podules.h"
+
+#define BASE_BAUD ( 3686400 / 16 )
+
+extern struct async_struct rs_table[];
+extern int dser_sport;
+
+void dser_init(struct fpod *fp, int type)
+{
+  rs_table[dser_sport].baud_base = BASE_BAUD;
+  rs_table[dser_sport].port      = (((int)fp->podaddr + 0x2000)>>2);
+  rs_table[dser_sport].irq       = fp->card+24;
+  dser_sport++;
+  rs_table[dser_sport].baud_base = BASE_BAUD;
+  rs_table[dser_sport].port      = (((int)fp->podaddr + 0x2020)>>2);
+  rs_table[dser_sport].irq       = fp->card+24;
+  dser_sport++;
+}
diff -r -u -N linux.orig/arch/arm/drivers/podules/eth.c linux.arm/arch/arm/drivers/podules/eth.c
--- linux.orig/arch/arm/drivers/podules/eth.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/podules/eth.c	Fri Oct 27 23:14:53 1995
@@ -0,0 +1,60 @@
+/*
+ * eth.c
+ *
+ * ethernet card podule functions
+ *
+ * Initialisation of kernel to use card
+ *
+ */
+
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/podules/podules.h>
+#include <linux/string.h>
+#include "podules.h"
+
+struct podcard ethercard[MAX_CARDS] = {{0,0,0,0,0},};
+extern unsigned long simple_strtoul(const char *,char **,unsigned int);
+
+void eth_init(struct fpod *fp, int type)
+{
+  int i;
+  char *s;
+  unsigned char d[6];
+  struct chunk_dir cd;
+
+  for(i=0; i<4; i++)
+  {
+    if(ethercard[i].addr==0)
+    {
+      ethercard[i].card = fp->card;
+      ethercard[i].addr = (int)fp->podaddr;
+      ethercard[i].irq  = fp->card+24;
+      ethercard[i].type = type;
+      ethercard[i].data = 0;
+      switch(type)
+      {
+        case 1:
+          if(podule_readchunk(&cd, 0xF5, 0, fp) && (s = strchr(cd.d.string, '(')))
+          {
+            int j;
+            for(j=0; j<6; j++)
+            {
+              d[j] = simple_strtoul(s+1, &s, 0x10);
+              if(*s != (j==5?')':':'))
+                break;
+            }
+            if(j == 6)
+            {
+              ethercard[i].data = podule_malloc(6);
+              memcpy(ethercard[i].data, d, 6);
+            }
+          }
+          else
+            ethercard[i].data = NULL;
+          break;
+      }
+      break;
+    }
+  }
+}
diff -r -u -N linux.orig/arch/arm/drivers/podules/mfm.c linux.arm/arch/arm/drivers/podules/mfm.c
--- linux.orig/arch/arm/drivers/podules/mfm.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/podules/mfm.c	Fri Oct 27 23:14:53 1995
@@ -0,0 +1,31 @@
+/*
+ * scsi.c
+ *
+ * ethernet card podule functions
+ *
+ * Initialisation of kernel to use card
+ *
+ */
+
+#include <linux/podules/podules.h>
+#include "podules.h"
+
+struct podcard mfmcard[MAX_CARDS] = { {0,0,0,0,0},};
+
+void mfm_pinit(struct fpod *fp, int type)
+{
+  int i;
+  for(i=0; i<4; i++)
+  {
+    if(mfmcard[i].addr==0)
+    {
+      mfmcard[i].card = fp->card;
+      mfmcard[i].addr = (((int)fp->podaddr) & 0x03EFF000) + 0x2000;
+      mfmcard[i].irq  = fp->card+24;
+      mfmcard[i].type = type;
+      fp->irqaddr = fp->podaddr + 0x3000;
+      fp->irqmask = 8;
+      break;
+    }
+  }
+}
diff -r -u -N linux.orig/arch/arm/drivers/podules/podlist.c linux.arm/arch/arm/drivers/podules/podlist.c
--- linux.orig/arch/arm/drivers/podules/podlist.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/podules/podlist.c	Fri Oct 27 23:14:53 1995
@@ -0,0 +1,34 @@
+/*
+ * Notes:
+ *  ecld is normally 0 since code can't handle non-extended eclds yet.
+ *  card identified by product & manufacturer codes.
+ *  type is a specific number identifing this card in a group of devices
+ *       or zero if you want all the drivers to probe this card.
+ *  /- - - - - - - - - - - - - - - - - - - - - - - - - - - -  Ecld of card (bit 1 must be 0)
+ *  |     /-------------------------------------------------- Product code
+ *  |     |       /- - - - - - - - - - - - - - - - - - - - -  Manufacturer code
+ *  |     |       |      /----------------------------------- Card initialisation
+ *  |     |       |      |           /- - - - - - - - - - - - Card IRQ handler
+ *  |     |       |      |           |         /------------- Card FIQ handler
+ *  |     |       |      |           |         |      / - - - Card type passed to init
+ *  |     |       |      |           |         |      |   /-- Card name
+ *  |     |       |      |           |         |      |   |
+ */
+ECARD(
+  0x00, 0x000B, 0x0000, mfm_pinit , pod_irq , none ,  0,
+  		"(HD63463)")
+ECARD(
+  0x00, 0x00A4, 0x0011, eth_init  , pod_irq , none ,  1,
+  		"(NQ8005)")
+ECARD(
+  0x00, 0x0003, 0x0000, eth_init  , pod_irq , none ,  2,
+  		"(i82586)")
+ECARD(
+  0x00, 0x0058, 0x0021, scsi_pinit, none    , none ,  1,
+  		"(NS5380)")
+ECARD(
+  0x00, 0x00A0, 0x003A, scsi_pinit, pod_irq , none ,  2,
+  		"(NS8490)")
+ECARD(
+  0x00, 0x00B9, 0x003F, dser_init , pod_irq , none ,  1,
+  		"(16550)")
diff -r -u -N linux.orig/arch/arm/drivers/podules/podules.c linux.arm/arch/arm/drivers/podules/podules.c
--- linux.orig/arch/arm/drivers/podules/podules.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/podules/podules.c	Fri Oct 27 23:14:53 1995
@@ -0,0 +1,302 @@
+/*
+ * podule.c
+ *
+ * Initialise podules
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/podules/podules.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include "podules.h"
+
+/* ---------------------------------------------------------------------------------------- */
+
+static struct fpod fpod[MAX_CARDS];
+
+#define ECARD(ecld, prod, man, init, irq, fiq, typ, name) \
+extern void init(struct fpod *fp, int type);\
+extern void irq(struct fpod *fp); \
+extern void fiq(struct fpod *fp);
+#include "podlist.c"
+#undef ECARD
+
+static struct podule
+{
+  char ecld;
+  int prod;
+  int manu;
+  void (*init)(struct fpod *fp, int type);
+  void (*irq)(struct fpod *fp);
+  void (*fiq)(struct fpod *fp);
+  int type;
+  char *name;
+} pods[] =
+{
+#define none NULL
+#define ECARD(ecld, prod, man, init, irq, fiq, type, name) \
+  { ecld, prod, man, init, irq, fiq, type, name },
+#include "podlist.c"
+  { 0x02, 0x0000, 0x0000, NULL, NULL, NULL, 0, NULL }
+};
+#undef ECARD
+
+
+/* ---------------------------------------------------------------------------------------- */
+
+static void not_supp(struct fpod *fp, int t)
+{
+  printk(" (not supported)");
+}
+
+static void pod_irq(struct fpod *fp)
+{
+  do_fast_IRQ(fp->card + 24);
+}
+
+/* ---------------------------------------------------------------------------------------- */
+
+static unsigned long kmem;
+static unsigned char have_expmask=0;
+static volatile unsigned char *expmask = (volatile unsigned char *)0x3360000;
+
+void *podule_malloc(int len)
+{
+  int r;
+
+  len = (len + 3) & ~3;
+
+  r = kmem;
+  kmem += len;
+  return (void *)r;
+}
+
+static void expansioncard_irq(void)
+{
+  char i;
+
+  for(i=0; i<MAX_CARDS; i++)
+  {
+    if(fpod[i].podptr && fpod[i].podptr->irq && (have_expmask==0 || (expmask[0] & 1<<i)))
+    {
+      if(fpod[i].irqmask)
+      {
+        if(fpod[i].irqaddr[0] & fpod[i].irqmask)
+          fpod[i].podptr->irq(&fpod[i]);
+        else
+          printk("Wild interrupt from podule %d\n", i);
+      }
+      else
+        fpod[i].podptr->irq(&fpod[i]);
+    }
+  }
+}
+#if 0
+static void expansioncard_fiq(void)
+{
+  char i;
+  for(i=0; i<MAX_CARDS; i++)
+  {
+    if(fpod[i].podptr && fpod[i].podptr->fiq)
+    {
+      if(fpod[i].fiqmask)
+      {
+        if(fpod[i].fiqaddr[0] & fpod[i].fiqmask)
+          fpod[i].podptr->fiq(fpod[i].podaddr,i);
+        else
+          printk("Wild fiq from podule %d\n",i);
+      }
+      else
+        fpod[i].podptr->fiq(fpod[i].podaddr,i);
+    }
+  }
+}
+#endif
+
+static void read_bytes(void *addr, struct fpod *fp, 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 || !fp->loader)
+  {
+    while(len--)
+      *a++ = fp->podaddr[(off++)*4];
+  }
+  else
+  {
+    while(len--)
+    { *(unsigned long *)0x108 = 0;
+      *a++ = read_loader(off++, fp->podaddr, fp->loader);
+    }
+  }
+}
+
+int podule_readchunk(struct chunk_dir *cd, int id, int num, struct fpod *fp)
+{
+  int index = 16;
+  int useld = 0;
+  while(1)
+  {
+    read_bytes((unsigned char *)cd, fp, index, 8, useld);
+    index += 8;
+    if(c_id(cd) == 0)
+    {
+      if(!useld && fp->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(!fp->loader)
+      {
+        fp->loader = podule_malloc(c_len(cd));
+        read_bytes(fp->loader, fp, (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:
+        read_bytes((unsigned char *)cd->d.string, fp, (int)c_start(cd), c_len(cd), useld);
+        break;
+
+      case 0x00:
+        break;
+    }
+  }
+  return 1;
+}
+
+static void find_cards(void)
+{
+  int i;
+  struct fpod *fp;
+
+  printk("Searching for expansion cards:");
+
+  for(i=0; i<MAX_CARDS; i++)
+  {
+    struct execld execld;
+    struct podule *pod;
+    struct chunk_dir cd;
+    fp = &fpod[i];
+
+    fp->podaddr = (unsigned char *)(0x033C0000 + 0x4000*i);
+
+    execld.r_ecld = 2;
+    read_bytes(&execld, fp, 0, 16, 0);
+
+    if(e_ecld(&execld) & 2)
+    {
+      have_expmask &= ~(1<<i); /* disable from expansion card */
+      continue;
+    }
+
+    fp->fiqaddr =
+    fp->irqaddr = fp->podaddr;
+    fp->irqmask = 1;
+    fp->fiqmask = 4;
+
+    for(pod = pods; !(pod->ecld & 2); pod++)
+    {
+      if(pod->ecld == (e_ecld(&execld) & 0xf8) && ((pod->ecld & 0x78) != 0 ||
+         pod->prod == e_prod(&execld) && pod->manu == e_manu(&execld)))
+      {
+        fp->podptr=pod;
+        if(e_is(&execld))
+        {
+          fp->fiqmask = e_fiqmask(&execld);
+          fp->fiqaddr = fp->podaddr + e_fiqaddr(&execld);
+          fp->irqmask = e_irqmask(&execld);
+          fp->irqaddr = fp->podaddr + e_irqaddr(&execld);
+        }
+        if(e_cd(&execld) && podule_readchunk(&cd, 0xf5, 0, fp))
+          printk("\n  %d: %s %s", i, cd.d.string, pod->name);
+        else
+          printk("\n  %d: %s", i, pod->name);
+        pod->init(fp, pod->type);
+        break;
+      }
+    }
+    if(pod->ecld & 2)
+    {
+      if(e_cd(&execld) && podule_readchunk(&cd, 0xf5, 0, fp))
+        printk("\n  %d: %s (not recognised)", i, cd.d.string);
+      else
+        printk("\n  %d: (not recognised)", i);
+    }
+  }
+  printk("\n");
+}
+
+unsigned long expansioncard_init(unsigned long kmem_start)
+{
+  int i;
+  kmem = (kmem_start+3) & ~3;
+
+  for(i=0; i<MAX_CARDS; i++)
+  {
+    memset(&fpod[i], 0, sizeof(struct fpod));
+    fpod[i].podaddr = (unsigned char *)(0x033C0000 + 0x4000*i);
+    fpod[i].card = i;
+  }
+
+  if(request_irq(IRQ_EXPANSIONCARD,(void (*)(int))expansioncard_irq,0,"expansion cards")!=0)
+  {
+    printk("Could not allocate expansion card IRQ\n");
+    return kmem;
+  }
+#if 0
+  if(request_irq(FIQ_EXPANSIONCARD+16,(void (*)(int))expansioncard_fiq,0,"expansion cards")!=0)
+  {
+    printk("Could not allocate expansion card FIQ\n");
+    free_irq(IRQ_EXPANSIONCARD);
+    return kmem;
+  }
+#endif
+  expmask[4] = 0x00;
+  expmask[0] = 0xff;
+  if(expmask[0] == 0xf0)
+  {
+    have_expmask = 0xff;
+    printk("Expansion card interrupt management hardware found\n");
+  }
+  expmask[4] = 0xff;
+
+  find_cards();
+
+  if(have_expmask)
+    expmask[4] = have_expmask;
+  return kmem;
+}
+
+struct podcard *get_card(struct podcard *pc, int *index, int type)
+{
+	for(; *index < MAX_CARDS; (*index)++)
+	{
+		if((pc[*index].type == 0 || pc[*index].type == type) && !pc[*index]->claimed)
+			return &pc[(*index)++];
+	}
+
+	return NULL;
+}
diff -r -u -N linux.orig/arch/arm/drivers/podules/podules.h linux.arm/arch/arm/drivers/podules/podules.h
--- linux.orig/arch/arm/drivers/podules/podules.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/podules/podules.h	Fri Oct 27 23:14:53 1995
@@ -0,0 +1,55 @@
+
+struct execld
+{
+  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)
+};
+
+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)
+};
+
+static struct fpod
+{
+  int card;
+  struct podule *podptr;
+  unsigned char *loader;
+  volatile unsigned char *podaddr;
+  volatile unsigned char *irqaddr;
+  volatile unsigned char *fiqaddr;
+  unsigned char irqmask;
+  unsigned char fiqmask;
+};
+
+/* read a chunk from a podule
+ * returns: 0 for failure
+ */
+int podule_readchunk(struct chunk_dir *cd, int id, int num, struct fpod *fp);
+void *podule_malloc(int len);
diff -r -u -N linux.orig/arch/arm/drivers/podules/scsi.c linux.arm/arch/arm/drivers/podules/scsi.c
--- linux.orig/arch/arm/drivers/podules/scsi.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/podules/scsi.c	Fri Oct 27 23:14:53 1995
@@ -0,0 +1,39 @@
+/*
+ * scsi.c
+ *
+ * ethernet card podule functions
+ *
+ * Initialisation of kernel to use card
+ *
+ */
+
+#include <linux/podules/podules.h>
+#include "podules.h"
+
+struct podcard scsicard[MAX_CARDS] = { {0,0,0,0,0},};
+
+void scsi_pinit(struct fpod *fp, int type)
+{
+  int i;
+  for(i=0; i<4; i++)
+  {
+    if(scsicard[i].addr==0)
+    {
+      scsicard[i].card = fp->card;
+      switch(type)
+      {
+        case 0:
+        case 1:
+          scsicard[i].addr = ((int)fp->podaddr) & 0x0300C000;
+          scsicard[i].irq  = 255;
+          break;
+        case 2:
+          scsicard[i].addr = (((int)fp->podaddr) & 0x0300C000)|0x00242000;
+          scsicard[i].irq  = fp->card+24;
+          break;
+      }
+      scsicard[i].type = type;
+      break;
+    }
+  }
+}
diff -r -u -N linux.orig/arch/arm/drivers/scsi/Makefile linux.arm/arch/arm/drivers/scsi/Makefile
--- linux.orig/arch/arm/drivers/scsi/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/Makefile	Fri Oct 27 23:15:17 1995
@@ -0,0 +1,120 @@
+
+# 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).
+#
+
+.c.s:
+	$(CC) $(CFLAGS) -S $<
+.s.o:
+	$(AS) -c -o $*.o $<
+.c.o:
+	$(CC) $(CFLAGS) -c $<
+
+ifeq (${CFLAGS},)
+CFLAGS = -D__KERNEL__ \
+	-Wall -Wstrict-prototypes -I. -I../../../../include \
+	-O2 -fomit-frame-pointer
+
+include ../../../../.config
+endif
+
+all: scsi.a
+
+SCSI_OBJS =
+SCSI_SRCS =
+SCSI_MODULE_OBJS =
+
+ifdef CONFIG_SCSI
+
+SCSI_OBJS := hosts.o scsi.o scsi_ioctl.o constants.o scsicam.o
+SCSI_SRCS := hosts.c scsi.c scsi_ioctl.c constants.c scsicam.c
+
+ifdef CONFIG_CHR_DEV_ST
+SCSI_OBJS := $(SCSI_OBJS) st.o
+SCSI_SRCS := $(SCSI_SRCS) st.c
+endif
+
+ifdef CONFIG_BLK_DEV_SD
+SCSI_OBJS := $(SCSI_OBJS) sd.o sd_ioctl.o
+SCSI_SRCS := $(SCSI_SRCS) sd.c sd_ioctl.c
+endif
+
+ifdef CONFIG_BLK_DEV_SR
+SCSI_OBJS := $(SCSI_OBJS) sr.o sr_ioctl.o
+SCSI_SRCS := $(SCSI_SRCS) sr.c sr_ioctl.c
+endif
+
+ifdef CONFIG_CHR_DEV_SG
+SCSI_OBJS := $(SCSI_OBJS) sg.o
+SCSI_SRCS := $(SCSI_SRCS) sg.c
+endif
+
+ifdef CONFIG_SCSI_CUMANA_1
+SCSI_OBJS := $(SCSI_OBJS) cumana_1.o
+SCSI_SRCS := $(SCSI_SRCS) cumana_1.c
+else
+SCSI_MODULE_OBJS := $(SCSI_MODULE_OBJS) cumana_1.o
+endif
+
+ifdef CONFIG_SCSI_ECOSCSI
+SCSI_OBJS := $(SCSI_OBJS) ecoscsi.o
+SCSI_SRCS := $(SCSI_SRCS) ecoscsi.c
+else
+SCSI_MODULE_OBJS := $(SCSI_MODULE_OBJS) ecoscsi.o
+endif
+
+ifdef CONFIG_SCSI_OAK1
+SCSI_OBJS := $(SCSI_OBJS) oak.o
+SCSI_SRCS := $(SCSI_SRCS) oak.c
+else
+SCSI_MODULE_OBJS := $(SCSI_MODULE_OBJS) oak-scsi.o
+
+oak-scsi.o: oak.o
+	ld -r -o oak-scsi.o oak.o `gcc --print-libgcc-file-name`
+
+endif
+
+ifdef CONFIG_SCSI_ACORNSCSI_3
+SCSI_OBJS := $(SCSI_OBJS) acornscsi.o acornscsi-io.o
+SCSI_SRCS := $(SCSI_SRCS) acornscsi.c acornscsi-io.S
+else
+SCSI_MODULE_OBJS := $(SCSI_MODULE_OBJS) acorn-scsi.o
+
+acorn-scsi.o: acornscsi.o acornscsi-io.o
+	ld -r -o acorn-scsi.o acornscsi.o acornscsi-io.o `gcc --print-libgcc-file-name`
+
+endif
+
+scsi.a: $(SCSI_OBJS)
+	rm -f scsi.a
+	$(AR) rcs scsi.a $(SCSI_OBJS)
+	sync
+
+modules: $(SCSI_MODULE_OBJS)
+	echo $(SCSI_MODULE_OBJS) > ../../../../modules/SCSI_MODULES
+	(cd ../../../../modules;for i in $(SCSI_MODULE_OBJS); do ln -sf ../arch/arm/drivers/scsi/$$i .; done)
+
+dep:
+	$(CPP) -M $(AHA152X) $(SCSI_SRCS) > .depend
+#	$(CPP) -M -DMODULE $(SCSI_MODULE_OBJS:.o=.c) >> .depend
+
+else
+
+scsi.a:
+	rm -f scsi.a
+	@echo No SCSI drivers configured
+	$(AR) rcs scsi.a
+
+dep:
+
+endif
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff -r -u -N linux.orig/arch/arm/drivers/scsi/NCR5380.c linux.arm/arch/arm/drivers/scsi/NCR5380.c
--- linux.orig/arch/arm/drivers/scsi/NCR5380.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/NCR5380.c	Fri Oct 27 23:15:11 1995
@@ -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 -r -u -N linux.orig/arch/arm/drivers/scsi/NCR5380.h linux.arm/arch/arm/drivers/scsi/NCR5380.h
--- linux.orig/arch/arm/drivers/scsi/NCR5380.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/NCR5380.h	Fri Oct 27 23:15:08 1995
@@ -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 -r -u -N linux.orig/arch/arm/drivers/scsi/acornscsi-io.S linux.arm/arch/arm/drivers/scsi/acornscsi-io.S
--- linux.orig/arch/arm/drivers/scsi/acornscsi-io.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/acornscsi-io.S	Fri Oct 27 23:15:23 1995
@@ -0,0 +1,82 @@
+@ 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
+		ldmia	r0!, {r3 - r10}
+		and	r3, r3, ip
+		orr	r3, r3, r4, lsl #16
+		and 	r4, r5, ip
+		orr	r4, r4, r6, lsl #16
+		and	r5, r7, ip
+		orr	r5, r5, r8, lsl #16
+		and	r6, r9, ip
+		orr	r6, r6, r10, lsl #16
+		stmia	r1!, {r3 - r6}
+		ldmia	r0!, {r3 - r10}
+		and	r3, r3, ip
+		orr	r3, r3, r4, lsl #16
+		and 	r4, r5, ip
+		orr	r4, r4, r6, lsl #16
+		and	r5, r7, ip
+		orr	r5, r5, r8, lsl #16
+		and	r6, r9, ip
+		orr	r6, r6, r10, lsl #16
+		stmia	r1!, {r3 - r6}
+		bne	acornscsi_in1lp
+		ldmea	fp, {r3 - r10, fp, sp, pc}^
+acornscsi_in16:	adds	r2, r2, #16
+		bmi	acornscsi_in8
+acornscsi_in16lp:
+		ldmia	r0!, {r3 - r10}
+		and	r3, r3, ip
+		orr	r3, r3, r4, lsl #16
+		and 	r4, r5, ip
+		orr	r4, r4, r6, lsl #16
+		and	r5, r7, ip
+		orr	r5, r5, r8, lsl #16
+		and	r6, r9, ip
+		orr	r6, r6, r10, lsl #16
+		stmia	r1!, {r3 - r6}
+		ldmeqea	fp, {r3 - r10, fp, sp, pc}^
+		sub	r2, r2, #16
+acornscsi_in8:	adds	r2, r2, #8
+		bmi	acornscsi_in4
+		ldmia	r0!, {r3 - r6}
+		and	r3, r3, ip
+		orr	r3, r3, r4, lsl #16
+		and	r4, r5, ip
+		orr	r4, r4, r6, lsl #16
+		stmia	r1!, {r3 - r4}
+		ldmeqea	fp, {r3 - r10, fp, sp, pc}^
+		sub	r2, r2, #8
+acornscsi_in4:	adds	r2, r2, #4
+		bmi	acornscsi_in2
+		ldmia	r0!, {r3, r4}
+		and	r3, r3, ip
+		orr	r3, r3, r4, lsl #16
+		str	r3, [r1], #4
+		ldmeqea	fp, {r3 - 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, {r3 - r10, fp, sp, pc}^
+
diff -r -u -N linux.orig/arch/arm/drivers/scsi/acornscsi.c linux.arm/arch/arm/drivers/scsi/acornscsi.c
--- linux.orig/arch/arm/drivers/scsi/acornscsi.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/acornscsi.c	Fri Oct 27 23:15:23 1995
@@ -0,0 +1,896 @@
+#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 "acornscsi.h"
+#include "constants.h"
+
+#include <asm/ecard.h>
+
+static int acornscsi_prods[] = { 0x0002 };
+static int acornscsi_manus[] = { 0x0000 };
+
+#define DEBUG_NO_WRITE	1
+#define DEBUG_QUEUES	2
+#define DEBUG_DMA	4
+#define DEBUG (DEBUG_NO_WRITE)
+
+#define POD_SPACE(x) ((x) + 0xd0000)
+
+extern void acornscsi_in (int port, char *buffer, int transfer);
+
+static inline void sbic_write(struct Scsi_Host *instance, int reg, int value)
+{/*printk("sbic_write: %02X = %02X\n", reg, 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)
+{/*printk("page_write: %02X\n", 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)
+{/*printk("dmac_write: %02X = %02X\n", reg, 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 void sbic_reset(struct Scsi_Host *instance)
+{
+    sbic_write(instance, OWNID, OWNID_EAF | instance->this_id);
+    sbic_write(instance, CMND, CMND_RESET);
+}
+
+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 struct Scsi_Host *first_instance = NULL;
+static Scsi_Host_Template *the_template = NULL;
+
+static void acornscsi_init (struct Scsi_Host *instance) {
+    struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *) instance->hostdata;
+    int i;
+
+    hostdata->aborted = 0;
+    hostdata->targets_present = 0;
+    hostdata->connected = NULL;
+    hostdata->connecting = NULL;
+    hostdata->issue_queue = NULL;
+    hostdata->disconnected_queue = NULL;
+    hostdata->page_reg = 0x40;
+    for (i = 0; i < 7; i++)
+	hostdata->busy[i] = 0;
+    page_write(instance, 0x40);
+
+    if (!the_template)
+    {
+	the_template = instance->hostt;
+	first_instance = instance;
+    }
+
+    /* setup chips - 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, DEVCON0_RQL | DEVCON0_EXW);
+    dmac_write(instance, DEVCON1, DEVCON1_BHLD);
+}
+
+/*
+ * 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.  acornscsi_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 acornscsi_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;
+
+static void acornscsi_main (void)
+{
+    Scsi_Cmnd *tmp, *prev;
+    struct Scsi_Host *instance;
+    struct acornscsi_hostdata *hostdata;
+    int done, i;
+
+    /*
+     * 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 acornscsi_hostdata *)instance->hostdata;
+	    cli();
+	    if (!hostdata->connected) {
+#if (DEBUG & DEBUG_QUEUES)
+		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)))
+		    {
+		    	int direction, this_len;
+			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();
+#if (DEBUG & DEBUG_QUEUES)
+			printk("scsi%d: main(): command for target %d lun %d"
+				" removed from issue queue\n", instance->host_no, tmp->target,
+				tmp->lun);
+#endif
+			hostdata->connected = tmp;
+			switch (tmp->cmnd[0])
+			{
+			case WRITE:
+			case WRITE_10:
+			    direction = 0;
+			    break;
+			default:
+			    direction = DESTID_DPD;
+			    break;
+			}
+			
+			sbic_write(instance, DESTID, direction | tmp->target);
+			sbic_write(instance, SOURCEID, SOURCEID_ER|SOURCEID_DSP);
+			sbic_write(instance, TARGETLUN, tmp->lun);
+			for (i = 0; i < tmp->cmd_len; i++)
+			    sbic_write(instance, 3+i, tmp->cmnd[i]);
+			sbic_write(instance, OWNID, tmp->cmd_len);
+
+			if (tmp->SCp.this_residual > 4096)
+			    this_len = 4096;
+			else
+			    this_len = tmp->SCp.this_residual;
+
+			tmp->SCp.have_data_in = this_len; /* our current length */
+			tmp->SCp.sent_command = 0;	  /* pointer into ram   */
+#if (DEBUG & DEBUG_DMA)
+			printk("total len: %08x this len: %08x\n", tmp->request_bufflen, this_len);
+#endif
+			sbic_write(instance, TRANSCNTH, (tmp->request_bufflen >> 16) & 0xff);
+			sbic_write(instance, TRANSCNTM, (tmp->request_bufflen >> 8) & 0xff);
+			sbic_write(instance, TRANSCNTL, tmp->request_bufflen & 0xff);
+			if (direction)
+			{
+			    this_len -= 1;
+			    dmac_write(instance, MODECON, MODECON_READ);
+			    dmac_write(instance, TXCNTLO, this_len & 0xff);
+			    dmac_write(instance, TXCNTHI, (this_len >> 8) & 0xff);
+			    dmac_write(instance, TXADRLO, 0);
+			    dmac_write(instance, TXADRMD, 0);
+			    dmac_write(instance, TXADRHI, 0);
+			    dmac_write(instance, MASKREG, MASKREG_M3|MASKREG_M2|MASKREG_M1);
+			}
+			sbic_write(instance, CMND, CMND_SELECTATNTRANSFER);
+			hostdata->busy[tmp->target] |= (1 << tmp->lun);
+		    }
+	    }
+	}
+    }
+    while (!done);
+    main_running = 0;
+}
+
+/*
+ * 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 void acornscsi_handletransfer (struct Scsi_Host *instance)
+{
+    struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
+    Scsi_Cmnd *cmd = (Scsi_Cmnd *)hostdata->connected;
+    int transferred, this_len, offset, page;
+    char *buffer;
+
+    dmac_write(instance, MASKREG, MASKREG_M3|MASKREG_M2|MASKREG_M1|MASKREG_M0);
+    dmac_clearintr(instance);
+
+    if (!cmd)
+    {
+    	printk("scsi%d: warning: dmac interrupt with no connected command\n", instance->host_no);
+    	return;
+    }
+
+    transferred = cmd->SCp.have_data_in;
+    offset = cmd->SCp.sent_command;
+    buffer = (char *)cmd->SCp.ptr;
+
+    cmd->SCp.this_residual -= transferred;
+    cmd->SCp.sent_command += transferred >> 1;
+    cmd->SCp.ptr += transferred;
+    
+    if (cmd->SCp.this_residual == 0)
+    { /* no more for this buffer - check for more buffers */
+	if (!cmd->SCp.buffers_residual)
+	{
+	    cmd->SCp.ptr = NULL;
+	    cmd->SCp.this_residual = 0;
+	}
+	else
+	{
+	    cmd->SCp.buffer++;
+	    cmd->SCp.buffers_residual--;
+	    cmd->SCp.ptr = (char *) cmd->SCp.buffer->address;
+	    cmd->SCp.this_residual = cmd->SCp.buffer->length;
+	}
+#if (DEBUG & DEBUG_DMA)
+    printk("new buffer len %08x, ", cmd->SCp.this_residual);
+#endif
+    }
+
+    if (cmd->SCp.this_residual > 4096)
+        this_len = 4096;
+    else
+        this_len = cmd->SCp.this_residual;
+#if (DEBUG & DEBUG_DMA)
+    printk("this_len: %08x, ", this_len);
+#endif
+    cmd->SCp.have_data_in = this_len;
+    this_len -= 1;
+    dmac_write (instance, TXCNTLO, this_len & 0xff);
+    dmac_write (instance, TXCNTHI, (this_len >> 8)  & 0xff);
+    dmac_write (instance, MASKREG, MASKREG_M3|MASKREG_M2|MASKREG_M1);
+#if (DEBUG & DEBUG_DMA)
+    printk("transferring %p, len %d\n", buffer, transferred);
+#endif
+    page = (offset >> 11) & 0x3f;
+    page_write (instance, hostdata->page_reg | page);
+    offset &= 2047;
+    while (transferred > 0)
+    {
+#if 1
+    	int this_trans;
+
+    	this_trans = (transferred < ((2048 - offset)<<1)) ? transferred : ((2048 - offset)<<1);
+    	acornscsi_in (instance->io_port + offset, buffer, this_trans);
+    	buffer += this_trans;
+    	offset = (offset + (this_trans>>1)) & 2047;
+    	transferred -= this_trans;
+    	if (offset == 0)
+    	{
+    		page = (page + 1) & 0x3f;
+    		page_write (instance, hostdata->page_reg | page);
+    	}
+#else
+	int v;
+	v = inw(instance->io_port + offset);
+	*buffer++ = v;
+	*buffer++ = v >> 8;
+	offset++;
+	if (offset > 2047)
+	{
+	    offset = 0;
+	    page = (page + 1) & 0x3f;
+	    page_write (instance, hostdata->page_reg | page);
+	}    
+	transferred -= 2;
+#endif
+    }
+/* now transfer buffer - to 'buffer', length 'transferred' */
+}
+
+static void acornscsi_transferresidual (struct Scsi_Host *instance, Scsi_Cmnd *cmd)
+{
+    struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
+    int residual;
+    int transferred;
+    int offset, page;
+    char *buffer;
+
+    residual = ((dmac_read(instance, TXCNTLO) | (dmac_read(instance, TXCNTHI) << 8)) + 1) & 0xffff;
+
+#if (DEBUG & DEBUG_DMA)
+    printk("scsi%d: residual %d\n", instance->host_no, residual);
+#endif
+
+    transferred = cmd->SCp.have_data_in - residual;
+    offset = cmd->SCp.sent_command;
+
+    cmd->SCp.this_residual -= transferred;
+    
+    buffer = cmd->SCp.ptr;
+    if (!buffer)
+	return;
+#if (DEBUG & DEBUG_DMA)
+    printk("scsi%d: transferring %p, len %d\n", instance->host_no, buffer, transferred);
+#endif
+    page = (offset >> 11) & 0x3f;
+    page_write (instance, hostdata->page_reg | page);
+    offset &= 2047;
+    while (transferred > 0)
+    {
+#if 1
+    	int this_trans;
+
+    	this_trans = (transferred < ((2048 - offset)<<1)) ? transferred : ((2048 - offset)<<1);
+    	acornscsi_in (instance->io_port + offset, buffer, this_trans);
+    	buffer += this_trans;
+    	offset = (offset + (this_trans>>1)) & 2047;
+    	transferred -= this_trans;
+    	if (offset == 0)
+    	{
+    		page = (page + 1) & 0x3f;
+    		page_write (instance, hostdata->page_reg | page);
+    	}
+#else
+    	int v;
+	v = inw(instance->io_port + off);
+	*buffer++ = v;
+	*buffer++ = v >> 8;
+	offset++;
+	if (offset > 2047)
+	{
+	    offset = 0;
+	    page = (page + 1) & 0x3f;
+	    page_write (instance, hostdata->page_reg | page);
+	}    
+	transferred -= 2;
+#endif
+    }
+}
+
+/*
+ * Function : acornscsi_runmain (void)
+ *
+ * Purpose  : insure that the coroutine is running and will process our
+ *            request.
+ */
+
+static inline void acornscsi_runmain (void) {
+    cli();
+    if (!main_running)
+    {
+	main_running = 1;
+	acornscsi_main();
+	sti();
+    } else
+	sti();
+}
+
+/*
+ * 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;
+    Scsi_Cmnd *tmp;
+
+#if (DEBUG & DEBUG_NO_WRITE)
+    switch (cmd->cmnd[0])
+    {
+    case WRITE:
+    case WRITE_10:
+	printk("scsi%d: WRITE attempted with NO_WRITE debugging flag set\n",
+	    cmd->host->host_no);
+	cmd->result = (DID_ERROR << 16);
+	done(cmd);
+	return 0;
+    }
+#endif
+
+    cmd->host_scribble = NULL;
+    cmd->scsi_done = done;
+    cmd->result = 0;
+
+    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;
+    }
+
+    /*
+     * Insert the cmd into the issue queue.  REQUEST SENSE commands
+     * are added to the head of the queue.
+     */
+
+    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 (DEBUG & DEBUG_QUEUES)
+    printk("scsi%d: command added to %s of queue\n", cmd->host->host_no,
+	(cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail");
+#endif
+    acornscsi_runmain();
+    return 0;
+}
+
+static void acornscsi_succompintr(struct Scsi_Host *instance, int asr, int ssr)
+{
+    struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
+    Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
+    
+    if (!cmd)
+    {
+	printk("scsi%d: bus phase but no command selected!\n", instance->host_no);
+	return;
+    }
+
+    switch (ssr & 0x0f)
+    {
+    case 0x00: /* reselect */
+	break;
+    case 0x01: /* select */
+	break;
+    case 0x03:
+	
+    case 0x04:
+    case 0x05:
+	break;
+    case 0x06: /* select & transfer ok */
+	cmd->SCp.Status = sbic_read(instance, TARGETLUN);
+	if (cmd->cmnd[0] != REQUEST_SENSE)
+	    cmd->result = cmd->SCp.Status;
+	else if (cmd->SCp.Status != GOOD)
+	    cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+	acornscsi_transferresidual(instance, cmd);
+	return;
+
+    case 0x08:
+    case 0x09:
+    case 0x0a:
+    case 0x0b:
+    case 0x0c:
+    case 0x0d:
+    case 0x0e:
+    case 0x0f:
+    	break;
+    }
+    return;
+}
+
+static void acornscsi_abortedinstr(struct Scsi_Host *instance, int asr, int ssr)
+{
+    struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
+    switch (ssr & 0x0f)
+    {
+    case 0x01: /* save data pointers has been issued */
+        sbic_write(instance, CMND, CMND_SELECTATNTRANSFER); /* resume */
+    	break;
+    }
+}
+
+static int acornscsi_terminatedintr(struct Scsi_Host *instance, int asr, int ssr)
+{
+    struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
+    Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
+    
+    if (!cmd)
+    {
+	printk("scsi%d: bus phase but no command selected!\n", instance->host_no);
+	return 0;
+    }
+
+    switch (ssr & 0x0f)
+    {
+    case 0x02: /* select timeout */
+    	hostdata->connected = NULL;
+	cmd->result = DID_BAD_TARGET << 16;
+	cmd->scsi_done (cmd);
+	hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+	return 0;
+
+    case 0x0B: /* unexpected status info */
+	sbic_write(instance, TRANSCNTH, 0);
+	sbic_write(instance, TRANSCNTM, 0);
+	sbic_write(instance, TRANSCNTL, 0);
+	dmac_write(instance, TXCNTHI, 0);
+	dmac_write(instance, TXCNTLO, 0);
+	sbic_write(instance, CMNDPHASE, 0x46);
+	sbic_write(instance, CMND, CMND_SELECTATNTRANSFER);
+	return 1;
+    }
+    return 1;
+}
+
+static int acornscsi_servreqintr(struct Scsi_Host *instance, int asr, int ssr)
+{
+    struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *)instance->hostdata;
+    Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected, *prev;
+    int sourceid, sourcelun, direction;
+
+    if (!cmd && ssr != 0x81 && ssr != 0x85)
+    {
+	printk("scsi%d: bus phase but no command selected!\n", instance->host_no);
+	return 0;
+    }
+
+    switch (ssr & 0x0f)
+    {
+    case 0x01: /* reselection occurred */
+	if (cmd)
+	{
+	    printk("scsi%d: reselection occurred with connected command ??\n",
+	        instance->host_no);
+	    return 1;
+	}
+	cli();
+	sourceid = sbic_read(instance, SOURCEID);
+	if (!(sourceid & 8))
+	{
+	    printk("scsi%d: invalid source id after reselection - chip/device fault?\n",
+	    	instance->host_no);
+	    return 1;
+	}
+	sourcelun = sbic_read(instance, DATA); /* should this be target lun or data? */
+	sourceid &= 7;
+	sourcelun &= 7;
+	for (cmd = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL;
+	    cmd; prev = cmd, cmd = (Scsi_Cmnd *) cmd->host_scribble)
+		if (cmd->target == sourceid && cmd->lun == sourcelun
+#ifdef SCSI2
+			&& cmd->tag == sourcetag
+#endif
+		) {
+		    if (prev)
+			prev->host_scribble = cmd->host_scribble;
+		    else
+			hostdata->disconnected_queue = (Scsi_Cmnd *) cmd->host_scribble;
+		    cmd->host_scribble = NULL;
+		    break;
+		}
+
+	if (!cmd) {
+#ifdef SCSI2
+	    printk("scsi%d: warning: target %d lun %d tag %d not in disconnect_queue.\n",
+		instance->host_no, sourceid, sourcelun, sourcetag);
+#else
+	    printk("scsi%d: warning: target %d lun %d not in disconnect_queue.\n",
+		instance->host_no, sourceid, sourcelun);
+#endif
+/*	    abort = 1;*/
+	    return 1;
+        }
+        hostdata->connected = cmd;
+#if (DEBUG & DEBUG_QUEUES)
+	printk ("scsi%d: command for target %d lun %d tag %d reconnected\n", instance->host_no,
+	    cmd->target, cmd->lun, cmd->tag);
+#endif
+        sti();
+	switch (cmd->cmnd[0])
+	{
+	case WRITE:
+	case WRITE_10:
+	    direction = 0;
+	    break;
+	default:
+	    direction = DESTID_DPD;
+	    break;
+	}
+
+	sbic_write(instance, DESTID, direction | cmd->target);
+	sbic_write(instance, SOURCEID, SOURCEID_ER|SOURCEID_DSP);
+	sbic_write(instance, TARGETLUN, cmd->lun);
+        sbic_write(instance, CMNDPHASE, 0x45); /* resume after reselection + transfer data */
+        sbic_write(instance, CMND, CMND_SELECTATNTRANSFER);
+        return 1;
+	
+    case 0x05: /* disconnection occurred */
+	if (sbic_read (instance, CMNDPHASE) != 0x60)
+	{
+	    cmd->device->disconnect = 1;
+	    cli();
+	    hostdata->connected = NULL;
+	    cmd->host_scribble = (char *)hostdata->disconnected_queue;
+	    hostdata->disconnected_queue = cmd;
+	    sti();
+#if (DEBUG & DEBUG_QUEUES)
+	    printk("scsi%d: command for target %d lun %d tag %d moved to disconnected queue\n",
+		instance->host_no, cmd->target, cmd->lun, cmd->tag);
+#endif
+	}
+	else
+	{
+	    hostdata->connected = NULL;
+    	    cmd->scsi_done (cmd);
+	    hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+	}
+    	return 0;
+
+    case 0x08: /* data out */
+    case 0x09: /* data in */
+    case 0x0a: /* command out */
+    case 0x0b: /* status */
+    case 0x0c: /* unspec info out */
+    case 0x0d: /* unspec info in */
+    case 0x0e: /* msg out */
+    case 0x0f: /* msg in */
+	break;
+    }
+    return 1;
+}
+
+static void acornscsi_intr(int irq, struct pt_regs *regs)
+{
+    struct Scsi_Host *instance;
+    int done=0;
+    int asr, ssr, s;
+
+
+    do
+    {
+	done = 1;
+	for (instance = first_instance; instance && (instance->hostt == the_template);
+	    instance = instance->next)
+	    if (instance->irq == irq)
+	    {
+	    	s = read_irqstat(instance);
+	    	if (s & 2)
+	    	    /* setup for next buffer & transfer current buffer */
+	    	    acornscsi_handletransfer(instance);
+
+	    	if (s & 8)
+	    	{
+		    asr = sbic_read(instance, ASR);
+		    ssr = sbic_read(instance, SSR);
+#if 0
+		    print_sbic_status(asr, ssr, sbic_read (instance, CMNDPHASE));
+#endif
+		    if(asr & ASR_INT)
+		    {
+			switch (ssr & 0xf0)
+			{
+			case 0x00: /* reset state interrupts */
+			    sbic_write(instance, CTRL,  CTRL_DMABURST | CTRL_IDI);
+			    sbic_write(instance, TIMEOUT, 10);
+			    sbic_write(instance, SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
+			    break;
+
+			case 0x10: /* sucessful conpletion interrupts */
+			    acornscsi_succompintr(instance, asr, ssr);
+			    done = 0;
+			    break;
+		        
+			case 0x20: /* paused or aborted interrupts */
+			    acornscsi_abortedinstr(instance, asr, ssr);
+			    break;
+
+			case 0x40: /* terminated interrupts */
+			    if (!acornscsi_terminatedintr(instance, asr, ssr))
+				done = 0;
+			    break;
+
+			case 0x80: /* service reqd interrupts */
+			    if (!acornscsi_servreqintr(instance, asr, ssr))
+				done = 0;
+			    break;
+			}
+		    }
+		}
+	    }
+	if (!done)
+	    acornscsi_runmain();
+    }
+    while(!done);
+}
+
+int acornscsi_abort(Scsi_Cmnd * cmd)
+{
+    struct Scsi_Host *instance = cmd->host;
+    struct acornscsi_hostdata *hostdata = (struct acornscsi_hostdata *) instance->hostdata;
+    Scsi_Cmnd *tmp, **prev;
+
+    printk("acornscsi_abort:\n");
+    print_sbic_status(sbic_read(instance, ASR), sbic_read(instance, SSR), sbic_read(instance, CMNDPHASE));
+
+    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 1
+	    printk("scsi%d: abort removed command from issue queue\n", instance->host_no);
+#endif
+	    tmp->scsi_done(tmp);
+	    return SCSI_ABORT_SUCCESS;
+	}
+    if (hostdata->connected == cmd)
+    {
+#if 0
+	sti();
+#if 1
+	printk("scsi%d: abort failed, command connected\n", instance->host_no);
+#endif
+	return SCSI_ABORT_SNOOZE;
+#else
+	cmd->result = DID_ABORT << 16;
+	printk("scsi%d: disconnecting\n", instance->host_no);
+	sbic_write(instance, CMND, CMND_DISCONNECT);
+	return SCSI_ABORT_PENDING;
+#endif
+    }
+printk("scsi%d: abort ???\n", instance->host_no);
+    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;
+    
+    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);
+    cmd->result = DID_RESET << 16;
+    cmd->scsi_done(cmd);
+
+    return SCSI_RESET_SUCCESS;
+}
+
+static struct expansion_card *ecs[4];
+
+int acornscsi_detect(Scsi_Host_Template * tpnt)
+{
+    int i, count = 0;
+    struct Scsi_Host *instance;
+
+    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);
+
+	printk("scsi%d: at port %X irq", instance->host_no, instance->io_port);
+	if (instance->irq == 0xff)
+	    printk ("s disabled");
+	else
+	    printk (" %d", instance->irq);
+	printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=1\n", CAN_QUEUE, CMD_PER_LUN);
+
+	++count;
+    }
+    return count;
+}
+
+const char *acornscsi_info(struct Scsi_Host *host)
+{
+    static const char string[]="";
+    return string;
+}
+
+#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 -r -u -N linux.orig/arch/arm/drivers/scsi/acornscsi.h linux.arm/arch/arm/drivers/scsi/acornscsi.h
--- linux.orig/arch/arm/drivers/scsi/acornscsi.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/acornscsi.h	Fri Oct 27 23:15:23 1995
@@ -0,0 +1,211 @@
+#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 *);
+
+#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 ACORNSCSI_3 \
+	{ NULL, NULL, "Acorn 16-bit SCSI", \
+	acornscsi_detect, \
+	NULL, \
+	acornscsi_info, \
+	NULL, \
+	acornscsi_queuecmd, \
+	acornscsi_abort, \
+	acornscsi_reset, \
+	NULL, \
+	NULL, \
+	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_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 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_READ		0x04
+#define MODECON_WRITE		0x08
+
+#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
+#endif
+
+struct acornscsi_hostdata
+{
+    unsigned char targets_present;
+    unsigned char page_reg;
+    volatile unsigned char busy[8];
+    volatile Scsi_Cmnd *connected;
+    volatile Scsi_Cmnd *connecting;
+    volatile Scsi_Cmnd *issue_queue;
+    volatile Scsi_Cmnd *disconnected_queue;
+    volatile unsigned aborted:1, aborting_connecting:1;
+};
+
+#endif /* ndef ASM */
+#endif /* ACORNSCSI_H */
diff -r -u -N linux.orig/arch/arm/drivers/scsi/constants.c linux.arm/arch/arm/drivers/scsi/constants.c
--- linux.orig/arch/arm/drivers/scsi/constants.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/constants.c	Fri Oct 27 23:15:17 1995
@@ -0,0 +1,557 @@
+/* 
+ * ASCII values for a number of symbolic constants, printing functions,
+ * etc.
+ */
+
+#include <linux/config.h>
+#include "../block/blk.h"
+#include <linux/kernel.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#define CONST_COMMAND 	0x01
+#define CONST_STATUS 	0x02
+#define CONST_SENSE 	0x04
+#define CONST_XSENSE 	0x08
+#define CONST_CMND	0x10
+#define CONST_MSG	0x20
+
+static const char unknown[] = "UNKNOWN";
+
+#ifdef CONFIG_SCSI_CONSTANTS
+#ifdef CONSTANTS
+#undef CONSTANTS
+#endif
+#define CONSTANTS (CONST_COMMAND | CONST_STATUS | CONST_SENSE | CONST_XSENSE | CONST_CMND | CONST_MSG)
+#endif
+
+#if (CONSTANTS & CONST_COMMAND)
+static const char * group_0_commands[] = {
+/* 00-03 */ "Test Unit Ready", "Rezero Unit", unknown, "Request Sense",
+/* 04-07 */ "Format Unit", "Read Block Limits", unknown, "Reasssign Blocks",
+/* 08-0d */ "Read (6)", unknown, "Write (6)", "Seek (6)", unknown, unknown,
+/* 0e-12 */ unknown, "Read Reverse", "Write Filemarks", "Space", "Inquiry",  
+/* 13-16 */ unknown, "Recover Buffered Data", "Mode Select", "Reserve",
+/* 17-1b */ "Release", "Copy", "Erase", "Mode Sense", "Start/Stop Unit",
+/* 1c-1d */ "Receive Diagnostic", "Send Diagnostic", 
+/* 1e-1f */ "Prevent/Allow Medium Removal", unknown,
+};
+
+
+static const char *group_1_commands[] = {
+/* 20-22 */  unknown, unknown, unknown,
+/* 23-28 */ unknown, unknown, "Read Capacity", unknown, unknown, "Read (10)", 
+/* 29-2d */ unknown, "Write (10)", "Seek (10)", unknown, unknown, 
+/* 2e-31 */ "Write Verify","Verify", "Search High", "Search Equal", 
+/* 32-34 */ "Search Low", "Set Limits", "Prefetch or Read Position", 
+/* 35-37 */ "Synchronize Cache","Lock/Unlock Cache", "Read Defect Data", 
+/* 38-3c */ unknown, "Compare","Copy Verify", "Write Buffer", "Read Buffer", 
+/* 3d-39 */ unknown, "Read Long",  unknown,
+};
+
+
+static const char *group_2_commands[] = {
+/* 40-41 */ "Change Definition", unknown, 
+/* 42-48 */ unknown, unknown, unknown, unknown, unknown, unknown, unknown,
+/* 49-4f */ unknown, unknown, unknown, "Log Select", "Log Sense", unknown,
+/* 50-55 */ unknown, unknown, unknown, unknown, unknown, "Mode Select (10)",
+/* 56-5b */ unknown, unknown, unknown, unknown, "Mode Sense (10)", unknown,
+/* 5c-5f */ unknown, unknown, unknown,
+};
+
+
+
+#define group(opcode) (((opcode) >> 5) & 7)
+
+#define RESERVED_GROUP  0
+#define VENDOR_GROUP 	1
+#define NOTEXT_GROUP	2
+
+static const char **commands[] = {
+group_0_commands, group_1_commands, group_2_commands, 
+(const char **) RESERVED_GROUP, (const char **) RESERVED_GROUP, 
+(const char **) NOTEXT_GROUP, (const char **) VENDOR_GROUP, 
+(const char **) VENDOR_GROUP};
+
+static const char reserved[] = "RESERVED";
+static const char vendor[] = "VENDOR SPECIFIC";
+
+static void print_opcode(int opcode) {
+  const char **table = commands[ group(opcode) ];
+  switch ((unsigned long) table) {
+  case RESERVED_GROUP:
+  	printk("%s(0x%02x) ", reserved, opcode); 
+  	break;
+  case NOTEXT_GROUP:
+  	printk("%s(0x%02x) ", unknown, opcode); 
+  	break;
+  case VENDOR_GROUP:
+  	printk("%s(0x%02x) ", vendor, opcode); 
+  	break;
+  default:
+  	printk("%s ",table[opcode & 0x1f]);
+  }
+}
+#else /* CONST & CONST_COMMAND */
+static void print_opcode(int opcode) {
+  printk("0x%02x ", opcode);
+}
+#endif  
+
+void print_command (unsigned char *command) {
+  int i,s;
+  print_opcode(command[0]);
+  for ( i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i) 
+  	printk("%02x ", command[i]);
+  printk("\n");
+}
+
+#if (CONSTANTS & CONST_STATUS)
+static const char * statuses[] = {
+/* 0-4 */ "Good", "Check Condition", "Condition Good", unknown, "Busy", 
+/* 5-9 */ unknown, unknown, unknown, "Intermediate Good", unknown, 
+/* a-d */ "Intermediate Good", unknown, "Reservation Conflict", unknown,
+/* e-f */ unknown, unknown,
+};
+#endif
+
+void print_status (int status) {
+  status = (status >> 1) & 0xf;
+#if (CONSTANTS & CONST_STATUS)
+  printk("%s ",statuses[status]);
+#else
+  printk("0x%0x ", status); 
+#endif 
+}
+
+#if (CONSTANTS & CONST_XSENSE)
+#define D 0x001  /* DIRECT ACCESS DEVICE (disk) */
+#define T 0x002  /* SEQUENTIAL ACCESS DEVICE (tape) */
+#define L 0x004  /* PRINTER DEVICE */
+#define P 0x008  /* PROCESSOR DEVICE */
+#define W 0x010  /* WRITE ONCE READ MULTIPLE DEVICE */
+#define R 0x020  /* READ ONLY (CD-ROM) DEVICE */
+#define S 0x040  /* SCANNER DEVICE */
+#define O 0x080  /* OPTICAL MEMORY DEVICE */
+#define M 0x100  /* MEDIA CHANGER DEVICE */
+#define C 0x200  /* COMMUNICATION DEVICE */
+
+struct error_info{
+  unsigned char code1, code2;
+  unsigned short int devices;
+  char * text;
+};
+
+struct error_info2{
+  unsigned char code1, code2_min, code2_max;
+  unsigned short int devices;
+  char * text;
+};
+
+static struct error_info2 additional2[] =
+{
+  {0x40,0x00,0x7f,D,"Ram failure (%x)"},
+  {0x40,0x80,0xff,D|T|L|P|W|R|S|O|M|C,"Diagnostic failure on component (%x)"},
+  {0x41,0x00,0xff,D,"Data path failure (%x)"},
+  {0x42,0x00,0xff,D,"Power-on or self-test failure (%x)"},
+  {0, 0, 0, 0, NULL}
+};
+
+static struct error_info additional[] =
+{
+  {0x00,0x01,T,"Filemark detected"},
+  {0x00,0x02,T|S,"End-of-partition/medium detected"},
+  {0x00,0x03,T,"Setmark detected"},
+  {0x00,0x04,T|S,"Beginning-of-partition/medium detected"},
+  {0x00,0x05,T|S,"End-of-data detected"},
+  {0x00,0x06,D|T|L|P|W|R|S|O|M|C,"I/O process terminated"},
+  {0x00,0x11,R,"Audio play operation in progress"},
+  {0x00,0x12,R,"Audio play operation paused"},
+  {0x00,0x13,R,"Audio play operation successfully completed"},
+  {0x00,0x14,R,"Audio play operation stopped due to error"},
+  {0x00,0x15,R,"No current audio status to return"},
+  {0x01,0x00,D|W|O,"No index/sector signal"},
+  {0x02,0x00,D|W|R|O|M,"No seek complete"},
+  {0x03,0x00,D|T|L|W|S|O,"Peripheral device write fault"},
+  {0x03,0x01,T,"No write current"},
+  {0x03,0x02,T,"Excessive write errors"},
+  {0x04,0x00,D|T|L|P|W|R|S|O|M|C,
+     "Logical unit not ready, cause not reportable"},
+  {0x04,0x01,D|T|L|P|W|R|S|O|M|C,
+     "Logical unit is in process of becoming ready"},
+  {0x04,0x02,D|T|L|P|W|R|S|O|M|C,
+     "Logical unit not ready, initializing command required"},
+  {0x04,0x03,D|T|L|P|W|R|S|O|M|C,
+     "Logical unit not ready, manual intervention required"},
+  {0x04,0x04,D|T|L|O,"Logical unit not ready, format in progress"},
+  {0x05,0x00,D|T|L|W|R|S|O|M|C,"Logical unit does not respond to selection"},
+  {0x06,0x00,D|W|R|O|M,"No reference position found"},
+  {0x07,0x00,D|T|L|W|R|S|O|M,"Multiple peripheral devices selected"},
+  {0x08,0x00,D|T|L|W|R|S|O|M|C,"Logical unit communication failure"},
+  {0x08,0x01,D|T|L|W|R|S|O|M|C,"Logical unit communication time-out"},
+  {0x08,0x02,D|T|L|W|R|S|O|M|C,"Logical unit communication parity error"},
+  {0x09,0x00,D|T|W|R|O,"Track following error"},
+  {0x09,0x01,W|R|O,"Tracking servo failure"},
+  {0x09,0x02,W|R|O,"Focus servo failure"},
+  {0x09,0x03,W|R|O,"Spindle servo failure"},
+  {0x0A,0x00,D|T|L|P|W|R|S|O|M|C,"Error log overflow"},
+  {0x0C,0x00,T|S,"Write error"},
+  {0x0C,0x01,D|W|O,"Write error recovered with auto reallocation"},
+  {0x0C,0x02,D|W|O,"Write error - auto reallocation failed"},
+  {0x10,0x00,D|W|O,"Id crc or ecc error"},
+  {0x11,0x00,D|T|W|R|S|O,"Unrecovered read error"},
+  {0x11,0x01,D|T|W|S|O,"Read retries exhausted"},
+  {0x11,0x02,D|T|W|S|O,"Error too long to correct"},
+  {0x11,0x03,D|T|W|S|O,"Multiple read errors"},
+  {0x11,0x04,D|W|O,"Unrecovered read error - auto reallocate failed"},
+  {0x11,0x05,W|R|O,"L-ec uncorrectable error"},
+  {0x11,0x06,W|R|O,"Circ unrecovered error"},
+  {0x11,0x07,W|O,"Data resynchronization error"},
+  {0x11,0x08,T,"Incomplete block read"},
+  {0x11,0x09,T,"No gap found"},
+  {0x11,0x0A,D|T|O,"Miscorrected error"},
+  {0x11,0x0B,D|W|O,"Unrecovered read error - recommend reassignment"},
+  {0x11,0x0C,D|W|O,"Unrecovered read error - recommend rewrite the data"},
+  {0x12,0x00,D|W|O,"Address mark not found for id field"},
+  {0x13,0x00,D|W|O,"Address mark not found for data field"},
+  {0x14,0x00,D|T|L|W|R|S|O,"Recorded entity not found"},
+  {0x14,0x01,D|T|W|R|O,"Record not found"},
+  {0x14,0x02,T,"Filemark or setmark not found"},
+  {0x14,0x03,T,"End-of-data not found"},
+  {0x14,0x04,T,"Block sequence error"},
+  {0x15,0x00,D|T|L|W|R|S|O|M,"Random positioning error"},
+  {0x15,0x01,D|T|L|W|R|S|O|M,"Mechanical positioning error"},
+  {0x15,0x02,D|T|W|R|O,"Positioning error detected by read of medium"},
+  {0x16,0x00,D|W|O,"Data synchronization mark error"},
+  {0x17,0x00,D|T|W|R|S|O,"Recovered data with no error correction applied"},
+  {0x17,0x01,D|T|W|R|S|O,"Recovered data with retries"},
+  {0x17,0x02,D|T|W|R|O,"Recovered data with positive head offset"},
+  {0x17,0x03,D|T|W|R|O,"Recovered data with negative head offset"},
+  {0x17,0x04,W|R|O,"Recovered data with retries and/or circ applied"},
+  {0x17,0x05,D|W|R|O,"Recovered data using previous sector id"},
+  {0x17,0x06,D|W|O,"Recovered data without ecc - data auto-reallocated"},
+  {0x17,0x07,D|W|O,"Recovered data without ecc - recommend reassignment"},
+  {0x18,0x00,D|T|W|R|O,"Recovered data with error correction applied"},
+  {0x18,0x01,D|W|R|O,"Recovered data with error correction and retries applied"},
+  {0x18,0x02,D|W|R|O,"Recovered data - data auto-reallocated"},
+  {0x18,0x03,R,"Recovered data with circ"},
+  {0x18,0x04,R,"Recovered data with lec"},
+  {0x18,0x05,D|W|R|O,"Recovered data - recommend reassignment"},
+  {0x19,0x00,D|O,"Defect list error"},
+  {0x19,0x01,D|O,"Defect list not available"},
+  {0x19,0x02,D|O,"Defect list error in primary list"},
+  {0x19,0x03,D|O,"Defect list error in grown list"},
+  {0x1A,0x00,D|T|L|P|W|R|S|O|M|C,"Parameter list length error"},
+  {0x1B,0x00,D|T|L|P|W|R|S|O|M|C,"Synchronous data transfer error"},
+  {0x1C,0x00,D|O,"Defect list not found"},
+  {0x1C,0x01,D|O,"Primary defect list not found"},
+  {0x1C,0x02,D|O,"Grown defect list not found"},
+  {0x1D,0x00,D|W|O,"Miscompare during verify operation"},
+  {0x1E,0x00,D|W|O,"Recovered id with ecc correction"},
+  {0x20,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid command operation code"},
+  {0x21,0x00,D|T|W|R|O|M,"Logical block address out of range"},
+  {0x21,0x01,M,"Invalid element address"},
+  {0x22,0x00,D,"Illegal function (should use 20 00, 24 00, or 26 00)"},
+  {0x24,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in cdb"},
+  {0x25,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit not supported"},
+  {0x26,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in parameter list"},
+  {0x26,0x01,D|T|L|P|W|R|S|O|M|C,"Parameter not supported"},
+  {0x26,0x02,D|T|L|P|W|R|S|O|M|C,"Parameter value invalid"},
+  {0x26,0x03,D|T|L|P|W|R|S|O|M|C,"Threshold parameters not supported"},
+  {0x27,0x00,D|T|W|O,"Write protected"},
+  {0x28,0x00,D|T|L|P|W|R|S|O|M|C,"Not ready to ready transition (medium may have changed)"},
+  {0x28,0x01,M,"Import or export element accessed"},
+  {0x29,0x00,D|T|L|P|W|R|S|O|M|C,"Power on, reset, or bus device reset occurred"},
+  {0x2A,0x00,D|T|L|W|R|S|O|M|C,"Parameters changed"},
+  {0x2A,0x01,D|T|L|W|R|S|O|M|C,"Mode parameters changed"},
+  {0x2A,0x02,D|T|L|W|R|S|O|M|C,"Log parameters changed"},
+  {0x2B,0x00,D|T|L|P|W|R|S|O|C,"Copy cannot execute since host cannot disconnect"},
+  {0x2C,0x00,D|T|L|P|W|R|S|O|M|C,"Command sequence error"},
+  {0x2C,0x01,S,"Too many windows specified"},
+  {0x2C,0x02,S,"Invalid combination of windows specified"},
+  {0x2D,0x00,T,"Overwrite error on update in place"},
+  {0x2F,0x00,D|T|L|P|W|R|S|O|M|C,"Commands cleared by another initiator"},
+  {0x30,0x00,D|T|W|R|O|M,"Incompatible medium installed"},
+  {0x30,0x01,D|T|W|R|O,"Cannot read medium - unknown format"},
+  {0x30,0x02,D|T|W|R|O,"Cannot read medium - incompatible format"},
+  {0x30,0x03,D|T,"Cleaning cartridge installed"},
+  {0x31,0x00,D|T|W|O,"Medium format corrupted"},
+  {0x31,0x01,D|L|O,"Format command failed"},
+  {0x32,0x00,D|W|O,"No defect spare location available"},
+  {0x32,0x01,D|W|O,"Defect list update failure"},
+  {0x33,0x00,T,"Tape length error"},
+  {0x36,0x00,L,"Ribbon, ink, or toner failure"},
+  {0x37,0x00,D|T|L|W|R|S|O|M|C,"Rounded parameter"},
+  {0x39,0x00,D|T|L|W|R|S|O|M|C,"Saving parameters not supported"},
+  {0x3A,0x00,D|T|L|W|R|S|O|M,"Medium not present"},
+  {0x3B,0x00,T|L,"Sequential positioning error"},
+  {0x3B,0x01,T,"Tape position error at beginning-of-medium"},
+  {0x3B,0x02,T,"Tape position error at end-of-medium"},
+  {0x3B,0x03,L,"Tape or electronic vertical forms unit not ready"},
+  {0x3B,0x04,L,"Slew failure"},
+  {0x3B,0x05,L,"Paper jam"},
+  {0x3B,0x06,L,"Failed to sense top-of-form"},
+  {0x3B,0x07,L,"Failed to sense bottom-of-form"},
+  {0x3B,0x08,T,"Reposition error"},
+  {0x3B,0x09,S,"Read past end of medium"},
+  {0x3B,0x0A,S,"Read past beginning of medium"},
+  {0x3B,0x0B,S,"Position past end of medium"},
+  {0x3B,0x0C,S,"Position past beginning of medium"},
+  {0x3B,0x0D,M,"Medium destination element full"},
+  {0x3B,0x0E,M,"Medium source element empty"},
+  {0x3D,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid bits in identify message"},
+  {0x3E,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit has not self-configured yet"},
+  {0x3F,0x00,D|T|L|P|W|R|S|O|M|C,"Target operating conditions have changed"},
+  {0x3F,0x01,D|T|L|P|W|R|S|O|M|C,"Microcode has been changed"},
+  {0x3F,0x02,D|T|L|P|W|R|S|O|M|C,"Changed operating definition"},
+  {0x3F,0x03,D|T|L|P|W|R|S|O|M|C,"Inquiry data has changed"},
+  {0x43,0x00,D|T|L|P|W|R|S|O|M|C,"Message error"},
+  {0x44,0x00,D|T|L|P|W|R|S|O|M|C,"Internal target failure"},
+  {0x45,0x00,D|T|L|P|W|R|S|O|M|C,"Select or reselect failure"},
+  {0x46,0x00,D|T|L|P|W|R|S|O|M|C,"Unsuccessful soft reset"},
+  {0x47,0x00,D|T|L|P|W|R|S|O|M|C,"Scsi parity error"},
+  {0x48,0x00,D|T|L|P|W|R|S|O|M|C,"Initiator detected error message received"},
+  {0x49,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid message error"},
+  {0x4A,0x00,D|T|L|P|W|R|S|O|M|C,"Command phase error"},
+  {0x4B,0x00,D|T|L|P|W|R|S|O|M|C,"Data phase error"},
+  {0x4C,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit failed self-configuration"},
+  {0x4E,0x00,D|T|L|P|W|R|S|O|M|C,"Overlapped commands attempted"},
+  {0x50,0x00,T,"Write append error"},
+  {0x50,0x01,T,"Write append position error"},
+  {0x50,0x02,T,"Position error related to timing"},
+  {0x51,0x00,T|O,"Erase failure"},
+  {0x52,0x00,T,"Cartridge fault"},
+  {0x53,0x00,D|T|L|W|R|S|O|M,"Media load or eject failed"},
+  {0x53,0x01,T,"Unload tape failure"},
+  {0x53,0x02,D|T|W|R|O|M,"Medium removal prevented"},
+  {0x54,0x00,P,"Scsi to host system interface failure"},
+  {0x55,0x00,P,"System resource failure"},
+  {0x57,0x00,R,"Unable to recover table-of-contents"},
+  {0x58,0x00,O,"Generation does not exist"},
+  {0x59,0x00,O,"Updated block read"},
+  {0x5A,0x00,D|T|L|P|W|R|S|O|M,"Operator request or state change input (unspecified)"},
+  {0x5A,0x01,D|T|W|R|O|M,"Operator medium removal request"},
+  {0x5A,0x02,D|T|W|O,"Operator selected write protect"},
+  {0x5A,0x03,D|T|W|O,"Operator selected write permit"},
+  {0x5B,0x00,D|T|L|P|W|R|S|O|M,"Log exception"},
+  {0x5B,0x01,D|T|L|P|W|R|S|O|M,"Threshold condition met"},
+  {0x5B,0x02,D|T|L|P|W|R|S|O|M,"Log counter at maximum"},
+  {0x5B,0x03,D|T|L|P|W|R|S|O|M,"Log list codes exhausted"},
+  {0x5C,0x00,D|O,"Rpl status change"},
+  {0x5C,0x01,D|O,"Spindles synchronized"},
+  {0x5C,0x02,D|O,"Spindles not synchronized"},
+  {0x60,0x00,S,"Lamp failure"},
+  {0x61,0x00,S,"Video acquisition error"},
+  {0x61,0x01,S,"Unable to acquire video"},
+  {0x61,0x02,S,"Out of focus"},
+  {0x62,0x00,S,"Scan head positioning error"},
+  {0x63,0x00,R,"End of user area encountered on this track"},
+  {0x64,0x00,R,"Illegal mode for this track"},
+  {0, 0, 0, NULL}
+};
+#endif
+
+#if (CONSTANTS & CONST_SENSE)
+static char *snstext[] = {
+	"None","Recovered Error","Not Ready","Medium Error","Hardware Error",
+	"Illegal Request","Unit Attention","Data Protect","Blank Check",
+	"Key=9","Copy Aborted","Aborted Command","End-Of-Medium",
+	"Volume Overflow", "Miscompare", "Key=15"};
+#endif
+
+
+/* Print sense information */
+void print_sense(char * devclass, Scsi_Cmnd * SCpnt)
+{
+	int i, s;
+	int sense_class, valid, code;
+	unsigned char * sense_buffer = SCpnt->sense_buffer;
+	char * error = NULL;
+	int dev = SCpnt->request.dev;
+
+	sense_class = (sense_buffer[0] >> 4) & 0x07;
+	code = sense_buffer[0] & 0xf;
+	valid = sense_buffer[0] & 0x80;
+
+	if (sense_class == 7) { 
+	  s = sense_buffer[7] + 8;
+	  if(s > sizeof(SCpnt->sense_buffer)) s = sizeof(SCpnt->sense_buffer);
+
+	  if (!valid)
+	    printk("extra data not valid ");
+	  
+	  if (sense_buffer[2] & 0x80) printk( "FMK ");
+	  if (sense_buffer[2] & 0x40) printk( "EOM ");
+	  if (sense_buffer[2] & 0x20) printk( "ILI ");
+
+	  switch (code) {
+	  case 0x0:
+	    error = "Current";
+	    break;
+	  case 0x1:
+	    error = "Deferred";
+	    break;
+	  default:
+	    error = "Invalid";
+	  }
+	  
+	  printk("%s error ", error);
+	  
+#if (CONSTANTS & CONST_SENSE)
+	  printk( "%s%x: sense key %s\n", devclass, dev, snstext[sense_buffer[2] & 0x0f]);
+#else
+	  printk("%s%x: sns = %2x %2x\n", devclass, dev, sense_buffer[0], sense_buffer[2]);
+#endif
+	
+	/* Check to see if additional sense information is available */
+	if(sense_buffer[7] + 7 < 13 ||
+	   (sense_buffer[12] == 0  && sense_buffer[13] ==  0)) goto done;
+	
+#if (CONSTANTS & CONST_XSENSE)
+	for(i=0; additional[i].text; i++)
+		if(additional[i].code1 == sense_buffer[12] &&
+		   additional[i].code2 == sense_buffer[13])
+			printk("Additional sense indicates %s\n", additional[i].text);
+	
+	for(i=0; additional2[i].text; i++)
+		if(additional2[i].code1 == sense_buffer[12] &&
+		   additional2[i].code2_min >= sense_buffer[13]  &&
+		   additional2[i].code2_max <= sense_buffer[13]) {
+			printk("Additional sense indicates ");
+			printk(additional2[i].text, sense_buffer[13]);
+			printk("\n");
+		};
+#else
+	printk("ASC=%2x ASCQ=%2x\n", sense_buffer[12], sense_buffer[13]);
+#endif
+	} else { 
+
+#if (CONSTANTS & CONST_SENSE)
+	  if (sense_buffer[0] < 15)
+	    printk("%s%x: old sense key %s\n", devclass, dev, snstext[sense_buffer[0] & 0x0f]);
+	  else
+#endif
+	    printk("%s%x: sns = %2x %2x\n", devclass, dev, sense_buffer[0], sense_buffer[2]);
+
+	  printk("Non-extended sense class %d code 0x%0x ", sense_class, code);
+	  s = 4;
+	}
+	
+      done:
+#if !(CONSTANTS & CONST_SENSE)
+	printk("Raw sense data:");
+	for (i = 0; i < s; ++i) 
+	  printk("0x%02x ", sense_buffer[i]);
+	printk("\n");
+#endif
+	return;
+}
+
+#if (CONSTANTS & CONST_MSG) 
+static const char *one_byte_msgs[] = {
+/* 0x00 */ "Command Complete", NULL, "Save Pointers",
+/* 0x03 */ "Restore Pointers", "Disconnect", "Initiator Error", 
+/* 0x06 */ "Abort", "Message Reject", "Nop", "Message Parity Error",
+/* 0x0a */ "Linked Command Complete", "Linked Command Complete w/flag",
+/* 0x0c */ "Bus device reset", "Abort Tag", "Clear Queue", 
+/* 0x0f */ "Initiate Recovery", "Release Recovery"
+};
+
+#define NO_ONE_BYTE_MSGS (sizeof(one_byte_msgs)  / sizeof (const char *))
+
+static const char *two_byte_msgs[] = {
+/* 0x20 */ "Simple Queue Tag", "Head of Queue Tag", "Ordered Queue Tag"
+/* 0x23 */ "Ignore Wide Residue"
+};
+
+#define NO_TWO_BYTE_MSGS (sizeof(two_byte_msgs)  / sizeof (const char *))
+
+static const char *extended_msgs[] = {
+/* 0x00 */ "Modify Data Pointer", "Synchronous Data Transfer Request",
+/* 0x02 */ "SCSI-I Extended Identify", "Wide Data Transfer Request"
+};
+
+#define NO_EXTENDED_MSGS (sizeof(two_byte_msgs)  / sizeof (const char *))
+#endif /* (CONSTANTS & CONST_MSG) */
+
+int print_msg (const unsigned char *msg) {
+    int len = 0, i;
+    if (msg[0] == EXTENDED_MESSAGE) {
+	len = 3 + msg[1];
+#if (CONSTANTS & CONST_MSG)
+	if (msg[2] < NO_EXTENDED_MSGS)
+	    printk ("%s ", extended_msgs[msg[2]]); 
+	else 
+	    printk ("Extended Message, reserved code (0x%02x) ", (int) msg[2]);
+	switch (msg[2]) {
+	case EXTENDED_MODIFY_DATA_POINTER:
+	    printk("pointer = %d", (int) (msg[3] << 24) | (msg[4] << 16) | 
+		(msg[5] << 8) | msg[6]);
+	    break;
+	case EXTENDED_SDTR:
+	    printk("period = %d ns, offset = %d", (int) msg[3] * 4, (int) 
+		msg[4]);
+	    break;
+	case EXTENDED_WDTR:
+	    printk("width = 2^%d bytes", msg[3]);
+	    break;
+	default:
+	    for (i = 2; i < len; ++i) 
+		printk("%02x ", msg[i]);
+	}
+#else
+	for (i = 0; i < len; ++i)
+	    printk("%02x ", msg[i]);
+#endif
+    /* Identify */
+    } else if (msg[0] & 0x80) {
+#if (CONSTANTS & CONST_MSG)
+	printk("Identify disconnect %sallowed %s %d ",
+	    (msg[0] & 0x40) ? "" : "not ",
+	    (msg[0] & 0x20) ? "target routine" : "lun",
+	    msg[0] & 0x7);
+#else
+    printk("%02x ", msg[0]);
+#endif
+    len = 1;
+    /* Normal One byte */
+    } else if (msg[0] < 0x1f) {
+#if (CONSTANTS & CONST_MSG)
+	if (msg[0] < NO_ONE_BYTE_MSGS)
+	    printk(one_byte_msgs[msg[0]]);
+	else
+	    printk("reserved (%02x) ", msg[0]);
+#else
+	printk("%02x ", msg[0]);
+#endif
+	len = 1;
+    /* Two byte */
+    } else if (msg[0] <= 0x2f) {
+#if (CONSTANTS & CONST_MSG)
+	if ((msg[0] - 0x20) < NO_TWO_BYTE_MSGS)
+	    printk("%s %02x ", two_byte_msgs[msg[0] - 0x20], 
+		msg[1]);
+	else 
+	    printk("reserved two byte (%02x %02x) ", 
+		msg[0], msg[1]);
+#else
+	printk("%02x %02x", msg[0], msg[1]);
+#endif
+	len = 2;
+    } else 
+#if (CONSTANTS & CONST_MSG)
+	printk(reserved);
+#else
+	printk("%02x ", msg[0]);
+#endif
+    return len;
+}
+
+void print_Scsi_Cmnd (Scsi_Cmnd *cmd) {
+    printk("scsi%d : destination target %d, lun %d\n", 
+    cmd->host->host_no, 
+    cmd->target, 
+    cmd->lun);
+    printk("        command = ");
+    print_command (cmd->cmnd);
+}
diff -r -u -N linux.orig/arch/arm/drivers/scsi/constants.h linux.arm/arch/arm/drivers/scsi/constants.h
--- linux.orig/arch/arm/drivers/scsi/constants.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/constants.h	Fri Oct 27 23:15:17 1995
@@ -0,0 +1,8 @@
+#ifndef _CONSTANTS_H
+#define _CONSTANTS_H
+extern void print_command(unsigned char *);
+extern int print_msg(unsigned char *);
+extern void print_sense(char *,  Scsi_Cmnd *);
+extern void print_status(int);
+extern void print_Scsi_Cmnd (Scsi_Cmnd *);
+#endif /* def _CONSTANTS_H */
diff -r -u -N linux.orig/arch/arm/drivers/scsi/cumana_1.c linux.arm/arch/arm/drivers/scsi/cumana_1.c
--- linux.orig/arch/arm/drivers/scsi/cumana_1.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/cumana_1.c	Fri Oct 27 23:15:07 1995
@@ -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 -r -u -N linux.orig/arch/arm/drivers/scsi/cumana_1.h linux.arm/arch/arm/drivers/scsi/cumana_1.h
--- linux.orig/arch/arm/drivers/scsi/cumana_1.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/cumana_1.h	Fri Oct 27 23:15:08 1995
@@ -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 -r -u -N linux.orig/arch/arm/drivers/scsi/ecoscsi.c linux.arm/arch/arm/drivers/scsi/ecoscsi.c
--- linux.orig/arch/arm/drivers/scsi/ecoscsi.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/ecoscsi.c	Fri Oct 27 23:15:07 1995
@@ -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 -r -u -N linux.orig/arch/arm/drivers/scsi/ecoscsi.h linux.arm/arch/arm/drivers/scsi/ecoscsi.h
--- linux.orig/arch/arm/drivers/scsi/ecoscsi.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/ecoscsi.h	Fri Oct 27 23:15:08 1995
@@ -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 -r -u -N linux.orig/arch/arm/drivers/scsi/hosts.c linux.arm/arch/arm/drivers/scsi/hosts.c
--- linux.orig/arch/arm/drivers/scsi/hosts.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/hosts.c	Fri Oct 27 23:15:08 1995
@@ -0,0 +1,307 @@
+/*
+ *	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.
+ */
+
+#include <linux/config.h>
+#include "../block/blk.h"
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mm.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;
+
+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;
+	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 four 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;
+
+	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;
+		}
+	}
+
+
+	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)] = MAP_PAGE_RESERVED;
+		}
+	    }
+	}
+      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: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -r -u -N linux.orig/arch/arm/drivers/scsi/hosts.c.old linux.arm/arch/arm/drivers/scsi/hosts.c.old
--- linux.orig/arch/arm/drivers/scsi/hosts.c.old	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/hosts.c.old	Fri Oct 27 23:15:22 1995
@@ -0,0 +1,228 @@
+/*
+ *	hosts.c Copyright (C) 1992 Drew Eckhardt
+ *	mid to lowlevel SCSI driver interface by
+ *		Drew Eckhardt
+ *
+ *	<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.
+ */
+
+#include <linux/config.h>
+#include "../block/blk.h"
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include "scsi.h"
+
+#ifndef NULL
+#define NULL 0L
+#endif
+
+#define HOSTS_C
+
+#include "hosts.h"
+
+#ifdef CONFIG_SCSI_CUMANA_1
+#include "cumana_1.h"
+#endif
+
+#ifdef CONFIG_SCSI_OAK
+#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_CUMANA_1
+       CUMANA_NCR5380,
+#endif
+#ifdef CONFIG_SCSI_OAK
+       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;
+
+int max_scsi_hosts = 0;
+static int next_host = 0;
+
+void
+scsi_unregister(struct Scsi_Host * sh){
+	struct Scsi_Host * shpnt;
+	int j;
+
+	j = sh->extra_bytes;
+
+	if(scsi_hostlist == sh)
+		scsi_hostlist = NULL;
+	else {
+		shpnt = scsi_hostlist;
+		while(shpnt->next != sh) shpnt = shpnt->next;
+		shpnt->next = shpnt->next->next;
+	};
+	next_host--;
+	scsi_init_free((char *) sh, sizeof(struct Scsi_Host) + j);
+}
+
+/* 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);
+	retval->host_busy = 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 = next_host++;
+	retval->host_queue = NULL;
+	retval->host_wait = NULL;
+	retval->last_reset = 0;
+	retval->irq = 0;
+	retval->hostt = tpnt;
+	retval->next = NULL;
+#ifdef DEBUG
+	printk("Register %x %x: %d\n", retval, retval->hostt, j);
+#endif
+
+	/* The next three are the default values which can be overridden
+	   if need be */
+	retval->this_id = tpnt->this_id;
+	retval->sg_tablesize = tpnt->sg_tablesize;
+	retval->unchecked_isa_dma = tpnt->unchecked_isa_dma;
+
+	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, j, count, pcount;
+	Scsi_Host_Template * tpnt;
+	count = 0;
+
+	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_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_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;
+			for(j = 0; j < tpnt->present; j++)
+				printk ("scsi%d: %s\n",
+					count++, tpnt->name);
+		}
+	}
+	printk ("scsi: %d hosts.\n", count);
+
+	/* 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
+
+	max_scsi_hosts = count;
+	return 0;
+}
diff -r -u -N linux.orig/arch/arm/drivers/scsi/hosts.h linux.arm/arch/arm/drivers/scsi/hosts.h
--- linux.orig/arch/arm/drivers/scsi/hosts.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/hosts.h	Fri Oct 27 23:15:08 1995
@@ -0,0 +1,374 @@
+/*
+ *	hosts.h Copyright (C) 1992 Drew Eckhardt 
+ *	mid to low-level SCSI driver interface header by	
+ *		Drew Eckhardt 
+ *
+ *	<drew@colorado.edu>
+ *
+ *       Modified by Eric Youngdale eric@tantalus.nrl.navy.mil to
+ *       add scatter-gather, multiple outstanding request, and other
+ *       enhancements.
+ * 
+ *	Further modified by Eric Youngdale to support multiple host adapters
+ *	of the same type.
+ */
+
+#ifndef _HOSTS_H
+#define _HOSTS_H
+
+/*
+	$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/hosts.h,v 1.3 1993/09/24 12:21:00 drew Exp drew $
+*/
+
+
+/* It is senseless to set SG_ALL any higher than this - the performance
+   does not get any better, and it wastes memory */
+#define SG_NONE 0
+#define SG_ALL 0xff
+
+#define DISABLE_CLUSTERING 0
+#define ENABLE_CLUSTERING 1
+
+/* The various choices mean:
+   NONE: Self evident.  Host adapter is not capable of scatter-gather.
+   ALL:  Means that the host adapter module can do scatter-gather,
+         and that there is no limit to the size of the table to which
+	 we scatter/gather data.
+  Anything else:  Indicates the maximum number of chains that can be
+        used in one scatter-gather request.
+*/
+
+/*
+	The Scsi_Host_Template type has all that is needed to interface with a SCSI
+	host in a device independent matter.  There is one entry for each different
+	type of host adapter that is supported on the system.
+*/
+
+typedef struct scsi_disk Disk;
+
+typedef struct  SHT
+	{
+
+	  /* Used with loadable modules so we can construct a linked list. */
+	  struct SHT * next;
+
+	  /* Used with loadable modules so that we know when it is safe to unload */
+	  int * usage_count;
+
+	/*
+		The name pointer is a pointer to the name of the SCSI
+		device detected.
+	*/
+
+	char *name;
+
+	/*
+		The detect function shall return non zero on detection,
+		indicating the number of host adapters of this particular
+		type were found.  It should also
+		initialize all data necessary for this particular
+		SCSI driver.  It is passed the host number, so this host
+		knows where the first entry is in the scsi_hosts[] array.
+
+		Note that the detect routine MUST not call any of the mid level
+		functions to queue commands because things are not guaranteed
+		to be set up yet.  The detect routine can send commands to
+		the host adapter as long as the program control will not be
+		passed to scsi.c in the processing of the command.  Note
+		especially that scsi_malloc/scsi_free must not be called.
+	*/
+
+	int (* detect)(struct SHT *); 
+
+	  /* Used with loadable modules to unload the host structures.  Note:
+	   there is a default action built into the modules code which may
+	   be sufficient for most host adapters.  Thus you may not have to supply
+	   this at all. */
+	int (*release)(struct Scsi_Host *);
+	/*
+		The info function will return whatever useful
+		information the developer sees fit.  If not provided, then
+		the name field will be used instead.
+	*/
+
+        const char *(* info)(struct Scsi_Host *);
+
+	/*
+		The command function takes a target, a command (this is a SCSI 
+		command formatted as per the SCSI spec, nothing strange), a 
+		data buffer pointer, and data buffer length pointer.  The return
+		is a status int, bit fielded as follows : 
+		Byte	What
+		0	SCSI status code
+		1	SCSI 1 byte message
+		2 	host error return.
+		3	mid level error return
+	*/
+
+	int (* command)(Scsi_Cmnd *);
+
+        /*
+                The QueueCommand function works in a similar manner
+                to the command function.  It takes an additional parameter,
+                void (* done)(int host, int code) which is passed the host 
+		# and exit result when the command is complete.  
+		Host number is the POSITION IN THE hosts array of THIS
+		host adapter.
+        */
+
+        int (* queuecommand)(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+
+	/*
+		Since the mid level driver handles time outs, etc, we want to 
+		be able to abort the current command.  Abort returns 0 if the 
+		abortion was successful.  The field SCpnt->abort reason
+		can be filled in with the appropriate reason why we wanted
+		the abort in the first place, and this will be used
+		in the mid-level code instead of the host_byte().
+		If non-zero, the code passed to it 
+		will be used as the return code, otherwise 
+		DID_ABORT  should be returned.
+
+		Note that the scsi driver should "clean up" after itself, 
+		resetting the bus, etc.  if necessary. 
+	*/
+
+	int (* abort)(Scsi_Cmnd *);
+
+	/*
+		The reset function will reset the SCSI bus.  Any executing 
+		commands should fail with a DID_RESET in the host byte.
+		The Scsi_Cmnd  is passed so that the reset routine can figure
+		out which host adapter should be reset, and also which command
+		within the command block was responsible for the reset in
+		the first place.  Some hosts do not implement a reset function,
+		and these hosts must call scsi_request_sense(SCpnt) to keep
+		the command alive.
+	*/ 
+
+	int (* reset)(Scsi_Cmnd *);
+	/*
+		This function is used to select synchronous communications,
+		which will result in a higher data throughput.  Not implemented
+		yet.
+	*/ 
+
+	int (* slave_attach)(int, int);
+	/*
+		This function determines the bios parameters for a given
+		harddisk.  These tend to be numbers that are made up by
+		the host adapter.  Parameters:
+		size, device number, list (heads, sectors, cylinders)
+	*/ 
+
+	int (* bios_param)(Disk *, int, int []);
+	
+	/*
+		This determines if we will use a non-interrupt driven
+		or an interrupt driven scheme,  It is set to the maximum number
+		of simultaneous commands a given host adapter will accept.
+	*/
+	int can_queue;
+
+	/*
+		In many instances, especially where disconnect / reconnect are 
+		supported, our host also has an ID on the SCSI bus.  If this is 
+		the case, then it must be reserved.  Please set this_id to -1 if
+ 		your setup is in single initiator mode, and the host lacks an 
+		ID.
+	*/
+	
+	int this_id;
+
+	/*
+	        This determines the degree to which the host adapter is capable
+		of scatter-gather.
+	*/
+
+	short unsigned int sg_tablesize;
+
+	/*
+	  True if this host adapter can make good use of linked commands.
+	  This will allow more than one command to be queued to a given
+	  unit on a given host.  Set this to the maximum number of command
+	  blocks to be provided for each device.  Set this to 1 for one
+	  command block per lun, 2 for two, etc.  Do not set this to 0.
+	  You should make sure that the host adapter will do the right thing
+	  before you try setting this above 1.
+	 */
+
+	short cmd_per_lun;
+	/*
+		present contains counter indicating how many boards of this
+		type were found when we did the scan.
+	*/
+
+	unsigned char present;	
+	/*
+	  true if this host adapter uses unchecked DMA onto an ISA bus.
+	*/
+	unsigned unchecked_isa_dma:1;
+	/*
+	  true if this host adapter can make good use of clustering.
+	  I originally thought that if the tablesize was large that it
+	  was a waste of CPU cycles to prepare a cluster list, but
+	  it works out that the Buslogic is faster if you use a smaller
+	  number of segments (i.e. use clustering).  I guess it is
+	  inefficient.
+	*/
+	unsigned use_clustering:1;
+	} Scsi_Host_Template;
+
+/*
+	The scsi_hosts array is	the array containing the data for all 
+	possible <supported> scsi hosts.   This is similar to the
+	Scsi_Host_Template, except that we have one entry for each
+	actual physical host adapter on the system, stored as a linked
+	list.  Note that if there are 2 aha1542 boards, then there will
+	be two Scsi_Host entries, but only 1 Scsi_Host_Template entries.
+*/
+
+struct Scsi_Host
+	{
+		struct Scsi_Host * next;
+		unsigned short extra_bytes;
+		volatile unsigned char host_busy;
+		char host_no;  /* Used for IOCTL_GET_IDLUN */
+		int last_reset;
+		struct wait_queue *host_wait;
+		Scsi_Cmnd *host_queue; 
+		Scsi_Host_Template * hostt;
+
+		/* Pointer to a circularly linked list - this indicates the hosts
+		   that should be locked out of performing I/O while we have an active
+		   command on this host. */
+		struct Scsi_Host * block;
+		unsigned wish_block:1;
+
+		/* These parameters should be set by the detect routine */
+		unsigned char *base;
+		unsigned int io_port;
+		unsigned char n_io_port;
+		unsigned char irq;
+		unsigned char dma_channel;
+
+		/*
+		  Set these if there are conflicts between memory
+		  in the < 1mb region and regions at 16mb multiples.
+		  The address must be on a page boundary.
+		*/
+		unsigned long forbidden_addr;
+		unsigned long forbidden_size;
+
+		/*
+		  The rest can be copied from the template, or specifically
+		  initialized, as required.
+		*/
+		
+		int this_id;
+		int can_queue;
+		short cmd_per_lun;
+		short unsigned int sg_tablesize;
+		unsigned unchecked_isa_dma:1;
+		/*
+		   True if this host was loaded as a loadable module
+		*/
+		unsigned loaded_as_module:1;
+		
+		int hostdata[0];  /* Used for storage of host specific stuff */
+	};
+
+extern struct Scsi_Host * scsi_hostlist;
+extern struct Scsi_Device_Template * scsi_devicelist;
+
+extern Scsi_Host_Template * scsi_hosts;
+
+/*
+	scsi_init initializes the scsi hosts.
+*/
+
+
+/* We use these goofy things because the MM is not set up when we init
+   the scsi subsystem.  By using these functions we can write code that
+   looks normal.  Also, it makes it possible to use the same code for a
+   loadable module. */
+
+extern void * scsi_init_malloc(unsigned int size, int priority);
+extern void scsi_init_free(char * ptr, unsigned int size);
+
+void scan_scsis (struct Scsi_Host * shpnt);
+
+extern int next_scsi_host;
+
+extern int scsi_loadable_module_flag;
+unsigned int scsi_init(void);
+extern struct Scsi_Host * scsi_register(Scsi_Host_Template *, int j);
+extern void scsi_unregister(struct Scsi_Host * i);
+extern int scsicam_bios_param (Disk *, int, int *);
+
+#define BLANK_HOST {"", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+
+struct Scsi_Device_Template
+{
+  struct Scsi_Device_Template * next;
+  char * name;
+  char * tag;
+  unsigned char scsi_type;
+  unsigned char major;
+  unsigned char nr_dev;  /* Number currently attached */
+  unsigned char dev_noticed; /* Number of devices detected. */
+  unsigned char dev_max; /* Current size of arrays */
+  unsigned blk:1;  /* 0 if character device */
+  int (*detect)(Scsi_Device *); /* Returns 1 if we can attach this device */
+  void (*init)(void);  /* Sizes arrays based upon number of devices detected */
+  void (*finish)(void);  /* Perform initialization after attachment */
+  int (*attach)(Scsi_Device *); /* Attach devices to arrays */
+  void (*detach)(Scsi_Device *);
+};
+
+extern struct Scsi_Device_Template sd_template;
+extern struct Scsi_Device_Template st_template;
+extern struct Scsi_Device_Template sr_template;
+extern struct Scsi_Device_Template sg_template;
+
+int scsi_register_device(struct Scsi_Device_Template * sdpnt);
+
+/* These are used by loadable modules */
+extern int scsi_register_module(int, void *);
+extern void scsi_unregister_module(int, void *);
+
+/* The different types of modules that we can load and unload */
+#define MODULE_SCSI_HA 1
+#define MODULE_SCSI_CONST 2
+#define MODULE_SCSI_IOCTL 3
+#define MODULE_SCSI_DEV 4
+
+
+/*
+ * This is an ugly hack.  If we expect to be able to load devices at run time, we need
+ * to leave extra room in some of the data structures.  Doing a realloc to enlarge
+ * the structures would be riddled with race conditions, so until a better solution 
+ * is discovered, we use this crude approach
+ */
+#define SD_EXTRA_DEVS 2
+#define ST_EXTRA_DEVS 2
+#define SR_EXTRA_DEVS 2
+#define SG_EXTRA_DEVS (SD_EXTRA_DEVS + SR_EXTRA_DEVS + ST_EXTRA_DEVS)
+
+#endif
+/*
+ * 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: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -r -u -N linux.orig/arch/arm/drivers/scsi/oak.c linux.arm/arch/arm/drivers/scsi/oak.c
--- linux.orig/arch/arm/drivers/scsi/oak.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/oak.c	Fri Oct 27 23:15:08 1995
@@ -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 -r -u -N linux.orig/arch/arm/drivers/scsi/oak.h linux.arm/arch/arm/drivers/scsi/oak.h
--- linux.orig/arch/arm/drivers/scsi/oak.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/oak.h	Fri Oct 27 23:15:10 1995
@@ -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 -r -u -N linux.orig/arch/arm/drivers/scsi/scsi.c linux.arm/arch/arm/drivers/scsi/scsi.c
--- linux.orig/arch/arm/drivers/scsi/scsi.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/scsi.c	Fri Oct 27 23:15:10 1995
@@ -0,0 +1,2568 @@
+/*
+ *	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 ericy@cais.com to
+ *       add scatter-gather, multiple outstanding request, and other
+ *       enhancements.
+ */
+
+#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 "../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 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};
+
+/* This variable is merely a hook so that we can debug the kernel with gdb. */
+Scsi_Cmnd * last_cmnd = 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 500
+#else
+	#define SCSI_TIMEOUT 100
+#endif
+
+#ifdef DEBUG
+	#define SENSE_TIMEOUT SCSI_TIMEOUT
+	#define ABORT_TIMEOUT SCSI_TIMEOUT
+	#define RESET_TIMEOUT SCSI_TIMEOUT
+#else
+	#define SENSE_TIMEOUT 50
+	#define RESET_TIMEOUT 50
+	#define ABORT_TIMEOUT 50
+#endif
+
+#define MIN_RESET_DELAY 100
+
+/* Do not call reset on error if we just did a reset within 10 sec. */
+#define MIN_RESET_PERIOD 1000
+
+/* 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. */
+
+     struct blist{
+       char * vendor;
+       char * model;
+       char * revision; /* Latest revision known to be bad.  Not used yet */
+     };
+
+static struct blist blacklist[] =
+{
+   {"CHINON","CD-ROM CDS-431","H42"},  /* Locks up if polled for lun != 0 */
+   {"CHINON","CD-ROM CDS-535","Q14"}, /* Lockup if polled for lun != 0 */
+   {"DENON","DRD-25X","V"},   /* A cdrom that locks up when probed at lun != 0 */
+   {"HITACHI","DK312C","CM81"},   /* Responds to all lun - dtg */
+   {"HITACHI","DK314C","CR21" }, /* responds to all lun */
+   {"IMS", "CDD521/10","2.06"},   /* Locks-up when LUN>0 polled. */
+   {"MAXTOR","XT-3280","PR02"},   /* Locks-up when LUN>0 polled. */
+   {"MAXTOR","XT-4380S","B3C"},   /* Locks-up when LUN>0 polled. */
+   {"MAXTOR","MXT-1240S","I1.2"}, /* Locks up when LUN > 0 polled */
+   {"MAXTOR","XT-4170S","B5A"},   /* Locks-up sometimes when LUN>0 polled. */
+   {"MAXTOR","XT-8760S","B7B"},   /* guess what? */
+   {"NEC","CD-ROM DRIVE:841","1.0"},  /* Locks-up when LUN>0 polled. */
+   {"RODIME","RO3000S","2.33"},  /* Locks up if polled for lun != 0 */
+   {"SEAGATE", "ST157N", "\004|j"}, /* causes failed REQUEST SENSE on lun 1 for aha152x
+				     * controller, which causes SCSI code to reset bus.*/
+   {"SEAGATE", "ST296","921"},   /* Responds to all lun */
+   {"SONY","CD-ROM CDU-541","4.3d"},
+   {"SONY","CD-ROM CDU-55S","1.0i"},
+   {"TANDBERG","TDC 3600","U07"},  /* Locks up if polled for lun != 0 */
+   {"TEAC","CD-ROM","1.06"},	/* causes failed REQUEST SENSE on lun 1 for seagate
+				 * controller, which causes SCSI code to reset bus.*/
+   {"TEXEL","CD-ROM","1.06"},   /* causes failed REQUEST SENSE on lun 1 for seagate
+				 * controller, which causes SCSI code to reset bus.*/
+   {"QUANTUM","LPS525S","3110"},/* Locks sometimes if polled for lun != 0 */
+   {"QUANTUM","PD1225S","3110"},/* Locks sometimes if polled for lun != 0 */
+   {"MEDIAVIS","CDR-H93MV","1.31"},  /* Locks up if polled for lun != 0 */
+   {"SANKYO", "CP525","6.64"},  /* causes failed REQ SENSE, extra reset */
+   {"HP", "C1750A", "3226"},    /* scanjet iic */
+   {"HP", "C1790A", ""},	/* scanjet iip */
+   {"HP", "C2500A", ""},	/* scanjet iicx */
+   {NULL, NULL, NULL}};
+
+static int blacklisted(unsigned char * response_data){
+  int i = 0;
+  unsigned char * pnt;
+  for(i=0; 1; i++){
+    if(blacklist[i].vendor == NULL) return 0;
+    pnt = &response_data[8];
+    while(*pnt && *pnt == ' ') pnt++;
+    if(memcmp(blacklist[i].vendor, pnt,
+	       strlen(blacklist[i].vendor))) continue;
+    pnt = &response_data[16];
+    while(*pnt && *pnt == ' ') pnt++;
+    if(memcmp(blacklist[i].model, pnt,
+	       strlen(blacklist[i].model))) continue;
+    return 1;
+  }
+}
+
+/*
+ *	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(%d, %06x)\n", SCpnt->host, SCpnt->result);
+#endif
+	SCpnt->request.dev = 0xfffe;
+
+	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 6.
+ *	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.
+ */
+
+void scan_scsis (struct Scsi_Host * shpnt)
+{
+  int dev, lun, type;
+  unsigned char scsi_cmd [12];
+  unsigned char scsi_result0 [256];
+  unsigned char * scsi_result;
+  Scsi_Device * SDpnt, *SDtail;
+  struct Scsi_Device_Template * sdtpnt;
+  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 = ((current == task[0]  || !shpnt->unchecked_isa_dma)
+		 ?  &scsi_result0[0] : scsi_malloc(512));
+
+
+  shpnt->host_queue = SCpnt;  /* We need this so that commands can time out */
+
+  for (dev = 0; dev < 8; ++dev)
+    if (shpnt->this_id != dev)
+
+/*
+ * We need the for so our continue, etc. work fine.
+ */
+      for (lun = 0; lun < max_scsi_luns; ++lun)
+	{
+	  memset(SDpnt, 0, sizeof(Scsi_Device));
+	  SDpnt->host = shpnt;
+	  SDpnt->id = dev;
+	  SDpnt->lun = lun;
+
+	  /* 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;
+
+	  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;
+
+	  /* Used for mutex if loading devices after boot */
+	  SCpnt->request.sem = NULL;
+
+	  SCpnt->request.dev = 0xffff; /* Mark not busy */
+
+	  scsi_do_cmd (SCpnt, (void *)  scsi_cmd, (void *) scsi_result,
+		       256,  scan_scsis_done, SCSI_TIMEOUT + 400, 5);
+
+	  /* Wait for command to finish. Use simple wait if we are booting, else
+	     do it right and use a mutex */
+
+	  if (current == task[0])
+	      while (SCpnt->request.dev != 0xfffe) barrier();
+	  else if (SCpnt->request.dev != 0xfffe) {
+	      struct semaphore sem = MUTEX_LOCKED;
+
+	      SCpnt->request.sem = &sem;
+	      down(&sem);
+
+	      /* Hmm.. Have to ask about this one */
+	      while (SCpnt->request.dev != 0xfffe) schedule();
+	      }
+
+#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->request.dev = 0xffff; /* Mark not busy */
+	  SCpnt->cmd_len = 0;
+
+	  scsi_do_cmd (SCpnt, (void *)  scsi_cmd, (void *) scsi_result,
+		       256,  scan_scsis_done, SCSI_TIMEOUT, 3);
+
+	  if (current == task[0])
+	      while (SCpnt->request.dev != 0xfffe) barrier();
+	  else if (SCpnt->request.dev != 0xfffe) {
+	      struct semaphore sem = MUTEX_LOCKED;
+
+	      SCpnt->request.sem = &sem;
+	      down(&sem);
+
+	      /* Hmm.. Have to ask about this one */
+	      while (SCpnt->request.dev != 0xfffe) schedule();
+	      }
+
+	  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 CD-ROM 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
+		  SDpnt->manufacturer = SCSI_MAN_UNKNOWN;
+
+	      SDpnt->removable = (0x80 &
+				  scsi_result[1]) >> 7;
+	      SDpnt->lockable = SDpnt->removable;
+	      SDpnt->changed = 0;
+	      SDpnt->access_count = 0;
+	      SDpnt->busy = 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->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;
+
+/*
+ * 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(strncmp("TEXEL", (char *) &scsi_result[8], 5) != 0 ||
+		     strncmp("CD-ROM", (char *) &scsi_result[16], 6) != 0
+/*
+ * XXX 1.06 has problems, some one should figure out the others too so
+ * ALL TEXEL drives don't suffer in performance, especially when I finish
+ * integrating my seagate patches which do multiple I_T_L nexuses.
+ */
+
+#ifdef notyet
+		     || (strncmp("1.06", (char *) &scsi_result[[, 4) != 0)))
+#endif
+				 )
+		    SDpnt->borken = 0;
+
+
+		  /* These devices need this "key" to unlock the device
+		     so we can use it */
+		  if(memcmp("INSITE", &scsi_result[8], 6) == 0 &&
+		     (memcmp("Floptical   F*8I", &scsi_result[16], 16) == 0
+		      || memcmp("I325VM", &scsi_result[16], 6) == 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->request.dev = 0xffff; /* Mark not busy */
+		    SCpnt->cmd_len = 0;
+
+		    scsi_do_cmd (SCpnt, (void *)  scsi_cmd,
+				 (void *) scsi_result, 0x2a,  scan_scsis_done,
+				 SCSI_TIMEOUT, 3);
+
+		    if (current == task[0])
+			while (SCpnt->request.dev != 0xfffe) barrier();
+		    else if (SCpnt->request.dev != 0xfffe) {
+			struct semaphore sem = MUTEX_LOCKED;
+
+			SCpnt->request.sem = &sem;
+			down(&sem);
+
+			/* Hmm.. Have to ask about this one */
+			while (SCpnt->request.dev != 0xfffe) schedule();
+			}
+		  }
+		  /* 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(blacklisted(scsi_result)) break;
+		  /* 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 */
+	}       /* for lun ends */
+
+  shpnt->host_queue = NULL;  /* No longer needed here */
+
+  /* Last device block does not exist.  Free memory. */
+  scsi_init_free((char *) SDpnt, sizeof(Scsi_Device));
+
+  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_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))
+				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");
+			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;
+  struct buffer_head * bh, *bhp;
+
+  if (!device)
+    panic ("No device passed to request_queueable().\n");
+
+  if (req && req->dev <= 0)
+    panic("Invalid device in request_queueable");
+
+  SCpnt =  device->host->host_queue;
+    while(SCpnt){
+      if(SCpnt->target == device->id &&
+	 SCpnt->lun == device->lun)
+	if(SCpnt->request.dev < 0) break;
+      SCpnt = SCpnt->next;
+    }
+
+  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->dev = -1;
+      wake_up(&wait_for_request);
+    }
+  } else {
+    SCpnt->request.dev = 0xffff; /* 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;
+  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)
+{
+  int dev = -1;
+  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;
+
+  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 && (dev = req->dev) <= 0) return NULL;
+
+  host = device->host;
+
+  if (intr_count && SCSI_BLOCK(host)) return NULL;
+
+  while (1==1){
+    SCpnt = host->host_queue;
+    while(SCpnt){
+      if(SCpnt->target == device->id &&
+	 SCpnt->lun == device->lun) {
+	SCwait = SCpnt;
+	if(SCpnt->request.dev < 0) break;
+      }
+      SCpnt = SCpnt->next;
+    }
+    save_flags(flags);
+    cli();
+    /* See if this request has already been queued by an interrupt routine */
+    if (req && ((req->dev < 0) || (req->dev != dev))) {
+      restore_flags(flags);
+      return NULL;
+    }
+    if (!SCpnt || SCpnt->request.dev >= 0)  /* Might have changed */
+      {
+	restore_flags(flags);
+	if(!wait) return NULL;
+	if (!SCwait) {
+	  printk("Attempt to allocate device target %d, lun %d\n",
+		 device->id ,device->lun);
+	  panic("No device found in allocate_device\n");
+	}
+	SCSI_SLEEP(&device->device_wait,
+		   (SCwait->request.dev > 0));
+      } 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->dev = -1;
+	      *reqp = req->next;
+	      wake_up(&wait_for_request);
+	    }
+	} else {
+	  SCpnt->request.dev = 0xffff; /* 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 */
+  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
+
+	if ((unsigned long) &SCpnt < current->kernel_stack_page)
+	  panic("Kernel stack overflow.");
+
+	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, target = %d, command = %08x, buffer =  %08x, \n"
+		"bufflen = %d, done = %08x)\n", SCpnt->host->host_no, SCpnt->target, SCpnt->cmnd, SCpnt->buffer, SCpnt->bufflen, SCpnt->done);
+#endif
+
+	if (host->can_queue)
+		{
+#ifdef DEBUG
+	printk("queuecommand : routine at %08x\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 %08x\n", host->hostt->command);
+#endif
+		temp=host->hostt->command (SCpnt);
+		SCpnt->result = temp;
+#ifdef DEBUG_DELAY
+	clock = jiffies + 400;
+	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, target = %d, buffer =%08x, "
+		"bufflen = %d, done = %08x, timeout = %d, retries = %d)\n"
+		"command : " , host->host_no, 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 , (void *) cmnd, 12);
+#if 0
+	SCpnt->host = host;
+	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 , (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
+	}
+
+
+
+/*
+	The scsi_done() function disables the timeout timer for the scsi host,
+	marks the host as not busy, and calls the user specified completion
+	function for that host's current command.
+*/
+
+static void reset (Scsi_Cmnd * SCpnt)
+{
+#ifdef DEBUG
+	printk("scsi: reset(%d)\n", SCpnt->host->host_no);
+#endif
+
+	SCpnt->flags |= (WAS_RESET | IS_RESETTING);
+	scsi_reset(SCpnt);
+
+#ifdef DEBUG
+	printk("performing request sense\n");
+#endif
+
+#if 0  /* FIXME - remove this when done */
+	if(SCpnt->flags & NEEDS_JUMPSTART) {
+	  SCpnt->flags &= ~NEEDS_JUMPSTART;
+	  scsi_request_sense (SCpnt);
+	}
+#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 : ", SCpnt->host->host_no);
+	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:
+			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 : target %d lun %d request sense failed, performing reset.\n",
+					SCpnt->host->host_no, SCpnt->target, SCpnt->lun);
+				reset(SCpnt);
+				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 : RESERVATION CONFLICT performing reset.\n",
+					SCpnt->host->host_no);
+				reset(SCpnt);
+				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 : resetting for second half of retries.\n",
+						SCpnt->host->host_no);
+					reset(SCpnt);
+					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 %08x\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.dev) != SCSI_DISK_MAJOR &&
+			MAJOR(SCpnt->request.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.dev == -1 || 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 appears to have died when"
+				 " the bus was reset\n", 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, id %d, lun %d ",
+			       SCpnt->pid, SCpnt->host->host_no, (int) SCpnt->target, (int)
+			       SCpnt->lun);
+			print_command (SCpnt->cmnd);
+			if (SCpnt->request.dev == -1 || 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 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
+	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.dev > 0) {
+#if 0
+				    if (!(SCpnt1->flags & IS_RESETTING) &&
+				      !(SCpnt1->internal_timeout & IN_ABORT))
+				    scsi_abort(SCpnt1, DID_RESET, SCpnt->pid);
+#endif
+				    SCpnt1->flags |= 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;
+				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
+			switch(temp) {
+			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:
+			case SCSI_RESET_WAKEUP:
+			  SCpnt->internal_timeout &= ~IN_RESET;
+			  scsi_request_sense (SCpnt);
+			  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;
+/* hack! */
+	      if(SCpnt == SCset && SCpnt->timeout == 0)
+	        continue;
+	      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;
+	}
+
+
+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;
+#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 %x ",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 %x %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;
+}
+
+
+/* These are special functions that can be used to obtain memory at boot time.
+   They act line a malloc function, but they simply take memory from the
+   pool */
+
+static unsigned long scsi_init_memory_start = 0;
+static unsigned long scsi_memory_lower_value = 0;
+static unsigned long scsi_memory_upper_value = 0;
+int scsi_loadable_module_flag; /* Set after we scan builtin drivers */
+
+void * scsi_init_malloc(unsigned int size, int priority)
+{
+  unsigned long retval;
+
+/* Use the statically allocated memory instead of kmalloc  (DB) */
+#if defined(USE_STATIC_SCSI_MEMORY)
+  if(scsi_loadable_module_flag && !(priority & GFP_DMA))
+#else
+  if(scsi_loadable_module_flag)
+#endif
+    retval = (unsigned long) kmalloc(size, priority);
+  else {
+    /*
+     * Keep all memory aligned on 16-byte boundaries. Some host adaptors
+     * (e.g. BusLogic BT-445S) require DMA buffers to be aligned that way.
+     */
+    size = (size + 15) & ~15;
+
+    if(scsi_loadable_module_flag &&
+       (scsi_init_memory_start + size) > scsi_memory_upper_value) {
+       retval = 0;
+       printk("scsi_init_malloc: no more statically allocated memory.\n");
+       return (void *)retval;
+       }
+    else {
+       retval = scsi_init_memory_start;
+       scsi_init_memory_start += size;
+       }
+    }
+  memset((void *) retval, 0, size);
+  return (void *) retval;
+}
+
+
+void scsi_init_free(char * ptr, unsigned int size)
+{ /* We need to compare addresses to see whether this was kmalloc'd or not */
+
+  if((unsigned long) ptr >= scsi_init_memory_start ||
+     (unsigned long) ptr <  scsi_memory_lower_value) kfree(ptr);
+  else {
+    size = (size + 15) & ~15; /* Use the same alignment as scsi_init_malloc() */
+
+    if(((unsigned long) ptr) + size == scsi_init_memory_start)
+      scsi_init_memory_start = (unsigned long) ptr;
+    }
+}
+
+/*
+	scsi_dev_init() is our initialization routine, which in turn calls host
+	initialization, bus scanning, and sd/st initialization routines.  It
+	should be called from main().
+*/
+
+unsigned long scsi_dev_init (unsigned long memory_start,unsigned long memory_end)
+	{
+	struct Scsi_Host * host = NULL;
+	Scsi_Device * SDpnt;
+	struct Scsi_Host * shpnt;
+	struct Scsi_Device_Template * sdtpnt;
+	Scsi_Cmnd * SCpnt;
+	int i;
+#ifdef FOO_ON_YOU
+	return;
+#endif
+
+	/* Init a few things so we can "malloc" memory. */
+	scsi_loadable_module_flag = 0;
+	/* Align everything on 16-byte boundaries. */
+	scsi_init_memory_start = (memory_start + 15) & ~ 15;
+	scsi_memory_lower_value = scsi_init_memory_start;
+
+	timer_table[SCSI_TIMER].fn = scsi_main_timeout;
+	timer_table[SCSI_TIMER].expires = 0;
+
+	/* initialize all hosts */
+	scsi_init();
+
+	scsi_devices = (Scsi_Device *) NULL;
+
+	for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+	  scan_scsis(shpnt);           /* 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) {
+	  int j;
+	  SDpnt->scsi_request_fn = NULL;
+	  for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+	      if(sdtpnt->attach) (*sdtpnt->attach)(SDpnt);
+
+	  if(SDpnt->attached){
+	    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->request.dev = -1; /* Mark not busy */
+	      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;
+	    }
+	  }
+	}
+
+	if (scsi_devicelist)
+	  dma_sectors = 16;  /* Base value we use */
+
+	if (memory_end-1 > ISA_DMA_THRESHOLD)
+	  scsi_need_isa_bounce_buffers = 1;
+	else
+	  scsi_need_isa_bounce_buffers = 0;
+
+	for (SDpnt=scsi_devices; SDpnt; SDpnt = SDpnt->next) {
+	  host = SDpnt->host;
+
+	  if(SDpnt->type != TYPE_TAPE)
+	    dma_sectors += ((host->sg_tablesize *
+			     sizeof(struct scatterlist) + 511) >> 9) *
+			       host->cmd_per_lun;
+
+	  if(host->unchecked_isa_dma &&
+	     memory_end - 1 > ISA_DMA_THRESHOLD &&
+	     SDpnt->type != TYPE_TAPE) {
+	    dma_sectors += (PAGE_SIZE >> 9) * host->sg_tablesize *
+	      host->cmd_per_lun;
+	    need_isa_buffer++;
+	  }
+	}
+
+	dma_sectors = (dma_sectors + 15) & 0xfff0;
+	dma_free_sectors = dma_sectors;  /* This must be a multiple of 16 */
+
+	dma_malloc_freelist = (unsigned char *)
+	  scsi_init_malloc(dma_sectors >> 3, GFP_ATOMIC);
+	memset(dma_malloc_freelist, 0, dma_sectors >> 3);
+
+	dma_malloc_pages = (unsigned char **)
+	  scsi_init_malloc(dma_sectors >> 1, GFP_ATOMIC);
+	memset(dma_malloc_pages, 0, dma_sectors >> 1);
+
+	for(i=0; i< dma_sectors >> 3; i++)
+	  dma_malloc_pages[i] = (unsigned char *)
+	    scsi_init_malloc(PAGE_SIZE, GFP_ATOMIC | GFP_DMA);
+
+
+	/* 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;
+
+
+/* This allocates statically some extra memory to be used for modules,
+   until the kmalloc problem is fixed (DB) */
+
+#if defined(USE_STATIC_SCSI_MEMORY)
+	scsi_memory_upper_value = scsi_init_memory_start + 256 * 1024;
+	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);
+	return scsi_memory_upper_value;
+#else
+	return scsi_init_memory_start;
+#endif
+	}
+
+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] & 0x07) == 1 && (data[3] & 0x0f) == 1)
+	  printk(" CCS\n");
+	else
+	  printk("\n");
+}
+
+/*
+ * 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;
+  struct Scsi_Host * host = NULL;
+  unsigned long flags;
+  Scsi_Device * SDpnt;
+  Scsi_Cmnd * SCpnt;
+  struct Scsi_Device_Template * sdtpnt;
+  int j, i;
+  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;
+
+      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);
+
+      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){
+	      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->request.dev = -1; /* Mark not busy */
+		SCpnt->request.sem = NULL;
+		SCpnt->use_sg = 0;
+		SCpnt->old_use_sg = 0;
+		SCpnt->underflow = 0;
+		SCpnt->timeout = 0;
+		SCpnt->transfersize = 0;
+		SCpnt->host_scribble = NULL;
+		host = SDpnt->host;
+		SCpnt->next = host->host_queue;
+		SCpnt->prev = NULL;
+		host->host_queue = SCpnt;
+		if(host->host_queue)
+		  host->host_queue->prev = SCpnt;
+	      }
+	    }
+	  }
+	/* Next, check to see if we need to extend the DMA buffer pool */
+      {
+	  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_devicelist)
+	    new_dma_sectors = 16;  /* Base value we use */
+
+	  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;
+
+	  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);
+
+	  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();
+	  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;
+
+	  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 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
+
+  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.dev != -1) {
+	    restore_flags(flags);
+	    for(SCpnt = shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
+	      if(SCpnt->request.dev == 0xffe0) SCpnt->request.dev = -1;
+	    printk("Device busy???\n");
+	    return;
+	  }
+	  SCpnt->request.dev = 0xffe0;  /* 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;
+      }
+
+  /* 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;
+	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;
+  }
+
+  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;
+    }
+}
+
+int scsi_register_module(int module_type, void * ptr)
+{
+  switch(module_type){
+  case MODULE_SCSI_HA:
+    return scsi_register_host((Scsi_Host_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:
+
+    /* Load upper level device handler of some kind */
+  case MODULE_SCSI_DEV:
+  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;
+    /* The rest of these are not yet implemented. */
+  case MODULE_SCSI_CONST:
+  case MODULE_SCSI_IOCTL:
+  case MODULE_SCSI_DEV:
+  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 (802 123434 8 8 0) (3 3 2) (%d %d %d) %d %x      */
+	printk("(%d) %d:%d:%d (%4.4x %ld %ld %ld %ld) (%d %d %x) (%d %d %d) %x %x %x\n",
+	       i++, SCpnt->host->host_no,
+	       SCpnt->target,
+	       SCpnt->lun,
+	       SCpnt->request.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("(%x %d %ld %ld %ld) ",
+		 req->dev,
+		 req->cmd,
+		 req->sector,
+		 req->nr_sectors,
+		 req->current_nr_sectors);
+	  req = req->next;
+	}
+	printk("\n");
+      }
+}
+#endif
+
+/*
+ * 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: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -r -u -N linux.orig/arch/arm/drivers/scsi/scsi.h linux.arm/arch/arm/drivers/scsi/scsi.h
--- linux.orig/arch/arm/drivers/scsi/scsi.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/scsi.h	Fri Oct 27 23:15:12 1995
@@ -0,0 +1,653 @@
+/*
+ *	scsi.h Copyright (C) 1992 Drew Eckhardt 
+ *	generic SCSI package header file by
+ *		Drew Eckhardt 
+ *
+ *	<drew@colorado.edu>
+ *
+ *       Modified by Eric Youngdale eric@tantalus.nrl.navy.mil to
+ *       add scatter-gather, multiple outstanding request, and other
+ *       enhancements.
+ */
+
+#ifndef _SCSI_H
+#define _SCSI_H
+
+/*
+	$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/scsi.h,v 1.3 1993/09/24 12:20:33 drew Exp $
+
+	For documentation on the OPCODES, MESSAGES, and SENSE values,
+	please consult the SCSI standard.
+
+*/
+
+/*
+	SCSI opcodes
+*/
+
+#define TEST_UNIT_READY 	0x00
+#define REZERO_UNIT		0x01
+#define REQUEST_SENSE		0x03
+#define FORMAT_UNIT		0x04
+#define READ_BLOCK_LIMITS	0x05
+#define REASSIGN_BLOCKS		0x07
+#define READ_6			0x08
+#define WRITE_6			0x0a
+#define SEEK_6			0x0b
+#define READ_REVERSE		0x0f
+#define WRITE_FILEMARKS		0x10
+#define SPACE			0x11
+#define INQUIRY			0x12
+#define RECOVER_BUFFERED_DATA	0x14
+#define MODE_SELECT		0x15
+#define RESERVE			0x16
+#define RELEASE			0x17
+#define COPY			0x18
+#define ERASE			0x19
+#define MODE_SENSE		0x1a
+#define START_STOP		0x1b
+#define RECEIVE_DIAGNOSTIC	0x1c
+#define SEND_DIAGNOSTIC		0x1d
+#define ALLOW_MEDIUM_REMOVAL	0x1e
+
+#define SET_WINDOW              0x24
+#define READ_CAPACITY		0x25
+#define READ_10			0x28
+#define WRITE_10		0x2a
+#define SEEK_10			0x2b
+#define WRITE_VERIFY		0x2e
+#define VERIFY			0x2f
+#define SEARCH_HIGH		0x30
+#define SEARCH_EQUAL		0x31
+#define SEARCH_LOW		0x32
+#define SET_LIMITS		0x33
+#define PRE_FETCH		0x34
+#define READ_POSITION		0x34
+#define SYNCHRONIZE_CACHE	0x35
+#define LOCK_UNLOCK_CACHE	0x36
+#define READ_DEFECT_DATA	0x37
+#define MEDIUM_SCAN             0x38
+#define COMPARE			0x39
+#define COPY_VERIFY		0x3a
+#define WRITE_BUFFER		0x3b
+#define READ_BUFFER		0x3c
+#define UPDATE_BLOCK            0x3d
+#define READ_LONG		0x3e
+#define WRITE_LONG              0x3f
+#define CHANGE_DEFINITION	0x40
+#define WRITE_SAME             0x41
+#define LOG_SELECT		0x4c
+#define LOG_SENSE		0x4d
+#define MODE_SELECT_10		0x55
+#define MODE_SENSE_10		0x5a
+#define WRITE_12                0xaa
+#define WRITE_VERIFY_12         0xae
+#define SEARCH_HIGH_12          0xb0
+#define SEARCH_EQUAL_12         0xb1
+#define SEARCH_LOW_12           0xb2
+#define SEND_VOLUME_TAG         0xb6
+
+extern void scsi_make_blocked_list(void);
+extern volatile int in_scan_scsis;
+extern const unsigned char scsi_command_size[8];
+#define COMMAND_SIZE(opcode) scsi_command_size[((opcode) >> 5) & 7]
+
+/*
+	MESSAGE CODES
+*/
+
+#define COMMAND_COMPLETE	0x00
+#define EXTENDED_MESSAGE	0x01
+#define 	EXTENDED_MODIFY_DATA_POINTER	0x00
+#define 	EXTENDED_SDTR			0x01
+#define 	EXTENDED_EXTENDED_IDENTIFY	0x02	/* SCSI-I only */
+#define 	EXTENDED_WDTR			0x03
+#define SAVE_POINTERS		0x02
+#define RESTORE_POINTERS 	0x03
+#define DISCONNECT		0x04
+#define INITIATOR_ERROR		0x05
+#define ABORT			0x06
+#define MESSAGE_REJECT		0x07
+#define NOP			0x08
+#define MSG_PARITY_ERROR	0x09
+#define LINKED_CMD_COMPLETE	0x0a
+#define LINKED_FLG_CMD_COMPLETE	0x0b
+#define BUS_DEVICE_RESET	0x0c
+
+#define INITIATE_RECOVERY	0x0f			/* SCSI-II only */
+#define RELEASE_RECOVERY	0x10			/* SCSI-II only */
+
+#define SIMPLE_QUEUE_TAG	0x20
+#define HEAD_OF_QUEUE_TAG	0x21
+#define ORDERED_QUEUE_TAG	0x22
+
+#define IDENTIFY_BASE		0x80
+#define IDENTIFY(can_disconnect, lun)   (IDENTIFY_BASE |\
+					 ((can_disconnect) ?  0x40 : 0) |\
+					 ((lun) & 0x07)) 
+
+				 
+/*
+	Status codes
+*/
+
+#define GOOD			0x00
+#define CHECK_CONDITION		0x01
+#define CONDITION_GOOD		0x02
+#define BUSY			0x04
+#define INTERMEDIATE_GOOD	0x08
+#define INTERMEDIATE_C_GOOD	0x0a
+#define RESERVATION_CONFLICT	0x0c
+#define QUEUE_FULL              0x1a
+
+#define STATUS_MASK		0x1e
+	
+/*
+	the return of the status word will be in the following format :
+	The low byte is the status returned by the SCSI command, 
+	with vendor specific bits masked.
+
+	The next byte is the message which followed the SCSI status.
+	This allows a stos to be used, since the Intel is a little
+	endian machine.
+
+	The final byte is a host return code, which is one of the following.
+
+	IE 
+	lsb		msb
+	status	msg	host code	
+
+        Our errors returned by OUR driver, NOT SCSI message.  Or'd with
+        SCSI message passed back to driver <IF any>.
+*/
+
+/* 	NO error							*/
+#define DID_OK 			0x00
+/* 	Couldn't connect before timeout period				*/
+#define DID_NO_CONNECT		0x01
+/*	BUS stayed busy through time out period				*/
+#define DID_BUS_BUSY		0x02
+/*	TIMED OUT for other reason					*/
+#define DID_TIME_OUT		0x03
+/*	BAD target.							*/
+#define DID_BAD_TARGET		0x04
+/*	Told to abort for some other reason				*/
+#define	DID_ABORT		0x05
+/*
+	Parity error
+*/
+#define DID_PARITY		0x06
+/*
+	Internal error
+*/
+#define DID_ERROR 		0x07	
+/*
+	Reset by somebody.
+*/
+#define DID_RESET 		0x08
+/*
+	Got an interrupt we weren't expecting.
+*/
+#define	DID_BAD_INTR		0x09
+
+/*
+	Driver status
+*/ 
+#define DRIVER_OK		0x00
+
+/*
+	These indicate the error that occurred, and what is available.
+*/
+
+#define DRIVER_BUSY		0x01
+#define DRIVER_SOFT		0x02
+#define DRIVER_MEDIA		0x03
+#define DRIVER_ERROR		0x04	
+
+#define DRIVER_INVALID		0x05
+#define DRIVER_TIMEOUT		0x06
+#define DRIVER_HARD		0x07
+
+#define SUGGEST_RETRY		0x10
+#define SUGGEST_ABORT		0x20 
+#define SUGGEST_REMAP		0x30
+#define SUGGEST_DIE		0x40
+#define SUGGEST_SENSE		0x80
+#define SUGGEST_IS_OK		0xff
+
+#define DRIVER_SENSE		0x08
+
+#define DRIVER_MASK 0x0f
+#define SUGGEST_MASK 0xf0
+
+/*
+
+	SENSE KEYS
+*/
+
+#define NO_SENSE 		0x00
+#define RECOVERED_ERROR		0x01
+#define NOT_READY		0x02
+#define MEDIUM_ERROR		0x03
+#define	HARDWARE_ERROR		0x04
+#define ILLEGAL_REQUEST		0x05
+#define UNIT_ATTENTION		0x06
+#define DATA_PROTECT		0x07
+#define BLANK_CHECK		0x08
+#define COPY_ABORTED		0x0a
+#define ABORTED_COMMAND		0x0b
+#define	VOLUME_OVERFLOW		0x0d
+#define MISCOMPARE		0x0e
+
+
+/*
+	DEVICE TYPES
+
+*/
+
+#define TYPE_DISK	0x00
+#define TYPE_TAPE	0x01
+#define TYPE_PROCESSOR	0x03	/* HP scanners use this */
+#define TYPE_WORM	0x04	/* Treated as ROM by our system */
+#define TYPE_ROM	0x05
+#define TYPE_SCANNER	0x06
+#define TYPE_MOD	0x07  /* Magneto-optical disk - treated as TYPE_DISK */
+#define TYPE_NO_LUN	0x7f
+
+
+#define MAX_COMMAND_SIZE 12
+/*
+	SCSI command sets
+
+*/
+
+#define SCSI_UNKNOWN	0
+#define	SCSI_1		1
+#define	SCSI_1_CCS	2
+#define	SCSI_2		3
+
+/*
+	Every SCSI command starts with a one byte OP-code.
+	The next byte's high three bits are the LUN of the
+	device.  Any multi-byte quantities are stored high byte
+	first, and may have a 5 bit MSB in the same byte
+	as the LUN.
+*/
+
+/*
+        Manufacturers list
+*/
+
+#define SCSI_MAN_UNKNOWN     0
+#define SCSI_MAN_NEC         1
+#define SCSI_MAN_TOSHIBA     2
+#define SCSI_MAN_NEC_OLDCDR  3
+
+/*
+	The scsi_device struct contains what we know about each given scsi
+	device.
+*/
+
+typedef struct scsi_device {
+        struct scsi_device * next; /* Used for linked list */
+	unsigned char id, lun;
+	unsigned int manufacturer; /* Manufacturer of device, for using vendor-specific cmd's */
+	int attached;          /* # of high level drivers attached to this */
+	int access_count;	/* Count of open channels/mounts */
+	struct wait_queue * device_wait;  /* Used to wait if device is busy */
+	struct Scsi_Host * host;
+	void (*scsi_request_fn)(void); /* Used to jumpstart things after an ioctl */
+	void *hostdata;                   /* available to low-level driver */
+	char type;
+	char scsi_level;
+	unsigned writeable:1;
+	unsigned removable:1; 
+	unsigned random:1;
+	unsigned changed:1;	/* Data invalid due to media change */
+	unsigned busy:1;	/* Used to prevent races */
+	unsigned lockable:1;    /* Able to prevent media removal */
+	unsigned borken:1;	/* Tell the Seagate driver to be 
+				   painfully slow on this device */ 
+	unsigned tagged_supported:1; /* Supports SCSI-II tagged queuing */
+	unsigned tagged_queue:1;   /*SCSI-II tagged queuing enabled */
+	unsigned disconnect:1;     /* can disconnect */
+	unsigned soft_reset:1;		/* Uses soft reset option */
+	unsigned char current_tag; /* current tag */
+	unsigned sync:1;	/* Negotiate for sync transfers */
+	unsigned char sync_min_period;	/* Not less than this period */
+	unsigned char sync_max_offset;  /* Not greater than this offset */
+} Scsi_Device;
+/*
+	Use these to separate status msg and our bytes
+*/
+
+#define status_byte(result) (((result) >> 1) & 0xf)
+#define msg_byte(result) (((result) >> 8) & 0xff)
+#define host_byte(result) (((result) >> 16) & 0xff)
+#define driver_byte(result) (((result) >> 24) & 0xff)
+#define suggestion(result) (driver_byte(result) & SUGGEST_MASK)
+
+#define sense_class(sense) (((sense) >> 4) & 0x7)
+#define sense_error(sense) ((sense) & 0xf)
+#define sense_valid(sense) ((sense) & 0x80);
+
+/*
+	These are the SCSI devices available on the system.
+*/
+
+extern Scsi_Device * scsi_devices;
+/*
+	Initializes all SCSI devices.  This scans all scsi busses.
+*/
+
+extern unsigned long scsi_dev_init (unsigned long, unsigned long);
+
+struct scatterlist {
+     char *  address; /* Location data is to be transferred to */
+     char * alt_address; /* Location of actual if address is a 
+			    dma indirect buffer.  NULL otherwise */
+     unsigned int length;
+     };
+
+#define ISA_DMA_THRESHOLD (0x00ffffff)
+#define CONTIGUOUS_BUFFERS(X,Y) ((X->b_data+X->b_size) == Y->b_data)
+
+
+/*
+ * These are the return codes for the abort and reset functions.  The mid-level
+ * code uses these to decide what to do next.  Each of the low level abort
+ * and reset functions must correctly indicate what it has done.
+ */
+
+/* We did not do anything.  Wait
+   some more for this command to complete, and if this does not work, try
+   something more serious. */ 
+#define SCSI_ABORT_SNOOZE 0
+
+/* This means that we were able to abort the command.  We have already
+   called the mid-level done function, and do not expect an interrupt that will
+   lead to another call to the mid-level done function for this command */
+#define SCSI_ABORT_SUCCESS 1
+
+/* We called for an abort of this command, and we should get an interrupt 
+   when this succeeds.  Thus we should not restore the timer for this
+   command in the mid-level abort function. */
+#define SCSI_ABORT_PENDING 2
+
+/* Unable to abort - command is currently on the bus.  Grin and bear it. */
+#define SCSI_ABORT_BUSY 3
+
+/* The command is not active in the low level code. Command probably
+   finished. */
+#define SCSI_ABORT_NOT_RUNNING 4
+
+/* Something went wrong.  The low level driver will indicate the correct
+ error condition when it calls scsi_done, so the mid-level abort function
+ can simply wait until this comes through */
+#define SCSI_ABORT_ERROR 5
+
+/* We do not know how to reset the bus, or we do not want to.  Bummer.
+   Anyway, just wait a little more for the command in question, and hope that
+   it eventually finishes.  If it never finishes, the SCSI device could
+   hang, so use this with caution. */
+#define SCSI_RESET_SNOOZE 0
+
+/* We do not know how to reset the bus, or we do not want to.  Bummer.
+   We have given up on this ever completing.  The mid-level code will
+   request sense information to decide how to proceed from here. */
+#define SCSI_RESET_PUNT 1
+
+/* This means that we were able to reset the bus.  We have restarted all of
+   the commands that should be restarted, and we should be able to continue
+   on normally from here.  We do not expect any interrupts that will return
+   DID_RESET to any of the other commands in the host_queue, and the mid-level
+   code does not need to do anything special to keep the commands alive. */
+#define SCSI_RESET_SUCCESS 2
+
+/* We called for a reset of this bus, and we should get an interrupt 
+   when this succeeds.  Each command should get its own status
+   passed up to scsi_done, but this has not happened yet. */
+#define SCSI_RESET_PENDING 3
+
+/* We did a reset, but do not expect an interrupt to signal DID_RESET.
+   This tells the upper level code to request the sense info, and this
+   should keep the command alive. */
+#define SCSI_RESET_WAKEUP 4
+
+/* Something went wrong, and we do not know how to fix it. */
+#define SCSI_RESET_ERROR 5
+
+void *   scsi_malloc(unsigned int);
+int      scsi_free(void *, unsigned int);
+extern unsigned int dma_free_sectors;   /* How much room do we have left */
+extern unsigned int need_isa_buffer;   /* True if some devices need indirection
+				 buffers */
+
+/*
+	The Scsi_Cmnd structure is used by scsi.c internally, and for communication with
+	low level drivers that support multiple outstanding commands.
+*/
+typedef struct scsi_pointer {
+  char * ptr;                     /* data pointer */
+  int this_residual;              /* left in this buffer */
+  struct scatterlist *buffer;     /* which buffer */
+  int buffers_residual;           /* how many buffers left */
+
+  volatile int Status;
+  volatile int Message;
+  volatile int have_data_in;
+  volatile int sent_command;
+  volatile int phase;
+} Scsi_Pointer;
+
+typedef struct scsi_cmnd {
+	struct Scsi_Host * host;
+	Scsi_Device * device;
+	unsigned char target, lun;
+	unsigned char cmd_len;
+	unsigned char old_cmd_len;
+	struct scsi_cmnd *next, *prev;	
+
+/* These elements define the operation we are about to perform */
+	unsigned char cmnd[12];
+	unsigned request_bufflen; /* Actual request size */
+
+	void * request_buffer;  /* Actual requested buffer */
+
+/* These elements define the operation we ultimately want to perform */
+	unsigned char data_cmnd[12];
+	unsigned short old_use_sg;  /* We save  use_sg here when requesting
+				       sense info */
+	unsigned short use_sg;  /* Number of pieces of scatter-gather */
+	unsigned short sglist_len;  /* size of malloc'd scatter-gather list */
+	unsigned short abort_reason;  /* If the mid-level code requests an
+					 abort, this is the reason. */
+	unsigned bufflen;     /* Size of data buffer */
+	void *buffer;   /* Data buffer */
+
+	unsigned underflow;	/* Return error if less than this amount is 
+				   transfered */
+
+	unsigned transfersize;	/* How much we are guaranteed to transfer with
+				   each SCSI transfer (ie, between disconnect /
+				   reconnects.   Probably == sector size */
+	
+	
+	
+	struct request request;  /* A copy of the command we are working on*/
+
+	unsigned char sense_buffer[16];	 /* Sense for this command, if needed*/
+
+
+	int retries;
+	int allowed;
+	int timeout_per_command, timeout_total, timeout;
+/*
+ *	We handle the timeout differently if it happens when a reset, 
+ *	abort, etc are in process. 
+ */
+
+	unsigned volatile char internal_timeout;
+
+	unsigned flags;
+		
+/* These variables are for the cdrom only.  Once we have variable size buffers
+   in the buffer cache, they will go away. */
+	int this_count; 
+/* End of special cdrom variables */
+	
+	/* Low-level done function - can be used by low-level driver to point
+	 to completion function.  Not used by mid/upper level code. */
+	void (*scsi_done)(struct scsi_cmnd *);  
+	void (*done)(struct scsi_cmnd *);  /* Mid-level done function */
+
+/* The following fields can be written to by the host specific code. 
+   Everything else should be left alone. */
+
+	Scsi_Pointer SCp;   /* Scratchpad used by some host adapters */
+
+	unsigned char * host_scribble; /* The host adapter is allowed to
+					  call scsi_malloc and get some memory
+					  and hang it here.  The host adapter
+					  is also expected to call scsi_free
+					  to release this memory.  (The memory
+					  obtained by scsi_malloc is guaranteed
+					  to be at an address < 16Mb). */
+
+	int result;                   /* Status code from lower level driver */
+
+	unsigned char tag;		/* SCSI-II queued command tag */
+	unsigned long pid;		/* Process ID, starts at 0 */
+	} Scsi_Cmnd;		 
+
+/*
+	scsi_abort aborts the current command that is executing on host host.
+	The error code, if non zero is returned in the host byte, otherwise 
+	DID_ABORT is returned in the hostbyte.
+*/
+
+extern int scsi_abort (Scsi_Cmnd *, int code, int pid);
+
+extern void scsi_do_cmd (Scsi_Cmnd *, const void *cmnd ,
+                  void *buffer, unsigned bufflen, void (*done)(struct scsi_cmnd *),
+                  int timeout, int retries);
+
+
+extern Scsi_Cmnd * allocate_device(struct request **, Scsi_Device *, int);
+
+extern Scsi_Cmnd * request_queueable(struct request *, Scsi_Device *);
+extern int scsi_reset (Scsi_Cmnd *);
+
+extern int max_scsi_hosts;
+
+#if defined(MAJOR_NR) && (MAJOR_NR != SCSI_TAPE_MAJOR)
+#include "hosts.h"
+static Scsi_Cmnd * end_scsi_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors)
+{
+	struct request * req;
+	struct buffer_head * bh;
+
+	req = &SCpnt->request;
+	req->errors = 0;
+	if (!uptodate) {
+		printk(DEVICE_NAME " I/O error: dev %04x, sector %lu\n",
+		       req->dev,req->sector);
+	}
+
+	do {
+	  if ((bh = req->bh) != NULL) {
+	    req->bh = bh->b_reqnext;
+	    req->nr_sectors -= bh->b_size >> 9;
+	    req->sector += bh->b_size >> 9;
+	    bh->b_reqnext = NULL;
+	    bh->b_uptodate = uptodate;
+	    unlock_buffer(bh);
+	    sectors -= bh->b_size >> 9;
+	    if ((bh = req->bh) != NULL) {
+	      req->current_nr_sectors = bh->b_size >> 9;
+	      if (req->nr_sectors < req->current_nr_sectors) {
+		req->nr_sectors = req->current_nr_sectors;
+		printk("end_scsi_request: buffer-list destroyed\n");
+	      }
+	    }
+	  }
+	} while(sectors && bh);
+	if (req->bh){
+	  req->buffer = bh->b_data;
+	  return SCpnt;
+	};
+	DEVICE_OFF(req->dev);
+	if (req->sem != NULL) {
+		up(req->sem);
+	}
+
+        if (SCpnt->host->block) {
+           struct Scsi_Host * next;
+
+           for (next = SCpnt->host->block; next != SCpnt->host;
+                                                   next = next->block)
+              wake_up(&next->host_wait);
+           }
+
+	req->dev = -1;
+	wake_up(&wait_for_request);
+	wake_up(&SCpnt->device->device_wait);
+	return NULL;
+}
+
+
+/* This is just like INIT_REQUEST, but we need to be aware of the fact
+   that an interrupt may start another request, so we run this with interrupts
+   turned off */
+
+#define INIT_SCSI_REQUEST \
+	if (!CURRENT) {\
+		CLEAR_INTR; \
+		restore_flags(flags);   \
+		return; \
+	} \
+	if (MAJOR(CURRENT->dev) != MAJOR_NR) \
+		panic(DEVICE_NAME ": request list destroyed"); \
+	if (CURRENT->bh) { \
+		if (!CURRENT->bh->b_lock) \
+			panic(DEVICE_NAME ": block not locked"); \
+	}
+#endif
+
+#define SCSI_SLEEP(QUEUE, CONDITION) {				\
+	if (CONDITION) {					\
+		struct wait_queue wait = { current, NULL};	\
+		add_wait_queue(QUEUE, &wait);			\
+        for(;;) {       					\
+		current->state = TASK_UNINTERRUPTIBLE;		\
+		if (CONDITION) {				\
+                   if (intr_count)                              \
+                      panic("scsi: trying to call schedule() in interrupt" \
+                            ", file %s, line %d.\n", __FILE__, __LINE__);  \
+		   schedule();				 	\
+		   }              			 	\
+	        else						\
+                   break;                            	        \
+		}						\
+		remove_wait_queue(QUEUE, &wait);		\
+		current->state = TASK_RUNNING;			\
+	}; }
+
+#endif
+
+/*
+ * 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: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -r -u -N linux.orig/arch/arm/drivers/scsi/scsi_ioctl.c linux.arm/arch/arm/drivers/scsi/scsi_ioctl.c
--- linux.orig/arch/arm/drivers/scsi/scsi_ioctl.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/scsi_ioctl.c	Fri Oct 27 23:15:12 1995
@@ -0,0 +1,330 @@
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+
+#include "../block/blk.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "scsi_ioctl.h"
+
+#define MAX_RETRIES 5	
+#define MAX_TIMEOUT 900
+#define MAX_BUF 4096
+
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+/*
+ * If we are told to probe a host, we will return 0 if  the host is not
+ * present, 1 if the host is present, and will return an identifying
+ * string at *arg, if arg is non null, filling to the length stored at
+ * (int *) arg
+ */
+
+static int ioctl_probe(struct Scsi_Host * host, void *buffer)
+{
+	int temp;
+	unsigned int len,slen;
+	const char * string;
+	
+	if ((temp = host->hostt->present) && buffer) {
+		len = get_fs_long ((unsigned long *) buffer);
+		if(host->hostt->info)
+		  string = host->hostt->info(host);
+		else 
+		  string = host->hostt->name;
+		if(string) {
+		  slen = strlen(string);
+		  if (len > slen)
+		    len = slen + 1;
+		  verify_area(VERIFY_WRITE, buffer, len);
+		  memcpy_tofs (buffer, string, len);
+		}
+	}
+	return temp;
+}
+
+/*
+ * 
+ * The SCSI_IOCTL_SEND_COMMAND ioctl sends a command out to the SCSI host.
+ * The MAX_TIMEOUT and MAX_RETRIES  variables are used.  
+ * 
+ * dev is the SCSI device struct ptr, *(int *) arg is the length of the
+ * input data, if any, not including the command string & counts, 
+ * *((int *)arg + 1) is the output buffer size in bytes.
+ * 
+ * *(char *) ((int *) arg)[2] the actual command byte.   
+ * 
+ * Note that no more than MAX_BUF data bytes will be transfered.  Since
+ * SCSI block device size is 512 bytes, I figured 1K was good.
+ * but (WDE) changed it to 8192 to handle large bad track buffers.
+ * ERY: I changed this to a dynamic allocation using scsi_malloc - we were
+ * getting a kernel stack overflow which was crashing the system when we
+ * were using 8192 bytes.
+ * 
+ * This size *does not* include the initial lengths that were passed.
+ * 
+ * The SCSI command is read from the memory location immediately after the
+ * length words, and the input data is right after the command.  The SCSI
+ * routines know the command size based on the opcode decode.  
+ * 
+ * The output area is then filled in starting from the command byte. 
+ */
+
+static void scsi_ioctl_done (Scsi_Cmnd * SCpnt)
+{
+  struct request * req;
+  
+  req = &SCpnt->request;
+  req->dev = 0xfffe; /* Busy, but indicate request done */
+  
+  if (req->sem != NULL) {
+    up(req->sem);
+  }
+}	
+
+static int ioctl_internal_command(Scsi_Device *dev, char * cmd)
+{
+	int result;
+	Scsi_Cmnd * SCpnt;
+
+	SCpnt = allocate_device(NULL, dev, 1);
+	scsi_do_cmd(SCpnt,  cmd, NULL,  0,
+			scsi_ioctl_done,  MAX_TIMEOUT,
+			MAX_RETRIES);
+
+	if (SCpnt->request.dev != 0xfffe){
+	  struct semaphore sem = MUTEX_LOCKED;
+	  SCpnt->request.sem = &sem;
+	  down(&sem);
+	  /* Hmm.. Have to ask about this one */
+	  while (SCpnt->request.dev != 0xfffe) schedule();
+	};
+
+	if(driver_byte(SCpnt->result) != 0)
+	  switch(SCpnt->sense_buffer[2] & 0xf) {
+	  case ILLEGAL_REQUEST:
+	    if(cmd[0] == ALLOW_MEDIUM_REMOVAL) dev->lockable = 0;
+	    else printk("SCSI device (ioctl) reports ILLEGAL REQUEST.\n");
+	    break;
+	  case NOT_READY: /* This happens if there is no disc in drive */
+	    if(dev->removable){
+	      printk("Device not ready.  Make sure there is a disc in the drive.\n");
+	      break;
+	    };
+	  case UNIT_ATTENTION:
+	    if (dev->removable){
+	      dev->changed = 1;
+	      SCpnt->result = 0; /* This is no longer considered an error */
+	      printk("Disc change detected.\n");
+	      break;
+	    };
+	  default: /* Fall through for non-removable media */
+	    printk("SCSI CD error: host %d id %d lun %d return code = %x\n",
+		   dev->host->host_no,
+		   dev->id,
+		   dev->lun,
+		   SCpnt->result);
+	    printk("\tSense class %x, sense error %x, extended sense %x\n",
+		   sense_class(SCpnt->sense_buffer[0]),
+		   sense_error(SCpnt->sense_buffer[0]),
+		   SCpnt->sense_buffer[2] & 0xf);
+
+	  };
+
+	result = SCpnt->result;
+	SCpnt->request.dev = -1;
+	wake_up(&SCpnt->device->device_wait);
+	return result;
+}
+
+static int ioctl_command(Scsi_Device *dev, void *buffer)
+{
+	char * buf;
+	char cmd[12];
+	char * cmd_in;
+	Scsi_Cmnd * SCpnt;
+	unsigned char opcode;
+	int inlen, outlen, cmdlen;
+	int needed, buf_needed;
+	int result;
+
+	if (!buffer)
+		return -EINVAL;
+	
+	inlen = get_fs_long((unsigned long *) buffer);
+	outlen = get_fs_long( ((unsigned long *) buffer) + 1);
+
+	cmd_in = (char *) ( ((int *)buffer) + 2);
+	opcode = get_fs_byte(cmd_in); 
+
+	needed = buf_needed = (inlen > outlen ? inlen : outlen);
+	if(buf_needed){
+	  buf_needed = (buf_needed + 511) & ~511;
+	  if (buf_needed > MAX_BUF) buf_needed = MAX_BUF;
+	  buf = (char *) scsi_malloc(buf_needed);
+	  if (!buf) return -ENOMEM;
+	  memset(buf, 0, buf_needed);
+	} else
+	  buf = NULL;
+
+	memcpy_fromfs ((void *) cmd,  cmd_in,  cmdlen = COMMAND_SIZE (opcode));
+	memcpy_fromfs ((void *) buf,  (void *) (cmd_in + cmdlen), inlen > MAX_BUF ? MAX_BUF : inlen);
+
+	cmd[1] = ( cmd[1] & 0x1f ) | (dev->lun << 5);
+
+#ifndef DEBUG_NO_CMD
+	
+	SCpnt = allocate_device(NULL, dev, 1);
+
+	scsi_do_cmd(SCpnt,  cmd,  buf, needed,  scsi_ioctl_done,  MAX_TIMEOUT, 
+			MAX_RETRIES);
+
+	if (SCpnt->request.dev != 0xfffe){
+	  struct semaphore sem = MUTEX_LOCKED;
+	  SCpnt->request.sem = &sem;
+	  down(&sem);
+	  /* Hmm.. Have to ask about this one */
+	  while (SCpnt->request.dev != 0xfffe) schedule();
+	};
+
+
+	/* If there was an error condition, pass the info back to the user. */
+	if(SCpnt->result) {
+	  result = verify_area(VERIFY_WRITE, cmd_in, sizeof(SCpnt->sense_buffer));
+	  if (result)
+	    return result;
+	  memcpy_tofs((void *) cmd_in,  SCpnt->sense_buffer, sizeof(SCpnt->sense_buffer));
+	} else {
+
+	  result = verify_area(VERIFY_WRITE, cmd_in, (outlen > MAX_BUF) ? MAX_BUF  : outlen);
+	  if (result)
+	    return result;
+	  memcpy_tofs ((void *) cmd_in,  buf,  (outlen > MAX_BUF) ? MAX_BUF  : outlen);
+	};
+	result = SCpnt->result;
+	SCpnt->request.dev = -1;  /* Mark as not busy */
+	if (buf) scsi_free(buf, buf_needed);
+
+	if(SCpnt->device->scsi_request_fn)
+	  (*SCpnt->device->scsi_request_fn)();
+
+	wake_up(&SCpnt->device->device_wait);
+	return result;
+#else
+	{
+	int i;
+	printk("scsi_ioctl : device %d.  command = ", dev->id);
+	for (i = 0; i < 12; ++i)
+		printk("%02x ", cmd[i]);
+	printk("\nbuffer =");
+	for (i = 0; i < 20; ++i)
+		printk("%02x ", buf[i]);
+	printk("\n");
+	printk("inlen = %d, outlen = %d, cmdlen = %d\n",
+		inlen, outlen, cmdlen);
+	printk("buffer = %d, cmd_in = %d\n", buffer, cmd_in);
+	}
+	return 0;
+#endif
+}
+
+/*
+	the scsi_ioctl() function differs from most ioctls in that it does
+	not take a major/minor number as the dev filed.  Rather, it takes
+	a pointer to a scsi_devices[] element, a structure. 
+*/
+int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg)
+{
+        char scsi_cmd[12];
+
+	/* No idea how this happens.... */
+	if (!dev) return -ENXIO;
+	
+	switch (cmd) {
+	        case SCSI_IOCTL_GET_IDLUN:
+	                verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
+			put_fs_long(dev->id + (dev->lun << 8) + 
+				    (dev->host->host_no << 16), (unsigned long *) arg);
+			return 0;
+		case SCSI_IOCTL_TAGGED_ENABLE:
+			if(!suser())  return -EACCES;
+			if(!dev->tagged_supported) return -EINVAL;
+			dev->tagged_queue = 1;
+			dev->current_tag = 1;
+			break;
+		case SCSI_IOCTL_TAGGED_DISABLE:
+			if(!suser())  return -EACCES;
+			if(!dev->tagged_supported) return -EINVAL;
+			dev->tagged_queue = 0;
+			dev->current_tag = 0;
+			break;
+		case SCSI_IOCTL_PROBE_HOST:
+			return ioctl_probe(dev->host, arg);
+		case SCSI_IOCTL_SEND_COMMAND:
+			if(!suser())  return -EACCES;
+			return ioctl_command((Scsi_Device *) dev, arg);
+		case SCSI_IOCTL_DOORLOCK:
+			if (!dev->removable || !dev->lockable) return 0;
+		        scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL;
+			scsi_cmd[1] = dev->lun << 5;
+			scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+			scsi_cmd[4] = SCSI_REMOVAL_PREVENT;
+			return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd);
+			break;
+		case SCSI_IOCTL_DOORUNLOCK:
+			if (!dev->removable || !dev->lockable) return 0;
+		        scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL;
+			scsi_cmd[1] = dev->lun << 5;
+			scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+			scsi_cmd[4] = SCSI_REMOVAL_ALLOW;
+			return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd);
+		case SCSI_IOCTL_TEST_UNIT_READY:
+		        scsi_cmd[0] = TEST_UNIT_READY;
+			scsi_cmd[1] = dev->lun << 5;
+			scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+			scsi_cmd[4] = 0;
+			return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd);
+			break;
+		default :			
+			return -EINVAL;
+	}
+	return -EINVAL;
+}
+
+/*
+ * Just like scsi_ioctl, only callable from kernel space with no 
+ * fs segment fiddling.
+ */
+
+int kernel_scsi_ioctl (Scsi_Device *dev, int cmd, void *arg) {
+  unsigned long oldfs;
+  int tmp;
+  oldfs = get_fs();
+  set_fs(get_ds());
+  tmp = scsi_ioctl (dev, cmd, arg);
+  set_fs(oldfs);
+  return tmp;
+}
+
+/*
+ * 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: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -r -u -N linux.orig/arch/arm/drivers/scsi/scsi_ioctl.h linux.arm/arch/arm/drivers/scsi/scsi_ioctl.h
--- linux.orig/arch/arm/drivers/scsi/scsi_ioctl.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/scsi_ioctl.h	Fri Oct 27 23:15:12 1995
@@ -0,0 +1,22 @@
+#ifndef _SCSI_IOCTL_H
+#define _SCSI_IOCTL_H 
+
+#define SCSI_IOCTL_PROBE_HOST 0
+#define SCSI_IOCTL_SEND_COMMAND 1
+#define SCSI_IOCTL_TEST_UNIT_READY 2
+#define SCSI_IOCTL_BENCHMARK_COMMAND 3
+#define SCSI_IOCTL_SYNC 4			/* Request synchronous parameters */
+/* The door lock/unlock constants are compatible with Sun constants for
+   the cdrom */
+#define SCSI_IOCTL_DOORLOCK 0x5380		/* lock the eject mechanism */
+#define SCSI_IOCTL_DOORUNLOCK 0x5381		/* unlock the mechanism	  */
+
+#define	SCSI_REMOVAL_PREVENT	1
+#define	SCSI_REMOVAL_ALLOW	0
+
+extern int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
+extern int kernel_scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
+
+#endif
+
+
diff -r -u -N linux.orig/arch/arm/drivers/scsi/scsi_module.c linux.arm/arch/arm/drivers/scsi/scsi_module.c
--- linux.orig/arch/arm/drivers/scsi/scsi_module.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/scsi_module.c	Fri Oct 27 23:15:12 1995
@@ -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 -r -u -N linux.orig/arch/arm/drivers/scsi/scsicam.c linux.arm/arch/arm/drivers/scsi/scsicam.c
--- linux.orig/arch/arm/drivers/scsi/scsicam.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/scsicam.c	Fri Oct 27 23:15:12 1995
@@ -0,0 +1,191 @@
+/*
+ * scsicam.c - SCSI CAM support functions, use for HDIO_GETGEO, etc.
+ *
+ * Copyright 1993, 1994 Drew Eckhardt
+ *      Visionary Computing 
+ *      (Unix and Linux consulting and custom programming)
+ *      drew@Colorado.EDU
+ *	+1 (303) 786-7975
+ *
+ * For more information, please consult the SCSI-CAM draft.
+ */
+
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include "../block/blk.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+
+static int partsize(struct buffer_head *bh, unsigned long capacity,
+    unsigned int  *cyls, unsigned int *hds, unsigned int *secs);
+static int setsize(unsigned long capacity,unsigned int *cyls,unsigned int *hds,
+    unsigned int *secs);
+
+/*
+ * Function : int scsicam_bios_param (Disk *disk, int dev, int *ip)
+ *
+ * Purpose : to determine the BIOS mapping used for a drive in a 
+ *	SCSI-CAM system, storing the results in ip as required
+ *	by the HDIO_GETGEO ioctl().
+ *
+ * Returns : -1 on failure, 0 on success.
+ *
+ */
+
+int scsicam_bios_param (Disk *disk, /* SCSI disk */
+	int dev,		/* Device major, minor */
+    	int *ip			/* Heads, sectors, cylinders in that order */) {
+    struct buffer_head *bh;
+    int ret_code;
+    int size = disk->capacity;
+
+    if (!(bh = bread(dev & ~0xf,0,1024)))
+	return -1;
+
+#ifdef DEBUG
+	printk ("scsicam_bios_param : trying existing mapping\n");
+#endif
+    ret_code = partsize (bh, (unsigned long) size, (unsigned int *) ip + 2, 
+	(unsigned int *) ip + 0, (unsigned int *) ip + 1);
+    brelse (bh);
+
+    if (ret_code == -1) {
+#ifdef DEBUG
+	printk ("scsicam_bios_param : trying optimal mapping\n");
+#endif
+	ret_code = setsize ((unsigned long) size, (unsigned int *) ip + 2, 
+    	    (unsigned int *) ip + 0, (unsigned int *) ip + 1);
+    }
+
+    return ret_code;
+}
+
+/*
+ * Function : static int partsize(struct buffer_head *bh, unsigned long 
+ *	capacity,unsigned int *cyls, unsigned int *hds, unsigned int secs);
+ *
+ * Purpose : to determine the BIOS mapping used to create the partition
+ *	table, storing the results in *cyls, *hds, and *secs 
+ *
+ * Returns : -1 on failure, 0 on success.
+ *
+ */
+
+static int partsize(struct buffer_head *bh, unsigned long capacity,
+    unsigned int  *cyls, unsigned int *hds, unsigned int *secs) {
+    struct partition *p, *largest = NULL;
+    int i, largest_cyl;
+    int cyl, end_head, end_cyl, end_sector;
+    unsigned int logical_end, physical_end;
+    
+
+    if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
+	for (largest_cyl = -1, p = (struct partition *) 
+    	    (0x1BE + bh->b_data), i = 0; i < 4; ++i, ++p) {
+    	    if (!p->sys_ind)
+    	    	continue;
+#ifdef DEBUG
+	printk ("scsicam_bios_param : partition %d has system \n",
+	    i);
+#endif
+    	    cyl = p->cyl + ((p->sector & 0xc0) << 2);
+    	    if (cyl > largest_cyl) {
+    	    	largest_cyl = cyl;
+    	    	largest = p;
+    	    }
+    	}
+    }
+
+    if (largest) {
+    	end_cyl = largest->end_cyl + ((largest->end_sector & 0xc0) << 2);
+    	end_head = largest->end_head;
+    	end_sector = largest->end_sector & 0x3f;
+#ifdef DEBUG
+	printk ("scsicam_bios_param : end at h = %d, c = %d, s = %d\n",
+	    end_head, end_cyl, end_sector);
+#endif
+
+    	physical_end =  end_cyl * (end_head + 1) * end_sector +
+    	    end_head * end_sector + end_sector;
+
+	/* This is the actual _sector_ number at the end */
+	logical_end = largest->start_sect + largest->nr_sects;
+
+    	if (logical_end == physical_end) {
+    	    *secs = end_sector;
+    	    *hds = end_head + 1;
+    	    *cyls = capacity / ((end_head + 1) * end_sector);
+    	    return 0;
+    	}
+#ifdef DEBUG
+	printk ("scsicam_bios_param : logical (%u) != physical (%u)\n",
+	    logical_end, physical_end);
+#endif
+    }
+    return -1;
+}
+
+/*
+ * Function : static int setsize(unsigned long capacity,unsigned int *cyls,
+ *	unsigned int *hds, unsigned int secs);
+ *
+ * Purpose : to determine a near-optimal int 0x13 mapping for a
+ *	SCSI disk in terms of lost space of size capacity, storing
+ *	the results in *cyls, *hds, and *secs.
+ *
+ * Returns : -1 on failure, 0 on success.
+ *
+ * Extracted from
+ *
+ * WORKING                                                    X3T9.2
+ * DRAFT                                                        792D
+ *
+ *
+ *                                                        Revision 6
+ *                                                         10-MAR-94
+ * Information technology -
+ * SCSI-2 Common access method
+ * transport and SCSI interface module
+ * 
+ * ANNEX A :
+ *
+ * setsize() converts a read capacity value to int 13h
+ * head-cylinder-sector requirements. It minimizes the value for
+ * number of heads and maximizes the number of cylinders. This
+ * will support rather large disks before the number of heads
+ * will not fit in 4 bits (or 6 bits). This algorithm also
+ * minimizes the number of sectors that will be unused at the end
+ * of the disk while allowing for very large disks to be
+ * accommodated. This algorithm does not use physical geometry. 
+ */
+
+static int setsize(unsigned long capacity,unsigned int *cyls,unsigned int *hds,
+    unsigned int *secs) { 
+    unsigned int rv = 0; 
+    unsigned long heads, sectors, cylinders, temp; 
+
+    cylinders = 1024L;			/* Set number of cylinders to max */ 
+    sectors = 62L;      		/* Maximize sectors per track */ 
+
+    temp = cylinders * sectors;		/* Compute divisor for heads */ 
+    heads = capacity / temp;		/* Compute value for number of heads */
+    if (capacity % temp) {		/* If no remainder, done! */ 
+    	heads++;                	/* Else, increment number of heads */ 
+    	temp = cylinders * heads;	/* Compute divisor for sectors */ 
+    	sectors = capacity / temp;	/* Compute value for sectors per
+					       track */ 
+    	if (capacity % temp) {		/* If no remainder, done! */ 
+      	    sectors++;                  /* Else, increment number of sectors */ 
+      	    temp = heads * sectors;	/* Compute divisor for cylinders */
+      	    cylinders = capacity / temp;/* Compute number of cylinders */ 
+      	} 
+    } 
+    if (cylinders == 0) rv=(unsigned)-1;/* Give error if 0 cylinders */ 
+
+    *cyls = (unsigned int) cylinders;	/* Stuff return values */ 
+    *secs = (unsigned int) sectors; 
+    *hds  = (unsigned int) heads; 
+    return(rv); 
+} 
diff -r -u -N linux.orig/arch/arm/drivers/scsi/sd.c linux.arm/arch/arm/drivers/scsi/sd.c
--- linux.orig/arch/arm/drivers/scsi/sd.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/sd.c	Fri Oct 27 23:15:13 1995
@@ -0,0 +1,1298 @@
+/*
+ *	sd.c Copyright (C) 1992 Drew Eckhardt 
+ *	     Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ *	Linux scsi disk driver
+ *		Initial versions: Drew Eckhardt 
+ *		Subsequent revisions: Eric Youngdale
+ *
+ *	<drew@colorado.edu>
+ *
+ *       Modified by Eric Youngdale ericy@cais.com to
+ *       add scatter-gather, multiple outstanding request, and other
+ *       enhancements.
+ *
+ *	 Modified by Eric Youngdale eric@aib.com to support loadable
+ *	 low-level scsi drivers.
+ */
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <asm/system.h>
+
+#define MAJOR_NR SCSI_DISK_MAJOR
+#include "../block/blk.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+#include "scsi_ioctl.h"
+#include "constants.h"
+
+#include <linux/genhd.h>
+
+/*
+static const char RCSid[] = "$Header:";
+*/
+
+#define MAX_RETRIES 5
+
+/*
+ *	Time out in seconds for disks and Magneto-opticals (which are slower).
+ */
+
+#define SD_TIMEOUT 600
+#define SD_MOD_TIMEOUT 750
+
+#define CLUSTERABLE_DEVICE(SC) (SC->host->hostt->use_clustering && \
+			    SC->device->type != TYPE_MOD)
+
+struct hd_struct * sd;
+int revalidate_scsidisk(int dev, int maxusage);
+
+Scsi_Disk * rscsi_disks = NULL;
+static int * sd_sizes;
+static int * sd_blocksizes;
+static int * sd_hardsizes;		/* Hardware sector size */
+
+extern int sd_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+
+static int check_scsidisk_media_change(dev_t);
+static int fop_revalidate_scsidisk(dev_t);
+
+static sd_init_onedisk(int);
+
+static void requeue_sd_request (Scsi_Cmnd * SCpnt);
+
+static void sd_init(void);
+static void sd_finish(void);
+static int sd_attach(Scsi_Device *);
+static int sd_detect(Scsi_Device *);
+static void sd_detach(Scsi_Device *);
+
+struct Scsi_Device_Template sd_template = {NULL, "disk", "sd", TYPE_DISK, 
+					     SCSI_DISK_MAJOR, 0, 0, 0, 1,
+					     sd_detect, sd_init,
+					     sd_finish, sd_attach, sd_detach};
+
+static int sd_open(struct inode * inode, struct file * filp)
+{
+        int target;
+	target =  DEVICE_NR(MINOR(inode->i_rdev));
+
+	if(target >= sd_template.dev_max || !rscsi_disks[target].device)
+	  return -ENXIO;   /* No such device */
+	
+/* Make sure that only one process can do a check_change_disk at one time.
+ This is also used to lock out further access when the partition table is being re-read. */
+
+	while (rscsi_disks[target].device->busy)
+	  barrier();
+
+	if(rscsi_disks[target].device->removable) {
+	  check_disk_change(inode->i_rdev);
+
+	  if(!rscsi_disks[target].device->access_count)
+	    sd_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
+	};
+	/*
+	 * See if we are requesting a non-existent partition.  Do this
+	 * after checking for disk change.
+	 */
+	if(sd_sizes[MINOR(inode->i_rdev)] == 0)
+	  return -ENXIO;
+
+	rscsi_disks[target].device->access_count++;
+	if (rscsi_disks[target].device->host->hostt->usage_count)
+	  (*rscsi_disks[target].device->host->hostt->usage_count)++;
+	return 0;
+}
+
+static void sd_release(struct inode * inode, struct file * file)
+{
+        int target;
+	sync_dev(inode->i_rdev);
+
+	target =  DEVICE_NR(MINOR(inode->i_rdev));
+
+	rscsi_disks[target].device->access_count--;
+	if (rscsi_disks[target].device->host->hostt->usage_count)
+	  (*rscsi_disks[target].device->host->hostt->usage_count)--;
+
+	if(rscsi_disks[target].device->removable) {
+	  if(!rscsi_disks[target].device->access_count)
+	    sd_ioctl(inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0);
+	};
+}
+
+static void sd_geninit(void);
+
+static struct file_operations sd_fops = {
+	NULL,			/* lseek - default */
+	block_read,		/* read - general block-dev read */
+	block_write,		/* write - general block-dev write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select */
+	sd_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	sd_open,		/* open code */
+	sd_release,		/* release */
+	block_fsync,		/* fsync */
+	NULL,			/* fasync */
+	check_scsidisk_media_change,  /* Disk change */
+	fop_revalidate_scsidisk     /* revalidate */
+};
+
+static struct gendisk sd_gendisk = {
+	MAJOR_NR,		/* Major number */
+	"sd",		/* Major name */
+	4,		/* Bits to shift to get real from partition */
+	1 << 4,		/* Number of partitions per real */
+	0,		/* maximum number of real */
+	sd_geninit,	/* init function */
+	NULL,		/* hd struct */
+	NULL,	/* block sizes */
+	0,		/* number */
+	NULL,	/* internal */
+	NULL		/* next */
+};
+
+static void sd_geninit (void)
+{
+	int i;
+
+	for (i = 0; i < sd_template.dev_max; ++i)
+	  if(rscsi_disks[i].device) 
+	    sd[i << 4].nr_sects = rscsi_disks[i].capacity;
+#if 0
+	/* No longer needed - we keep track of this as we attach/detach */
+	sd_gendisk.nr_real = sd_template.dev_max;
+#endif
+}
+
+/*
+	rw_intr is the interrupt routine for the device driver.  It will
+	be notified on the end of a SCSI read / write, and
+	will take on of several actions based on success or failure.
+*/
+
+static void rw_intr (Scsi_Cmnd *SCpnt)
+{
+  int result = SCpnt->result;
+  int this_count = SCpnt->bufflen >> 9;
+
+#ifdef DEBUG
+  printk("sd%c : rw_intr(%d, %d)\n", 'a' + MINOR(SCpnt->request.dev), SCpnt->host->host_no, result);
+#endif
+
+/*
+  First case : we assume that the command succeeded.  One of two things will
+  happen here.  Either we will be finished, or there will be more
+  sectors that we were unable to read last time.
+*/
+
+  if (!result) {
+
+#ifdef DEBUG
+    printk("sd%c : %d sectors remain.\n", 'a' + MINOR(SCpnt->request.dev), SCpnt->request.nr_sectors);
+    printk("use_sg is %d\n ",SCpnt->use_sg);
+#endif
+    if (SCpnt->use_sg) {
+      struct scatterlist * sgpnt;
+      int i;
+      sgpnt = (struct scatterlist *) SCpnt->buffer;
+      for(i=0; i<SCpnt->use_sg; i++) {
+#ifdef DEBUG
+	printk(":%x %x %d\n",sgpnt[i].alt_address, sgpnt[i].address, sgpnt[i].length);
+#endif
+	if (sgpnt[i].alt_address) {
+	  if (SCpnt->request.cmd == READ)
+	    memcpy(sgpnt[i].alt_address, sgpnt[i].address, sgpnt[i].length);
+	  scsi_free(sgpnt[i].address, sgpnt[i].length);
+	};
+      };
+      scsi_free(SCpnt->buffer, SCpnt->sglist_len);  /* Free list of scatter-gather pointers */
+    } else {
+      if (SCpnt->buffer != SCpnt->request.buffer) {
+#ifdef DEBUG
+	printk("nosg: %x %x %d\n",SCpnt->request.buffer, SCpnt->buffer,
+		   SCpnt->bufflen);
+#endif	
+	  if (SCpnt->request.cmd == READ)
+	    memcpy(SCpnt->request.buffer, SCpnt->buffer,
+		   SCpnt->bufflen);
+	  scsi_free(SCpnt->buffer, SCpnt->bufflen);
+      };
+    };
+/*
+ * 	If multiple sectors are requested in one buffer, then
+ *	they will have been finished off by the first command.  If
+ *	not, then we have a multi-buffer command.
+ */
+    if (SCpnt->request.nr_sectors > this_count)
+      {
+	SCpnt->request.errors = 0;
+	
+	if (!SCpnt->request.bh)
+	  {
+#ifdef DEBUG
+	    printk("sd%c : handling page request, no buffer\n",
+		   'a' + MINOR(SCpnt->request.dev));
+#endif
+/*
+  The SCpnt->request.nr_sectors field is always done in 512 byte sectors,
+  even if this really isn't the case.
+*/
+	    panic("sd.c: linked page request (%lx %x)",
+		  SCpnt->request.sector, this_count);
+	  }
+      }
+    SCpnt = end_scsi_request(SCpnt, 1, this_count);
+    requeue_sd_request(SCpnt);
+    return;
+  }
+
+/* Free up any indirection buffers we allocated for DMA purposes. */
+    if (SCpnt->use_sg) {
+      struct scatterlist * sgpnt;
+      int i;
+      sgpnt = (struct scatterlist *) SCpnt->buffer;
+      for(i=0; i<SCpnt->use_sg; i++) {
+#ifdef DEBUG
+	printk("err: %x %x %d\n",SCpnt->request.buffer, SCpnt->buffer,
+		   SCpnt->bufflen);
+#endif
+	if (sgpnt[i].alt_address) {
+	  scsi_free(sgpnt[i].address, sgpnt[i].length);
+	};
+      };
+      scsi_free(SCpnt->buffer, SCpnt->sglist_len);  /* Free list of scatter-gather pointers */
+    } else {
+#ifdef DEBUG
+      printk("nosgerr: %x %x %d\n",SCpnt->request.buffer, SCpnt->buffer,
+		   SCpnt->bufflen);
+#endif
+      if (SCpnt->buffer != SCpnt->request.buffer)
+	scsi_free(SCpnt->buffer, SCpnt->bufflen);
+    };
+
+/*
+	Now, if we were good little boys and girls, Santa left us a request
+	sense buffer.  We can extract information from this, so we
+	can choose a block to remap, etc.
+*/
+
+        if (driver_byte(result) != 0) {
+	  if (suggestion(result) == SUGGEST_REMAP) {
+#ifdef REMAP
+/*
+	Not yet implemented.  A read will fail after being remapped,
+	a write will call the strategy routine again.
+*/
+	    if rscsi_disks[DEVICE_NR(SCpnt->request.dev)].remap
+	      {
+		result = 0;
+	      }
+	    else
+	      
+#endif
+	    }
+
+	  if ((SCpnt->sense_buffer[0] & 0x7f) == 0x70) {
+	    if ((SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION) {
+	      if(rscsi_disks[DEVICE_NR(SCpnt->request.dev)].device->removable) {
+	      /* detected disc change.  set a bit and quietly refuse	*/
+	      /* further access.					*/
+	      
+		rscsi_disks[DEVICE_NR(SCpnt->request.dev)].device->changed = 1;
+		SCpnt = end_scsi_request(SCpnt, 0, this_count);
+		requeue_sd_request(SCpnt);
+		return;
+	      }
+	    }
+	  }
+	  
+
+/* 	If we had an ILLEGAL REQUEST returned, then we may have
+performed an unsupported command.  The only thing this should be would
+be a ten byte read where only a six byte read was supported.  Also,
+on a system where READ CAPACITY failed, we have have read past the end
+of the 	disk. 
+*/
+
+	  if (SCpnt->sense_buffer[2] == ILLEGAL_REQUEST) {
+	    if (rscsi_disks[DEVICE_NR(SCpnt->request.dev)].ten) {
+	      rscsi_disks[DEVICE_NR(SCpnt->request.dev)].ten = 0;
+	      requeue_sd_request(SCpnt);
+	      result = 0;
+	    } else {
+	    }
+	  }
+	}  /* driver byte != 0 */
+	if (result) {
+		printk("SCSI disk error : host %d id %d lun %d return code = %x\n",
+		       rscsi_disks[DEVICE_NR(SCpnt->request.dev)].device->host->host_no,
+		       rscsi_disks[DEVICE_NR(SCpnt->request.dev)].device->id,
+		       rscsi_disks[DEVICE_NR(SCpnt->request.dev)].device->lun, result);
+
+		if (driver_byte(result) & DRIVER_SENSE)
+			print_sense("sd", SCpnt);
+		SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.current_nr_sectors);
+		requeue_sd_request(SCpnt);
+		return;
+	}
+}
+
+/*
+	requeue_sd_request() is the request handler function for the sd driver.
+	Its function in life is to take block device requests, and translate
+	them to SCSI commands.
+*/
+
+static void do_sd_request (void)
+{
+  Scsi_Cmnd * SCpnt = NULL;
+  struct request * req = NULL;
+  unsigned long flags;
+  int flag = 0;
+
+  save_flags(flags);
+  while (1==1){
+    cli();
+    if (CURRENT != NULL && CURRENT->dev == -1) {
+      restore_flags(flags);
+      return;
+    };
+
+    INIT_SCSI_REQUEST;
+
+
+/* We have to be careful here.  allocate_device will get a free pointer, but
+   there is no guarantee that it is queueable.  In normal usage, we want to
+   call this, because other types of devices may have the host all tied up,
+   and we want to make sure that we have at least one request pending for this
+   type of device.   We can also come through here while servicing an
+   interrupt, because of the need to start another command.  If we call
+   allocate_device more than once, then the system can wedge if the command
+   is not queueable.  The request_queueable function is safe because it checks
+   to make sure that the host is able to take another command before it returns
+   a pointer.  */
+
+    if (flag++ == 0)
+      SCpnt = allocate_device(&CURRENT,
+			      rscsi_disks[DEVICE_NR(MINOR(CURRENT->dev))].device, 0); 
+    else SCpnt = NULL;
+
+    /*
+     * The following restore_flags leads to latency problems.  FIXME.
+     * Using a "sti()" gets rid of the latency problems but causes
+     * race conditions and crashes.
+     */
+    restore_flags(flags);
+
+/* This is a performance enhancement.  We dig down into the request list and
+   try and find a queueable request (i.e. device not busy, and host able to
+   accept another command.  If we find one, then we queue it. This can
+   make a big difference on systems with more than one disk drive.  We want
+   to have the interrupts off when monkeying with the request list, because
+   otherwise the kernel might try and slip in a request in between somewhere. */
+
+    if (!SCpnt && sd_template.nr_dev > 1){
+      struct request *req1;
+      req1 = NULL;
+      cli();
+      req = CURRENT;
+      while(req){
+	SCpnt = request_queueable(req,
+				  rscsi_disks[DEVICE_NR(MINOR(req->dev))].device);
+	if(SCpnt) break;
+	req1 = req;
+	req = req->next;
+      };
+      if (SCpnt && req->dev == -1) {
+	if (req == CURRENT) 
+	  CURRENT = CURRENT->next;
+	else
+	  req1->next = req->next;
+      };
+      restore_flags(flags);
+    };
+    
+    if (!SCpnt) return; /* Could not find anything to do */
+        
+    /* Queue command */
+    requeue_sd_request(SCpnt);
+  };  /* While */
+}    
+
+static void requeue_sd_request (Scsi_Cmnd * SCpnt)
+{
+	int dev, block, this_count;
+	unsigned char cmd[10];
+	int bounce_size, contiguous;
+	int max_sg;
+	struct buffer_head * bh, *bhp;
+	char * buff, *bounce_buffer;
+
+repeat:
+
+	if(!SCpnt || SCpnt->request.dev <= 0) {
+	  do_sd_request();
+	  return;
+	}
+
+	dev =  MINOR(SCpnt->request.dev);
+	block = SCpnt->request.sector;
+	this_count = 0;
+
+#ifdef DEBUG
+	printk("Doing sd request, dev = %d, block = %d\n", dev, block);
+#endif
+
+	if (dev >= (sd_template.dev_max << 4) || 
+	    !rscsi_disks[DEVICE_NR(dev)].device ||
+	    block + SCpnt->request.nr_sectors > sd[dev].nr_sects)
+		{
+		SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+		goto repeat;
+		}
+
+	block += sd[dev].start_sect;
+	dev = DEVICE_NR(dev);
+
+	if (rscsi_disks[dev].device->changed)
+	        {
+/*
+ * quietly refuse to do anything to a changed disc until the changed bit has been reset
+ */
+		/* printk("SCSI disk has been changed.  Prohibiting further I/O.\n");	*/
+		SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+		goto repeat;
+		}
+
+#ifdef DEBUG
+	printk("sd%c : real dev = /dev/sd%c, block = %d\n", 'a' + MINOR(SCpnt->request.dev), dev, block);
+#endif
+
+	/*
+	 * If we have a 1K hardware sectorsize, prevent access to single
+	 * 512 byte sectors.  In theory we could handle this - in fact
+	 * the scsi cdrom driver must be able to handle this because
+	 * we typically use 1K blocksizes, and cdroms typically have
+	 * 2K hardware sectorsizes.  Of course, things are simpler
+	 * with the cdrom, since it is read-only.  For performance
+	 * reasons, the filesystems should be able to handle this
+	 * and not force the scsi disk driver to use bounce buffers
+	 * for this.
+	 */
+	if (rscsi_disks[dev].sector_size == 1024)
+	  if((block & 1) || (SCpnt->request.nr_sectors & 1)) {
+	 	printk("sd.c:Bad block number requested");
+		SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+		goto repeat;
+	}
+	
+	switch (SCpnt->request.cmd)
+		{
+		case WRITE :
+			if (!rscsi_disks[dev].device->writeable)
+				{
+				SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+				goto repeat;
+				}
+			cmd[0] = WRITE_6;
+			break;
+		case READ :
+			cmd[0] = READ_6;
+			break;
+		default :
+			panic ("Unknown sd command %d\n", SCpnt->request.cmd);
+		      }
+
+	SCpnt->this_count = 0;
+
+	/* If the host adapter can deal with very large scatter-gather
+	   requests, it is a waste of time to cluster */
+	contiguous = (!CLUSTERABLE_DEVICE(SCpnt) ? 0 :1);
+	bounce_buffer = NULL;
+	bounce_size = (SCpnt->request.nr_sectors << 9);
+
+	/* First see if we need a bounce buffer for this request.  If we do, make sure
+	   that we can allocate a buffer.  Do not waste space by allocating a bounce
+	   buffer if we are straddling the 16Mb line */
+
+	
+	if (contiguous && SCpnt->request.bh &&
+	    ((long) SCpnt->request.bh->b_data) + (SCpnt->request.nr_sectors << 9) - 1 > 
+	    ISA_DMA_THRESHOLD && SCpnt->host->unchecked_isa_dma) {
+	  if(((long) SCpnt->request.bh->b_data) > ISA_DMA_THRESHOLD)
+	    bounce_buffer = (char *) scsi_malloc(bounce_size);
+	  if(!bounce_buffer) contiguous = 0;
+	};
+
+	if(contiguous && SCpnt->request.bh && SCpnt->request.bh->b_reqnext)
+	  for(bh = SCpnt->request.bh, bhp = bh->b_reqnext; bhp; bh = bhp, 
+	      bhp = bhp->b_reqnext) {
+	    if(!CONTIGUOUS_BUFFERS(bh,bhp)) { 
+	      if(bounce_buffer) scsi_free(bounce_buffer, bounce_size);
+	      contiguous = 0;
+	      break;
+	    } 
+	  };
+	if (!SCpnt->request.bh || contiguous) {
+
+	  /* case of page request (i.e. raw device), or unlinked buffer */
+	  this_count = SCpnt->request.nr_sectors;
+	  buff = SCpnt->request.buffer;
+	  SCpnt->use_sg = 0;
+
+	} else if (SCpnt->host->sg_tablesize == 0 ||
+		   (need_isa_buffer && 
+		    dma_free_sectors <= 10)) {
+
+	  /* Case of host adapter that cannot scatter-gather.  We also
+	   come here if we are running low on DMA buffer memory.  We set
+	   a threshold higher than that we would need for this request so
+	   we leave room for other requests.  Even though we would not need
+	   it all, we need to be conservative, because if we run low enough
+	   we have no choice but to panic. */
+
+	  if (SCpnt->host->sg_tablesize != 0 &&
+	      need_isa_buffer && 
+	      dma_free_sectors <= 10)
+	    printk("Warning: SCSI DMA buffer space running low.  Using non scatter-gather I/O.\n");
+
+	  this_count = SCpnt->request.current_nr_sectors;
+	  buff = SCpnt->request.buffer;
+	  SCpnt->use_sg = 0;
+
+	} else {
+
+	  /* Scatter-gather capable host adapter */
+	  struct scatterlist * sgpnt;
+	  int count, this_count_max;
+	  int counted;
+
+	  bh = SCpnt->request.bh;
+	  this_count = 0;
+	  this_count_max = (rscsi_disks[dev].ten ? 0xffff : 0xff);
+	  count = 0;
+	  bhp = NULL;
+	  while(bh) {
+	    if ((this_count + (bh->b_size >> 9)) > this_count_max) break;
+	    if(!bhp || !CONTIGUOUS_BUFFERS(bhp,bh) ||
+	       !CLUSTERABLE_DEVICE(SCpnt) ||
+	       (SCpnt->host->unchecked_isa_dma &&
+	       ((unsigned long) bh->b_data-1) == ISA_DMA_THRESHOLD)) {
+	      if (count < SCpnt->host->sg_tablesize) count++;
+	      else break;
+	    };
+	    this_count += (bh->b_size >> 9);
+	    bhp = bh;
+	    bh = bh->b_reqnext;
+	  };
+#if 0
+	  if(SCpnt->host->unchecked_isa_dma &&
+	     ((unsigned int) SCpnt->request.bh->b_data-1) == ISA_DMA_THRESHOLD) count--;
+#endif
+	  SCpnt->use_sg = count;  /* Number of chains */
+	  count = 512;/* scsi_malloc can only allocate in chunks of 512 bytes*/
+	  while( count < (SCpnt->use_sg * sizeof(struct scatterlist))) 
+	    count = count << 1;
+	  SCpnt->sglist_len = count;
+	  max_sg = count / sizeof(struct scatterlist);
+	  if(SCpnt->host->sg_tablesize < max_sg) max_sg = SCpnt->host->sg_tablesize;
+	  sgpnt = (struct scatterlist * ) scsi_malloc(count);
+	  memset(sgpnt, 0, count);  /* Zero so it is easy to fill */
+	  if (!sgpnt) {
+	    printk("Warning - running *really* short on DMA buffers\n");
+	    SCpnt->use_sg = 0;  /* No memory left - bail out */
+	    this_count = SCpnt->request.current_nr_sectors;
+	    buff = SCpnt->request.buffer;
+	  } else {
+	    buff = (char *) sgpnt;
+	    counted = 0;
+	    for(count = 0, bh = SCpnt->request.bh, bhp = bh->b_reqnext;
+		count < SCpnt->use_sg && bh; 
+		count++, bh = bhp) {
+
+	      bhp = bh->b_reqnext;
+
+	      if(!sgpnt[count].address) sgpnt[count].address = bh->b_data;
+	      sgpnt[count].length += bh->b_size;
+	      counted += bh->b_size >> 9;
+
+	      if (((long) sgpnt[count].address) + sgpnt[count].length - 1 > 
+		  ISA_DMA_THRESHOLD && (SCpnt->host->unchecked_isa_dma) &&
+		  !sgpnt[count].alt_address) {
+		sgpnt[count].alt_address = sgpnt[count].address;
+		/* We try and avoid exhausting the DMA pool, since it is easier
+		   to control usage here.  In other places we might have a more
+		   pressing need, and we would be screwed if we ran out */
+		if(dma_free_sectors < (sgpnt[count].length >> 9) + 10) {
+		  sgpnt[count].address = NULL;
+		} else {
+		  sgpnt[count].address = (char *) scsi_malloc(sgpnt[count].length);
+		};
+/* If we start running low on DMA buffers, we abort the scatter-gather
+   operation, and free all of the memory we have allocated.  We want to
+   ensure that all scsi operations are able to do at least a non-scatter/gather
+   operation */
+		if(sgpnt[count].address == NULL){ /* Out of dma memory */
+#if 0
+		  printk("Warning: Running low on SCSI DMA buffers");
+		  /* Try switching back to a non scatter-gather operation. */
+		  while(--count >= 0){
+		    if(sgpnt[count].alt_address) 
+		      scsi_free(sgpnt[count].address, sgpnt[count].length);
+		  };
+		  this_count = SCpnt->request.current_nr_sectors;
+		  buff = SCpnt->request.buffer;
+		  SCpnt->use_sg = 0;
+		  scsi_free(sgpnt, SCpnt->sglist_len);
+#endif
+		  SCpnt->use_sg = count;
+		  this_count = counted -= bh->b_size >> 9;
+		  break;
+		};
+
+	      };
+
+	      /* Only cluster buffers if we know that we can supply DMA buffers
+		 large enough to satisfy the request.  Do not cluster a new
+		 request if this would mean that we suddenly need to start
+		 using DMA bounce buffers */
+	      if(bhp && CONTIGUOUS_BUFFERS(bh,bhp) && CLUSTERABLE_DEVICE(SCpnt)) {
+		char * tmp;
+
+		if (((long) sgpnt[count].address) + sgpnt[count].length +
+		    bhp->b_size - 1 > ISA_DMA_THRESHOLD && 
+		    (SCpnt->host->unchecked_isa_dma) &&
+		    !sgpnt[count].alt_address) continue;
+
+		if(!sgpnt[count].alt_address) {count--; continue; }
+		if(dma_free_sectors > 10)
+		  tmp = (char *) scsi_malloc(sgpnt[count].length + bhp->b_size);
+		else {
+		  tmp = NULL;
+		  max_sg = SCpnt->use_sg;
+		};
+		if(tmp){
+		  scsi_free(sgpnt[count].address, sgpnt[count].length);
+		  sgpnt[count].address = tmp;
+		  count--;
+		  continue;
+		};
+
+		/* If we are allowed another sg chain, then increment counter so we
+		   can insert it.  Otherwise we will end up truncating */
+
+		if (SCpnt->use_sg < max_sg) SCpnt->use_sg++;
+	      };  /* contiguous buffers */
+	    }; /* for loop */
+
+	    this_count = counted; /* This is actually how many we are going to transfer */
+
+	    if(count < SCpnt->use_sg || SCpnt->use_sg > SCpnt->host->sg_tablesize){
+	      bh = SCpnt->request.bh;
+	      printk("Use sg, count %d %x %d\n", SCpnt->use_sg, count, dma_free_sectors);
+	      printk("maxsg = %x, counted = %d this_count = %d\n", max_sg, counted, this_count);
+	      while(bh){
+		printk("[%p %lx] ", bh->b_data, bh->b_size);
+		bh = bh->b_reqnext;
+	      };
+	      if(SCpnt->use_sg < 16)
+		for(count=0; count<SCpnt->use_sg; count++)
+		  printk("{%d:%p %p %d}  ", count,
+			 sgpnt[count].address,
+			 sgpnt[count].alt_address,
+			 sgpnt[count].length);
+	      panic("Ooops");
+	    };
+
+	    if (SCpnt->request.cmd == WRITE)
+	      for(count=0; count<SCpnt->use_sg; count++)
+		if(sgpnt[count].alt_address)
+		  memcpy(sgpnt[count].address, sgpnt[count].alt_address, 
+			 sgpnt[count].length);
+	  };  /* Able to malloc sgpnt */
+	};  /* Host adapter capable of scatter-gather */
+
+/* Now handle the possibility of DMA to addresses > 16Mb */
+
+	if(SCpnt->use_sg == 0){
+	  if (((long) buff) + (this_count << 9) - 1 > ISA_DMA_THRESHOLD && 
+	    (SCpnt->host->unchecked_isa_dma)) {
+	    if(bounce_buffer)
+	      buff = bounce_buffer;
+	    else
+	      buff = (char *) scsi_malloc(this_count << 9);
+	    if(buff == NULL) {  /* Try backing off a bit if we are low on mem*/
+	      this_count = SCpnt->request.current_nr_sectors;
+	      buff = (char *) scsi_malloc(this_count << 9);
+	      if(!buff) panic("Ran out of DMA buffers.");
+	    };
+	    if (SCpnt->request.cmd == WRITE)
+	      memcpy(buff, (char *)SCpnt->request.buffer, this_count << 9);
+	  };
+	};
+#ifdef DEBUG
+	printk("sd%c : %s %d/%d 512 byte blocks.\n", 'a' + MINOR(SCpnt->request.dev),
+		(SCpnt->request.cmd == WRITE) ? "writing" : "reading",
+		this_count, SCpnt->request.nr_sectors);
+#endif
+
+	cmd[1] = (SCpnt->lun << 5) & 0xe0;
+
+	if (rscsi_disks[dev].sector_size == 1024){
+	  if(block & 1) panic("sd.c:Bad block number requested");
+	  if(this_count & 1) panic("sd.c:Bad block number requested");
+	  block = block >> 1;
+	  this_count = this_count >> 1;
+	};
+
+	if (rscsi_disks[dev].sector_size == 256){
+	  block = block << 1;
+	  this_count = this_count << 1;
+	};
+
+	if (((this_count > 0xff) ||  (block > 0x1fffff)) && rscsi_disks[dev].ten)
+		{
+		if (this_count > 0xffff)
+			this_count = 0xffff;
+
+		cmd[0] += READ_10 - READ_6 ;
+		cmd[2] = (unsigned char) (block >> 24) & 0xff;
+		cmd[3] = (unsigned char) (block >> 16) & 0xff;
+		cmd[4] = (unsigned char) (block >> 8) & 0xff;
+		cmd[5] = (unsigned char) block & 0xff;
+		cmd[6] = cmd[9] = 0;
+		cmd[7] = (unsigned char) (this_count >> 8) & 0xff;
+		cmd[8] = (unsigned char) this_count & 0xff;
+		}
+	else
+		{
+		if (this_count > 0xff)
+			this_count = 0xff;
+
+		cmd[1] |= (unsigned char) ((block >> 16) & 0x1f);
+		cmd[2] = (unsigned char) ((block >> 8) & 0xff);
+		cmd[3] = (unsigned char) block & 0xff;
+		cmd[4] = (unsigned char) this_count;
+		cmd[5] = 0;
+		}
+
+/*
+ * We shouldn't disconnect in the middle of a sector, so with a dumb 
+ * host adapter, it's safe to assume that we can at least transfer 
+ * this many bytes between each connect / disconnect.  
+ */
+
+        SCpnt->transfersize = rscsi_disks[dev].sector_size;
+        SCpnt->underflow = this_count << 9; 
+	scsi_do_cmd (SCpnt, (void *) cmd, buff, 
+		     this_count * rscsi_disks[dev].sector_size,
+		     rw_intr, 
+		     (SCpnt->device->type == TYPE_DISK ? 
+		                     SD_TIMEOUT : SD_MOD_TIMEOUT),
+		     MAX_RETRIES);
+}
+
+static int check_scsidisk_media_change(dev_t full_dev){
+        int retval;
+	int target;
+	struct inode inode;
+	int flag = 0;
+
+	target =  DEVICE_NR(MINOR(full_dev));
+
+	if (target >= sd_template.dev_max ||
+	    !rscsi_disks[target].device) {
+		printk("SCSI disk request error: invalid device.\n");
+		return 0;
+	};
+
+	if(!rscsi_disks[target].device->removable) return 0;
+
+	inode.i_rdev = full_dev;  /* This is all we really need here */
+	retval = sd_ioctl(&inode, NULL, SCSI_IOCTL_TEST_UNIT_READY, 0);
+
+	if(retval){ /* Unable to test, unit probably not ready.  This usually
+		     means there is no disc in the drive.  Mark as changed,
+		     and we will figure it out later once the drive is
+		     available again.  */
+
+	  rscsi_disks[target].device->changed = 1;
+	  return 1; /* This will force a flush, if called from
+		       check_disk_change */
+	};
+
+	retval = rscsi_disks[target].device->changed;
+	if(!flag) rscsi_disks[target].device->changed = 0;
+	return retval;
+}
+
+static void sd_init_done (Scsi_Cmnd * SCpnt)
+{
+  struct request * req;
+  
+  req = &SCpnt->request;
+  req->dev = 0xfffe; /* Busy, but indicate request done */
+  
+  if (req->sem != NULL) {
+    up(req->sem);
+  }
+}
+
+static int sd_init_onedisk(int i)
+{
+  unsigned char cmd[10];
+  unsigned char *buffer;
+  char spintime;
+  int the_result, retries;
+  Scsi_Cmnd * SCpnt;
+
+  /* We need to retry the READ_CAPACITY because a UNIT_ATTENTION is considered
+     a fatal error, and many devices report such an error just after a scsi
+     bus reset. */
+
+  SCpnt = allocate_device(NULL, rscsi_disks[i].device, 1);
+  buffer = (unsigned char *) scsi_malloc(512);
+
+  spintime = 0;
+
+  /* Spin up drives, as required.  Only do this at boot time */
+  if (current == task[0]){
+    do{
+      cmd[0] = TEST_UNIT_READY;
+      cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
+      memset ((void *) &cmd[2], 0, 8);
+      SCpnt->request.dev = 0xffff;  /* Mark as really busy again */
+      SCpnt->cmd_len = 0;
+      SCpnt->sense_buffer[0] = 0;
+      SCpnt->sense_buffer[2] = 0;
+      
+      scsi_do_cmd (SCpnt,
+		   (void *) cmd, (void *) buffer,
+		   512, sd_init_done,  SD_TIMEOUT,
+		   MAX_RETRIES);
+      
+      while(SCpnt->request.dev != 0xfffe) barrier();
+      
+      the_result = SCpnt->result;
+      
+      /* Look for non-removable devices that return NOT_READY.  Issue command
+	 to spin up drive for these cases. */
+      if(the_result && !rscsi_disks[i].device->removable && 
+	 SCpnt->sense_buffer[2] == NOT_READY) {
+	int time1;
+	if(!spintime){
+	  printk( "sd%c: Spinning up disk...", 'a' + i );
+	  cmd[0] = START_STOP;
+	  cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
+	  cmd[1] |= 1;  /* Return immediately */
+	  memset ((void *) &cmd[2], 0, 8);
+	  cmd[4] = 1; /* Start spin cycle */
+	  SCpnt->request.dev = 0xffff;  /* Mark as really busy again */
+	  SCpnt->cmd_len = 0;
+	  SCpnt->sense_buffer[0] = 0;
+	  SCpnt->sense_buffer[2] = 0;
+	  
+	  scsi_do_cmd (SCpnt,
+		       (void *) cmd, (void *) buffer,
+		       512, sd_init_done,  SD_TIMEOUT,
+		       MAX_RETRIES);
+	  
+	  while(SCpnt->request.dev != 0xfffe) barrier();
+
+	  spintime = jiffies;
+	};
+
+	time1 = jiffies;
+	while(jiffies < time1 + HZ); /* Wait 1 second for next try */
+	printk( "." );
+      };
+    } while(the_result && spintime && spintime+5000 > jiffies);
+    if (spintime) {
+       if (the_result)
+           printk( "not responding...\n" );
+       else
+           printk( "ready\n" );
+    }
+  };  /* current == task[0] */
+
+
+  retries = 3;
+  do {
+    cmd[0] = READ_CAPACITY;
+    cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
+    memset ((void *) &cmd[2], 0, 8);
+    memset ((void *) buffer, 0, 8);
+    SCpnt->request.dev = 0xffff;  /* Mark as really busy again */
+    SCpnt->cmd_len = 0;
+    SCpnt->sense_buffer[0] = 0;
+    SCpnt->sense_buffer[2] = 0;
+    
+    scsi_do_cmd (SCpnt,
+		 (void *) cmd, (void *) buffer,
+		 8, sd_init_done,  SD_TIMEOUT,
+		 MAX_RETRIES);
+    
+    if (current == task[0])
+      while(SCpnt->request.dev != 0xfffe) barrier();
+    else
+      if (SCpnt->request.dev != 0xfffe){
+      	struct semaphore sem = MUTEX_LOCKED;
+	SCpnt->request.sem = &sem;
+	down(&sem);
+	/* Hmm.. Have to ask about this one.. */
+	while (SCpnt->request.dev != 0xfffe) schedule();
+      };
+    
+    the_result = SCpnt->result;
+    retries--;
+
+  } while(the_result && retries);
+
+  SCpnt->request.dev = -1;  /* Mark as not busy */
+
+  wake_up(&SCpnt->device->device_wait); 
+
+  /* Wake up a process waiting for device*/
+
+  /*
+   *	The SCSI standard says "READ CAPACITY is necessary for self configuring software"
+   *	While not mandatory, support of READ CAPACITY is strongly encouraged.
+   *	We used to die if we couldn't successfully do a READ CAPACITY.
+   *	But, now we go on about our way.  The side effects of this are
+   *
+   *	1.  We can't know block size with certainty.  I have said "512 bytes is it"
+   *	   	as this is most common.
+   *
+   *	2.  Recovery from when some one attempts to read past the end of the raw device will
+   *	    be slower.
+   */
+
+  if (the_result)
+    {
+      printk ("sd%c : READ CAPACITY failed.\n"
+	      "sd%c : status = %x, message = %02x, host = %d, driver = %02x \n",
+	      'a' + i, 'a' + i,
+	      status_byte(the_result),
+	      msg_byte(the_result),
+	      host_byte(the_result),
+	      driver_byte(the_result)
+	      );
+      if (driver_byte(the_result)  & DRIVER_SENSE)
+	printk("sd%c : extended sense code = %1x \n", 'a' + i, SCpnt->sense_buffer[2] & 0xf);
+      else
+	printk("sd%c : sense not available. \n", 'a' + i);
+
+      printk("sd%c : block size assumed to be 512 bytes, disk size 1GB.  \n", 'a' + i);
+      rscsi_disks[i].capacity = 0x1fffff;
+      rscsi_disks[i].sector_size = 512;
+
+      /* Set dirty bit for removable devices if not ready - sometimes drives
+	 will not report this properly. */
+      if(rscsi_disks[i].device->removable && 
+	 SCpnt->sense_buffer[2] == NOT_READY)
+	rscsi_disks[i].device->changed = 1;
+
+    }
+  else
+    {
+      rscsi_disks[i].capacity = (buffer[0] << 24) |
+	(buffer[1] << 16) |
+	  (buffer[2] << 8) |
+	    buffer[3];
+
+      rscsi_disks[i].sector_size = (buffer[4] << 24) |
+	(buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
+
+      if (rscsi_disks[i].sector_size != 512 &&
+	  rscsi_disks[i].sector_size != 1024 &&
+	  rscsi_disks[i].sector_size != 256)
+	{
+	  printk ("sd%c : unsupported sector size %d.\n",
+		  'a' + i, rscsi_disks[i].sector_size);
+	  if(rscsi_disks[i].device->removable){
+	    rscsi_disks[i].capacity = 0;
+	  } else {
+	    printk ("scsi : deleting disk entry.\n");
+	    rscsi_disks[i].device = NULL;
+	    sd_template.nr_dev--;
+	    return i;
+	  };
+	}
+    {
+       /*
+          The msdos fs need to know the hardware sector size
+          So I have created this table. See ll_rw_blk.c
+          Jacques Gelinas (Jacques@solucorp.qc.ca)
+       */
+       int m;
+       int hard_sector = rscsi_disks[i].sector_size;
+       /* There is 16 minor allocated for each devices */
+       for (m=i<<4; m<((i+1)<<4); m++){
+         sd_hardsizes[m] = hard_sector;
+       }
+       printk ("SCSI Hardware sector size is %d bytes on device sd%c\n"
+         ,hard_sector,i+'a');
+    }
+      if(rscsi_disks[i].sector_size == 1024)
+	rscsi_disks[i].capacity <<= 1;  /* Change this into 512 byte sectors */
+      if(rscsi_disks[i].sector_size == 256)
+	rscsi_disks[i].capacity >>= 1;  /* Change this into 512 byte sectors */
+    }
+
+  rscsi_disks[i].ten = 1;
+  rscsi_disks[i].remap = 1;
+  scsi_free(buffer, 512);
+  return i;
+}
+
+/*
+	The sd_init() function looks at all SCSI drives present, determines
+	their size, and reads partition	table entries for them.
+*/
+
+
+static void sd_init()
+{
+	int i;
+	static int sd_registered = 0;
+
+	if (sd_template.dev_noticed == 0) return;
+
+	if(!sd_registered) {
+	  if (register_blkdev(MAJOR_NR,"sd",&sd_fops)) {
+	    printk("Unable to get major %d for SCSI disk\n",MAJOR_NR);
+	    return;
+	  }
+	  sd_registered++;
+	}
+
+	/* We do not support attaching loadable devices yet. */
+	if(rscsi_disks) return;
+
+	sd_template.dev_max = sd_template.dev_noticed + SD_EXTRA_DEVS;
+
+	rscsi_disks = (Scsi_Disk *) 
+	  scsi_init_malloc(sd_template.dev_max * sizeof(Scsi_Disk), GFP_ATOMIC);
+	memset(rscsi_disks, 0, sd_template.dev_max * sizeof(Scsi_Disk));
+
+	sd_sizes = (int *) scsi_init_malloc((sd_template.dev_max << 4) * 
+					    sizeof(int), GFP_ATOMIC);
+	memset(sd_sizes, 0, (sd_template.dev_max << 4) * sizeof(int));
+
+	sd_blocksizes = (int *) scsi_init_malloc((sd_template.dev_max << 4) * 
+						 sizeof(int), GFP_ATOMIC);
+
+	sd_hardsizes = (int *) scsi_init_malloc((sd_template.dev_max << 4) * 
+						   sizeof(struct hd_struct), GFP_ATOMIC);
+
+	for(i=0;i<(sd_template.dev_max << 4);i++){
+		sd_blocksizes[i] = 1024;
+		sd_hardsizes[i] = 512;
+	}
+	blksize_size[MAJOR_NR] = sd_blocksizes;
+	hardsect_size[MAJOR_NR] = sd_hardsizes;
+	sd = (struct hd_struct *) scsi_init_malloc((sd_template.dev_max << 4) *
+						   sizeof(struct hd_struct),
+						   GFP_ATOMIC);
+
+
+	sd_gendisk.max_nr = sd_template.dev_max;
+	sd_gendisk.part = sd;
+	sd_gendisk.sizes = sd_sizes;
+	sd_gendisk.real_devices = (void *) rscsi_disks;
+
+}
+
+static void sd_finish()
+{
+        int i;
+
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+
+	sd_gendisk.next = gendisk_head;
+	gendisk_head = &sd_gendisk;
+
+	for (i = 0; i < sd_template.dev_max; ++i)
+	    if (!rscsi_disks[i].capacity && 
+		  rscsi_disks[i].device)
+	      {
+		i = sd_init_onedisk(i);
+		if (scsi_loadable_module_flag 
+		    && !rscsi_disks[i].has_part_table) {
+		  sd_sizes[i << 4] = rscsi_disks[i].capacity;
+		  revalidate_scsidisk(i << 4, 0);
+		}
+		rscsi_disks[i].has_part_table = 1;
+	      }
+
+	/* If our host adapter is capable of scatter-gather, then we increase
+	   the read-ahead to 16 blocks (32 sectors).  If not, we use
+	   a two block (4 sector) read ahead. */
+	if(rscsi_disks[0].device && rscsi_disks[0].device->host->sg_tablesize)
+	  read_ahead[MAJOR_NR] = 120;
+	/* 64 sector read-ahead */
+	else
+	  read_ahead[MAJOR_NR] = 4;  /* 4 sector read-ahead */
+	
+	return;
+}
+
+static int sd_detect(Scsi_Device * SDp){
+  if(SDp->type != TYPE_DISK && SDp->type != TYPE_MOD) return 0;
+
+  printk("Detected scsi disk sd%c at scsi%d, id %d, lun %d\n", 
+	 'a'+ (sd_template.dev_noticed++),
+	 SDp->host->host_no , SDp->id, SDp->lun); 
+
+	 return 1;
+
+}
+
+static int sd_attach(Scsi_Device * SDp){
+   Scsi_Disk * dpnt;
+   int i;
+
+   if(SDp->type != TYPE_DISK && SDp->type != TYPE_MOD) return 0;
+
+   if(sd_template.nr_dev >= sd_template.dev_max) {
+	SDp->attached--;
+	return 1;
+   }
+   
+   for(dpnt = rscsi_disks, i=0; i<sd_template.dev_max; i++, dpnt++) 
+     if(!dpnt->device) break;
+
+   if(i >= sd_template.dev_max) panic ("scsi_devices corrupt (sd)");
+
+   SDp->scsi_request_fn = do_sd_request;
+   rscsi_disks[i].device = SDp;
+   rscsi_disks[i].has_part_table = 0;
+   sd_template.nr_dev++;
+   sd_gendisk.nr_real++;
+   return 0;
+}
+
+#define DEVICE_BUSY rscsi_disks[target].device->busy
+#define USAGE rscsi_disks[target].device->access_count
+#define CAPACITY rscsi_disks[target].capacity
+#define MAYBE_REINIT  sd_init_onedisk(target)
+#define GENDISK_STRUCT sd_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.
+ */
+int revalidate_scsidisk(int dev, int maxusage){
+	  int target, major;
+	  struct gendisk * gdev;
+	  unsigned long flags;
+	  int max_p;
+	  int start;
+	  int i;
+
+	  target =  DEVICE_NR(MINOR(dev));
+	  gdev = &GENDISK_STRUCT;
+
+	  save_flags(flags);
+	  cli();
+	  if (DEVICE_BUSY || USAGE > maxusage) {
+	    restore_flags(flags);
+	    printk("Device busy for revalidation (usage=%d)\n", USAGE);
+	    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;
+	  return 0;
+}
+
+static int fop_revalidate_scsidisk(dev_t dev){
+  return revalidate_scsidisk(dev, 0);
+}
+
+
+static void sd_detach(Scsi_Device * SDp)
+{
+  Scsi_Disk * dpnt;
+  int i;
+  int max_p;
+  int major;
+  int start;
+  
+  for(dpnt = rscsi_disks, i=0; i<sd_template.dev_max; i++, dpnt++) 
+    if(dpnt->device == SDp) {
+
+      /* If we are disconnecting a disk driver, sync and invalidate everything */
+      max_p = sd_gendisk.max_p;
+      start = i << sd_gendisk.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);
+	sd_gendisk.part[start+i].start_sect = 0;
+	sd_gendisk.part[start+i].nr_sects = 0;
+	sd_sizes[start+i] = 0;
+      };
+      
+      dpnt->has_part_table = 0;
+      dpnt->device = NULL;
+      dpnt->capacity = 0;
+      SDp->attached--;
+      sd_template.dev_noticed--;
+      sd_template.nr_dev--;
+      sd_gendisk.nr_real--;
+      return;
+    }
+  return;
+}
+
+/*
+ * 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: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -r -u -N linux.orig/arch/arm/drivers/scsi/sd.h linux.arm/arch/arm/drivers/scsi/sd.h
--- linux.orig/arch/arm/drivers/scsi/sd.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/sd.h	Fri Oct 27 23:15:13 1995
@@ -0,0 +1,47 @@
+/*
+ *	sd.h Copyright (C) 1992 Drew Eckhardt 
+ *	SCSI disk driver header file by
+ *		Drew Eckhardt 
+ *
+ *	<drew@colorado.edu>
+ *
+ *       Modified by Eric Youngdale eric@tantalus.nrl.navy.mil to
+ *       add scatter-gather, multiple outstanding request, and other
+ *       enhancements.
+ */
+#ifndef _SD_H
+	#define _SD_H
+/*
+	$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/sd.h,v 1.1 1992/07/24 06:27:38 root Exp root $
+*/
+
+#ifndef _SCSI_H
+#include "scsi.h"
+#endif
+
+#ifndef _GENDISK_H
+#include <linux/genhd.h>
+#endif
+
+/*
+	This is an arbitrary constant, and may be changed to whatever
+	suits your purposes.  Note that smaller will get you a few bytes
+	more in kernel space if that is your thing.
+*/
+
+extern struct hd_struct * sd;
+
+typedef struct scsi_disk {
+		unsigned capacity;		/* size in blocks */
+		unsigned sector_size;		/* size in bytes */
+		Scsi_Device  *device;		
+		unsigned char sector_bit_size;	/* sector_size = 2 to the  bit size power */
+		unsigned char sector_bit_shift;	/* power of 2 sectors per FS block */
+		unsigned ten:1;			/* support ten byte read / write */
+		unsigned remap:1;		/* support remapping  */
+		unsigned has_part_table:1;	/* has partition table */
+		} Scsi_Disk;
+	
+extern Scsi_Disk * rscsi_disks;
+
+#endif
diff -r -u -N linux.orig/arch/arm/drivers/scsi/sd_ioctl.c linux.arm/arch/arm/drivers/scsi/sd_ioctl.c
--- linux.orig/arch/arm/drivers/scsi/sd_ioctl.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/sd_ioctl.c	Fri Oct 27 23:15:17 1995
@@ -0,0 +1,91 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/hdreg.h>
+#include <linux/errno.h>
+
+#include <asm/segment.h>
+
+#include "../block/blk.h"
+#include "scsi.h"
+#include "scsi_ioctl.h"
+#include "hosts.h"
+#include "sd.h"
+
+extern int revalidate_scsidisk(int, int);
+
+int sd_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
+{
+	int dev = inode->i_rdev;
+	int error;
+	struct Scsi_Host * host;
+	int diskinfo[4];
+	struct hd_geometry *loc = (struct hd_geometry *) arg;
+
+	switch (cmd) {
+         	case HDIO_REQ:   /* Return BIOS disk parameters */
+			if (!loc)  return -EINVAL;
+			error = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
+			if (error)
+				return error;
+			host = rscsi_disks[MINOR(dev) >> 4].device->host;
+			diskinfo[0] = 0;
+			diskinfo[1] = 0;
+			diskinfo[2] = 0;
+			if(host->hostt->bios_param != NULL)
+			      host->hostt->bios_param(&rscsi_disks[MINOR(dev) >> 4],
+							  dev,
+							  &diskinfo[0]);
+			put_fs_byte(diskinfo[0],
+				(char *) &loc->heads);
+			put_fs_byte(diskinfo[1],
+				(char *) &loc->sectors);
+			put_fs_word(diskinfo[2],
+				(short *) &loc->cylinders);
+			put_fs_long(sd[MINOR(inode->i_rdev)].start_sect,
+				(long *) &loc->start);
+			return 0;
+         	case BLKGETSIZE:   /* Return device size */
+			if (!arg)  return -EINVAL;
+			error = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
+			if (error)
+				return error;
+			put_fs_long(sd[MINOR(inode->i_rdev)].nr_sects,
+				(long *) arg);
+			return 0;
+		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 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: /* Re-read partition tables */
+			return revalidate_scsidisk(dev, 1);
+		default:
+			return scsi_ioctl(rscsi_disks[MINOR(dev) >> 4].device , cmd, (void *) arg);
+	}
+}
+/*
+ * 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: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -r -u -N linux.orig/arch/arm/drivers/scsi/sg.c linux.arm/arch/arm/drivers/scsi/sg.c
--- linux.orig/arch/arm/drivers/scsi/sg.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/sg.c	Fri Oct 27 23:15:14 1995
@@ -0,0 +1,441 @@
+/*
+   History:
+    Started: Aug 9 by Lawrence Foard (entropy@world.std.com), to allow user 
+     process control of SCSI devices.
+    Development Sponsored by Killy Corp. NY NY
+    
+    Borrows code from st driver.
+*/
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/mtio.h>
+#include <linux/ioctl.h>
+#include <linux/fcntl.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include "../block/blk.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "scsi_ioctl.h"
+#include "sg.h"
+
+static void sg_init(void);
+static int sg_attach(Scsi_Device *);
+static int sg_detect(Scsi_Device *);
+static void sg_detach(Scsi_Device *);
+
+
+struct Scsi_Device_Template sg_template = {NULL, NULL, "sg", 0xff, 
+					     SCSI_GENERIC_MAJOR, 0, 0, 0, 0,
+					     sg_detect, sg_init,
+					     NULL, sg_attach, sg_detach};
+
+#ifdef SG_BIG_BUFF
+static char *big_buff;
+static struct wait_queue *big_wait;   /* wait for buffer available */
+static int big_inuse=0;
+#endif
+
+struct scsi_generic
+ {
+  Scsi_Device *device;
+  int users;   /* how many people have it open? */
+  struct wait_queue *generic_wait; /* wait for device to be available */
+  struct wait_queue *read_wait;    /* wait for response */
+  struct wait_queue *write_wait;   /* wait for free buffer */
+  int timeout; /* current default value for device */
+  int buff_len; /* length of current buffer */
+  char *buff;   /* the buffer */
+  struct sg_header header; /* header of pending command */
+  char exclude; /* opened for exclusive access */
+  char pending;  /* don't accept writes now */
+  char complete; /* command complete allow a read */
+ };
+
+static struct scsi_generic *scsi_generics=NULL;
+static void sg_free(char *buff,int size);
+
+static int sg_ioctl(struct inode * inode,struct file * file,
+	     unsigned int cmd_in, unsigned long arg)
+ {
+  int dev = MINOR(inode->i_rdev);
+  if ((dev<0) || (dev>=sg_template.dev_max))
+   return -ENXIO;
+  switch(cmd_in)
+   {
+    case SG_SET_TIMEOUT:
+     scsi_generics[dev].timeout=get_fs_long((int *) arg);
+     return 0;
+    case SG_GET_TIMEOUT:
+     return scsi_generics[dev].timeout;
+    default:
+     return scsi_ioctl(scsi_generics[dev].device, cmd_in, (void *) arg);
+   }
+ }
+
+static int sg_open(struct inode * inode, struct file * filp)
+ {
+  int dev=MINOR(inode->i_rdev);
+  int flags=filp->f_flags;
+  if (dev>=sg_template.dev_max || !scsi_generics[dev].device)
+   return -ENXIO;
+  if (O_RDWR!=(flags & O_ACCMODE))
+   return -EACCES;
+  if (flags & O_EXCL)
+   {
+    while(scsi_generics[dev].users)
+     {
+      if (flags & O_NONBLOCK)
+       return -EBUSY;
+      interruptible_sleep_on(&scsi_generics[dev].generic_wait);
+      if (current->signal & ~current->blocked)
+       return -ERESTARTSYS;
+     }
+    scsi_generics[dev].exclude=1;
+   }
+  else
+   while(scsi_generics[dev].exclude)
+    {
+     if (flags & O_NONBLOCK)
+      return -EBUSY;
+     interruptible_sleep_on(&scsi_generics[dev].generic_wait);
+     if (current->signal & ~current->blocked)
+      return -ERESTARTSYS;
+    }
+  if (!scsi_generics[dev].users && scsi_generics[dev].pending && scsi_generics[dev].complete)
+   {
+    if (scsi_generics[dev].buff != NULL)
+      sg_free(scsi_generics[dev].buff,scsi_generics[dev].buff_len);
+    scsi_generics[dev].buff=NULL;
+    scsi_generics[dev].pending=0;
+   }
+  if (!scsi_generics[dev].users)
+   scsi_generics[dev].timeout=SG_DEFAULT_TIMEOUT;
+  if (scsi_generics[dev].device->host->hostt->usage_count)
+    (*scsi_generics[dev].device->host->hostt->usage_count)++;
+  scsi_generics[dev].users++;
+  return 0;
+ }
+
+static void sg_close(struct inode * inode, struct file * filp)
+ {
+  int dev=MINOR(inode->i_rdev);
+  scsi_generics[dev].users--;
+  if (scsi_generics[dev].device->host->hostt->usage_count)
+    (*scsi_generics[dev].device->host->hostt->usage_count)--;
+  scsi_generics[dev].exclude=0;
+  wake_up(&scsi_generics[dev].generic_wait);
+ }
+
+static char *sg_malloc(int size)
+ {
+  if (size<=4096)
+   return (char *) scsi_malloc(size);
+#ifdef SG_BIG_BUFF
+  if (size<SG_BIG_BUFF)
+   {
+    while(big_inuse)
+     {
+      interruptible_sleep_on(&big_wait);
+      if (current->signal & ~current->blocked)
+       return NULL;
+     }
+    big_inuse=1;
+    return big_buff;
+   }
+#endif   
+  return NULL;
+ }
+
+static void sg_free(char *buff,int size) 
+ {
+#ifdef SG_BIG_BUFF
+  if (buff==big_buff)
+   {
+    big_inuse=0;
+    wake_up(&big_wait);
+    return;
+   }
+#endif
+  scsi_free(buff,size);
+ }
+
+static int sg_read(struct inode *inode,struct file *filp,char *buf,int count)
+ {
+  int dev=MINOR(inode->i_rdev);
+  int i;
+  struct scsi_generic *device=&scsi_generics[dev];
+  if ((i=verify_area(VERIFY_WRITE,buf,count)))
+   return i;
+  while(!device->pending || !device->complete)
+   {
+    if (filp->f_flags & O_NONBLOCK)
+     return -EWOULDBLOCK;
+    interruptible_sleep_on(&device->read_wait);
+    if (current->signal & ~current->blocked)
+     return -ERESTARTSYS;
+   }
+  device->header.pack_len=device->header.reply_len;
+  device->header.result=0;
+  if (count>=sizeof(struct sg_header))
+   {
+    memcpy_tofs(buf,&device->header,sizeof(struct sg_header));
+    buf+=sizeof(struct sg_header);
+    if (count>device->header.pack_len)
+     count=device->header.pack_len;
+    if (count > sizeof(struct sg_header)) {
+       memcpy_tofs(buf,device->buff,count-sizeof(struct sg_header));
+    }
+   }
+  else
+   count=0;
+  sg_free(device->buff,device->buff_len);
+  device->buff = NULL;
+  device->pending=0;
+  wake_up(&device->write_wait);
+  return count;
+ }
+
+static void sg_command_done(Scsi_Cmnd * SCpnt)
+ {
+  int dev=SCpnt->request.dev;
+  struct scsi_generic *device=&scsi_generics[dev];
+  if (!device->pending)
+   {
+    printk("unexpected done for sg %d\n",dev);
+    SCpnt->request.dev=-1;
+    return;
+   }
+  memcpy(device->header.sense_buffer, SCpnt->sense_buffer, sizeof(SCpnt->sense_buffer));
+  if (SCpnt->sense_buffer[0])
+   {
+    device->header.result=EIO;
+   }
+  else
+   device->header.result=SCpnt->result;
+  device->complete=1;
+  SCpnt->request.dev=-1;
+  wake_up(&scsi_generics[dev].read_wait);
+ }
+
+static int sg_write(struct inode *inode,struct file *filp,char *buf,int count)
+ {
+  int dev=MINOR(inode->i_rdev);
+  Scsi_Cmnd *SCpnt;
+  int bsize,size,amt,i;
+  unsigned char opcode;
+  unsigned char cmnd[MAX_COMMAND_SIZE];
+  struct scsi_generic *device=&scsi_generics[dev];
+
+  if ((i=verify_area(VERIFY_READ,buf,count)))
+   return i;
+  /*
+   * The minimum scsi command length is 6 bytes.  If we get anything less than this,
+   * it is clearly bogus.
+   */
+  if (count<(sizeof(struct sg_header) + 6))
+   return -EIO;
+  /* make sure we can fit */
+  while(device->pending)
+   {
+    if (filp->f_flags & O_NONBLOCK)
+     return -EWOULDBLOCK;
+#ifdef DEBUG
+    printk("sg_write: sleeping on pending request\n");
+#endif     
+    interruptible_sleep_on(&device->write_wait);
+    if (current->signal & ~current->blocked)
+     return -ERESTARTSYS;
+   }
+  device->pending=1;
+  device->complete=0;
+  memcpy_fromfs(&device->header,buf,sizeof(struct sg_header));
+  /* fix input size */
+  device->header.pack_len=count;
+  buf+=sizeof(struct sg_header);
+  bsize=(device->header.pack_len>device->header.reply_len) ? device->header.pack_len : device->header.reply_len;
+  bsize-=sizeof(struct sg_header);
+  amt=bsize;
+  if (!bsize)
+   bsize++;
+  bsize=(bsize+511) & ~511;
+  if ((bsize<0) || !(device->buff=sg_malloc(device->buff_len=bsize)))
+   {
+    device->pending=0;
+    wake_up(&device->write_wait);
+    return -ENOMEM;
+   }
+#ifdef DEBUG
+  printk("allocating device\n");
+#endif
+  if (!(SCpnt=allocate_device(NULL,device->device, !(filp->f_flags & O_NONBLOCK))))
+   {
+    device->pending=0;
+    wake_up(&device->write_wait);
+    sg_free(device->buff,device->buff_len);
+    device->buff = NULL;
+    return -EWOULDBLOCK;
+   } 
+#ifdef DEBUG
+  printk("device allocated\n");
+#endif    
+  /* now issue command */
+  SCpnt->request.dev=dev;
+  SCpnt->sense_buffer[0]=0;
+  opcode = get_fs_byte(buf);
+  size=COMMAND_SIZE(opcode);
+  if (opcode >= 0xc0 && device->header.twelve_byte) size = 12;
+  SCpnt->cmd_len = size;
+  /*
+   * Verify that the user has actually passed enough bytes for this command.
+   */
+  if (count<(sizeof(struct sg_header) + size))
+    {
+      device->pending=0;
+      wake_up(&device->write_wait);
+      sg_free(device->buff,device->buff_len);
+      device->buff = NULL;
+      return -EIO;
+    }
+
+  memcpy_fromfs(cmnd,buf,size);
+  buf+=size;
+  memcpy_fromfs(device->buff,buf,device->header.pack_len-size-sizeof(struct sg_header));
+  cmnd[1]=(cmnd[1] & 0x1f) | (device->device->lun<<5);
+#ifdef DEBUG
+  printk("do cmd\n");
+#endif
+  scsi_do_cmd (SCpnt,(void *) cmnd,
+               (void *) device->buff,amt,
+	       sg_command_done,device->timeout,SG_DEFAULT_RETRIES);
+#ifdef DEBUG
+  printk("done cmd\n");
+#endif               
+  return count;
+ }
+
+static struct file_operations sg_fops = {
+   NULL,            /* lseek */
+   sg_read,         /* read */
+   sg_write,        /* write */
+   NULL,            /* readdir */
+   NULL,            /* select */
+   sg_ioctl,        /* ioctl */
+   NULL,            /* mmap */
+   sg_open,         /* open */
+   sg_close,        /* release */
+   NULL		    /* fsync */
+};
+
+
+static int sg_detect(Scsi_Device * SDp){
+  ++sg_template.dev_noticed;
+  return 1;
+}
+
+/* Driver initialization */
+static void sg_init()
+ {
+   static int sg_registered = 0;
+   
+   if (sg_template.dev_noticed == 0) return;
+
+   if(!sg_registered) {
+     if (register_chrdev(SCSI_GENERIC_MAJOR,"sg",&sg_fops)) 
+       {
+	 printk("Unable to get major %d for generic SCSI device\n",
+		SCSI_GENERIC_MAJOR);
+	 return;
+       }
+     sg_registered++;
+   }
+
+   /* If we have already been through here, return */
+   if(scsi_generics) return;
+
+#ifdef DEBUG
+  printk("sg: Init generic device.\n");
+#endif
+
+#ifdef SG_BIG_BUFF
+  big_buff= (char *) scsi_init_malloc(SG_BIG_BUFF, GFP_ATOMIC | GFP_DMA);
+#endif
+
+   scsi_generics = (struct scsi_generic *) 
+     scsi_init_malloc((sg_template.dev_noticed + SG_EXTRA_DEVS) 
+		      * sizeof(struct scsi_generic), GFP_ATOMIC);
+   memset(scsi_generics, 0, (sg_template.dev_noticed + SG_EXTRA_DEVS)
+	  * sizeof(struct scsi_generic));
+
+   sg_template.dev_max = sg_template.dev_noticed + SG_EXTRA_DEVS;
+ }
+
+static int sg_attach(Scsi_Device * SDp)
+ {
+   struct scsi_generic * gpnt;
+   int i;
+
+   if(sg_template.nr_dev >= sg_template.dev_max) 
+     {
+       SDp->attached--;
+       return 1;
+     }
+
+   for(gpnt = scsi_generics, i=0; i<sg_template.dev_max; i++, gpnt++) 
+     if(!gpnt->device) break;
+
+   if(i >= sg_template.dev_max) panic ("scsi_devices corrupt (sg)");
+
+   scsi_generics[i].device=SDp;
+   scsi_generics[i].users=0;
+   scsi_generics[i].generic_wait=NULL;
+   scsi_generics[i].read_wait=NULL;
+   scsi_generics[i].write_wait=NULL;
+   scsi_generics[i].buff=NULL;
+   scsi_generics[i].exclude=0;
+   scsi_generics[i].pending=0;
+   scsi_generics[i].timeout=SG_DEFAULT_TIMEOUT;
+   sg_template.nr_dev++;
+   return 0;
+ };
+
+
+
+static void sg_detach(Scsi_Device * SDp)
+{
+  struct scsi_generic * gpnt;
+  int i;
+  
+  for(gpnt = scsi_generics, i=0; i<sg_template.dev_max; i++, gpnt++) 
+    if(gpnt->device == SDp) {
+      gpnt->device = NULL;
+      SDp->attached--;
+      sg_template.nr_dev--;
+      return;
+    }
+  return;
+}
+
+/*
+ * 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: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -r -u -N linux.orig/arch/arm/drivers/scsi/sg.h linux.arm/arch/arm/drivers/scsi/sg.h
--- linux.orig/arch/arm/drivers/scsi/sg.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/sg.h	Fri Oct 27 23:15:13 1995
@@ -0,0 +1,36 @@
+/*
+   History:
+    Started: Aug 9 by Lawrence Foard (entropy@world.std.com), to allow user 
+     process control of SCSI devices.
+    Development Sponsored by Killy Corp. NY NY
+*/
+
+/* 
+ An SG device is accessed by writing "packets" to it, the replies
+ are then read using the read call. The same header is used for 
+ reply, just ignore reply_len field.
+*/
+
+struct sg_header
+ {
+  int pack_len;    /* length of incoming packet <4096 (including header) */
+  int reply_len;   /* maximum length <4096 of expected reply */
+  int pack_id;     /* id number of packet */
+  int result;      /* 0==ok, otherwise refer to errno codes */
+  unsigned int twelve_byte:1; /* Force 12 byte command length for group 6 & 7 commands  */
+  unsigned int other_flags:31;			/* for future use */
+  unsigned char sense_buffer[16]; /* used only by reads */
+  /* command follows then data for command */
+ };
+
+/* ioctl's */
+#define SG_SET_TIMEOUT 0x2201  /* set timeout *(int *)arg==timeout */
+#define SG_GET_TIMEOUT 0x2202  /* get timeout return timeout */
+
+#define SG_DEFAULT_TIMEOUT 6000 /* 1 minute timeout */
+#define SG_DEFAULT_RETRIES 1
+
+#define SG_MAX_QUEUE 4 /* maximum outstanding request, arbitrary, may be
+                          changed if sufficient DMA buffer room available */
+
+#define SG_BIG_BUFF 32768
diff -r -u -N linux.orig/arch/arm/drivers/scsi/sr.c linux.arm/arch/arm/drivers/scsi/sr.c
--- linux.orig/arch/arm/drivers/scsi/sr.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/sr.c	Fri Oct 27 23:15:14 1995
@@ -0,0 +1,1088 @@
+/*
+ *      sr.c Copyright (C) 1992 David Giller
+ *	     Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ *      adapted from:
+ *	sd.c Copyright (C) 1992 Drew Eckhardt 
+ *	Linux scsi disk driver by
+ *		Drew Eckhardt 
+ *
+ *	<drew@colorado.edu>
+ *
+ *       Modified by Eric Youngdale ericy@cais.com to
+ *       add scatter-gather, multiple outstanding request, and other
+ *       enhancements.
+ *
+ *	 Modified by Eric Youngdale eric@aib.com to support loadable
+ *	 low-level scsi drivers.
+ */
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/cdrom.h>
+#include <asm/system.h>
+
+#define MAJOR_NR SCSI_CDROM_MAJOR
+#include "../block/blk.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "sr.h"
+#include "scsi_ioctl.h"   /* For the door lock/unlock commands */
+#include "constants.h"
+
+#define MAX_RETRIES 3
+#define SR_TIMEOUT 15000
+
+static void sr_init(void);
+static void sr_finish(void);
+static int sr_attach(Scsi_Device *);
+static int sr_detect(Scsi_Device *);
+static void sr_detach(Scsi_Device *);
+
+struct Scsi_Device_Template sr_template = {NULL, "cdrom", "sr", TYPE_ROM, 
+					     SCSI_CDROM_MAJOR, 0, 0, 0, 1,
+					     sr_detect, sr_init,
+					     sr_finish, sr_attach, sr_detach};
+
+Scsi_CD * scsi_CDs;
+static int * sr_sizes;
+
+static int * sr_blocksizes;
+
+static int sr_open(struct inode *, struct file *);
+static void get_sectorsize(int);
+
+extern int sr_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+
+void requeue_sr_request (Scsi_Cmnd * SCpnt);
+static int check_cdrom_media_change(dev_t);
+
+static void sr_release(struct inode * inode, struct file * file)
+{
+	sync_dev(inode->i_rdev);
+	if(! --scsi_CDs[MINOR(inode->i_rdev)].device->access_count)
+	  sr_ioctl(inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0);
+	if (scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)
+	  (*scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)--;
+}
+
+static struct file_operations sr_fops = 
+{
+	NULL,			/* lseek - default */
+	block_read,		/* read - general block-dev read */
+	block_write,		/* write - general block-dev write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select */
+	sr_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	sr_open,       		/* special open code */
+	sr_release,		/* release */
+	NULL,			/* fsync */
+	NULL,			/* fasync */
+	check_cdrom_media_change,  /* Disk change */
+	NULL			/* revalidate */
+};
+
+/*
+ * This function checks to see if the media has been changed in the
+ * CDROM drive.  It is possible that we have already sensed a change,
+ * or the drive may have sensed one and not yet reported it.  We must
+ * be ready for either case. This function always reports the current
+ * value of the changed bit.  If flag is 0, then the changed bit is reset.
+ * This function could be done as an ioctl, but we would need to have
+ * an inode for that to work, and we do not always have one.
+ */
+
+int check_cdrom_media_change(dev_t full_dev){
+	int retval, target;
+	struct inode inode;
+	int flag = 0;
+
+	target =  MINOR(full_dev);
+
+	if (target >= sr_template.nr_dev) {
+		printk("CD-ROM request error: invalid device.\n");
+		return 0;
+	};
+
+	inode.i_rdev = full_dev;  /* This is all we really need here */
+	retval = sr_ioctl(&inode, NULL, SCSI_IOCTL_TEST_UNIT_READY, 0);
+
+	if(retval){ /* Unable to test, unit probably not ready.  This usually
+		     means there is no disc in the drive.  Mark as changed,
+		     and we will figure it out later once the drive is
+		     available again.  */
+
+	  scsi_CDs[target].device->changed = 1;
+	  return 1; /* This will force a flush, if called from
+		       check_disk_change */
+	};
+
+	retval = scsi_CDs[target].device->changed;
+	if(!flag) {
+	  scsi_CDs[target].device->changed = 0;
+	  /* If the disk changed, the capacity will now be different,
+	     so we force a re-read of this information */
+	  if (retval) scsi_CDs[target].needs_sector_size = 1;
+	};
+	return retval;
+}
+
+/*
+ * rw_intr is the interrupt routine for the device driver.  It will be notified on the 
+ * end of a SCSI read / write, and will take on of several actions based on success or failure.
+ */
+
+static void rw_intr (Scsi_Cmnd * SCpnt)
+{
+	int result = SCpnt->result;
+	int this_count = SCpnt->this_count;
+
+#ifdef DEBUG
+	printk("sr.c done: %x %x\n",result, SCpnt->request.bh->b_data);
+#endif
+	if (!result)
+		{ /* No error */
+		  if (SCpnt->use_sg == 0) {
+		    if (SCpnt->buffer != SCpnt->request.buffer)
+		      {
+			int offset;
+			offset = (SCpnt->request.sector % 4) << 9;
+			memcpy((char *)SCpnt->request.buffer, 
+			       (char *)SCpnt->buffer + offset, 
+			       this_count << 9);
+			/* Even though we are not using scatter-gather, we look
+			   ahead and see if there is a linked request for the
+			   other half of this buffer.  If there is, then satisfy
+			   it. */
+			if((offset == 0) && this_count == 2 &&
+			   SCpnt->request.nr_sectors > this_count && 
+			   SCpnt->request.bh &&
+			   SCpnt->request.bh->b_reqnext &&
+			   SCpnt->request.bh->b_reqnext->b_size == 1024) {
+			  memcpy((char *)SCpnt->request.bh->b_reqnext->b_data, 
+				 (char *)SCpnt->buffer + 1024, 
+				 1024);
+			  this_count += 2;
+			};
+			
+			scsi_free(SCpnt->buffer, 2048);
+		      }
+		  } else {
+		    struct scatterlist * sgpnt;
+		    int i;
+		    sgpnt = (struct scatterlist *) SCpnt->buffer;
+		    for(i=0; i<SCpnt->use_sg; i++) {
+		      if (sgpnt[i].alt_address) {
+			if (sgpnt[i].alt_address != sgpnt[i].address) {
+			  memcpy(sgpnt[i].alt_address, sgpnt[i].address, sgpnt[i].length);
+			};
+			scsi_free(sgpnt[i].address, sgpnt[i].length);
+		      };
+		    };
+		    scsi_free(SCpnt->buffer, SCpnt->sglist_len);  /* Free list of scatter-gather pointers */
+		    if(SCpnt->request.sector % 4) this_count -= 2;
+/* See   if there is a padding record at the end that needs to be removed */
+		    if(this_count > SCpnt->request.nr_sectors)
+		      this_count -= 2;
+		  };
+
+#ifdef DEBUG
+		printk("(%x %x %x) ",SCpnt->request.bh, SCpnt->request.nr_sectors, 
+		       this_count);
+#endif
+		if (SCpnt->request.nr_sectors > this_count)
+			{	 
+			SCpnt->request.errors = 0;
+			if (!SCpnt->request.bh)
+			    panic("sr.c: linked page request (%lx %x)",
+				  SCpnt->request.sector, this_count);
+			}
+
+		  SCpnt = end_scsi_request(SCpnt, 1, this_count);  /* All done */
+		  requeue_sr_request(SCpnt);
+		  return;
+		} /* Normal completion */
+
+	/* We only come through here if we have an error of some kind */
+
+/* Free up any indirection buffers we allocated for DMA purposes. */
+	if (SCpnt->use_sg) {
+	  struct scatterlist * sgpnt;
+	  int i;
+	  sgpnt = (struct scatterlist *) SCpnt->buffer;
+	  for(i=0; i<SCpnt->use_sg; i++) {
+	    if (sgpnt[i].alt_address) {
+	      scsi_free(sgpnt[i].address, sgpnt[i].length);
+	    };
+	  };
+	  scsi_free(SCpnt->buffer, SCpnt->sglist_len);  /* Free list of scatter-gather pointers */
+	} else {
+	  if (SCpnt->buffer != SCpnt->request.buffer)
+	    scsi_free(SCpnt->buffer, SCpnt->bufflen);
+	};
+
+	if (driver_byte(result) != 0) {
+		if ((SCpnt->sense_buffer[0] & 0x7f) == 0x70) {
+			if ((SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION) {
+				/* detected disc change.  set a bit and quietly refuse	*/
+				/* further access.					*/
+		    
+				scsi_CDs[DEVICE_NR(SCpnt->request.dev)].device->changed = 1;
+				SCpnt = end_scsi_request(SCpnt, 0, this_count);
+			        requeue_sr_request(SCpnt);
+				return;
+			}
+		}
+	    
+		if (SCpnt->sense_buffer[2] == ILLEGAL_REQUEST) {
+			printk("CD-ROM error: ");
+			print_sense("sr", SCpnt);
+			printk("command was: ");
+			print_command(SCpnt->cmnd);
+			if (scsi_CDs[DEVICE_NR(SCpnt->request.dev)].ten) {
+				scsi_CDs[DEVICE_NR(SCpnt->request.dev)].ten = 0;
+				requeue_sr_request(SCpnt);
+				result = 0;
+				return;
+			} else {
+			  SCpnt = end_scsi_request(SCpnt, 0, this_count);
+			  requeue_sr_request(SCpnt); /* Do next request */
+			  return;
+			}
+
+		}
+
+		if (SCpnt->sense_buffer[2] == NOT_READY) {
+			printk("CDROM not ready.  Make sure you have a disc in the drive.\n");
+			SCpnt = end_scsi_request(SCpnt, 0, this_count);
+			requeue_sr_request(SCpnt); /* Do next request */
+			return;
+		};
+	      }
+	
+	/* We only get this far if we have an error we have not recognized */
+	if(result) {
+	  printk("SCSI CD error : host %d id %d lun %d return code = %03x\n", 
+		 scsi_CDs[DEVICE_NR(SCpnt->request.dev)].device->host->host_no, 
+		 scsi_CDs[DEVICE_NR(SCpnt->request.dev)].device->id,
+		 scsi_CDs[DEVICE_NR(SCpnt->request.dev)].device->lun,
+		 result);
+	    
+	  if (status_byte(result) == CHECK_CONDITION)
+		  print_sense("sr", SCpnt);
+	  
+	  SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.current_nr_sectors);
+	  requeue_sr_request(SCpnt);
+  }
+}
+
+/*
+ * Here I tried to implement better support for PhotoCD's.
+ * 
+ * Much of this has do be done with vendor-specific SCSI-commands.
+ * So I have to complete it step by step. Useful information is welcome.
+ *
+ * Actually works:
+ *   - NEC:     Detection and support of multisession CD's. Special handling
+ *              for XA-disks is not necessary.
+ *     
+ *   - TOSHIBA: setting density is done here now, mounting PhotoCD's should
+ *              work now without running the program "set_density"
+ *              Multisession CD's are supported too.
+ *
+ *   kraxel@cs.tu-berlin.de (Gerd Knorr)
+ */
+
+static void sr_photocd(struct inode *inode)
+{
+  unsigned long   sector,min,sec,frame;
+  unsigned char   buf[40];    /* the buffer for the ioctl */
+  unsigned char   *cmd;       /* the scsi-command */
+  unsigned char   *send;      /* the data we send to the drive ... */
+  unsigned char   *rec;       /* ... and get back */
+  int             rc,is_xa,no_multi;
+
+  if (scsi_CDs[MINOR(inode->i_rdev)].xa_flags & 0x02) {
+#ifdef DEBUG
+    printk("sr_photocd: CDROM and/or the driver does not support multisession CD's");
+#endif
+    return;
+  }
+  
+  if (!suser()) {
+    /* I'm not the superuser, so SCSI_IOCTL_SEND_COMMAND isn't allowed for me.
+     * That's why mpcd_sector will be initialized with zero, because I'm not
+     * able to get the right value. Necessary only if access_count is 1, else
+     * no disk change happened since the last call of this function and we can
+     * keep the old value.
+     */
+    if (1 == scsi_CDs[MINOR(inode->i_rdev)].device->access_count) {
+      scsi_CDs[MINOR(inode->i_rdev)].mpcd_sector = 0;
+      scsi_CDs[MINOR(inode->i_rdev)].xa_flags &= ~0x01;
+    }
+    return;
+  }
+
+  sector   = 0;
+  is_xa    = 0;
+  no_multi = 0;
+  cmd = rec = &buf[8];
+
+  switch(scsi_CDs[MINOR(inode->i_rdev)].device->manufacturer) {
+
+  case SCSI_MAN_NEC:
+#ifdef DEBUG
+    printk("sr_photocd: use NEC code\n");
+#endif
+    memset(buf,0,40);
+    *((unsigned long*)buf)   = 0x0;   /* we send nothing...     */
+    *((unsigned long*)buf+1) = 0x16;  /* and receive 0x16 bytes */
+    cmd[0] = 0xde;
+    cmd[1] = 0x03;
+    cmd[2] = 0xb0;
+    rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+			   SCSI_IOCTL_SEND_COMMAND, buf);
+    if (rc != 0) {
+      printk("sr_photocd: ioctl error (NEC): 0x%x\n",rc);
+      break;
+    }
+    if (rec[14] != 0 && rec[14] != 0xb0) {
+      printk("sr_photocd: Hmm, seems the CDROM doesn't support multisession CD's\n");
+      no_multi = 1;
+      break;
+    }
+    min   = (unsigned long) rec[15]/16*10 + (unsigned long) rec[15]%16;
+    sec   = (unsigned long) rec[16]/16*10 + (unsigned long) rec[16]%16;
+    frame = (unsigned long) rec[17]/16*10 + (unsigned long) rec[17]%16;
+    sector = min*CD_SECS*CD_FRAMES + sec*CD_FRAMES + frame;
+    is_xa  = (rec[14] == 0xb0);
+#ifdef DEBUG
+    if (sector) {
+      printk("sr_photocd: multisession CD detected. start: %lu\n",sector);
+    }
+#endif
+    break;
+
+  case SCSI_MAN_TOSHIBA:
+#ifdef DEBUG
+    printk("sr_photocd: use TOSHIBA code\n");
+#endif
+    
+    /* we request some disc information (is it a XA-CD ?,
+       where starts the last session ?) */
+    memset(buf,0,40);
+    *((unsigned long*)buf)   = 0;
+    *((unsigned long*)buf+1) = 4;  /* we receive 4 bytes from the drive */
+    cmd[0] = 0xc7;
+    cmd[1] = 3;
+    rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+			   SCSI_IOCTL_SEND_COMMAND, buf);
+    if (rc != 0) {
+      if (rc == 0x28000002) {
+	/* Got a "not ready" - error. No chance to find out if this is
+	   because there is no CD in the drive or because the drive
+	   don't knows multisession CD's. So I need to do an extra check... */
+	if (kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+			      SCSI_IOCTL_TEST_UNIT_READY, NULL)) {
+	  printk("sr_photocd: drive not ready\n");
+	} else {
+	  printk("sr_photocd: Hmm, seems the CDROM doesn't support multisession CD's\n");
+	  no_multi = 1;
+	}
+      } else
+	printk("sr_photocd: ioctl error (TOSHIBA #1): 0x%x\n",rc);
+      break; /* if the first ioctl fails, we don't call the second one */
+    }
+    is_xa  = (rec[0] == 0x20);
+    min    = (unsigned long) rec[1]/16*10 + (unsigned long) rec[1]%16;
+    sec    = (unsigned long) rec[2]/16*10 + (unsigned long) rec[2]%16;
+    frame  = (unsigned long) rec[3]/16*10 + (unsigned long) rec[3]%16;
+    sector = min*CD_SECS*CD_FRAMES + sec*CD_FRAMES + frame;
+    if (sector) {
+      sector -= CD_BLOCK_OFFSET;
+#ifdef DEBUG
+      printk("sr_photocd: multisession CD detected: start: %lu\n",sector);
+#endif
+    }
+
+    /* now we do a get_density... */
+    memset(buf,0,40);
+    *((unsigned long*)buf)   = 0;
+    *((unsigned long*)buf+1) = 12;
+    cmd[0] = 0x1a;
+    cmd[2] = 1;
+    cmd[4] = 12;
+    rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+			   SCSI_IOCTL_SEND_COMMAND, buf);
+    if (rc != 0) {
+      printk("sr_photocd: ioctl error (TOSHIBA #2): 0x%x\n",rc);
+      break;
+    }
+#ifdef DEBUG
+    printk("sr_photocd: get_density: 0x%x\n",rec[4]);
+#endif
+    
+    /* ...and only if necessary a set_density */
+    if ((rec[4] != 0x81 && is_xa) || (rec[4] != 0 && !is_xa)) {
+#ifdef DEBUG
+      printk("sr_photocd: doing set_density\n");
+#endif
+      memset(buf,0,40);
+      *((unsigned long*)buf)   = 12;  /* sending 12 bytes... */
+      *((unsigned long*)buf+1) = 0;
+      cmd[0] = 0x15;
+      cmd[1] = (1 << 4);
+      cmd[4] = 12;
+      send = &cmd[6];                 /* this is a 6-Byte command          */
+      send[ 3] = 0x08;                /* the data for the command          */
+      send[ 4] = (is_xa) ? 0x81 : 0;  /* density 0x81 for XA-CD's, 0 else  */
+      send[10] = 0x08;
+      rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+			     SCSI_IOCTL_SEND_COMMAND, buf);
+      if (rc != 0) {
+	printk("sr_photocd: ioctl error (TOSHIBA #3): 0x%x\n",rc);
+      }
+      /* The set_density command may have changed the sector size or capacity. */
+      scsi_CDs[MINOR(inode->i_rdev)].needs_sector_size = 1;
+    }
+    break;
+
+  case SCSI_MAN_NEC_OLDCDR:
+  case SCSI_MAN_UNKNOWN:
+  default:
+    sector = 0;
+    no_multi = 1;
+    break; }
+
+  scsi_CDs[MINOR(inode->i_rdev)].mpcd_sector = sector;
+  if (is_xa)
+    scsi_CDs[MINOR(inode->i_rdev)].xa_flags |= 0x01;
+  else
+    scsi_CDs[MINOR(inode->i_rdev)].xa_flags &= ~0x01;
+  if (no_multi)
+    scsi_CDs[MINOR(inode->i_rdev)].xa_flags |= 0x02;
+  return;
+}
+
+static int sr_open(struct inode * inode, struct file * filp)
+{
+	if(MINOR(inode->i_rdev) >= sr_template.nr_dev || 
+	   !scsi_CDs[MINOR(inode->i_rdev)].device) return -ENXIO;   /* No such device */
+
+	if (filp->f_mode & 2)  
+	    return -EROFS;
+
+        check_disk_change(inode->i_rdev);
+
+	if(!scsi_CDs[MINOR(inode->i_rdev)].device->access_count++)
+	  sr_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
+	if (scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)
+	  (*scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)++;
+
+	sr_photocd(inode);
+
+	/* If this device did not have media in the drive at boot time, then
+	   we would have been unable to get the sector size.  Check to see if
+	   this is the case, and try again.
+	   */
+
+	if(scsi_CDs[MINOR(inode->i_rdev)].needs_sector_size)
+	  get_sectorsize(MINOR(inode->i_rdev));
+
+	return 0;
+}
+
+
+/*
+ * do_sr_request() is the request handler function for the sr driver.  Its function in life 
+ * is to take block device requests, and translate them to SCSI commands.
+ */
+	
+static void do_sr_request (void)
+{
+  Scsi_Cmnd * SCpnt = NULL;
+  struct request * req = NULL;
+  unsigned long flags;
+  int flag = 0;
+
+  while (1==1){
+    save_flags(flags);
+    cli();
+    if (CURRENT != NULL && CURRENT->dev == -1) {
+      restore_flags(flags);
+      return;
+    };
+    
+    INIT_SCSI_REQUEST;
+
+    if (flag++ == 0)
+      SCpnt = allocate_device(&CURRENT,
+			      scsi_CDs[DEVICE_NR(MINOR(CURRENT->dev))].device, 0); 
+    else SCpnt = NULL;
+    restore_flags(flags);
+
+/* This is a performance enhancement.  We dig down into the request list and
+   try and find a queueable request (i.e. device not busy, and host able to
+   accept another command.  If we find one, then we queue it. This can
+   make a big difference on systems with more than one disk drive.  We want
+   to have the interrupts off when monkeying with the request list, because
+   otherwise the kernel might try and slip in a request in between somewhere. */
+
+    if (!SCpnt && sr_template.nr_dev > 1){
+      struct request *req1;
+      req1 = NULL;
+      save_flags(flags);
+      cli();
+      req = CURRENT;
+      while(req){
+	SCpnt = request_queueable(req,
+				  scsi_CDs[DEVICE_NR(MINOR(req->dev))].device);
+	if(SCpnt) break;
+	req1 = req;
+	req = req->next;
+      };
+      if (SCpnt && req->dev == -1) {
+	if (req == CURRENT) 
+	  CURRENT = CURRENT->next;
+	else
+	  req1->next = req->next;
+      };
+      restore_flags(flags);
+    };
+    
+    if (!SCpnt)
+      return; /* Could not find anything to do */
+    
+  wake_up(&wait_for_request);
+
+/* Queue command */
+  requeue_sr_request(SCpnt);
+  };  /* While */
+}    
+
+void requeue_sr_request (Scsi_Cmnd * SCpnt)
+{
+	unsigned int dev, block, realcount;
+	unsigned char cmd[10], *buffer, tries;
+	int this_count, start, end_rec;
+
+	tries = 2;
+
+      repeat:
+	if(!SCpnt || SCpnt->request.dev <= 0) {
+	  do_sr_request();
+	  return;
+	}
+
+	dev =  MINOR(SCpnt->request.dev);
+	block = SCpnt->request.sector;	
+	buffer = NULL;
+	this_count = 0;
+
+	if (dev >= sr_template.nr_dev)
+		{
+		/* printk("CD-ROM request error: invalid device.\n");			*/
+		SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+		tries = 2;
+		goto repeat;
+		}
+
+	if (!scsi_CDs[dev].use)
+		{
+		/* printk("CD-ROM request error: device marked not in use.\n");		*/
+		SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+		tries = 2;
+		goto repeat;
+		}
+
+	if (scsi_CDs[dev].device->changed)
+	        {
+/* 
+ * quietly refuse to do anything to a changed disc until the changed bit has been reset
+ */
+		/* printk("CD-ROM has been changed.  Prohibiting further I/O.\n");	*/
+		SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+		tries = 2;
+		goto repeat;
+		}
+	
+	switch (SCpnt->request.cmd)
+		{
+		case WRITE: 		
+			SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+			goto repeat;
+			break;
+		case READ : 
+		        cmd[0] = READ_6;
+			break;
+		default : 
+			panic ("Unknown sr command %d\n", SCpnt->request.cmd);
+		}
+	
+	cmd[1] = (SCpnt->lun << 5) & 0xe0;
+
+/*
+           Now do the grungy work of figuring out which sectors we need, and
+	   where in memory we are going to put them.
+
+	   The variables we need are:
+
+	   this_count= number of 512 byte sectors being read 
+	   block     = starting cdrom sector to read.
+	   realcount = # of cdrom sectors to read
+
+	   The major difference between a scsi disk and a scsi cdrom
+is that we will always use scatter-gather if we can, because we can
+work around the fact that the buffer cache has a block size of 1024,
+and we have 2048 byte sectors.  This code should work for buffers that
+are any multiple of 512 bytes long.  */
+
+	SCpnt->use_sg = 0;
+
+	if (SCpnt->host->sg_tablesize > 0 &&
+	    (!need_isa_buffer ||
+	    dma_free_sectors >= 10)) {
+	  struct buffer_head * bh;
+	  struct scatterlist * sgpnt;
+	  int count, this_count_max;
+	  bh = SCpnt->request.bh;
+	  this_count = 0;
+	  count = 0;
+	  this_count_max = (scsi_CDs[dev].ten ? 0xffff : 0xff) << 4;
+	  /* Calculate how many links we can use.  First see if we need
+	   a padding record at the start */
+	  this_count = SCpnt->request.sector % 4;
+	  if(this_count) count++;
+	  while(bh && count < SCpnt->host->sg_tablesize) {
+	    if ((this_count + (bh->b_size >> 9)) > this_count_max) break;
+	    this_count += (bh->b_size >> 9);
+	    count++;
+	    bh = bh->b_reqnext;
+	  };
+	  /* Fix up in case of an odd record at the end */
+	  end_rec = 0;
+	  if(this_count % 4) {
+	    if (count < SCpnt->host->sg_tablesize) {
+	      count++;
+	      end_rec = (4 - (this_count % 4)) << 9;
+	      this_count += 4 - (this_count % 4);
+	    } else {
+	      count--;
+	      this_count -= (this_count % 4);
+	    };
+	  };
+	  SCpnt->use_sg = count;  /* Number of chains */
+	  count = 512;/* scsi_malloc can only allocate in chunks of 512 bytes*/
+	  while( count < (SCpnt->use_sg * sizeof(struct scatterlist))) 
+	    count = count << 1;
+	  SCpnt->sglist_len = count;
+	  sgpnt = (struct scatterlist * ) scsi_malloc(count);
+	  if (!sgpnt) {
+	    printk("Warning - running *really* short on DMA buffers\n");
+	    SCpnt->use_sg = 0;  /* No memory left - bail out */
+	  } else {
+	    buffer = (unsigned char *) sgpnt;
+	    count = 0;
+	    bh = SCpnt->request.bh;
+	    if(SCpnt->request.sector % 4) {
+	      sgpnt[count].length = (SCpnt->request.sector % 4) << 9;
+	      sgpnt[count].address = (char *) scsi_malloc(sgpnt[count].length);
+	      if(!sgpnt[count].address) panic("SCSI DMA pool exhausted.");
+	      sgpnt[count].alt_address = sgpnt[count].address; /* Flag to delete
+								  if needed */
+	      count++;
+	    };
+	    for(bh = SCpnt->request.bh; count < SCpnt->use_sg; 
+		count++, bh = bh->b_reqnext) {
+	      if (bh) { /* Need a placeholder at the end of the record? */
+		sgpnt[count].address = bh->b_data;
+		sgpnt[count].length = bh->b_size;
+		sgpnt[count].alt_address = NULL;
+	      } else {
+		sgpnt[count].address = (char *) scsi_malloc(end_rec);
+		if(!sgpnt[count].address) panic("SCSI DMA pool exhausted.");
+		sgpnt[count].length = end_rec;
+		sgpnt[count].alt_address = sgpnt[count].address;
+		if (count+1 != SCpnt->use_sg) panic("Bad sr request list");
+		break;
+	      };
+	      if (((int) sgpnt[count].address) + sgpnt[count].length > 
+		  ISA_DMA_THRESHOLD & (SCpnt->host->unchecked_isa_dma)) {
+		sgpnt[count].alt_address = sgpnt[count].address;
+		/* We try and avoid exhausting the DMA pool, since it is easier
+		   to control usage here.  In other places we might have a more
+		   pressing need, and we would be screwed if we ran out */
+		if(dma_free_sectors < (sgpnt[count].length >> 9) + 5) {
+		  sgpnt[count].address = NULL;
+		} else {
+		  sgpnt[count].address = (char *) scsi_malloc(sgpnt[count].length);
+		};
+/* If we start running low on DMA buffers, we abort the scatter-gather
+   operation, and free all of the memory we have allocated.  We want to
+   ensure that all scsi operations are able to do at least a non-scatter/gather
+   operation */
+		if(sgpnt[count].address == NULL){ /* Out of dma memory */
+		  printk("Warning: Running low on SCSI DMA buffers");
+		  /* Try switching back to a non scatter-gather operation. */
+		  while(--count >= 0){
+		    if(sgpnt[count].alt_address) 
+		      scsi_free(sgpnt[count].address, sgpnt[count].length);
+		  };
+		  SCpnt->use_sg = 0;
+		  scsi_free(buffer, SCpnt->sglist_len);
+		  break;
+		}; /* if address == NULL */
+	      };  /* if need DMA fixup */
+	    };  /* for loop to fill list */
+#ifdef DEBUG
+	    printk("SR: %d %d %d %d %d *** ",SCpnt->use_sg, SCpnt->request.sector,
+		   this_count, 
+		   SCpnt->request.current_nr_sectors,
+		   SCpnt->request.nr_sectors);
+	    for(count=0; count<SCpnt->use_sg; count++)
+	      printk("SGlist: %d %x %x %x\n", count,
+		     sgpnt[count].address, 
+		     sgpnt[count].alt_address, 
+		     sgpnt[count].length);
+#endif
+	  };  /* Able to allocate scatter-gather list */
+	};
+	
+	if (SCpnt->use_sg == 0){
+	  /* We cannot use scatter-gather.  Do this the old fashion way */
+	  if (!SCpnt->request.bh)  	
+	    this_count = SCpnt->request.nr_sectors;
+	  else
+	    this_count = (SCpnt->request.bh->b_size >> 9);
+	  
+	  start = block % 4;
+	  if (start)
+	    {				  
+	      this_count = ((this_count > 4 - start) ? 
+			    (4 - start) : (this_count));
+	      buffer = (unsigned char *) scsi_malloc(2048);
+	    } 
+	  else if (this_count < 4)
+	    {
+	      buffer = (unsigned char *) scsi_malloc(2048);
+	    }
+	  else
+	    {
+	      this_count -= this_count % 4;
+	      buffer = (unsigned char *) SCpnt->request.buffer;
+	      if (((int) buffer) + (this_count << 9) > ISA_DMA_THRESHOLD & 
+		  (SCpnt->host->unchecked_isa_dma))
+		buffer = (unsigned char *) scsi_malloc(this_count << 9);
+	    }
+	};
+
+	if (scsi_CDs[dev].sector_size == 2048)
+	  block = block >> 2; /* These are the sectors that the cdrom uses */
+	else
+	  block = block & 0xfffffffc;
+
+	realcount = (this_count + 3) / 4;
+
+	if (scsi_CDs[dev].sector_size == 512) realcount = realcount << 2;
+
+	if (((realcount > 0xff) || (block > 0x1fffff)) && scsi_CDs[dev].ten) 
+		{
+		if (realcount > 0xffff)
+		        {
+			realcount = 0xffff;
+			this_count = realcount * (scsi_CDs[dev].sector_size >> 9);
+			}
+
+		cmd[0] += READ_10 - READ_6 ;
+		cmd[2] = (unsigned char) (block >> 24) & 0xff;
+		cmd[3] = (unsigned char) (block >> 16) & 0xff;
+		cmd[4] = (unsigned char) (block >> 8) & 0xff;
+		cmd[5] = (unsigned char) block & 0xff;
+		cmd[6] = cmd[9] = 0;
+		cmd[7] = (unsigned char) (realcount >> 8) & 0xff;
+		cmd[8] = (unsigned char) realcount & 0xff;
+		}
+	else
+		{
+		  if (realcount > 0xff)
+		    {
+		      realcount = 0xff;
+		      this_count = realcount * (scsi_CDs[dev].sector_size >> 9);
+		    }
+		  
+		  cmd[1] |= (unsigned char) ((block >> 16) & 0x1f);
+		  cmd[2] = (unsigned char) ((block >> 8) & 0xff);
+		  cmd[3] = (unsigned char) block & 0xff;
+		  cmd[4] = (unsigned char) realcount;
+		  cmd[5] = 0;
+		}   
+
+#ifdef DEBUG
+	{ 
+	  int i;
+	  printk("ReadCD: %d %d %d %d\n",block, realcount, buffer, this_count);
+	  printk("Use sg: %d\n", SCpnt->use_sg);
+	  printk("Dumping command: ");
+	  for(i=0; i<12; i++) printk("%2.2x ", cmd[i]);
+	  printk("\n");
+	};
+#endif
+
+/* Some dumb host adapters can speed transfers by knowing the
+ * minimum transfersize in advance.
+ *
+ * We shouldn't disconnect in the middle of a sector, but the cdrom
+ * sector size can be larger than the size of a buffer and the
+ * transfer may be split to the size of a buffer.  So it's safe to
+ * assume that we can at least transfer the minimum of the buffer
+ * size (1024) and the sector size between each connect / disconnect.
+ */
+
+        SCpnt->transfersize = (scsi_CDs[dev].sector_size > 1024) ?
+                        1024 : scsi_CDs[dev].sector_size;
+
+	SCpnt->this_count = this_count;
+	scsi_do_cmd (SCpnt, (void *) cmd, buffer, 
+		     realcount * scsi_CDs[dev].sector_size, 
+		     rw_intr, SR_TIMEOUT, MAX_RETRIES);
+}
+
+static int sr_detect(Scsi_Device * SDp){
+  
+  if(SDp->type != TYPE_ROM && SDp->type != TYPE_WORM) return 0;
+
+  printk("Detected scsi CD-ROM sr%d at scsi%d, id %d, lun %d\n", 
+	 sr_template.dev_noticed++,
+	 SDp->host->host_no , SDp->id, SDp->lun); 
+
+	 return 1;
+}
+
+static int sr_attach(Scsi_Device * SDp){
+  Scsi_CD * cpnt;
+  int i;
+  
+  if(SDp->type != TYPE_ROM && SDp->type != TYPE_WORM) return 1;
+  
+  if (sr_template.nr_dev >= sr_template.dev_max)
+    {
+	SDp->attached--;
+	return 1;
+    }
+  
+  for(cpnt = scsi_CDs, i=0; i<sr_template.dev_max; i++, cpnt++) 
+    if(!cpnt->device) break;
+  
+  if(i >= sr_template.dev_max) panic ("scsi_devices corrupt (sr)");
+  
+  SDp->scsi_request_fn = do_sr_request;
+  scsi_CDs[i].device = SDp;
+  sr_template.nr_dev++;
+  if(sr_template.nr_dev > sr_template.dev_max)
+    panic ("scsi_devices corrupt (sr)");
+  return 0;
+}
+     
+
+static void sr_init_done (Scsi_Cmnd * SCpnt)
+{
+  struct request * req;
+  
+  req = &SCpnt->request;
+  req->dev = 0xfffe; /* Busy, but indicate request done */
+  
+  if (req->sem != NULL) {
+    up(req->sem);
+  }
+}
+
+static void get_sectorsize(int i){
+  unsigned char cmd[10];
+  unsigned char *buffer;
+  int the_result, retries;
+  Scsi_Cmnd * SCpnt;
+  
+  buffer = (unsigned char *) scsi_malloc(512);
+  SCpnt = allocate_device(NULL, scsi_CDs[i].device, 1);
+
+  retries = 3;
+  do {
+    cmd[0] = READ_CAPACITY;
+    cmd[1] = (scsi_CDs[i].device->lun << 5) & 0xe0;
+    memset ((void *) &cmd[2], 0, 8);
+    SCpnt->request.dev = 0xffff;  /* Mark as really busy */
+    SCpnt->cmd_len = 0;
+    
+    memset(buffer, 0, 8);
+
+    scsi_do_cmd (SCpnt,
+		 (void *) cmd, (void *) buffer,
+		 512, sr_init_done,  SR_TIMEOUT,
+		 MAX_RETRIES);
+    
+    if (current == task[0])
+      while(SCpnt->request.dev != 0xfffe) barrier();
+    else
+      if (SCpnt->request.dev != 0xfffe){
+      	struct semaphore sem = MUTEX_LOCKED;
+	SCpnt->request.sem = &sem;
+	down(&sem);
+	/* Hmm.. Have to ask about this */
+	while (SCpnt->request.dev != 0xfffe) schedule();
+      };
+    
+    the_result = SCpnt->result;
+    retries--;
+    
+  } while(the_result && retries);
+  
+  SCpnt->request.dev = -1;  /* Mark as not busy */
+  
+  wake_up(&SCpnt->device->device_wait); 
+
+  if (the_result) {
+    scsi_CDs[i].capacity = 0x1fffff;
+    scsi_CDs[i].sector_size = 2048;  /* A guess, just in case */
+    scsi_CDs[i].needs_sector_size = 1;
+  } else {
+    scsi_CDs[i].capacity = (buffer[0] << 24) |
+      (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
+    scsi_CDs[i].sector_size = (buffer[4] << 24) |
+      (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
+    if(scsi_CDs[i].sector_size == 0) scsi_CDs[i].sector_size = 2048;
+    if(scsi_CDs[i].sector_size != 2048 && 
+       scsi_CDs[i].sector_size != 512) {
+      printk ("scd%d : unsupported sector size %d.\n",
+	      i, scsi_CDs[i].sector_size);
+      scsi_CDs[i].capacity = 0;
+      scsi_CDs[i].needs_sector_size = 1;
+    };
+    if(scsi_CDs[i].sector_size == 2048)
+      scsi_CDs[i].capacity *= 4;
+    scsi_CDs[i].needs_sector_size = 0;
+    sr_sizes[i] = scsi_CDs[i].capacity;
+  };
+  scsi_free(buffer, 512);
+}
+
+static void sr_init()
+{
+	int i;
+	static int sr_registered = 0;
+
+	if(sr_template.dev_noticed == 0) return;
+
+	if(!sr_registered) {
+	  if (register_blkdev(MAJOR_NR,"sr",&sr_fops)) {
+	    printk("Unable to get major %d for SCSI-CD\n",MAJOR_NR);
+	    return;
+	  }
+	  sr_registered++;
+	}
+
+	
+	if (scsi_CDs) return;
+	sr_template.dev_max = sr_template.dev_noticed + SR_EXTRA_DEVS;
+	scsi_CDs = (Scsi_CD *) scsi_init_malloc(sr_template.dev_max * sizeof(Scsi_CD), GFP_ATOMIC);
+	memset(scsi_CDs, 0, sr_template.dev_max * sizeof(Scsi_CD));
+
+	sr_sizes = (int *) scsi_init_malloc(sr_template.dev_max * sizeof(int), GFP_ATOMIC);
+	memset(sr_sizes, 0, sr_template.dev_max * sizeof(int));
+
+	sr_blocksizes = (int *) scsi_init_malloc(sr_template.dev_max * 
+						 sizeof(int), GFP_ATOMIC);
+	for(i=0;i<sr_template.dev_max;i++) sr_blocksizes[i] = 2048;
+	blksize_size[MAJOR_NR] = sr_blocksizes;
+
+}
+
+void sr_finish()
+{
+  int i;
+
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	blk_size[MAJOR_NR] = sr_sizes;	
+
+	for (i = 0; i < sr_template.nr_dev; ++i)
+		{
+		  /* If we have already seen this, then skip it.  Comes up
+		     with loadable modules. */
+		  if (scsi_CDs[i].capacity) continue;
+		  scsi_CDs[i].capacity = 0x1fffff;
+		  scsi_CDs[i].sector_size = 2048;  /* A guess, just in case */
+		  scsi_CDs[i].needs_sector_size = 1;
+#if 0
+		  /* seems better to leave this for later */
+		  get_sectorsize(i);
+		  printk("Scd sectorsize = %d bytes.\n", scsi_CDs[i].sector_size);
+#endif
+		  scsi_CDs[i].use = 1;
+		  scsi_CDs[i].ten = 1;
+		  scsi_CDs[i].remap = 1;
+		  sr_sizes[i] = scsi_CDs[i].capacity;
+		}
+
+
+	/* If our host adapter is capable of scatter-gather, then we increase
+	   the read-ahead to 16 blocks (32 sectors).  If not, we use
+	   a two block (4 sector) read ahead. */
+	if(scsi_CDs[0].device && scsi_CDs[0].device->host->sg_tablesize)
+	  read_ahead[MAJOR_NR] = 32;  /* 32 sector read-ahead.  Always removable. */
+	else
+	  read_ahead[MAJOR_NR] = 4;  /* 4 sector read-ahead */
+
+	return;
+}	
+
+static void sr_detach(Scsi_Device * SDp)
+{
+  Scsi_CD * cpnt;
+  int i, major;
+  
+  major = MAJOR_NR << 8;
+
+  for(cpnt = scsi_CDs, i=0; i<sr_template.dev_max; i++, cpnt++) 
+    if(cpnt->device == SDp) {
+      /*
+       * Since the cdrom is read-only, no need to sync the device.
+       * We should be kind to our buffer cache, however.
+       */
+      invalidate_inodes(major | i);
+      invalidate_buffers(major | i);
+
+      /*
+       * Reset things back to a sane state so that one can re-load a new
+       * driver (perhaps the same one).
+       */
+      cpnt->device = NULL;
+      cpnt->capacity = 0;
+      SDp->attached--;
+      sr_template.nr_dev--;
+      sr_template.dev_noticed--;
+      sr_sizes[i] = 0;
+      return;
+    }
+  return;
+}
+
+/*
+ * 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: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -r -u -N linux.orig/arch/arm/drivers/scsi/sr.h linux.arm/arch/arm/drivers/scsi/sr.h
--- linux.orig/arch/arm/drivers/scsi/sr.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/sr.h	Fri Oct 27 23:15:14 1995
@@ -0,0 +1,39 @@
+/*
+ *      sr.h by David Giller
+ *      CD-ROM disk driver header file
+ *      
+ *      adapted from:
+ *	sd.h Copyright (C) 1992 Drew Eckhardt 
+ *	SCSI disk driver header file by
+ *		Drew Eckhardt 
+ *
+ *	<drew@colorado.edu>
+ *
+ *       Modified by Eric Youngdale eric@tantalus.nrl.navy.mil to
+ *       add scatter-gather, multiple outstanding request, and other
+ *       enhancements.
+ */
+
+#ifndef _SR_H
+#define _SR_H
+
+#include "scsi.h"
+
+typedef struct
+	{
+	unsigned 	capacity;		/* size in blocks 			*/
+	unsigned 	sector_size;		/* size in bytes 			*/
+	Scsi_Device  	*device;		
+	unsigned long   mpcd_sector;            /* for reading multisession-CD's        */
+	char            xa_flags;               /* some flags for handling XA-CD's      */
+	unsigned char	sector_bit_size;	/* sector size = 2^sector_bit_size	*/
+	unsigned char	sector_bit_shift;	/* sectors/FS block = 2^sector_bit_shift*/
+	unsigned 	needs_sector_size:1;   	/* needs to get sector size */
+	unsigned 	ten:1;			/* support ten byte commands		*/
+	unsigned 	remap:1;		/* support remapping			*/
+	unsigned 	use:1;			/* is this device still supportable	*/
+	} Scsi_CD;
+	
+extern Scsi_CD * scsi_CDs;
+
+#endif
diff -r -u -N linux.orig/arch/arm/drivers/scsi/sr_ioctl.c linux.arm/arch/arm/drivers/scsi/sr_ioctl.c
--- linux.orig/arch/arm/drivers/scsi/sr_ioctl.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/sr_ioctl.c	Fri Oct 27 23:15:16 1995
@@ -0,0 +1,449 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <asm/segment.h>
+#include <linux/errno.h>
+
+#include "../block/blk.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "sr.h"
+#include "scsi_ioctl.h"
+
+#include <linux/cdrom.h>
+
+#define IOCTL_RETRIES 3
+/* The CDROM is fairly slow, so we need a little extra time */
+/* In fact, it is very slow if it has to spin up first */
+#define IOCTL_TIMEOUT 3000
+
+extern int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
+
+static void sr_ioctl_done(Scsi_Cmnd * SCpnt)
+{
+  struct request * req;
+  
+  req = &SCpnt->request;
+  req->dev = 0xfffe; /* Busy, but indicate request done */
+  
+  if (req->sem != NULL) {
+    up(req->sem);
+  }
+}
+
+/* We do our own retries because we want to know what the specific
+   error code is.  Normally the UNIT_ATTENTION code will automatically
+   clear after one error */
+
+static int do_ioctl(int target, unsigned char * sr_cmd, void * buffer, unsigned buflength)
+{
+	Scsi_Cmnd * SCpnt;
+	int result;
+
+	SCpnt = allocate_device(NULL, scsi_CDs[target].device, 1);
+	scsi_do_cmd(SCpnt,
+		    (void *) sr_cmd, buffer, buflength, sr_ioctl_done, 
+		    IOCTL_TIMEOUT, IOCTL_RETRIES);
+
+
+	if (SCpnt->request.dev != 0xfffe){
+	  struct semaphore sem = MUTEX_LOCKED;
+	  SCpnt->request.sem = &sem;
+	  down(&sem);
+	  /* Hmm.. Have to ask about this */
+	  while (SCpnt->request.dev != 0xfffe) schedule();
+	};
+
+	result = SCpnt->result;
+
+/* Minimal error checking.  Ignore cases we know about, and report the rest. */
+	if(driver_byte(result) != 0)
+	  switch(SCpnt->sense_buffer[2] & 0xf) {
+	  case UNIT_ATTENTION:
+	    scsi_CDs[target].device->changed = 1;
+	    printk("Disc change detected.\n");
+	    break;
+	  case NOT_READY: /* This happens if there is no disc in drive */
+	    printk("CDROM not ready.  Make sure there is a disc in the drive.\n");
+	    break;
+	  case ILLEGAL_REQUEST:
+	    printk("CDROM (ioctl) reports ILLEGAL REQUEST.\n");
+	    break;
+	  default:
+	    printk("SCSI CD error: host %d id %d lun %d return code = %03x\n", 
+		   scsi_CDs[target].device->host->host_no, 
+		   scsi_CDs[target].device->id,
+		   scsi_CDs[target].device->lun,
+		   result);
+	    printk("\tSense class %x, sense error %x, extended sense %x\n",
+		   sense_class(SCpnt->sense_buffer[0]), 
+		   sense_error(SCpnt->sense_buffer[0]),
+		   SCpnt->sense_buffer[2] & 0xf);
+	    
+	};
+
+	result = SCpnt->result;
+	SCpnt->request.dev = -1; /* Deallocate */
+	wake_up(&SCpnt->device->device_wait);
+	/* Wake up a process waiting for device*/
+      	return result;
+}
+
+int sr_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
+{
+        u_char 	sr_cmd[10];
+
+	int dev = inode->i_rdev;
+	int result, target, err;
+
+	target = MINOR(dev);
+
+	if (target >= sr_template.nr_dev ||
+	    !scsi_CDs[target].device) return -ENXIO;
+
+	switch (cmd) 
+		{
+		/* Sun-compatible */
+		case CDROMPAUSE:
+
+			sr_cmd[0] = SCMD_PAUSE_RESUME;
+			sr_cmd[1] = scsi_CDs[target].device->lun << 5;
+			sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = 0;
+			sr_cmd[5] = sr_cmd[6] = sr_cmd[7] = 0;
+			sr_cmd[8] = 0;
+			sr_cmd[9] = 0;
+
+			result = do_ioctl(target, sr_cmd, NULL, 255);
+			return result;
+
+		case CDROMRESUME:
+
+			sr_cmd[0] = SCMD_PAUSE_RESUME;
+			sr_cmd[1] = scsi_CDs[target].device->lun << 5;
+			sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = 0;
+			sr_cmd[5] = sr_cmd[6] = sr_cmd[7] = 0;
+			sr_cmd[8] = 1;
+			sr_cmd[9] = 0;
+
+			result = do_ioctl(target, sr_cmd, NULL, 255);
+
+			return result;
+
+		case CDROMPLAYMSF:
+			{
+			struct cdrom_msf msf;
+			memcpy_fromfs(&msf, (void *) arg, sizeof(msf));
+
+			sr_cmd[0] = SCMD_PLAYAUDIO_MSF;
+			sr_cmd[1] = scsi_CDs[target].device->lun << 5;
+			sr_cmd[2] = 0;
+			sr_cmd[3] = msf.cdmsf_min0;
+			sr_cmd[4] = msf.cdmsf_sec0;
+			sr_cmd[5] = msf.cdmsf_frame0;
+			sr_cmd[6] = msf.cdmsf_min1;
+			sr_cmd[7] = msf.cdmsf_sec1;
+			sr_cmd[8] = msf.cdmsf_frame1;
+			sr_cmd[9] = 0;
+
+			result = do_ioctl(target, sr_cmd, NULL, 255);
+			return result;
+			}
+
+		case CDROMPLAYTRKIND:
+			{
+			struct cdrom_ti ti;
+			memcpy_fromfs(&ti, (void *) arg, sizeof(ti));
+
+			sr_cmd[0] = SCMD_PLAYAUDIO_TI;
+			sr_cmd[1] = scsi_CDs[target].device->lun << 5;
+			sr_cmd[2] = 0;
+			sr_cmd[3] = 0;
+			sr_cmd[4] = ti.cdti_trk0;
+			sr_cmd[5] = ti.cdti_ind0;
+			sr_cmd[6] = 0;
+			sr_cmd[7] = ti.cdti_trk1;
+			sr_cmd[8] = ti.cdti_ind1;
+			sr_cmd[9] = 0;
+
+			result = do_ioctl(target, sr_cmd, NULL, 255);
+
+			return result;
+			}
+
+		case CDROMREADTOCHDR:
+			{
+			struct cdrom_tochdr tochdr;
+			char * buffer;
+
+			sr_cmd[0] = SCMD_READ_TOC;
+			sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 0x02;    /* MSF format */
+			sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
+			sr_cmd[6] = 0;
+			sr_cmd[7] = 0;              /* MSB of length (12) */
+			sr_cmd[8] = 12;             /* LSB of length */
+			sr_cmd[9] = 0;
+
+			buffer = (unsigned char *) scsi_malloc(512);
+			if(!buffer) return -ENOMEM;
+
+			result = do_ioctl(target, sr_cmd, buffer, 12);
+
+			tochdr.cdth_trk0 = buffer[2];
+			tochdr.cdth_trk1 = buffer[3];
+
+			scsi_free(buffer, 512);
+
+			err = verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct cdrom_tochdr));
+			if (err)
+				return err;
+			memcpy_tofs ((void *) arg, &tochdr, sizeof (struct cdrom_tochdr));
+			
+			return result;
+		        }
+
+		case CDROMREADTOCENTRY:
+			{
+			struct cdrom_tocentry tocentry;
+			char * buffer;
+
+			verify_area (VERIFY_READ, (void *) arg, sizeof (struct cdrom_tocentry));
+			memcpy_fromfs (&tocentry, (void *) arg, sizeof (struct cdrom_tocentry));
+
+			sr_cmd[0] = SCMD_READ_TOC;
+			sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 0x02;    /* MSF format */
+			sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
+			sr_cmd[6] = tocentry.cdte_track;
+			sr_cmd[7] = 0;             /* MSB of length (12)  */
+			sr_cmd[8] = 12;            /* LSB of length */
+			sr_cmd[9] = 0;
+
+			buffer = (unsigned char *) scsi_malloc(512);
+			if(!buffer) return -ENOMEM;
+
+			result = do_ioctl (target, sr_cmd, buffer, 12);
+
+			if (tocentry.cdte_format == CDROM_MSF) {
+			  tocentry.cdte_addr.msf.minute = buffer[9];
+			  tocentry.cdte_addr.msf.second = buffer[10];
+			  tocentry.cdte_addr.msf.frame = buffer[11];
+			  tocentry.cdte_ctrl = buffer[5] & 0xf;
+			}
+			else
+			  tocentry.cdte_addr.lba = (int) buffer[0];
+
+			scsi_free(buffer, 512);
+
+			err = verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct cdrom_tocentry));
+			if (err)
+				return err;
+			memcpy_tofs ((void *) arg, &tocentry, sizeof (struct cdrom_tocentry));
+
+			return result;
+		        }
+
+		case CDROMSTOP:
+		        sr_cmd[0] = START_STOP;
+			sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 1;
+			sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
+			sr_cmd[4] = 0;
+
+			result = do_ioctl(target, sr_cmd, NULL, 255);
+			return result;
+			
+		case CDROMSTART:
+		        sr_cmd[0] = START_STOP;
+			sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 1;
+			sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
+			sr_cmd[4] = 1;
+
+			result = do_ioctl(target, sr_cmd, NULL, 255);
+			return result;
+
+		case CDROMEJECT:
+			if (scsi_CDs[target].device -> access_count == 1)
+			  sr_ioctl (inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0);
+
+		        sr_cmd[0] = START_STOP;
+			sr_cmd[1] = ((scsi_CDs[target].device -> lun) << 5) | 1;
+			sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
+			sr_cmd[4] = 0x02;
+
+			if (!(result = do_ioctl(target, sr_cmd, NULL, 255)))
+			  scsi_CDs[target].device -> changed = 1;
+
+			return result;
+
+		case CDROMVOLCTRL:
+			{
+			  char * buffer, * mask;
+			  struct cdrom_volctrl volctrl;
+
+			  verify_area (VERIFY_READ, (void *) arg, sizeof (struct cdrom_volctrl));
+			  memcpy_fromfs (&volctrl, (void *) arg, sizeof (struct cdrom_volctrl));
+
+			  /* First we get the current params so we can just twiddle the volume */
+
+			  sr_cmd[0] = MODE_SENSE;
+			  sr_cmd[1] = (scsi_CDs[target].device -> lun) << 5;
+			  sr_cmd[2] = 0xe;    /* Want mode page 0xe, CDROM audio params */
+			  sr_cmd[3] = 0;
+			  sr_cmd[4] = 28;
+			  sr_cmd[5] = 0;
+
+			  buffer = (unsigned char *) scsi_malloc(512);
+			  if(!buffer) return -ENOMEM;
+
+			  if ((result = do_ioctl (target, sr_cmd, buffer, 28))) {
+			    printk ("Hosed while obtaining audio mode page\n");
+			    scsi_free(buffer, 512);
+			    return result;
+			  }
+
+			  sr_cmd[0] = MODE_SENSE;
+			  sr_cmd[1] = (scsi_CDs[target].device -> lun) << 5;
+			  sr_cmd[2] = 0x4e;   /* Want the mask for mode page 0xe */
+			  sr_cmd[3] = 0;
+			  sr_cmd[4] = 28;
+			  sr_cmd[5] = 0;
+
+			  mask = (unsigned char *) scsi_malloc(512);
+			  if(!mask) {
+			    scsi_free(buffer, 512);
+			    return -ENOMEM;
+			  };
+
+			  if ((result = do_ioctl (target, sr_cmd, mask, 28))) {
+			    printk ("Hosed while obtaining mask for audio mode page\n");
+			    scsi_free(buffer, 512);
+			    scsi_free(mask, 512);
+			    return result;
+			  }
+
+			  /* Now mask and substitute our own volume and reuse the rest */
+			  buffer[0] = 0;  /* Clear reserved field */
+
+			  buffer[21] = volctrl.channel0 & mask[21];
+			  buffer[23] = volctrl.channel1 & mask[23];
+			  buffer[25] = volctrl.channel2 & mask[25];
+			  buffer[27] = volctrl.channel3 & mask[27];
+
+			  sr_cmd[0] = MODE_SELECT;
+			  sr_cmd[1] = ((scsi_CDs[target].device -> lun) << 5) | 0x10;    /* Params are SCSI-2 */
+			  sr_cmd[2] = sr_cmd[3] = 0;
+			  sr_cmd[4] = 28;
+			  sr_cmd[5] = 0;
+
+			  result = do_ioctl (target, sr_cmd, buffer, 28);
+			  scsi_free(buffer, 512);
+			  scsi_free(mask, 512);
+			  return result;
+			}
+
+		case CDROMSUBCHNL:
+			{
+			  struct cdrom_subchnl subchnl;
+			  char * buffer;
+			  
+			  sr_cmd[0] = SCMD_READ_SUBCHANNEL;
+			  sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 0x02;    /* MSF format */
+			  sr_cmd[2] = 0x40;    /* I do want the subchannel info */
+			  sr_cmd[3] = 0x01;    /* Give me current position info */
+			  sr_cmd[4] = sr_cmd[5] = 0;
+			  sr_cmd[6] = 0;
+			  sr_cmd[7] = 0;
+			  sr_cmd[8] = 16;
+			  sr_cmd[9] = 0;
+
+			  buffer = (unsigned char*) scsi_malloc(512);
+			  if(!buffer) return -ENOMEM;
+
+			  result = do_ioctl(target, sr_cmd, buffer, 16);
+
+			  subchnl.cdsc_audiostatus = buffer[1];
+			  subchnl.cdsc_format = CDROM_MSF;
+			  subchnl.cdsc_ctrl = buffer[5] & 0xf;
+			  subchnl.cdsc_trk = buffer[6];
+			  subchnl.cdsc_ind = buffer[7];
+
+			  subchnl.cdsc_reladdr.msf.minute = buffer[13];
+			  subchnl.cdsc_reladdr.msf.second = buffer[14];
+			  subchnl.cdsc_reladdr.msf.frame = buffer[15];
+			  subchnl.cdsc_absaddr.msf.minute = buffer[9];
+			  subchnl.cdsc_absaddr.msf.second = buffer[10];
+			  subchnl.cdsc_absaddr.msf.frame = buffer[11];
+
+			  scsi_free(buffer, 512);
+
+			  err = verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct cdrom_subchnl));
+			  if (err)
+				  return err;
+			  memcpy_tofs ((void *) arg, &subchnl, sizeof (struct cdrom_subchnl));
+			  return result;
+			}
+
+		case CDROMREADMODE2:
+			return -EINVAL;
+		case CDROMREADMODE1:
+			return -EINVAL;
+			
+		/* block-copy from ../block/sbpcd.c with some adjustments... */
+		case CDROMMULTISESSION: /* tell start-of-last-session to user */
+			{
+			  struct cdrom_multisession  ms_info;
+			  long                       lba;
+			  
+			  err = verify_area(VERIFY_READ, (void *) arg,
+					    sizeof(struct cdrom_multisession));
+			  if (err) return (err);
+			
+			  memcpy_fromfs(&ms_info, (void *) arg, sizeof(struct cdrom_multisession));
+
+			  if (ms_info.addr_format==CDROM_MSF) { /* MSF-bin requested */
+			    lba = scsi_CDs[target].mpcd_sector+CD_BLOCK_OFFSET;
+			    ms_info.addr.msf.minute = lba / (CD_SECS*CD_FRAMES);
+			    lba %= CD_SECS*CD_FRAMES;
+			    ms_info.addr.msf.second = lba / CD_FRAMES;
+			    ms_info.addr.msf.frame  = lba % CD_FRAMES;
+			  } else if (ms_info.addr_format==CDROM_LBA) /* lba requested */
+			    ms_info.addr.lba=scsi_CDs[target].mpcd_sector;
+			  else return (-EINVAL);
+			
+			  ms_info.xa_flag=scsi_CDs[target].xa_flags & 0x01;
+			 			  
+			  err=verify_area(VERIFY_WRITE,(void *) arg,
+					  sizeof(struct cdrom_multisession));
+			  if (err) return (err);
+
+			  memcpy_tofs((void *) arg, &ms_info, sizeof(struct cdrom_multisession));
+			  return (0);
+			}
+
+		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;
+		RO_IOCTLS(dev,arg);
+		default:
+			return scsi_ioctl(scsi_CDs[target].device,cmd,(void *) arg);
+		}
+}
+
+/*
+ * 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: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff -r -u -N linux.orig/arch/arm/drivers/scsi/st.c linux.arm/arch/arm/drivers/scsi/st.c
--- linux.orig/arch/arm/drivers/scsi/st.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/st.c	Fri Oct 27 23:15:16 1995
@@ -0,0 +1,2066 @@
+/*
+  SCSI Tape Driver for Linux version 1.1 and newer. See the accompanying
+  file README.st for more information.
+
+  History:
+  Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara.
+  Contribution and ideas from several people including (in alphabetical
+  order) Klaus Ehrenfried, Steve Hirsch, Wolfgang Denk, Andreas Koppenh"ofer,
+  J"org Weule, and Eric Youngdale.
+
+  Copyright 1992, 1993, 1994, 1995 Kai Makisara
+		 email Kai.Makisara@metla.fi
+
+  Last modified: Sat Feb 18 10:51:25 1995 by root@kai.home
+*/
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mtio.h>
+#include <linux/ioctl.h>
+#include <linux/fcntl.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#define MAJOR_NR SCSI_TAPE_MAJOR
+#include "../block/blk.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "scsi_ioctl.h"
+#include "st.h"
+#include "constants.h"
+
+/* #define DEBUG */
+
+/* #define ST_NOWAIT */
+
+/* #define ST_IN_FILE_POS */
+
+/* #define ST_RECOVERED_WRITE_FATAL */
+
+#define ST_TWO_FM 0
+
+#define ST_FAST_MTEOM 0
+
+#define ST_BUFFER_WRITES 1
+
+#define ST_ASYNC_WRITES 1
+
+#define ST_READ_AHEAD 1
+
+#define ST_BLOCK_SIZE 1024
+
+#define ST_MAX_BUFFERS (2 + ST_EXTRA_DEVS)
+
+#define ST_BUFFER_BLOCKS 32
+
+#define ST_WRITE_THRESHOLD_BLOCKS 30
+
+#define ST_BUFFER_SIZE (ST_BUFFER_BLOCKS * ST_BLOCK_SIZE)
+#define ST_WRITE_THRESHOLD (ST_WRITE_THRESHOLD_BLOCKS * ST_BLOCK_SIZE)
+
+/* The buffer size should fit into the 24 bits for length in the
+   6-byte SCSI read and write commands. */
+#if ST_BUFFER_SIZE >= (2 << 24 - 1)
+#error "Buffer size should not exceed (2 << 24 - 1) bytes!"
+#endif
+
+#ifdef DEBUG
+static int debugging = 1;
+#endif
+
+#define MAX_RETRIES 0
+#define MAX_WRITE_RETRIES 0
+#define MAX_READY_RETRIES 5
+#define NO_TAPE  NOT_READY
+
+#define ST_TIMEOUT 90000
+#define ST_LONG_TIMEOUT 200000
+
+static int st_nbr_buffers;
+static ST_buffer **st_buffers;
+static int st_buffer_size = ST_BUFFER_SIZE;
+static int st_write_threshold = ST_WRITE_THRESHOLD;
+static int st_max_buffers = ST_MAX_BUFFERS;
+
+static Scsi_Tape * scsi_tapes;
+
+static void st_init(void);
+static int st_attach(Scsi_Device *);
+static int st_detect(Scsi_Device *);
+static void st_detach(Scsi_Device *);
+
+struct Scsi_Device_Template st_template = {NULL, "tape", "st", TYPE_TAPE, 
+					     SCSI_TAPE_MAJOR, 0, 0, 0, 0,
+					     st_detect, st_init,
+					     NULL, st_attach, st_detach};
+
+static int st_int_ioctl(struct inode * inode,struct file * file,
+	     unsigned int cmd_in, unsigned long arg);
+
+
+
+
+/* Convert the result to success code */
+	static int
+st_chk_result(Scsi_Cmnd * SCpnt)
+{
+  int dev = SCpnt->request.dev;
+  int result = SCpnt->result;
+  unsigned char * sense = SCpnt->sense_buffer, scode;
+  char *stp;
+
+  if (!result /* && SCpnt->sense_buffer[0] == 0 */ )
+    return 0;
+#ifdef DEBUG
+  if (debugging) {
+    printk("st%d: Error: %x, cmd: %x %x %x %x %x %x Len: %d\n", dev, result,
+	   SCpnt->cmnd[0], SCpnt->cmnd[1], SCpnt->cmnd[2],
+	   SCpnt->cmnd[3], SCpnt->cmnd[4], SCpnt->cmnd[5],
+	   SCpnt->request_bufflen);
+    if (driver_byte(result) & DRIVER_SENSE)
+      print_sense("st", SCpnt);
+  } else
+#endif
+    scode = sense[2] & 0x0f;
+    if (!(driver_byte(result) & DRIVER_SENSE) ||
+	((sense[0] & 0x70) == 0x70 &&
+	 scode != NO_SENSE &&
+	 scode != RECOVERED_ERROR &&
+	 scode != UNIT_ATTENTION &&
+	 scode != BLANK_CHECK &&
+	 scode != VOLUME_OVERFLOW)) {  /* Abnormal conditions for tape */
+      printk("st%d: Error %x. ", dev, result);
+      if (driver_byte(result) & DRIVER_SENSE)
+	print_sense("st", SCpnt);
+      else
+	printk("\n");
+    }
+
+  if ((sense[0] & 0x70) == 0x70 &&
+      scode == RECOVERED_ERROR
+#ifdef ST_RECOVERED_WRITE_FATAL
+      && SCpnt->cmnd[0] != WRITE_6
+      && SCpnt->cmnd[0] != WRITE_FILEMARKS
+#endif
+      ) {
+    scsi_tapes[dev].recover_count++;
+    scsi_tapes[dev].mt_status->mt_erreg += (1 << MT_ST_SOFTERR_SHIFT);
+    if (SCpnt->cmnd[0] == READ_6)
+      stp = "read";
+    else if (SCpnt->cmnd[0] == WRITE_6)
+      stp = "write";
+    else
+      stp = "ioctl";
+    printk("st%d: Recovered %s error (%d).\n", dev, stp,
+	   scsi_tapes[dev].recover_count);
+    return 0;
+  }
+  return (-EIO);
+}
+
+
+/* Wakeup from interrupt */
+	static void
+st_sleep_done (Scsi_Cmnd * SCpnt)
+{
+  int st_nbr, remainder;
+  Scsi_Tape * STp;
+
+  if ((st_nbr = SCpnt->request.dev) < st_template.nr_dev && st_nbr >= 0) {
+    STp = &(scsi_tapes[st_nbr]);
+    if ((STp->buffer)->writing &&
+	(SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+	(SCpnt->sense_buffer[2] & 0x40)) {
+      /* EOM at write-behind, has all been written? */
+      if ((SCpnt->sense_buffer[0] & 0x80) != 0)
+	remainder = (SCpnt->sense_buffer[3] << 24) |
+	      (SCpnt->sense_buffer[4] << 16) |
+		(SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6];
+      else
+	remainder = 0;
+      if ((SCpnt->sense_buffer[2] & 0x0f) == VOLUME_OVERFLOW ||
+	  remainder > 0)
+	(STp->buffer)->last_result = SCpnt->result; /* Error */
+      else
+	(STp->buffer)->last_result = INT_MAX; /* OK */
+    }
+    else
+      (STp->buffer)->last_result = SCpnt->result;
+    (STp->buffer)->last_result_fatal = st_chk_result(SCpnt);
+    if ((STp->buffer)->writing)
+      SCpnt->request.dev = -1;
+    else
+      SCpnt->request.dev = 0xffff;
+    if ((STp->buffer)->writing <= 0)
+      wake_up( &(STp->waiting) );
+  }
+#ifdef DEBUG
+  else if (debugging)
+    printk("st?: Illegal interrupt device %x\n", st_nbr);
+#endif
+}
+
+
+/* Handle the write-behind checking */
+	static void
+write_behind_check(int dev)
+{
+  Scsi_Tape * STp;
+  ST_buffer * STbuffer;
+  unsigned long flags;
+
+  STp = &(scsi_tapes[dev]);
+  STbuffer = STp->buffer;
+
+  save_flags(flags);
+  cli();
+  if (STbuffer->last_result < 0) {
+    STbuffer->writing = (- STbuffer->writing);
+    sleep_on( &(STp->waiting) );
+    STbuffer->writing = (- STbuffer->writing);
+  }
+  restore_flags(flags);
+
+  if (STbuffer->writing < STbuffer->buffer_bytes)
+    memcpy(STbuffer->b_data,
+	   STbuffer->b_data + STbuffer->writing,
+	   STbuffer->buffer_bytes - STbuffer->writing);
+  STbuffer->buffer_bytes -= STbuffer->writing;
+  if (STp->drv_block >= 0) {
+    if (STp->block_size == 0)
+      STp->drv_block++;
+    else
+      STp->drv_block += STbuffer->writing / STp->block_size;
+  }
+  STbuffer->writing = 0;
+
+  return;
+}
+
+
+/* Back over EOF if it has been inadvertently crossed (ioctl not used because
+   it messes up the block number). */
+	static int
+back_over_eof(int dev)
+{
+  Scsi_Cmnd *SCpnt;
+  Scsi_Tape *STp = &(scsi_tapes[dev]);
+  unsigned char cmd[10];
+  unsigned int flags;
+
+  cmd[0] = SPACE;
+  cmd[1] = 0x01; /* Space FileMarks */
+  cmd[2] = cmd[3] = cmd[4] = 0xff;  /* -1 filemarks */
+  cmd[5] = 0;
+
+  SCpnt = allocate_device(NULL, STp->device, 1);
+  SCpnt->sense_buffer[0] = 0;
+  SCpnt->request.dev = dev;
+  scsi_do_cmd(SCpnt,
+	      (void *) cmd, (void *) (STp->buffer)->b_data, 0,
+	      st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
+
+  /* need to do the check with interrupts off. -RAB */
+  save_flags(flags);
+  cli();
+  if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+  restore_flags(flags);
+  
+  SCpnt->request.dev = -1;
+  if ((STp->buffer)->last_result != 0) {
+    printk("st%d: Backing over filemark failed.\n", dev);
+    if ((STp->mt_status)->mt_fileno >= 0)
+      (STp->mt_status)->mt_fileno += 1;
+    (STp->mt_status)->mt_blkno = 0;
+  }
+
+  return (STp->buffer)->last_result_fatal;
+}
+
+
+/* Flush the write buffer (never need to write if variable blocksize). */
+	static int
+flush_write_buffer(int dev)
+{
+  int offset, transfer, blks;
+  int result;
+  unsigned int flags;
+  unsigned char cmd[10];
+  Scsi_Cmnd *SCpnt;
+  Scsi_Tape *STp = &(scsi_tapes[dev]);
+
+  if ((STp->buffer)->writing) {
+    write_behind_check(dev);
+    if ((STp->buffer)->last_result_fatal) {
+#ifdef DEBUG
+      if (debugging)
+	printk("st%d: Async write error (flush) %x.\n", dev,
+	       (STp->buffer)->last_result);
+#endif
+      if ((STp->buffer)->last_result == INT_MAX)
+	return (-ENOSPC);
+      return (-EIO);
+    }
+  }
+
+  result = 0;
+  if (STp->dirty == 1) {
+    SCpnt = allocate_device(NULL, STp->device, 1);
+
+    offset = (STp->buffer)->buffer_bytes;
+    transfer = ((offset + STp->block_size - 1) /
+		STp->block_size) * STp->block_size;
+#ifdef DEBUG
+    if (debugging)
+      printk("st%d: Flushing %d bytes.\n", dev, transfer);
+#endif
+    memset((STp->buffer)->b_data + offset, 0, transfer - offset);
+
+    SCpnt->sense_buffer[0] = 0;
+    memset(cmd, 0, 10);
+    cmd[0] = WRITE_6;
+    cmd[1] = 1;
+    blks = transfer / STp->block_size;
+    cmd[2] = blks >> 16;
+    cmd[3] = blks >> 8;
+    cmd[4] = blks;
+    SCpnt->request.dev = dev;
+    scsi_do_cmd (SCpnt,
+		 (void *) cmd, (STp->buffer)->b_data, transfer,
+		 st_sleep_done, ST_TIMEOUT, MAX_WRITE_RETRIES);
+
+    /* this must be done with interrupts off */
+    save_flags (flags);
+    cli();
+    if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+    restore_flags(flags);
+ 
+    if ((STp->buffer)->last_result_fatal != 0) {
+      printk("st%d: Error on flush.\n", dev);
+      if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+	  (SCpnt->sense_buffer[2] & 0x40) &&
+	  (SCpnt->sense_buffer[2] & 0x0f) != VOLUME_OVERFLOW) {
+	STp->dirty = 0;
+	(STp->buffer)->buffer_bytes = 0;
+	result = (-ENOSPC);
+      }
+      else
+	result = (-EIO);
+      STp->drv_block = (-1);
+    }
+    else {
+      if (STp->drv_block >= 0)
+	STp->drv_block += blks;
+      STp->dirty = 0;
+      (STp->buffer)->buffer_bytes = 0;
+    }
+    SCpnt->request.dev = -1;  /* Mark as not busy */
+  }
+  return result;
+}
+
+
+/* Flush the tape buffer. The tape will be positioned correctly unless
+   seek_next is true. */
+	static int
+flush_buffer(struct inode * inode, struct file * filp, int seek_next)
+{
+  int dev;
+  int backspace, result;
+  Scsi_Tape * STp;
+  ST_buffer * STbuffer;
+
+  dev = MINOR(inode->i_rdev) & 127;
+  STp = &(scsi_tapes[dev]);
+  STbuffer = STp->buffer;
+
+  if (STp->ready != ST_READY)
+    return 0;
+
+  if (STp->rw == ST_WRITING)  /* Writing */
+    return flush_write_buffer(dev);
+
+  if (STp->block_size == 0)
+    return 0;
+
+  backspace = ((STp->buffer)->buffer_bytes +
+    (STp->buffer)->read_pointer) / STp->block_size -
+      ((STp->buffer)->read_pointer + STp->block_size - 1) /
+	STp->block_size;
+  (STp->buffer)->buffer_bytes = 0;
+  (STp->buffer)->read_pointer = 0;
+  result = 0;
+  if (!seek_next) {
+    if ((STp->eof == ST_FM) && !STp->eof_hit) {
+      result = back_over_eof(dev); /* Back over the EOF hit */
+      if (!result) {
+	STp->eof = ST_NOEOF;
+	STp->eof_hit = 0;
+      }
+    }
+    if (!result && backspace > 0)
+      result = st_int_ioctl(inode, filp, MTBSR, backspace);
+  }
+  return result;
+
+}
+
+
+/* Open the device */
+	static int
+scsi_tape_open(struct inode * inode, struct file * filp)
+{
+    int dev;
+    unsigned short flags;
+    unsigned int processor_flags;
+    int i;
+    unsigned char cmd[10];
+    Scsi_Cmnd * SCpnt;
+    Scsi_Tape * STp;
+
+    dev = MINOR(inode->i_rdev) & 127;
+    if (dev >= st_template.dev_max || !scsi_tapes[dev].device)
+      return (-ENXIO);
+    STp = &(scsi_tapes[dev]);
+    if (STp->in_use) {
+      printk("st%d: Device already in use.\n", dev);
+      return (-EBUSY);
+    }
+
+    /* Allocate buffer for this user */
+    for (i=0; i < st_nbr_buffers; i++)
+      if (!st_buffers[i]->in_use)
+	break;
+    if (i >= st_nbr_buffers) {
+      printk("st%d: No free buffers.\n", dev);
+      return (-EBUSY);
+    }
+    STp->buffer = st_buffers[i];
+    (STp->buffer)->in_use = 1;
+    (STp->buffer)->writing = 0;
+    STp->in_use = 1;
+
+    flags = filp->f_flags;
+    STp->write_prot = ((flags & O_ACCMODE) == O_RDONLY);
+
+    STp->dirty = 0;
+    STp->rw = ST_IDLE;
+    STp->ready = ST_READY;
+    if (STp->eof != ST_EOD)  /* Save EOD across opens */
+      STp->eof = ST_NOEOF;
+    STp->eof_hit = 0;
+    STp->recover_count = 0;
+
+    SCpnt = allocate_device(NULL, STp->device, 1);
+    if (!SCpnt) {
+      printk("st%d: Tape request not allocated", dev);
+      return (-EBUSY);
+    }
+
+    SCpnt->sense_buffer[0]=0;
+    memset ((void *) &cmd[0], 0, 10);
+    cmd[0] = TEST_UNIT_READY;
+    SCpnt->request.dev = dev;
+    scsi_do_cmd(SCpnt,
+                (void *) cmd, (void *) (STp->buffer)->b_data,
+                0, st_sleep_done, ST_LONG_TIMEOUT,
+		MAX_READY_RETRIES);
+
+
+    /* this must be done with interrupts off */
+    save_flags (processor_flags);
+    cli();
+    if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+    restore_flags(processor_flags);
+
+    if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+	(SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */
+      (STp->mt_status)->mt_fileno = 0 ;
+      SCpnt->sense_buffer[0]=0;
+      memset ((void *) &cmd[0], 0, 10);
+      cmd[0] = TEST_UNIT_READY;
+      SCpnt->request.dev = dev;
+      scsi_do_cmd(SCpnt,
+		  (void *) cmd, (void *) (STp->buffer)->b_data,
+		  0, st_sleep_done, ST_LONG_TIMEOUT,
+		  MAX_READY_RETRIES);
+
+
+    /* this must be done with interrupts off */
+    save_flags (processor_flags);
+    cli();
+    if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+    restore_flags(processor_flags);
+
+      (STp->mt_status)->mt_fileno = STp->drv_block = 0;
+      STp->eof = ST_NOEOF;
+    }
+
+    if ((STp->buffer)->last_result_fatal != 0) {
+      if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+	  (SCpnt->sense_buffer[2] & 0x0f) == NO_TAPE) {
+        (STp->mt_status)->mt_fileno = STp->drv_block = 0 ;
+	printk("st%d: No tape.\n", dev);
+	STp->ready = ST_NO_TAPE;
+      } else {
+        (STp->mt_status)->mt_fileno = STp->drv_block = (-1);
+	STp->ready = ST_NOT_READY;
+      }
+      SCpnt->request.dev = -1;  /* Mark as not busy */
+      (STp->buffer)->in_use = 0;
+      STp->buffer = NULL;
+      STp->density = 0;   /* Clear the erroneous "residue" */
+      STp->write_prot = 0;
+      STp->block_size = 0;
+      STp->eof = ST_NOEOF;
+      (STp->mt_status)->mt_fileno = STp->drv_block = 0;
+      if (scsi_tapes[dev].device->host->hostt->usage_count)
+	(*scsi_tapes[dev].device->host->hostt->usage_count)++;
+      return 0;
+    }
+
+    SCpnt->sense_buffer[0]=0;
+    memset ((void *) &cmd[0], 0, 10);
+    cmd[0] = READ_BLOCK_LIMITS;
+    SCpnt->request.dev = dev;
+    scsi_do_cmd(SCpnt,
+                (void *) cmd, (void *) (STp->buffer)->b_data,
+		6, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES);
+
+
+    /* this must be done with interrupts off */
+    save_flags (processor_flags);
+    cli();
+    if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+    restore_flags(processor_flags);
+
+    if (!SCpnt->result && !SCpnt->sense_buffer[0]) {
+      STp->max_block = ((STp->buffer)->b_data[1] << 16) |
+	((STp->buffer)->b_data[2] << 8) | (STp->buffer)->b_data[3];
+      STp->min_block = ((STp->buffer)->b_data[4] << 8) |
+	(STp->buffer)->b_data[5];
+#ifdef DEBUG
+      if (debugging)
+	printk("st%d: Block limits %d - %d bytes.\n", dev, STp->min_block,
+	       STp->max_block);
+#endif
+    }
+    else {
+      STp->min_block = STp->max_block = (-1);
+#ifdef DEBUG
+      if (debugging)
+	printk("st%d: Can't read block limits.\n", dev);
+#endif
+    }
+
+    SCpnt->sense_buffer[0]=0;
+    memset ((void *) &cmd[0], 0, 10);
+    cmd[0] = MODE_SENSE;
+    cmd[4] = 12;
+    SCpnt->request.dev = dev;
+    scsi_do_cmd(SCpnt,
+                (void *) cmd, (void *) (STp->buffer)->b_data,
+                12, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES);
+
+
+    /* this must be done with interrupts off */
+    save_flags (processor_flags);
+    cli();
+    if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+    restore_flags(processor_flags);
+
+    if ((STp->buffer)->last_result_fatal != 0) {
+#ifdef DEBUG
+      if (debugging)
+	printk("st%d: No Mode Sense.\n", dev);
+#endif
+      (STp->buffer)->b_data[2] =
+      (STp->buffer)->b_data[3] = 0;
+    }
+    SCpnt->request.dev = -1;  /* Mark as not busy */
+
+#ifdef DEBUG
+    if (debugging)
+      printk("st%d: Mode sense. Length %d, medium %x, WBS %x, BLL %d\n", dev,
+	     (STp->buffer)->b_data[0], (STp->buffer)->b_data[1],
+	     (STp->buffer)->b_data[2], (STp->buffer)->b_data[3]);
+#endif
+
+    if ((STp->buffer)->b_data[3] >= 8) {
+      STp->drv_buffer = ((STp->buffer)->b_data[2] >> 4) & 7;
+      STp->density = (STp->buffer)->b_data[4];
+      STp->block_size = (STp->buffer)->b_data[9] * 65536 +
+	(STp->buffer)->b_data[10] * 256 + (STp->buffer)->b_data[11];
+#ifdef DEBUG
+      if (debugging)
+	printk("st%d: Density %x, tape length: %x, drv buffer: %d\n",
+	       dev, STp->density, (STp->buffer)->b_data[5] * 65536 +
+	       (STp->buffer)->b_data[6] * 256 + (STp->buffer)->b_data[7],
+	       STp->drv_buffer);
+#endif
+      if (STp->block_size > st_buffer_size) {
+	printk("st%d: Blocksize %d too large for buffer.\n", dev,
+	       STp->block_size);
+	(STp->buffer)->in_use = 0;
+	STp->in_use = 0;
+	return (-EIO);
+      }
+
+    }
+    else
+      STp->block_size = 512;  /* "Educated Guess" (?) */
+
+    if (STp->block_size > 0) {
+      (STp->buffer)->buffer_blocks = st_buffer_size / STp->block_size;
+      (STp->buffer)->buffer_size =
+	(STp->buffer)->buffer_blocks * STp->block_size;
+    }
+    else {
+      (STp->buffer)->buffer_blocks = 1;
+      (STp->buffer)->buffer_size = st_buffer_size;
+    }
+    (STp->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0;
+
+#ifdef DEBUG
+    if (debugging)
+      printk("st%d: Block size: %d, buffer size: %d (%d blocks).\n", dev,
+	     STp->block_size, (STp->buffer)->buffer_size,
+	     (STp->buffer)->buffer_blocks);
+#endif
+
+    STp->drv_write_prot = ((STp->buffer)->b_data[2] & 0x80) != 0;
+    if (STp->drv_write_prot) {
+      STp->write_prot = 1;
+#ifdef DEBUG
+      if (debugging)
+	printk( "st%d: Write protected\n", dev);
+#endif
+    }
+
+    if (scsi_tapes[dev].device->host->hostt->usage_count)
+      (*scsi_tapes[dev].device->host->hostt->usage_count)++;
+
+    return 0;
+}
+
+
+/* Close the device*/
+	static void
+scsi_tape_close(struct inode * inode, struct file * filp)
+{
+    int dev;
+    int result;
+    int rewind;
+    static unsigned char cmd[10];
+    Scsi_Cmnd * SCpnt;
+    Scsi_Tape * STp;
+    unsigned int flags;
+   
+    dev = MINOR(inode->i_rdev);
+    rewind = (dev & 0x80) == 0;
+    dev = dev & 127;
+    STp = &(scsi_tapes[dev]);
+
+    if ( STp->rw == ST_WRITING) {
+
+      result = flush_write_buffer(dev);
+
+#ifdef DEBUG
+      if (debugging)
+	printk("st%d: File length %ld bytes.\n", dev, (long)(filp->f_pos));
+#endif
+
+      if (result == 0 || result == (-ENOSPC)) {
+	SCpnt = allocate_device(NULL, STp->device, 1);
+
+	SCpnt->sense_buffer[0] = 0;
+	memset(cmd, 0, 10);
+	cmd[0] = WRITE_FILEMARKS;
+	cmd[4] = 1 + STp->two_fm;
+	SCpnt->request.dev = dev;
+	scsi_do_cmd( SCpnt,
+		    (void *) cmd, (void *) (STp->buffer)->b_data,
+		    0, st_sleep_done, ST_TIMEOUT, MAX_WRITE_RETRIES);
+
+
+	/* this must be done with interrupts off */
+	save_flags (flags);
+	cli();
+	if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+	restore_flags(flags);
+
+
+	if ((STp->buffer)->last_result_fatal != 0) {
+	  SCpnt->request.dev = -1;  /* Mark as not busy */
+	  printk("st%d: Error on write filemark.\n", dev);
+	}
+	else {
+	  SCpnt->request.dev = -1;  /* Mark as not busy */
+	  if ((STp->mt_status)->mt_fileno >= 0)
+	      (STp->mt_status)->mt_fileno++ ;
+	  STp->drv_block = 0;
+	  if (STp->two_fm)
+	    back_over_eof(dev);
+	}
+
+      }
+
+#ifdef DEBUG
+      if (debugging)
+	printk("st%d: Buffer flushed, %d EOF(s) written\n", dev, cmd[4]);
+#endif
+    }
+    else if (!rewind) {
+#ifndef ST_IN_FILE_POS
+      if ((STp->eof == ST_FM) && !STp->eof_hit)
+	back_over_eof(dev);
+#else
+      flush_buffer(inode, filp, 0);
+#endif
+    }
+
+    if (rewind)
+      st_int_ioctl(inode, filp, MTREW, 1);
+
+    if (STp->buffer != NULL)
+      (STp->buffer)->in_use = 0;
+    STp->in_use = 0;
+
+    if (scsi_tapes[dev].device->host->hostt->usage_count)
+      (*scsi_tapes[dev].device->host->hostt->usage_count)--;
+
+    return;
+}
+
+
+/* Write command */
+	static int
+st_write(struct inode * inode, struct file * filp, char * buf, int count)
+{
+    int dev;
+    int total, do_count, blks, retval, transfer;
+    int write_threshold;
+    int doing_write = 0;
+    static unsigned char cmd[10];
+    char *b_point;
+    Scsi_Cmnd * SCpnt;
+    Scsi_Tape * STp;
+    unsigned int flags;
+
+    dev = MINOR(inode->i_rdev) & 127;
+    STp = &(scsi_tapes[dev]);
+    if (STp->ready != ST_READY)
+      return (-EIO);
+#ifdef DEBUG
+    if (!STp->in_use) {
+      printk("st%d: Incorrect device.\n", dev);
+      return (-EIO);
+    }
+#endif
+
+    if (STp->write_prot)
+      return (-EACCES);
+
+    if (STp->block_size == 0 && count > st_buffer_size)
+      return (-EOVERFLOW);
+
+    if (STp->rw == ST_READING) {
+      retval = flush_buffer(inode, filp, 0);
+      if (retval)
+	return retval;
+      STp->rw = ST_WRITING;
+    }
+
+    if (STp->moves_after_eof < 255)
+      STp->moves_after_eof++;
+
+    if ((STp->buffer)->writing) {
+      write_behind_check(dev);
+      if ((STp->buffer)->last_result_fatal) {
+#ifdef DEBUG
+	if (debugging)
+	  printk("st%d: Async write error (write) %x.\n", dev,
+		 (STp->buffer)->last_result);
+#endif
+	if ((STp->buffer)->last_result == INT_MAX) {
+	  retval = (-ENOSPC);  /* All has been written */
+	  STp->eof = ST_EOM_OK;
+	}
+	else
+	  retval = (-EIO);
+	return retval;
+      }
+    }
+
+    if (STp->eof == ST_EOM_OK)
+      return (-ENOSPC);
+    else if (STp->eof == ST_EOM_ERROR)
+      return (-EIO);
+
+    if (!STp->do_buffer_writes) {
+      if (STp->block_size != 0 && (count % STp->block_size) != 0)
+	return (-EIO);   /* Write must be integral number of blocks */
+      write_threshold = 1;
+    }
+    else
+      write_threshold = (STp->buffer)->buffer_size;
+    if (!STp->do_async_writes)
+      write_threshold--;
+
+    SCpnt = allocate_device(NULL, STp->device, 1);
+
+    total = count;
+
+    memset(cmd, 0, 10);
+    cmd[0] = WRITE_6;
+    cmd[1] = (STp->block_size != 0);
+
+    STp->rw = ST_WRITING;
+
+    b_point = buf;
+    while((STp->block_size == 0 && !STp->do_async_writes && count > 0) ||
+	  (STp->block_size != 0 &&
+	   (STp->buffer)->buffer_bytes + count > write_threshold))
+    {
+      doing_write = 1;
+      if (STp->block_size == 0)
+	do_count = count;
+      else {
+	do_count = (STp->buffer)->buffer_size - (STp->buffer)->buffer_bytes;
+	if (do_count > count)
+	  do_count = count;
+      }
+      memcpy_fromfs((STp->buffer)->b_data +
+		    (STp->buffer)->buffer_bytes, b_point, do_count);
+
+      if (STp->block_size == 0)
+        blks = transfer = do_count;
+      else {
+	blks = ((STp->buffer)->buffer_bytes + do_count) /
+	  STp->block_size;
+	transfer = blks * STp->block_size;
+      }
+      cmd[2] = blks >> 16;
+      cmd[3] = blks >> 8;
+      cmd[4] = blks;
+      SCpnt->sense_buffer[0] = 0;
+      SCpnt->request.dev = dev;
+      scsi_do_cmd (SCpnt,
+		   (void *) cmd, (STp->buffer)->b_data, transfer,
+		   st_sleep_done, ST_TIMEOUT, MAX_WRITE_RETRIES);
+
+
+	/* this must be done with interrupts off */
+	save_flags (flags);
+	cli();
+	if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+	restore_flags(flags);
+
+      if ((STp->buffer)->last_result_fatal != 0) {
+#ifdef DEBUG
+	if (debugging)
+	  printk("st%d: Error on write:\n", dev);
+#endif
+	if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+	    (SCpnt->sense_buffer[2] & 0x40)) {
+	  if (STp->block_size != 0 && (SCpnt->sense_buffer[0] & 0x80) != 0)
+	    transfer = (SCpnt->sense_buffer[3] << 24) |
+	      (SCpnt->sense_buffer[4] << 16) |
+		(SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6];
+	  else if (STp->block_size == 0 &&
+		   (SCpnt->sense_buffer[2] & 0x0f) == VOLUME_OVERFLOW)
+	    transfer = do_count;
+	  else
+	    transfer = 0;
+	  if (STp->block_size != 0)
+	    transfer *= STp->block_size;
+	  if (transfer <= do_count) {
+	    filp->f_pos += do_count - transfer;
+	    count -= do_count - transfer;
+	    if (STp->drv_block >= 0) {
+	      if (STp->block_size == 0 && transfer < do_count)
+		STp->drv_block++;
+	      else if (STp->block_size != 0)
+		STp->drv_block += (do_count - transfer) / STp->block_size;
+	    }
+	    STp->eof = ST_EOM_OK;
+	    retval = (-ENOSPC); /* EOM within current request */
+#ifdef DEBUG
+	    if (debugging)
+	      printk("st%d: EOM with %d bytes unwritten.\n",
+		     dev, transfer);
+#endif
+	  }
+	  else {
+	    STp->eof = ST_EOM_ERROR;
+	    STp->drv_block = (-1);    /* Too cautious? */
+	    retval = (-EIO); /* EOM for old data */
+#ifdef DEBUG
+	    if (debugging)
+	      printk("st%d: EOM with lost data.\n", dev);
+#endif
+	  }
+	}
+	else {
+	  STp->drv_block = (-1);    /* Too cautious? */
+	  retval = (-EIO);
+	}
+
+	SCpnt->request.dev = -1;  /* Mark as not busy */
+	(STp->buffer)->buffer_bytes = 0;
+	STp->dirty = 0;
+	if (count < total)
+	  return total - count;
+	else
+	  return retval;
+      }
+      filp->f_pos += do_count;
+      b_point += do_count;
+      count -= do_count;
+      if (STp->drv_block >= 0) {
+	if (STp->block_size == 0)
+	  STp->drv_block++;
+	else
+	  STp->drv_block += blks;
+      }
+      (STp->buffer)->buffer_bytes = 0;
+      STp->dirty = 0;
+    }
+    if (count != 0) {
+      STp->dirty = 1;
+      memcpy_fromfs((STp->buffer)->b_data +
+		    (STp->buffer)->buffer_bytes,b_point,count);
+      filp->f_pos += count;
+      (STp->buffer)->buffer_bytes += count;
+      count = 0;
+    }
+
+    if (doing_write && (STp->buffer)->last_result_fatal != 0) {
+      SCpnt->request.dev = -1;
+      return (STp->buffer)->last_result_fatal;
+    }
+
+    if (STp->do_async_writes &&
+	((STp->buffer)->buffer_bytes >= STp->write_threshold ||
+	 STp->block_size == 0) ) {
+      /* Schedule an asynchronous write */
+      if (STp->block_size == 0)
+	(STp->buffer)->writing = (STp->buffer)->buffer_bytes;
+      else
+	(STp->buffer)->writing = ((STp->buffer)->buffer_bytes /
+	  STp->block_size) * STp->block_size;
+      STp->dirty = !((STp->buffer)->writing ==
+		     (STp->buffer)->buffer_bytes);
+
+      if (STp->block_size == 0)
+	blks = (STp->buffer)->writing;
+      else
+	blks = (STp->buffer)->writing / STp->block_size;
+      cmd[2] = blks >> 16;
+      cmd[3] = blks >> 8;
+      cmd[4] = blks;
+      SCpnt->result = (STp->buffer)->last_result = -1;
+      SCpnt->sense_buffer[0] = 0;
+      SCpnt->request.dev = dev;
+      scsi_do_cmd (SCpnt,
+		   (void *) cmd, (STp->buffer)->b_data,
+		   (STp->buffer)->writing,
+		   st_sleep_done, ST_TIMEOUT, MAX_WRITE_RETRIES);
+    }
+    else
+      SCpnt->request.dev = -1;  /* Mark as not busy */
+
+    STp->at_sm &= (total != 0);
+    return( total);
+}   
+
+
+/* Read command */
+	static int
+st_read(struct inode * inode, struct file * filp, char * buf, int count)
+{
+    int dev;
+    int total;
+    int transfer, blks, bytes;
+    static unsigned char cmd[10];
+    Scsi_Cmnd * SCpnt;
+    Scsi_Tape * STp;
+    unsigned int flags;
+
+    dev = MINOR(inode->i_rdev) & 127;
+    STp = &(scsi_tapes[dev]);
+    if (STp->ready != ST_READY)
+      return (-EIO);
+#ifdef DEBUG
+    if (!STp->in_use) {
+      printk("st%d: Incorrect device.\n", dev);
+      return (-EIO);
+    }
+#endif
+
+    if (STp->block_size == 0 && count > st_buffer_size)
+      return (-EOVERFLOW);
+
+    if (!(STp->do_read_ahead) && STp->block_size != 0 &&
+	(count % STp->block_size) != 0)
+      return (-EIO);	/* Read must be integral number of blocks */
+
+    if (STp->rw == ST_WRITING) {
+      transfer = flush_buffer(inode, filp, 0);
+      if (transfer)
+	return transfer;
+      STp->rw = ST_READING;
+    }
+    if (STp->moves_after_eof < 255)
+      STp->moves_after_eof++;
+
+#ifdef DEBUG
+    if (debugging && STp->eof != ST_NOEOF)
+      printk("st%d: EOF flag up. Bytes %d\n", dev,
+	     (STp->buffer)->buffer_bytes);
+#endif
+    if (((STp->buffer)->buffer_bytes == 0) &&
+	(STp->eof == ST_EOM_OK || STp->eof == ST_EOD))
+      return (-EIO);  /* EOM or Blank Check */
+
+    STp->rw = ST_READING;
+
+    SCpnt = allocate_device(NULL, STp->device, 1);
+
+    for (total = 0; total < count; ) {
+
+      if ((STp->buffer)->buffer_bytes == 0 &&
+	  STp->eof == ST_NOEOF) {
+
+	memset(cmd, 0, 10);
+	cmd[0] = READ_6;
+	cmd[1] = (STp->block_size != 0);
+	if (STp->block_size == 0)
+	  blks = bytes = count;
+	else {
+	  if (STp->do_read_ahead) {
+	    blks = (STp->buffer)->buffer_blocks;
+	    bytes = blks * STp->block_size;
+	  }
+	  else {
+	    bytes = count;
+	    if (bytes > st_buffer_size)
+	      bytes = st_buffer_size;
+	    blks = bytes / STp->block_size;
+	    bytes = blks * STp->block_size;
+	  }
+	}
+	cmd[2] = blks >> 16;
+	cmd[3] = blks >> 8;
+	cmd[4] = blks;
+
+	SCpnt->sense_buffer[0] = 0;
+	SCpnt->request.dev = dev;
+	scsi_do_cmd (SCpnt,
+		     (void *) cmd, (STp->buffer)->b_data,
+		     bytes, st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
+
+
+	/* this must be done with interrupts off */
+	save_flags (flags);
+	cli();
+	if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+	restore_flags(flags);
+
+	(STp->buffer)->read_pointer = 0;
+	STp->eof_hit = 0;
+	STp->at_sm = 0;
+
+	if ((STp->buffer)->last_result_fatal) {
+#ifdef DEBUG
+	  if (debugging)
+	    printk("st%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n", dev,
+		   SCpnt->sense_buffer[0], SCpnt->sense_buffer[1],
+		   SCpnt->sense_buffer[2], SCpnt->sense_buffer[3],
+		   SCpnt->sense_buffer[4], SCpnt->sense_buffer[5],
+		   SCpnt->sense_buffer[6], SCpnt->sense_buffer[7]);
+#endif
+	  if ((SCpnt->sense_buffer[0] & 0x70) == 0x70) { /* extended sense */
+
+	    if ((SCpnt->sense_buffer[2] & 0xe0) != 0) { /* EOF, EOM, or ILI */
+
+	      if ((SCpnt->sense_buffer[0] & 0x80) != 0)
+		transfer = (SCpnt->sense_buffer[3] << 24) |
+		  (SCpnt->sense_buffer[4] << 16) |
+		    (SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6];
+	      else
+		transfer = 0;
+	      if (STp->block_size == 0 &&
+		  (SCpnt->sense_buffer[2] & 0x0f) == MEDIUM_ERROR)
+		transfer = bytes;
+
+	      if (SCpnt->sense_buffer[2] & 0x20) {
+		if (STp->block_size == 0) {
+		  if (transfer <= 0)
+		    transfer = 0;
+		  (STp->buffer)->buffer_bytes = bytes - transfer;
+		}
+		else {
+		  printk("st%d: Incorrect block size.\n", dev);
+		  SCpnt->request.dev = -1;  /* Mark as not busy */
+		  return (-EIO);
+		}
+	      }
+	      else if (SCpnt->sense_buffer[2] & 0x40) {
+		STp->eof = ST_EOM_OK;
+		if (STp->block_size == 0)
+		  (STp->buffer)->buffer_bytes = bytes - transfer;
+		else
+		  (STp->buffer)->buffer_bytes =
+		    bytes - transfer * STp->block_size;
+#ifdef DEBUG
+		if (debugging)
+		  printk("st%d: EOM detected (%d bytes read).\n", dev,
+			 (STp->buffer)->buffer_bytes);
+#endif
+	      }
+	      else if (SCpnt->sense_buffer[2] & 0x80) {
+		STp->eof = ST_FM;
+		if (STp->block_size == 0)
+		  (STp->buffer)->buffer_bytes = 0;
+		else
+		  (STp->buffer)->buffer_bytes =
+		    bytes - transfer * STp->block_size;
+#ifdef DEBUG
+		if (debugging)
+		  printk(
+		    "st%d: EOF detected (%d bytes read, transferred %d bytes).\n",
+			 dev, (STp->buffer)->buffer_bytes, total);
+#endif
+	      }
+	    } /* end of EOF, EOM, ILI test */
+	    else { /* nonzero sense key */
+#ifdef DEBUG
+	      if (debugging)
+		printk("st%d: Tape error while reading.\n", dev);
+#endif
+	      SCpnt->request.dev = -1;
+	      STp->drv_block = (-1);
+	      if (total)
+		return total;
+	      else if (STp->moves_after_eof == 1 &&
+		       (SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) {
+#ifdef DEBUG
+		if (debugging)
+		  printk("st%d: Zero returned for first BLANK CHECK after EOF.\n",
+			 dev);
+#endif
+		STp->eof = ST_EOD;
+		return 0; /* First BLANK_CHECK after EOF */
+	      }
+	      else
+		return -EIO;
+	    }
+	  } /* End of extended sense test */
+	  else {
+	    transfer = (STp->buffer)->last_result_fatal;
+	    SCpnt->request.dev = -1;  /* Mark as not busy */
+	    return transfer;
+	  }
+	} /* End of error handling */
+	else /* Read successful */
+	  (STp->buffer)->buffer_bytes = bytes;
+
+	if (STp->drv_block >= 0) {
+	  if (STp->block_size == 0)
+	    STp->drv_block++;
+	  else
+	    STp->drv_block += (STp->buffer)->buffer_bytes / STp->block_size;
+	}
+
+      } /* if ((STp->buffer)->buffer_bytes == 0 &&
+	   STp->eof == ST_NOEOF) */
+
+      if ((STp->buffer)->buffer_bytes > 0) {
+#ifdef DEBUG
+	if (debugging && STp->eof != ST_NOEOF)
+	  printk("st%d: EOF up. Left %d, needed %d.\n", dev,
+		 (STp->buffer)->buffer_bytes, count - total);
+#endif
+	transfer = (STp->buffer)->buffer_bytes < count - total ?
+	  (STp->buffer)->buffer_bytes : count - total;
+	memcpy_tofs(buf, (STp->buffer)->b_data +
+		    (STp->buffer)->read_pointer,transfer);
+	filp->f_pos += transfer;
+	buf += transfer;
+	total += transfer;
+	(STp->buffer)->buffer_bytes -= transfer;
+	(STp->buffer)->read_pointer += transfer;
+      }
+      else if (STp->eof != ST_NOEOF) {
+	STp->eof_hit = 1;
+	SCpnt->request.dev = -1;  /* Mark as not busy */
+	if (total == 0 && STp->eof == ST_FM) {
+	  STp->eof = ST_NOEOF;
+	  STp->drv_block = 0;
+	  if (STp->moves_after_eof > 1)
+	    STp->moves_after_eof = 0;
+	  if ((STp->mt_status)->mt_fileno >= 0)
+	    (STp->mt_status)->mt_fileno++;
+	}
+	if (total == 0 && STp->eof == ST_EOM_OK)
+	  return (-EIO);  /* ST_EOM_ERROR not used in read */
+	return total;
+      }
+
+      if (STp->block_size == 0)
+	count = total;  /* Read only one variable length block */
+
+    } /* for (total = 0; total < count; ) */
+
+    SCpnt->request.dev = -1;  /* Mark as not busy */
+
+    return total;
+}
+
+
+
+/* Set the driver options */
+	static int
+st_set_options(struct inode * inode, long options)
+{
+  int dev, value;
+  Scsi_Tape *STp;
+
+  dev = MINOR(inode->i_rdev) & 127;
+  STp = &(scsi_tapes[dev]);
+  if ((options & MT_ST_OPTIONS) == MT_ST_BOOLEANS) {
+    STp->do_buffer_writes = (options & MT_ST_BUFFER_WRITES) != 0;
+    STp->do_async_writes  = (options & MT_ST_ASYNC_WRITES) != 0;
+    STp->do_read_ahead    = (options & MT_ST_READ_AHEAD) != 0;
+    STp->two_fm		  = (options & MT_ST_TWO_FM) != 0;
+    STp->fast_mteom	  = (options & MT_ST_FAST_MTEOM) != 0;
+#ifdef DEBUG
+    debugging = (options & MT_ST_DEBUGGING) != 0;
+    printk(
+"st%d: options: buffer writes: %d, async writes: %d, read ahead: %d\n",
+	   dev, STp->do_buffer_writes, STp->do_async_writes,
+	   STp->do_read_ahead);
+    printk("              two FMs: %d, fast mteom: %d debugging: %d\n",
+	   STp->two_fm, STp->fast_mteom, debugging);
+#endif
+  }
+  else if ((options & MT_ST_OPTIONS) == MT_ST_WRITE_THRESHOLD) {
+    value = (options & ~MT_ST_OPTIONS) * ST_BLOCK_SIZE;
+    if (value < 1 || value > st_buffer_size) {
+      printk("st: Write threshold %d too small or too large.\n",
+	     value);
+      return (-EIO);
+    }
+    STp->write_threshold = value;
+#ifdef DEBUG
+    printk("st%d: Write threshold set to %d bytes.\n", dev,
+	   STp->write_threshold);
+#endif
+  }
+  else
+    return (-EIO);
+
+  return 0;
+}
+
+
+/* Internal ioctl function */
+	static int
+st_int_ioctl(struct inode * inode,struct file * file,
+	     unsigned int cmd_in, unsigned long arg)
+{
+   int dev = MINOR(inode->i_rdev);
+   int timeout = ST_LONG_TIMEOUT;
+   long ltmp;
+   int ioctl_result;
+   unsigned char cmd[10];
+   Scsi_Cmnd * SCpnt;
+   Scsi_Tape * STp;
+   int fileno, blkno, at_sm, undone, datalen;
+   unsigned int flags;
+
+   dev = dev & 127;
+   STp = &(scsi_tapes[dev]);
+   if (STp->ready != ST_READY)
+     return (-EIO);
+   fileno = (STp->mt_status)->mt_fileno ;
+   blkno = STp->drv_block;
+   at_sm = STp->at_sm;
+
+   memset(cmd, 0, 10);
+   datalen = 0;
+   switch (cmd_in) {
+     case MTFSF:
+     case MTFSFM:
+       cmd[0] = SPACE;
+       cmd[1] = 0x01; /* Space FileMarks */
+       cmd[2] = (arg >> 16);
+       cmd[3] = (arg >> 8);
+       cmd[4] = arg;
+#ifdef DEBUG
+       if (debugging)
+	 printk("st%d: Spacing tape forward over %d filemarks.\n", dev,
+		cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+#endif
+       if (fileno >= 0)
+	 fileno += arg;
+       blkno = 0;
+       at_sm &= (arg != 0);
+       break; 
+     case MTBSF:
+     case MTBSFM:
+       cmd[0] = SPACE;
+       cmd[1] = 0x01; /* Space FileMarks */
+       ltmp = (-arg);
+       cmd[2] = (ltmp >> 16);
+       cmd[3] = (ltmp >> 8);
+       cmd[4] = ltmp;
+#ifdef DEBUG
+       if (debugging) {
+	 if (cmd[2] & 0x80)
+	   ltmp = 0xff000000;
+	 ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
+	 printk("st%d: Spacing tape backward over %ld filemarks.\n", dev, (-ltmp));
+       }
+#endif
+       if (fileno >= 0)
+	 fileno -= arg;
+       blkno = (-1);  /* We can't know the block number */
+       at_sm &= (arg != 0);
+       break; 
+     case MTFSR:
+       cmd[0] = SPACE;
+       cmd[1] = 0x00; /* Space Blocks */
+       cmd[2] = (arg >> 16);
+       cmd[3] = (arg >> 8);
+       cmd[4] = arg;
+#ifdef DEBUG
+       if (debugging)
+	 printk("st%d: Spacing tape forward %d blocks.\n", dev,
+		cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+#endif
+       if (blkno >= 0)
+	 blkno += arg;
+       at_sm &= (arg != 0);
+       break; 
+     case MTBSR:
+       cmd[0] = SPACE;
+       cmd[1] = 0x00; /* Space Blocks */
+       ltmp = (-arg);
+       cmd[2] = (ltmp >> 16);
+       cmd[3] = (ltmp >> 8);
+       cmd[4] = ltmp;
+#ifdef DEBUG
+       if (debugging) {
+	 if (cmd[2] & 0x80)
+	   ltmp = 0xff000000;
+	 ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
+	 printk("st%d: Spacing tape backward %ld blocks.\n", dev, (-ltmp));
+       }
+#endif
+       if (blkno >= 0)
+	 blkno -= arg;
+       at_sm &= (arg != 0);
+       break; 
+     case MTFSS:
+       cmd[0] = SPACE;
+       cmd[1] = 0x04; /* Space Setmarks */
+       cmd[2] = (arg >> 16);
+       cmd[3] = (arg >> 8);
+       cmd[4] = arg;
+#ifdef DEBUG
+       if (debugging)
+	 printk("st%d: Spacing tape forward %d setmarks.\n", dev,
+		cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+#endif
+       if (arg != 0) {
+	 blkno = fileno = (-1);
+	 at_sm = 1;
+       }
+       break; 
+     case MTBSS:
+       cmd[0] = SPACE;
+       cmd[1] = 0x04; /* Space Setmarks */
+       ltmp = (-arg);
+       cmd[2] = (ltmp >> 16);
+       cmd[3] = (ltmp >> 8);
+       cmd[4] = ltmp;
+#ifdef DEBUG
+       if (debugging) {
+	 if (cmd[2] & 0x80)
+	   ltmp = 0xff000000;
+	 ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
+	 printk("st%d: Spacing tape backward %ld setmarks.\n", dev, (-ltmp));
+       }
+#endif
+       if (arg != 0) {
+	 blkno = fileno = (-1);
+	 at_sm = 1;
+       }
+       break; 
+     case MTWEOF:
+     case MTWSM:
+       if (STp->write_prot)
+	 return (-EACCES);
+       cmd[0] = WRITE_FILEMARKS;
+       if (cmd_in == MTWSM)
+	 cmd[1] = 2;
+       cmd[2] = (arg >> 16);
+       cmd[3] = (arg >> 8);
+       cmd[4] = arg;
+       timeout = ST_TIMEOUT;
+#ifdef DEBUG
+       if (debugging) {
+	 if (cmd_in == MTWEOF)
+	   printk("st%d: Writing %d filemarks.\n", dev,
+		  cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+	 else
+	   printk("st%d: Writing %d setmarks.\n", dev,
+		  cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+       }
+#endif
+       if (fileno >= 0)
+	 fileno += arg;
+       blkno = 0;
+       at_sm = (cmd_in == MTWSM);
+       break; 
+     case MTREW:
+       cmd[0] = REZERO_UNIT;
+#ifdef ST_NOWAIT
+       cmd[1] = 1;  /* Don't wait for completion */
+       timeout = ST_TIMEOUT;
+#endif
+#ifdef DEBUG
+       if (debugging)
+	 printk("st%d: Rewinding tape.\n", dev);
+#endif
+       fileno = blkno = at_sm = 0 ;
+       break; 
+     case MTOFFL:
+       cmd[0] = START_STOP;
+#ifdef ST_NOWAIT
+       cmd[1] = 1;  /* Don't wait for completion */
+       timeout = ST_TIMEOUT;
+#endif
+#ifdef DEBUG
+       if (debugging)
+	 printk("st%d: Unloading tape.\n", dev);
+#endif
+       fileno = blkno = at_sm = 0 ;
+       break; 
+     case MTNOP:
+#ifdef DEBUG
+       if (debugging)
+	 printk("st%d: No op on tape.\n", dev);
+#endif
+       return 0;  /* Should do something ? */
+       break;
+     case MTRETEN:
+       cmd[0] = START_STOP;
+#ifdef ST_NOWAIT
+       cmd[1] = 1;  /* Don't wait for completion */
+       timeout = ST_TIMEOUT;
+#endif
+       cmd[4] = 3;
+#ifdef DEBUG
+       if (debugging)
+	 printk("st%d: Retensioning tape.\n", dev);
+#endif
+       fileno = blkno = at_sm = 0;
+       break; 
+     case MTEOM:
+       if (!STp->fast_mteom) {
+	 /* space to the end of tape */
+	 ioctl_result = st_int_ioctl(inode, file, MTFSF, 0x3fff);
+	 fileno = (STp->mt_status)->mt_fileno ;
+	 if (STp->eof == ST_EOD || STp->eof == ST_EOM_OK)
+	   return 0;
+	 /* The next lines would hide the number of spaced FileMarks
+	    That's why I inserted the previous lines. I had no luck
+	    with detecting EOM with FSF, so we go now to EOM.
+	    Joerg Weule */
+       }
+       else
+	 fileno = (-1);
+       cmd[0] = SPACE;
+       cmd[1] = 3;
+#ifdef DEBUG
+       if (debugging)
+	 printk("st%d: Spacing to end of recorded medium.\n", dev);
+#endif
+       blkno = 0;
+       at_sm = 0;
+       break; 
+     case MTERASE:
+       if (STp->write_prot)
+	 return (-EACCES);
+       cmd[0] = ERASE;
+       cmd[1] = 1;  /* To the end of tape */
+#ifdef ST_NOWAIT
+       cmd[1] |= 2;  /* Don't wait for completion */
+       timeout = ST_TIMEOUT;
+#else
+       timeout = ST_LONG_TIMEOUT * 8;
+#endif
+#ifdef DEBUG
+       if (debugging)
+	 printk("st%d: Erasing tape.\n", dev);
+#endif
+       fileno = blkno = at_sm = 0 ;
+       break;
+     case MTSEEK:
+       if ((STp->device)->scsi_level < SCSI_2) {
+	 cmd[0] = QFA_SEEK_BLOCK;
+	 cmd[2] = (arg >> 16);
+	 cmd[3] = (arg >> 8);
+	 cmd[4] = arg;
+	 cmd[5] = 0;
+       }
+       else {
+	 cmd[0] = SEEK_10;
+	 cmd[1] = 4;
+	 cmd[3] = (arg >> 24);
+	 cmd[4] = (arg >> 16);
+	 cmd[5] = (arg >> 8);
+	 cmd[6] = arg;
+       }
+#ifdef ST_NOWAIT
+       cmd[1] |= 1;  /* Don't wait for completion */
+       timeout = ST_TIMEOUT;
+#endif
+#ifdef DEBUG
+       if (debugging)
+	 printk("st%d: Seeking tape to block %ld.\n", dev, arg);
+#endif
+       fileno = blkno = (-1);
+       at_sm = 0;
+       break;
+     case MTSETBLK:  /* Set block length */
+     case MTSETDENSITY: /* Set tape density */
+     case MTSETDRVBUFFER: /* Set drive buffering */
+       if (STp->dirty || (STp->buffer)->buffer_bytes != 0)
+	 return (-EIO);   /* Not allowed if data in buffer */
+       if (cmd_in == MTSETBLK &&
+	   arg != 0 &&
+	   (arg < STp->min_block || arg > STp->max_block ||
+	    arg > st_buffer_size)) {
+	 printk("st%d: Illegal block size.\n", dev);
+	 return (-EINVAL);
+       }
+       cmd[0] = MODE_SELECT;
+       cmd[4] = datalen = 12;
+
+       memset((STp->buffer)->b_data, 0, 12);
+       if (cmd_in == MTSETDRVBUFFER)
+	 (STp->buffer)->b_data[2] = (arg & 7) << 4;
+       else
+	 (STp->buffer)->b_data[2] = 
+	   STp->drv_buffer << 4;
+       (STp->buffer)->b_data[3] = 8;     /* block descriptor length */
+       if (cmd_in == MTSETDENSITY)
+	 (STp->buffer)->b_data[4] = arg;
+       else
+	 (STp->buffer)->b_data[4] = STp->density;
+       if (cmd_in == MTSETBLK)
+	 ltmp = arg;
+       else
+	 ltmp = STp->block_size;
+       (STp->buffer)->b_data[9] = (ltmp >> 16);
+       (STp->buffer)->b_data[10] = (ltmp >> 8);
+       (STp->buffer)->b_data[11] = ltmp;
+       timeout = ST_TIMEOUT;
+#ifdef DEBUG
+       if (debugging) {
+	 if (cmd_in == MTSETBLK)
+	   printk("st%d: Setting block size to %d bytes.\n", dev,
+		  (STp->buffer)->b_data[9] * 65536 +
+		  (STp->buffer)->b_data[10] * 256 +
+		  (STp->buffer)->b_data[11]);
+	 else if (cmd_in == MTSETDENSITY)
+	   printk("st%d: Setting density code to %x.\n", dev,
+		  (STp->buffer)->b_data[4]);
+	 else
+	   printk("st%d: Setting drive buffer code to %d.\n", dev,
+		  ((STp->buffer)->b_data[2] >> 4) & 7);
+       }
+#endif
+       break;
+     default:
+       printk("st%d: Unknown st_ioctl command %x.\n", dev, cmd_in);
+       return (-ENOSYS);
+     }
+
+   SCpnt = allocate_device(NULL, STp->device, 1);
+   SCpnt->sense_buffer[0] = 0;
+   SCpnt->request.dev = dev;
+   scsi_do_cmd(SCpnt,
+	       (void *) cmd, (void *) (STp->buffer)->b_data, datalen,
+	       st_sleep_done, timeout, MAX_RETRIES);
+
+
+	/* this must be done with interrupts off */
+	save_flags (flags);
+	cli();
+	if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+	restore_flags(flags);
+
+
+   ioctl_result = (STp->buffer)->last_result_fatal;
+
+   SCpnt->request.dev = -1;  /* Mark as not busy */
+
+   if (cmd_in == MTFSF)
+     STp->moves_after_eof = 0;
+   else
+     STp->moves_after_eof = 1;
+   if (!ioctl_result) {  /* SCSI command successful */
+     if (cmd_in != MTSEEK) {
+       STp->drv_block = blkno;
+       (STp->mt_status)->mt_fileno = fileno;
+       STp->at_sm = at_sm;
+     }
+     else {
+       STp->drv_block = (STp->mt_status)->mt_fileno = (-1);
+       STp->at_sm = 0;
+     }
+     if (cmd_in == MTBSFM)
+       ioctl_result = st_int_ioctl(inode, file, MTFSF, 1);
+     else if (cmd_in == MTFSFM)
+       ioctl_result = st_int_ioctl(inode, file, MTBSF, 1);
+     else if (cmd_in == MTSETBLK) {
+       STp->block_size = arg;
+       if (arg != 0) {
+	 (STp->buffer)->buffer_blocks =
+	   st_buffer_size / STp->block_size;
+	 (STp->buffer)->buffer_size =
+	   (STp->buffer)->buffer_blocks * STp->block_size;
+       }
+       else {
+	 (STp->buffer)->buffer_blocks = 1;
+	 (STp->buffer)->buffer_size = st_buffer_size;
+       }
+       (STp->buffer)->buffer_bytes =
+	 (STp->buffer)->read_pointer = 0;
+     }
+     else if (cmd_in == MTSETDRVBUFFER)
+       STp->drv_buffer = (arg & 7);
+     else if (cmd_in == MTSETDENSITY)
+       STp->density = arg;
+     else if (cmd_in == MTEOM) {
+       STp->eof = ST_EOD;
+       STp->eof_hit = 0;
+     }
+     else if (cmd_in != MTSETBLK && cmd_in != MTNOP) {
+       STp->eof = ST_NOEOF;
+       STp->eof_hit = 0;
+     }
+   } else {  /* SCSI command was not completely successful */
+     if (SCpnt->sense_buffer[2] & 0x40) {
+       if (cmd_in != MTBSF && cmd_in != MTBSFM &&
+	   cmd_in != MTBSR && cmd_in != MTBSS)
+	 STp->eof = ST_EOM_OK;
+       STp->eof_hit = 0;
+       STp->drv_block = 0;
+     }
+     undone = (
+          (SCpnt->sense_buffer[3] << 24) +
+          (SCpnt->sense_buffer[4] << 16) +
+          (SCpnt->sense_buffer[5] << 8) +
+          SCpnt->sense_buffer[6] );
+     if ( (cmd_in == MTFSF) || (cmd_in == MTFSFM) ) {
+       if (fileno >= 0)
+	 (STp->mt_status)->mt_fileno = fileno - undone ;
+       else
+	 (STp->mt_status)->mt_fileno = fileno;
+       STp->drv_block = 0;
+     }
+     else if ( (cmd_in == MTBSF) || (cmd_in == MTBSFM) ) {
+       (STp->mt_status)->mt_fileno = fileno + undone ;
+       STp->drv_block = 0;
+     }
+     else if (cmd_in == MTFSR) {
+       if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */
+	 (STp->mt_status)->mt_fileno++;
+	 STp->drv_block = 0;
+       }
+       else {
+	 if (blkno >= undone)
+	   STp->drv_block = blkno - undone;
+	 else
+	   STp->drv_block = (-1);
+       }
+     }
+     else if (cmd_in == MTBSR) {
+       if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */
+	 (STp->mt_status)->mt_fileno--;
+	 STp->drv_block = (-1);
+       }
+       else {
+	 if (blkno >= 0)
+	   STp->drv_block = blkno + undone;
+	 else
+	   STp->drv_block = (-1);
+       }
+     }
+     else if (cmd_in == MTEOM || cmd_in == MTSEEK) {
+       (STp->mt_status)->mt_fileno = (-1);
+       STp->drv_block = (-1);
+     }
+     if (STp->eof == ST_NOEOF &&
+	 (SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK)
+       STp->eof = ST_EOD;
+   }
+
+   return ioctl_result;
+}
+
+
+
+/* The ioctl command */
+	static int
+st_ioctl(struct inode * inode,struct file * file,
+	 unsigned int cmd_in, unsigned long arg)
+{
+   int dev = MINOR(inode->i_rdev);
+   int i, cmd, result;
+   struct mtop mtc;
+   struct mtpos mt_pos;
+   unsigned char scmd[10];
+   Scsi_Cmnd *SCpnt;
+   Scsi_Tape *STp;
+   unsigned int flags;
+
+   dev = dev & 127;
+   STp = &(scsi_tapes[dev]);
+#ifdef DEBUG
+   if (debugging && !STp->in_use) {
+     printk("st%d: Incorrect device.\n", dev);
+     return (-EIO);
+   }
+#endif
+
+   cmd = cmd_in & IOCCMD_MASK;
+   if (cmd == (MTIOCTOP & IOCCMD_MASK)) {
+
+     if (((cmd_in & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(mtc))
+       return (-EINVAL);
+
+     i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(mtc));
+     if (i)
+        return i;
+
+     memcpy_fromfs((char *) &mtc, (char *)arg, sizeof(struct mtop));
+
+     i = flush_buffer(inode, file, mtc.mt_op == MTSEEK ||
+		      mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
+		      mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM);
+     if (i < 0)
+       return i;
+     if (mtc.mt_op != MTNOP && mtc.mt_op != MTSETBLK &&
+	 mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTWSM &&
+	 mtc.mt_op != MTSETDRVBUFFER)
+       STp->rw = ST_IDLE;  /* Prevent automatic WEOF */
+
+     if (mtc.mt_op == MTSETDRVBUFFER &&
+	 (mtc.mt_count & MT_ST_OPTIONS) != 0)
+       return st_set_options(inode, mtc.mt_count);
+     else
+       return st_int_ioctl(inode, file, mtc.mt_op, mtc.mt_count);
+   }
+   else if (cmd == (MTIOCGET & IOCCMD_MASK)) {
+
+     if (((cmd_in & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtget))
+       return (-EINVAL);
+     i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct mtget));
+     if (i)
+       return i;
+
+     (STp->mt_status)->mt_dsreg =
+       ((STp->block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK) |
+       ((STp->density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK);
+     (STp->mt_status)->mt_blkno = STp->drv_block;
+     if (STp->block_size != 0) {
+       if (STp->rw == ST_WRITING)
+	 (STp->mt_status)->mt_blkno +=
+	   (STp->buffer)->buffer_bytes / STp->block_size;
+       else if (STp->rw == ST_READING)
+	 (STp->mt_status)->mt_blkno -= ((STp->buffer)->buffer_bytes +
+	   STp->block_size - 1) / STp->block_size;
+     }
+
+     (STp->mt_status)->mt_gstat = 0;
+     if (STp->drv_write_prot)
+       (STp->mt_status)->mt_gstat |= GMT_WR_PROT(0xffffffff);
+     if ((STp->mt_status)->mt_blkno == 0) {
+       if ((STp->mt_status)->mt_fileno == 0)
+	 (STp->mt_status)->mt_gstat |= GMT_BOT(0xffffffff);
+       else
+	 (STp->mt_status)->mt_gstat |= GMT_EOF(0xffffffff);
+     }
+     if (STp->eof == ST_EOM_OK || STp->eof == ST_EOM_ERROR)
+       (STp->mt_status)->mt_gstat |= GMT_EOT(0xffffffff);
+     else if (STp->eof == ST_EOD)
+       (STp->mt_status)->mt_gstat |= GMT_EOD(0xffffffff);
+     if (STp->density == 1)
+       (STp->mt_status)->mt_gstat |= GMT_D_800(0xffffffff);
+     else if (STp->density == 2)
+       (STp->mt_status)->mt_gstat |= GMT_D_1600(0xffffffff);
+     else if (STp->density == 3)
+       (STp->mt_status)->mt_gstat |= GMT_D_6250(0xffffffff);
+     if (STp->ready == ST_READY)
+       (STp->mt_status)->mt_gstat |= GMT_ONLINE(0xffffffff);
+     if (STp->ready == ST_NO_TAPE)
+       (STp->mt_status)->mt_gstat |= GMT_DR_OPEN(0xffffffff);
+     if (STp->at_sm)
+       (STp->mt_status)->mt_gstat |= GMT_SM(0xffffffff);
+
+     memcpy_tofs((char *)arg, (char *)(STp->mt_status),
+		 sizeof(struct mtget));
+
+     (STp->mt_status)->mt_erreg = 0;  /* Clear after read */
+     return 0;
+   }
+   else if (cmd == (MTIOCPOS & IOCCMD_MASK)) {
+     if (STp->ready != ST_READY)
+       return (-EIO);
+#ifdef DEBUG
+     if (debugging)
+       printk("st%d: get tape position.\n", dev);
+#endif
+     if (((cmd_in & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtpos))
+       return (-EINVAL);
+
+     i = flush_buffer(inode, file, 0);
+     if (i < 0)
+       return i;
+
+     i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct mtpos));
+     if (i)
+       return i;
+
+     SCpnt = allocate_device(NULL, STp->device, 1);
+
+     SCpnt->sense_buffer[0]=0;
+     memset (scmd, 0, 10);
+     if ((STp->device)->scsi_level < SCSI_2) {
+       scmd[0] = QFA_REQUEST_BLOCK;
+       scmd[4] = 3;
+     }
+     else {
+       scmd[0] = READ_POSITION;
+       scmd[1] = 1;
+     }
+     SCpnt->request.dev = dev;
+     SCpnt->sense_buffer[0] = 0;
+     scsi_do_cmd(SCpnt,
+		 (void *) scmd, (void *) (STp->buffer)->b_data,
+		 20, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES);
+
+
+	/* this must be done with interrupts off */
+	save_flags (flags);
+	cli();
+	if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+	restore_flags(flags);
+
+     
+     if ((STp->buffer)->last_result_fatal != 0) {
+       mt_pos.mt_blkno = (-1);
+#ifdef DEBUG
+       if (debugging)
+	 printk("st%d: Can't read tape position.\n", dev);
+#endif
+       result = (-EIO);
+     }
+     else {
+       result = 0;
+       if ((STp->device)->scsi_level < SCSI_2)
+	 mt_pos.mt_blkno = ((STp->buffer)->b_data[0] << 16) 
+	   + ((STp->buffer)->b_data[1] << 8) 
+	     + (STp->buffer)->b_data[2];
+       else
+	 mt_pos.mt_blkno = ((STp->buffer)->b_data[4] << 24)
+	   + ((STp->buffer)->b_data[5] << 16) 
+	     + ((STp->buffer)->b_data[6] << 8) 
+	       + (STp->buffer)->b_data[7];
+
+     }
+
+     SCpnt->request.dev = -1;  /* Mark as not busy */
+
+     memcpy_tofs((char *)arg, (char *) (&mt_pos), sizeof(struct mtpos));
+     return result;
+   }
+   else if (STp->ready == ST_READY)
+     return scsi_ioctl(STp->device, cmd_in, (void *) arg);
+   else
+     return (-EIO);
+}
+
+
+/* Set the boot options. Syntax: st=xxx,yyy
+   where xxx is buffer size in 512 byte blocks and yyy is write threshold
+   in 512 byte blocks. */
+	void
+st_setup(char *str, int *ints)
+{
+  if (ints[0] > 0 && ints[1] > 0)
+    st_buffer_size = ints[1] * ST_BLOCK_SIZE;
+  if (ints[0] > 1 && ints[2] > 0) {
+    st_write_threshold = ints[2] * ST_BLOCK_SIZE;
+    if (st_write_threshold > st_buffer_size)
+      st_write_threshold = st_buffer_size;
+  }
+  if (ints[0] > 2 && ints[3] > 0)
+    st_max_buffers = ints[3];
+}
+
+
+static struct file_operations st_fops = {
+   NULL,            /* lseek - default */
+   st_read,         /* read - general block-dev read */
+   st_write,        /* write - general block-dev write */
+   NULL,            /* readdir - bad */
+   NULL,            /* select */
+   st_ioctl,        /* ioctl */
+   NULL,            /* mmap */
+   scsi_tape_open,  /* open */
+   scsi_tape_close, /* release */
+   NULL		    /* fsync */
+};
+
+static int st_attach(Scsi_Device * SDp){
+   Scsi_Tape * tpnt;
+   int i;
+
+   if(SDp->type != TYPE_TAPE) return 1;
+
+   if(st_template.nr_dev >= st_template.dev_max) 
+     {
+     	SDp->attached--;
+     	return 1;
+     }
+
+   for(tpnt = scsi_tapes, i=0; i<st_template.dev_max; i++, tpnt++) 
+     if(!tpnt->device) break;
+
+   if(i >= st_template.dev_max) panic ("scsi_devices corrupt (st)");
+
+   scsi_tapes[i].device = SDp;
+   if (SDp->scsi_level <= 2)
+     scsi_tapes[i].mt_status->mt_type = MT_ISSCSI1;
+   else
+     scsi_tapes[i].mt_status->mt_type = MT_ISSCSI2;
+
+   st_template.nr_dev++;
+   return 0;
+};
+
+static int st_detect(Scsi_Device * SDp)
+{
+  if(SDp->type != TYPE_TAPE) return 0;
+
+  printk("Detected scsi tape st%d at scsi%d, id %d, lun %d\n", 
+	 st_template.dev_noticed++,
+	 SDp->host->host_no , SDp->id, SDp->lun); 
+  
+  return 1;
+}
+
+/* Driver initialization */
+static void st_init()
+{
+  int i;
+  Scsi_Tape * STp;
+  static int st_registered = 0;
+
+  if (st_template.dev_noticed == 0) return;
+
+  if(!st_registered) {
+    if (register_chrdev(MAJOR_NR,"st",&st_fops)) {
+      printk("Unable to get major %d for SCSI tapes\n",MAJOR_NR);
+      return;
+    }
+    st_registered++;
+  }
+
+  if (scsi_tapes) return;
+  scsi_tapes = (Scsi_Tape *) scsi_init_malloc(
+  		(st_template.dev_noticed + ST_EXTRA_DEVS) * 
+					      sizeof(Scsi_Tape), GFP_ATOMIC);
+  st_template.dev_max = st_template.dev_noticed + ST_EXTRA_DEVS;
+
+#ifdef DEBUG
+  printk("st: Buffer size %d bytes, write threshold %d bytes.\n",
+	 st_buffer_size, st_write_threshold);
+#endif
+
+  for (i=0; i < st_template.dev_max; ++i) {
+    STp = &(scsi_tapes[i]);
+    STp->device = NULL;
+    STp->capacity = 0xfffff;
+    STp->dirty = 0;
+    STp->rw = ST_IDLE;
+    STp->eof = ST_NOEOF;
+    STp->waiting = NULL;
+    STp->in_use = 0;
+    STp->drv_buffer = 1;  /* Try buffering if no mode sense */
+    STp->density = 0;
+    STp->do_buffer_writes = ST_BUFFER_WRITES;
+    STp->do_async_writes = ST_ASYNC_WRITES;
+    STp->do_read_ahead = ST_READ_AHEAD;
+    STp->two_fm = ST_TWO_FM;
+    STp->fast_mteom = ST_FAST_MTEOM;
+    STp->write_threshold = st_write_threshold;
+    STp->drv_block = 0;
+    STp->moves_after_eof = 1;
+    STp->at_sm = 0;
+    STp->mt_status = (struct mtget *) scsi_init_malloc(sizeof(struct mtget), GFP_ATOMIC);
+    /* Initialize status */
+    memset((void *) scsi_tapes[i].mt_status, 0, sizeof(struct mtget));
+  }
+
+  /* Allocate the buffers */
+  st_nbr_buffers = st_template.dev_noticed + ST_EXTRA_DEVS;
+  if (st_nbr_buffers > st_max_buffers)
+    st_nbr_buffers = st_max_buffers;
+  st_buffers = (ST_buffer **) scsi_init_malloc(st_nbr_buffers * 
+					       sizeof(ST_buffer *), GFP_ATOMIC);
+  /* FIXME - if we are hitting this because we are loading a tape module
+  as a loadable driver, we should not use kmalloc - it will allocate
+  a 64Kb region in order to buffer about 32Kb.  Try using 31 blocks
+  instead. */
+  
+  for (i=0; i < st_nbr_buffers; i++) {
+    st_buffers[i] = (ST_buffer *) scsi_init_malloc(sizeof(ST_buffer) - 
+						   1 + st_buffer_size, GFP_ATOMIC | GFP_DMA);
+#ifdef DEBUG
+/*    printk("st: Buffer address: %p\n", st_buffers[i]); */
+#endif
+    st_buffers[i]->in_use = 0;
+    st_buffers[i]->writing = 0;
+  }
+  return;
+}
+
+static void st_detach(Scsi_Device * SDp)
+{
+  Scsi_Tape * tpnt;
+  int i;
+  
+  for(tpnt = scsi_tapes, i=0; i<st_template.dev_max; i++, tpnt++) 
+    if(tpnt->device == SDp) {
+      tpnt->device = NULL;
+      SDp->attached--;
+      st_template.nr_dev--;
+      st_template.dev_noticed--;
+      return;
+    }
+  return;
+}
diff -r -u -N linux.orig/arch/arm/drivers/scsi/st.h linux.arm/arch/arm/drivers/scsi/st.h
--- linux.orig/arch/arm/drivers/scsi/st.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/scsi/st.h	Fri Oct 27 23:15:15 1995
@@ -0,0 +1,78 @@
+
+#ifndef _ST_H
+	#define _ST_H
+/*
+	$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/st.h,v 1.1 1992/04/24 18:01:50 root Exp root $
+*/
+
+#ifndef _SCSI_H
+#include "scsi.h"
+#endif
+
+typedef struct {
+  int in_use;
+  int buffer_size;
+  int buffer_blocks;
+  int buffer_bytes;
+  int read_pointer;
+  int writing;
+  int last_result;
+  int last_result_fatal;
+  unsigned char b_data[1];
+} ST_buffer;
+
+typedef struct {
+  unsigned capacity;
+  struct wait_queue * waiting;
+  Scsi_Device* device;
+  unsigned char dirty;
+  unsigned char rw;
+  unsigned char ready;
+  unsigned char eof;
+  unsigned char write_prot;
+  unsigned char drv_write_prot;
+  unsigned char in_use;
+  unsigned char eof_hit;
+  unsigned char drv_buffer;
+  unsigned char do_buffer_writes;
+  unsigned char do_async_writes;
+  unsigned char do_read_ahead;
+  unsigned char two_fm;
+  unsigned char fast_mteom;
+  unsigned char density;
+  ST_buffer * buffer;
+  int block_size;
+  int min_block;
+  int max_block;
+  int write_threshold;
+  int recover_count;
+  int drv_block;	/* The block where the drive head is */
+  unsigned char moves_after_eof;
+  unsigned char at_sm;
+  struct mtget * mt_status;
+  Scsi_Cmnd SCpnt;
+} Scsi_Tape;
+
+/* Values of eof */
+#define	ST_NOEOF	0
+#define	ST_FM		1
+#define	ST_EOM_OK	2
+#define ST_EOM_ERROR	3
+#define ST_EOD		4
+
+/* Values of rw */
+#define	ST_IDLE		0
+#define	ST_READING	1
+#define	ST_WRITING	2
+
+/* Values of ready state */
+#define ST_READY	0
+#define ST_NOT_READY	1
+#define ST_NO_TAPE	2
+
+/* Positioning SCSI-commands for Tandberg, etc. drives */
+#define	QFA_REQUEST_BLOCK	0x02
+#define	QFA_SEEK_BLOCK		0x0c
+
+#endif
+
diff -r -u -N linux.orig/arch/arm/drivers/sound/Makefile linux.arm/arch/arm/drivers/sound/Makefile
--- linux.orig/arch/arm/drivers/sound/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/Makefile	Fri Oct 27 23:14:56 1995
@@ -0,0 +1,50 @@
+
+.c.s:
+	$(CC) $(CFLAGS) -S $<
+.s.o:
+	$(AS) -c -o $*.o $<
+.c.o:
+	$(CC) $(CFLAGS) -c $<
+.S.o:
+	$(CC) $(CFLAGS) -c $<
+
+
+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
+
+SRCS	:=soundcont.c soundinit.c voice.c voice_samp.c voice_wave.c
+
+OBJS	:= $(OBJS) audio.o
+SRCS	:= $(SRCS) audio.c
+
+OBJS	:= $(OBJS) arcaudio.o
+SRCS	:= $(SRCS) arcaudio.c
+
+OBJS	:= $(OBJS) mkbeep.o
+SRCS	:= $(SRCS) mkbeep.c
+
+OBJS	:= $(OBJS) debug.o
+SRCS	:= $(SRCS) debug.c
+
+
+all: sound.a
+
+sound.a: $(OBJS)
+	rm -f sound.a
+	$(AR) rcs sound.a $(OBJS)
+	sync
+
+dep:
+	$(CPP) -M $(SRCS) > .depend
+
+modules: $(BLOCK_MODULE_OBJS)
+	echo $(SOUND_MODULE_OBJS) > ../../../../modules/SOUND_MODULES
+	(cd ../../../../modules;for i in $(SOUND_MODULE_OBJS); do ln -sf ../arch/arm/drivers/sound/$$i .; done)
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
+
diff -r -u -N linux.orig/arch/arm/drivers/sound/arcaudio.c linux.arm/arch/arm/drivers/sound/arcaudio.c
--- linux.orig/arch/arm/drivers/sound/arcaudio.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/arcaudio.c	Fri Oct 27 23:14:54 1995
@@ -0,0 +1,234 @@
+/* 
+ * arcaudio.c
+ *
+ * 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, char *buf, int nr)
+{
+	int retval=0,c;
+	struct voice_driver *vd=(struct voice_driver *)filp->private_data;
+	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 -r -u -N linux.orig/arch/arm/drivers/sound/arm2unix linux.arm/arch/arm/drivers/sound/arm2unix
--- linux.orig/arch/arm/drivers/sound/arm2unix	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/arm2unix	Fri Oct 27 23:14:57 1995
@@ -0,0 +1,60 @@
+#!/usr/bin/sed -f
+# Convert ARM RiscOS assembler to gas assembler
+# (c) 1995 Russell King
+#
+# Any tabs in this file are supposed to be there!
+#
+# Although LDR rx,=n is converted, the associated data for n is not.
+#  These must be added by hand.
+# ADRLs cannot be converted either, but are converted to ***'s
+
+# Comments
+s/^;/@/
+s/\([ 	]*\);/\1@/
+
+# Convert labels
+s/^[0-9A-Za-z_][0-9A-Za-z_]*$/_&:/
+
+# Convert branches
+s/[ 	][ 	]*\(B[A-Za-z]\{0,3\}\)[ 	][ ]*\([^,][^,]*\)$/		\1	_\2/
+
+# adrls
+s/[ 	][ 	]*ADRL\([A-Za-z][A-Za-z]\)[ 	][ ]*\([0-9A-Za-z_]\{1,3\}\),\(..*\)/		add\1	\2,pc,#(\3 - . - 8) \& ~4095 ;		***\1	\2,\2,#((\3 - . - 4) \& 4095)]/
+s/[ 	][ 	]*ADRL[ 	][ ]*\([0-9A-Za-z_]\{1,3\}\),\(..*\)/		add	\1,pc,#(\2 - . - 8) \& ~4095 ;		***	\1,\1,#((\2 - . - 4) \& 4095)]/
+#s/[ 	][ 	]*ADR[^L]\([A-Za-z]\{0,2\}\)[ 	][ ]*\([0-9A-Za-z_]\{1,3\}\),\(..*\)/		add\1	\2,pc,#(\3 - . - 8)/
+
+# LDRxx	xxx,=xxxx
+#     1  2    3
+s/[ 	][ 	]*LDR\([A-Za-z]\{0,2\}T*\)[ 	][ ]*\([0-9A-Za-z_]\{1,3\}\),=\(..*\)/		sub\1	\2,pc,#(8 + . -LC\3) \& ~4095 ;		ldr\1	\2,[\2,#-((4 + . - LC\3) \& 4095)]/
+
+# Convert aligns
+s/ALIGN/.align/
+
+# Exports
+s/EXPORT[ 	][ ]*\(.*\)/.global	_\1/
+
+# Imports
+/[ 	][ 	]*IMPORT/d
+
+# DCDs, DCBs, strings, %s etc
+s/[ 	][ 	]*DCD[ 	][ ]*/		.word	/
+s/[ 	][ 	]*DCB[ 	][ ]*/		.byte	/
+s/[ 	][ 	]*=[ 	][ ]*/		.ascii	/
+s/[ 	][ 	]*%[ 	][ ]*/		.space	/
+
+# Convert hex numbers
+s/\&\([0-9a-fA-F][0-9a-fA-F]*\)/0x\1/g
+
+# Non-standard register names
+s/ws/r12/g
+
+# Convert shifts
+s/LSR/ lsr /g
+s/ASR/ asr /g
+s/LSL/ lsl /g
+s/ROR/ ror /g
+s/ROL/ rol /g
+s/RRX/ rrx #0/g
+
+# Data refs
+s/,Da/,_Da/
diff -r -u -N linux.orig/arch/arm/drivers/sound/audio.c linux.arm/arch/arm/drivers/sound/audio.c
--- linux.orig/arch/arm/drivers/sound/audio.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/audio.c	Fri Oct 27 23:14:54 1995
@@ -0,0 +1,137 @@
+/*
+ * 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"
+
+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, char *buf, int nr)
+{
+	int retval=0,c;
+	struct voice_driver *vd=(struct voice_driver *)filp->private_data;
+	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 -r -u -N linux.orig/arch/arm/drivers/sound/config.h linux.arm/arch/arm/drivers/sound/config.h
--- linux.orig/arch/arm/drivers/sound/config.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/config.h	Fri Oct 27 23:14:55 1995
@@ -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 -r -u -N linux.orig/arch/arm/drivers/sound/debug.c linux.arm/arch/arm/drivers/sound/debug.c
--- linux.orig/arch/arm/drivers/sound/debug.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/debug.c	Fri Oct 27 23:14:54 1995
@@ -0,0 +1,66 @@
+/*
+ * debug.c
+ *
+ * Show debugging information
+ *
+ */
+
+#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"
+
+
+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 -r -u -N linux.orig/arch/arm/drivers/sound/mkbeep.c linux.arm/arch/arm/drivers/sound/mkbeep.c
--- linux.orig/arch/arm/drivers/sound/mkbeep.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/mkbeep.c	Fri Oct 27 23:14:54 1995
@@ -0,0 +1,34 @@
+#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 = 0x640;/* freq; */
+	driver->vd_volume= vol;
+
+	voice_install(driver, 0);
+	return 0;
+}
diff -r -u -N linux.orig/arch/arm/drivers/sound/sndvars.S linux.arm/arch/arm/drivers/sound/sndvars.S
--- linux.orig/arch/arm/drivers/sound/sndvars.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/sndvars.S	Fri Oct 27 23:14:56 1995
@@ -0,0 +1,51 @@
+@ linux/arch/arm/drivers/sound/sndvars.S
+@
+@ Absolute variable locations in the sound system
+@
+@ Map is:
+@   0x1000 - 0x1FFF	=	sound buffer 0
+@   0x2000 - 0x2FFF	=	sound buffer 1
+@
+
+#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 -r -u -N linux.orig/arch/arm/drivers/sound/soundcont.c linux.arm/arch/arm/drivers/sound/soundcont.c
--- linux.orig/arch/arm/drivers/sound/soundcont.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/soundcont.c	Fri Oct 27 23:14:54 1995
@@ -0,0 +1,148 @@
+/*
+ * soundcont.c
+ *
+ * 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 -r -u -N linux.orig/arch/arm/drivers/sound/soundcont.h linux.arm/arch/arm/drivers/sound/soundcont.h
--- linux.orig/arch/arm/drivers/sound/soundcont.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/soundcont.h	Fri Oct 27 23:14:55 1995
@@ -0,0 +1,35 @@
+/*
+ * 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);
+
+#endif
diff -r -u -N linux.orig/arch/arm/drivers/sound/soundinit.c linux.arm/arch/arm/drivers/sound/soundinit.c
--- linux.orig/arch/arm/drivers/sound/soundinit.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/soundinit.c	Fri Oct 27 23:14:54 1995
@@ -0,0 +1,132 @@
+/*
+ * soundinit.c
+ *
+ * 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/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);
+
+struct sound_ops snddevs[MAX_OPS];
+
+int sound_setup(void)
+{
+}
+
+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 -r -u -N linux.orig/arch/arm/drivers/sound/soundinit.h linux.arm/arch/arm/drivers/sound/soundinit.h
--- linux.orig/arch/arm/drivers/sound/soundinit.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/soundinit.h	Fri Oct 27 23:14:55 1995
@@ -0,0 +1,66 @@
+/*
+ * 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
+
+#endif
diff -r -u -N linux.orig/arch/arm/drivers/sound/soundirq.S linux.arm/arch/arm/drivers/sound/soundirq.S
--- linux.orig/arch/arm/drivers/sound/soundirq.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/soundirq.S	Fri Oct 27 23:14:56 1995
@@ -0,0 +1,252 @@
+@ soundirq.s
+@
+@ 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 -r -u -N linux.orig/arch/arm/drivers/sound/soundirq.h linux.arm/arch/arm/drivers/sound/soundirq.h
--- linux.orig/arch/arm/drivers/sound/soundirq.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/soundirq.h	Fri Oct 27 23:14:55 1995
@@ -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(void);
+
+/*
+ * 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 -r -u -N linux.orig/arch/arm/drivers/sound/v_drv_sam.S linux.arm/arch/arm/drivers/sound/v_drv_sam.S
--- linux.orig/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	Fri Oct 27 23:14:56 1995
@@ -0,0 +1,106 @@
+@
+@ voice_drv.s
+@
+@ Voice low level filler.
+@
+@ Takes data out of the voice buffer, translates it,
+@ and puts it in the output buffer.
+@
+@ This is quite general, and can be used for samples and wavetables.
+@
+@ Just create the wave table in the conversion table in vidc
+@ output format, and use the sample buffer to hold the indexes (ramp).
+@
+
+		.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 -r -u -N linux.orig/arch/arm/drivers/sound/v_drv_wave.S linux.arm/arch/arm/drivers/sound/v_drv_wave.S
--- linux.orig/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	Fri Oct 27 23:14:56 1995
@@ -0,0 +1,63 @@
+@
+@ v_drv_wave.s
+@
+@ 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 -r -u -N linux.orig/arch/arm/drivers/sound/voice.c linux.arm/arch/arm/drivers/sound/voice.c
--- linux.orig/arch/arm/drivers/sound/voice.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/voice.c	Fri Oct 27 23:14:54 1995
@@ -0,0 +1,175 @@
+/*
+ * voice.c
+ *
+ * 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 -r -u -N linux.orig/arch/arm/drivers/sound/voice.h linux.arm/arch/arm/drivers/sound/voice.h
--- linux.orig/arch/arm/drivers/sound/voice.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/voice.h	Fri Oct 27 23:14:55 1995
@@ -0,0 +1,85 @@
+/*
+ * 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, 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);
+
+/*
+ * 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);
+
+#endif
diff -r -u -N linux.orig/arch/arm/drivers/sound/voice_samp.c linux.arm/arch/arm/drivers/sound/voice_samp.c
--- linux.orig/arch/arm/drivers/sound/voice_samp.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/voice_samp.c	Fri Oct 27 23:14:54 1995
@@ -0,0 +1,81 @@
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include "soundinit.h"
+#include "soundirq.h"
+#include "voice.h"
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+static int voice_sample_write(struct voice_driver *vd, 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 -r -u -N linux.orig/arch/arm/drivers/sound/voice_wave.c linux.arm/arch/arm/drivers/sound/voice_wave.c
--- linux.orig/arch/arm/drivers/sound/voice_wave.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/drivers/sound/voice_wave.c	Fri Oct 27 23:14:55 1995
@@ -0,0 +1,126 @@
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include "soundinit.h"
+#include "soundirq.h"
+#include "voice.h"
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+static int voice_wave_write(struct voice_driver *vd, 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 -r -u -N linux.orig/arch/arm/kernel/Makefile linux.arm/arch/arm/kernel/Makefile
--- linux.orig/arch/arm/kernel/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/Makefile	Fri Oct 27 23:14:16 1995
@@ -0,0 +1,45 @@
+#
+# 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.s:
+	$(CPP) -traditional $< -o $*.s
+.c.s:
+	$(CC) $(CFLAGS) -S $<
+.s.o:
+	$(AS) -o $*.o $<
+.c.o:
+	$(CC) $(CFLAGS) -c $<
+
+OBJS  = arm.o ioport.o irq.o ptrace.o signal.o traps.o setup.o process.o ecard.o
+
+all: kernel.o head.o
+
+head.o: head.s
+
+head.s: head.S $(TOPDIR)/include/linux/tasks.h
+	$(CPP) -traditional -o $*.s $<
+
+kernel.o: $(OBJS)
+	$(LD) -r -o kernel.o $(OBJS)
+#	stripper kernel.o kernel2.o
+	sync
+
+dep:
+	$(CPP) -M *.c > .depend
+
+dummy:
+modules:
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
+
diff -r -u -N linux.orig/arch/arm/kernel/arm.c linux.arm/arch/arm/kernel/arm.c
--- linux.orig/arch/arm/kernel/arm.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/arm.c	Fri Oct 27 23:14:15 1995
@@ -0,0 +1,21 @@
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+
+void arm_syscall(int no, unsigned long *regs)
+{
+	int i;
+	printk("arm_syscall: %d (lr=%08lX, pc=%08lX)\n", no, regs[14], regs[15]);
+	printk("regs:\n");
+	for(i=0; i<14; i++)
+		printk("%2d = %08lX\n",i, regs[i]);
+	printk("Task: %s (%d)\n", current->comm, current->pid);
+	send_sig(SIGSEGV, current, 1);
+}
+
+void deferred(int n)
+{
+	printk("%s: deferred system call %X\n", current->comm, n);
+}
+
diff -r -u -N linux.orig/arch/arm/kernel/ecard.c linux.arm/arch/arm/kernel/ecard.c
--- linux.orig/arch/arm/kernel/ecard.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/ecard.c	Fri Oct 27 23:14:19 1995
@@ -0,0 +1,263 @@
+/*
+ * linux/arch/arm/kernel/ecard.c
+ *
+ * Find all installed podules
+ *
+ * Copyright 1995 Russell King
+ *
+ * Information from Acorns RiscOS3 PRMs
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <asm/irq-no.h>
+#include <asm/ecard.h>
+
+/*
+ * from linux/arch/arm/kernel/irq.c
+ */
+extern void do_fast_IRQ(int irq);
+
+#define MAX_ECARDS 4
+
+#define expmask ((volatile unsigned char *)0x03360000)
+
+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(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 &&
+			(have_expmask==0 || (expmask[0] & 1<<i)))
+		{
+			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 podule %d\n", i);
+			}
+			else
+				do_fast_IRQ(expcard[i].irq);
+		}
+	}
+}
+
+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, expansioncard_irq, 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 *)(0x033C0000 + 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: 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 -r -u -N linux.orig/arch/arm/kernel/head.S linux.arm/arch/arm/kernel/head.S
--- linux.orig/arch/arm/kernel/head.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/head.S	Fri Oct 27 23:14:18 1995
@@ -0,0 +1,32 @@
+		.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
+		.align
+
+		.global	__entry
+__entry:	
+		ldr	r3, [pc, #LC0 - . - 8]
+		ldr	r4, [pc, #LC1 - . - 8]
+		mov	r2, #0
+clrzilp:	cmp	r3, r4
+		strcc	r2, [r3],#4
+		bcc	clrzilp
+		mov	fp, #0
+		mov	sl, r4
+		ldr	r3, [pc, #LC2 - . - 8]
+		str	r0, [r3]
+		b	_start_kernel
+
diff -r -u -N linux.orig/arch/arm/kernel/ioport.c linux.arm/arch/arm/kernel/ioport.c
--- linux.orig/arch/arm/kernel/ioport.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/ioport.c	Fri Oct 27 23:14:15 1995
@@ -0,0 +1,96 @@
+/*
+ *	linux/kernel/ioport.c
+ *
+ * This contains the io-permission bitmap code - written by obz, with changes
+ * by Linus.
+ */
+
+#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 -r -u -N linux.orig/arch/arm/kernel/irq.c linux.arm/arch/arm/kernel/irq.c
--- linux.orig/arch/arm/kernel/irq.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/irq.c	Fri Oct 27 23:14:16 1995
@@ -0,0 +1,327 @@
+/*
+ *	linux/kernel/irq.c
+ *
+ *	Copyright (C) 1992 Linus Torvalds
+ *
+ * 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.
+ * The same sigaction struct is used, and with similar semantics (ie there
+ * is a SA_INTERRUPT flag etc). Naturally it's not a 1:1 relation, but there
+ * are similarities.
+ *
+ * sa_handler(int irq_NR) is the default function called (0 if no).
+ * sa_mask is horribly ugly (I won't even mention it)
+ * sa_flags contains various info: SA_INTERRUPT etc
+ * sa_restorer is the unused
+ */
+
+#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 <asm/system.h>
+
+extern char *ioc;
+unsigned long enabled_irqs=0;
+
+void disable_irq(unsigned int irq_nr)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	enabled_irqs&=~(1<<irq_nr);
+	if(!intr_count)
+	{
+	  ioc[0x18]=(unsigned char)(enabled_irqs & 0xff);
+	  ioc[0x28]=(unsigned char)((enabled_irqs >> 8) & 0xff);
+	}
+	ioc[0x38]=(unsigned char)((enabled_irqs >> 16) & 0xff);
+	restore_flags(flags);
+}
+
+void enable_irq(unsigned int irq_nr)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	enabled_irqs|=1<<irq_nr;
+	if(!intr_count)
+	{
+	  ioc[0x18]=(unsigned char)(enabled_irqs & 0xff);
+	  ioc[0x28]=(unsigned char)((enabled_irqs >> 8) & 0xff);
+	}
+	ioc[0x38]=(unsigned char)((enabled_irqs >> 16) & 0xff);
+	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]++;
+	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]++;
+	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 int probe_irq_on (void)
+{
+	return 0;
+}
+
+int probe_irq_off (unsigned int 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 -r -u -N linux.orig/arch/arm/kernel/process.c linux.arm/arch/arm/kernel/process.c
--- linux.orig/arch/arm/kernel/process.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/process.c	Fri Oct 27 23:14:19 1995
@@ -0,0 +1,201 @@
+/*
+ *  linux/arch/i386/kernel/process.c
+ *
+ *  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 a i386..
+ */
+asmlinkage int sys_idle(void)
+{
+	if (current->pid != 0)
+		return -EPERM;
+
+	/* endless idle loop with no priority at all */
+	current->counter = -100;
+	for (;;)
+		schedule();
+}
+
+/*
+ * This routine reboots the machine by asking the keyboard
+ * controller to pulse the reset-line low. We try that for a while,
+ * and if it doesn't work, we do some other stupid things.
+ */
+
+/* --------------------------------------------------------------------------------------- *
+ * Reset										   *
+ * --------------------------------------------------------------------------------------- */
+
+void hard_reset_now(void)
+{	/* 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");
+#if 0
+	printk("PC : %08lx",0xffff & regs->cs,regs->eip);
+	if (regs->cs & 3)
+		printk(" ESP: %04x:%08lx",0xffff & regs->ss,regs->esp);
+	printk(" EFLAGS: %08lx\n",regs->eflags);
+	printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
+		regs->eax,regs->ebx,regs->ecx,regs->edx);
+	printk("ESI: %08lx EDI: %08lx EBP: %08lx",
+		regs->esi, regs->edi, regs->ebp);
+	printk(" DS: %04x ES: %04x FS: %04x GS: %04x\n",
+		0xffff & regs->ds,0xffff & regs->es,
+		0xffff & regs->fs,0xffff & regs->gs);
+#endif
+}
+
+/*
+ * 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 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->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->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;
+
+/* Flag indicating the math stuff is valid. We don't support this for the
+   soft-float routines yet */
+#if 0
+	if (hard_math) {
+		if ((dump->u_fpvalid = current->used_math) != 0) {
+			if (last_task_used_math == current)
+				__asm__("clts ; fnsave %0": :"m" (dump->i387));
+			else
+				memcpy(&dump->i387,&current->tss.i387.hard,sizeof(dump->i387));
+		}
+	} else
+#endif
+	 {
+		/* we should dump the emulator state here, but we need to
+		   convert it into standard 387 format first.. */
+		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(COPYVM | SIGCHLD, regs->SP, regs);
+}
+
+asmlinkage int sys_clone(struct pt_regs regs)
+{
+#ifdef CLONE_ACTUALLY_WORKS_OK
+	unsigned long clone_flags;
+	unsigned long newsp;
+
+	newsp = regs.ebx;
+	clone_flags = regs.ecx;
+	if (!newsp)
+		newsp = regs.esp;
+	if (newsp == regs.esp)
+		clone_flags |= COPYVM;
+	return do_fork(clone_flags, newsp, &regs);
+#else
+	return -ENOSYS;
+#endif
+}
+
+/*
+ * sys_execve() executes a new program.
+ */
+asmlinkage int sys_execve(char *filenamei, char **argv, char **envp)
+{
+	int error;
+	char * filename;
+	struct pt_regs *regs;
+	__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 -r -u -N linux.orig/arch/arm/kernel/ptrace.c linux.arm/arch/arm/kernel/ptrace.c
--- linux.orig/arch/arm/kernel/ptrace.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/ptrace.c	Fri Oct 27 23:14:16 1995
@@ -0,0 +1,543 @@
+#define THREE_LEVEL
+/* ptrace.c */
+/* By Ross Biro 1/23/92 */
+/* edited by Linus Torvalds */
+
+#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->tss.sregs[13];
+	stack += offset;
+	return (*((long *)stack));
+}
+
+/*
+ * same as put_stack_long
+ */
+static inline long put_stack_long(struct task_struct *task, int offset,
+	unsigned long data)
+{
+	unsigned char *stack;
+
+	stack = (unsigned char *) task->tss.sregs[13];
+	stack += offset;
+	return (*((long *)stack));
+}
+
+/*
+ * same as get_long
+ */
+static unsigned long get_long(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_task, addr);
+	if (pgd_none(*pgdir)) {
+		do_no_page(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(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(vma, addr, 0);
+		goto repeat;
+	}
+	page = pte_page(*pgtable);
+
+	if(page >= high_memory)
+		return 0;
+	page += addr & ~PAGE_MASK;
+
+	return *(unsigned long *)page;
+}
+
+/*
+ * same as put_long
+ */
+static void put_long(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_task, addr);
+	if (!pgd_present(*pgdir)) {
+		do_no_page(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(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(vma, addr, 1);
+		goto repeat;
+	}
+	page = pte_page(*pgtable);
+	if (!pte_write(*pgtable)) {
+		do_wp_page(vma, addr, 1);
+		goto repeat;
+	}
+	
+	if (page < high_memory) {
+		page += addr & ~PAGE_MASK;
+		*(unsigned long *) page = data;
+	}
+	*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;
+	for (vma = tsk->mm->mmap ; ; vma = vma->vm_next) {
+		if (!vma)
+			return NULL;
+		if (vma->vm_end > addr)
+			break;
+	}
+	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(vma, addr & ~(sizeof(long)-1));
+		high = get_long(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(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(vma, addr & ~(sizeof(long)-1));
+		high = get_long(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(vma, addr & ~(sizeof(long)-1),low);
+		put_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1),high);
+	} else
+		put_long(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;
+		}
+
+#if 0
+	/* 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, sizeof(long)*addr - MAGICNUMBER);
+			  if (addr == DS || addr == ES ||
+			      addr == FS || addr == GS ||
+			      addr == CS || addr == SS)
+			    tmp &= 0xffff;
+			};
+			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;
+		}
+#endif
+      /* 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. */
+/*			long tmp;*/
+
+			if ((unsigned long) data > NSIG)
+				return -EIO;
+			if (request == PTRACE_SYSCALL)
+				child->flags |= PF_TRACESYS;
+			else
+				child->flags &= ~PF_TRACESYS;
+			child->exit_code = data;
+			child->state = TASK_RUNNING;
+#if 0
+	/* make sure the single step bit is not set. */
+			tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;
+			put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
+#endif
+			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;*/
+
+			child->state = TASK_RUNNING;
+			child->exit_code = SIGKILL;
+#if 0
+	/* make sure the single step bit is not set. */
+			tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;
+			put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
+#endif
+			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);
+			child->state = TASK_RUNNING;
+			child->exit_code = data;
+			REMOVE_LINKS(child);
+			child->p_pptr = child->p_opptr;
+			SET_LINKS(child);
+			/* make sure the single step bit is not set. */
+#if 0
+			tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;
+			put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);
+#endif
+			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 -r -u -N linux.orig/arch/arm/kernel/setup.c linux.arm/arch/arm/kernel/setup.c
--- linux.orig/arch/arm/kernel/setup.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/setup.c	Fri Oct 27 23:14:18 1995
@@ -0,0 +1,154 @@
+/*
+ *  linux/arch/arm/kernel/setup.c
+ *
+ *  Copyright (C) 1995  Russell King
+ */
+
+/*
+ * 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 <linux/tty.h>
+#include <linux/ioport.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+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
+};
+
+extern int bytes_per_char_h;
+extern int bytes_per_char_v;
+extern char arc_hd_files[8][128];
+extern int no_hds;
+int memc_ctrl_reg;
+
+unsigned char aux_device_present;
+extern int ramdisk_size;
+extern int root_mountflags;
+extern int etext, edata, end;
+
+extern char *empty_zero_page;
+
+#define COMMAND_LINE_SIZE 256
+
+static char command_line[COMMAND_LINE_SIZE] = { 0, };
+
+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];		/**/
+};
+
+struct param_struct *params;
+
+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;
+	
+	memory_start = (unsigned long)params - 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 = 2;
+
+	aux_device_present = 0;
+	memory_end = 0x02000000 + params->page_size * params->nr_pages;
+	ramdisk_size = params->ramdisk_size;
+
+#ifdef CONFIG_MAX_16M
+	if (memory_end > 16*1024*1024)
+		memory_end = 16*1024*1024;
+#endif
+	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 0
+		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++;
+			}
+		}
+#endif
+		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;
+#if 0
+	/* request io space for devices used on all i[345]86 PC'S */
+	request_region(0x00,0x20,"dma1");
+	request_region(0x40,0x20,"timer");
+	request_region(0x70,0x10,"rtc");
+	request_region(0x80,0x20,"dma page reg");
+	request_region(0xc0,0x20,"dma2");
+	request_region(0xf0,0x2,"npu");
+	request_region(0xf8,0x8,"npu");
+#endif
+}
diff -r -u -N linux.orig/arch/arm/kernel/signal.c linux.arm/arch/arm/kernel/signal.c
--- linux.orig/arch/arm/kernel/signal.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/signal.c	Fri Oct 27 23:14:16 1995
@@ -0,0 +1,259 @@
+/*
+ *  linux/arch/arm/kernel/signal.c
+ *
+ *  Copyright (C) 1995 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)))
+
+asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options);
+
+/*
+ * 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->R0 = -EINTR;
+	while (1) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule();
+		if (do_signal(mask,regs))
+			return regs->R0; /* rmk */
+	}
+}
+
+/*
+ * 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->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->R0;
+badframe:
+	do_exit(SIGSEGV);
+	return 0;
+}
+
+/*
+ * Set up a signal frame... Make the stack look the way iBCS2 expects
+ * it to look.
+ */
+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));
+
+	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));
+
+	*fp=frame;
+	if(current->exec_domain && current->exec_domain->signal_invmap)
+		regs->R0=current->exec_domain->signal_invmap[signr];
+	else
+		regs->R0=signr;
+	regs->LR=0x00000020; /* Return address for signals (SWI SYS_sigreturn) */
+}
+
+/*
+ * 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->PC & 0x03fffffc)-4);
+	int swiinstr;
+
+	if (pcinstr < LOGICAL_END && (*((unsigned long *)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->sigaction + 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->sigaction + 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->sigaction[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:
+				printk("CORE: %s\n",
+					signr==SIGBUS ?"SIGBUS":
+					signr==SIGQUIT?"SIGQUIT":
+					signr==SIGILL ?"SIGILL":
+					signr==SIGTRAP?"SIGTRAP":
+					signr==SIGIOT ?"SIGIOT":
+					signr==SIGFPE ?"SIGFPE":
+					signr==SIGSEGV?"SIGSEGV":"unknown");
+				register_dump(regs);
+				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->R0 == -ERESTARTNOHAND ||
+			   (regs->R0 == -ERESTARTSYS && !(sa->sa_flags & SA_RESTART)))
+			   	regs->R0 = -EINTR;
+		}
+		handler_signal |= 1 << (signr-1);
+		mask &= ~sa->sa_mask;
+	}
+	if (swiinstr &&
+	    (regs->R0 == -ERESTARTNOHAND ||
+	     regs->R0 == -ERESTARTSYS ||
+	     regs->R0 == -ERESTARTNOINTR)) {
+		regs->R0 = regs->ORIG_R0;
+		regs->PC -= 4;
+	}
+	if (!handler_signal)		/* no handler will be called - return 0 */
+		return 0;
+	eip = regs->PC;
+	frame = (unsigned long *) regs->SP;
+	signr = 1;
+	sa = current->sigaction;
+	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->SP = (unsigned long) frame;
+	regs->PC = eip;		/* "return" to the first handler */
+	current->tss.trap_no = current->tss.error_code = 0;
+	return 1;
+}
diff -r -u -N linux.orig/arch/arm/kernel/traps.c linux.arm/arch/arm/kernel/traps.c
--- linux.orig/arch/arm/kernel/traps.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/kernel/traps.c	Fri Oct 27 23:14:16 1995
@@ -0,0 +1,154 @@
+#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->PC & ~0xFC000003;
+	flags = regs->PC & 0xFC000003;
+
+	printk("pc : %08lX  lr : %08lX  sp : %08lX  ip : %08lX\n",
+			pc, regs->LR, regs->SP, regs->FP);
+	printk("fp : %08lX  r10: %08lX  r9 : %08lX  r8 : %08lX\n",
+			regs->IP, regs->R10, regs->R9, regs->R8);
+	printk("r7 : %08lX  r6 : %08lX  r5 : %08lX  r4 : %08lX\n",
+			regs->R7, regs->R6, regs->R5, regs->R4);
+	printk("r3 : %08lX  r2 : %08lX  r1 : %08lX  r0 : %08lX\n",
+			regs->R3, regs->R2, regs->R1, regs->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]);
+}
+
+void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret)
+{
+	static int died=0;
+	int i, j, mode, pc;
+	unsigned long cstack, sstack;
+	
+	if(died)
+		while(1);
+	died = 1;
+
+	console_verbose();
+
+	printk("===============================================================\n");
+	printk("Internal error: %s: %04x\n", str, err & 0xffff);
+	
+	register_dump(regs);
+	mode = regs->PC & 3;
+	pc = regs->PC & ~0xfc000003;
+
+	if(mode == 3)
+		cstack = (unsigned long)(regs + 1);
+	else
+	if(mode == 0)
+		cstack = (unsigned long)regs->SP;
+
+	sstack = (unsigned long)current->kernel_stack_page;
+
+	printk("Process %s (pid: %d, process nr: %d, stackpage=%08lx)\n",
+		current->comm, current->pid, i, current->kernel_stack_page);
+
+	if(STACK_MAGIC != *(unsigned long *)sstack)
+		printk("*** Corrupted stack page\n");
+
+	printk("Stack: ");
+
+	if (mode != 0)
+	{
+		if ((cstack < 0x02048000 || cstack > 0x04000000) && current->pid)
+			printk("%08lx invalid kernel stack\n", cstack);
+		else
+		{
+			if(cstack > sstack + 4096)
+				printk("(sp overflow)\n       ");
+			else
+			if(cstack < sstack)
+				printk("(sp underflow)\n       ");
+			for (i = cstack - sstack, j = 0; i < 4096 && j < kstack_depth_to_print; j++, i+=4)
+			{
+				if ((j & 7) == 0 && j)
+					printk("\n       ");
+				printk("%08lx ", ((unsigned long *)sstack)[i>>2]);
+			}
+			printk("\n");
+		}
+	}
+	else
+	{
+		if (cstack >= 0x01800000 || verify_area (VERIFY_READ, cstack, 0x1800000 - cstack))
+			printk ("%08lx invalid user stack\n", cstack);
+		else
+		{
+ 			for (i = 0, j = 0; j < kstack_depth_to_print; j++, i++)
+			{
+				if ((j & 7) == 0 && j)
+					printk ("\n       ");
+				printk("%08lx ", ((unsigned long *)cstack)[i]);
+			}
+			printk("\n");
+		}
+	}
+
+	printk("Code: ");
+
+	for(i=-2; i<3; i++)
+		printk("%08lx ", ((unsigned long *)pc)[i]);
+
+	printk("\n");
+
+	printk("===============================================================\n");
+
+	died = 0;
+
+	if(ret != -1)
+		do_exit(ret);
+	else
+		while(1);
+
+}
+
+void do_undefinstr(int address, struct pt_regs *regs, int mode)
+{
+	current->tss.error_code = 0;
+	current->tss.trap_no = 6;
+	die_if_kernel("Undefined instruction", regs, mode, SIGILL);
+}
+
+void do_excpt(int address, struct pt_regs *regs, int mode)
+{
+	current->tss.error_code = 0;
+	current->tss.trap_no = 11;
+	die_if_kernel("Address exception", regs, mode, SIGBUS);
+}
+
+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("", regs, 0, -1);
+}
+
Binary files linux.orig/arch/arm/lib/ARM3REF/backtrace.o.ref and linux.arm/arch/arm/lib/ARM3REF/backtrace.o.ref differ
Binary files linux.orig/arch/arm/lib/ARM3REF/bitops.o.ref and linux.arm/arch/arm/lib/ARM3REF/bitops.o.ref differ
Binary files linux.orig/arch/arm/lib/ARM3REF/delay.o.ref and linux.arm/arch/arm/lib/ARM3REF/delay.o.ref differ
Binary files linux.orig/arch/arm/lib/ARM3REF/floppydma.o.ref and linux.arm/arch/arm/lib/ARM3REF/floppydma.o.ref differ
Binary files linux.orig/arch/arm/lib/ARM3REF/io.o.ref and linux.arm/arch/arm/lib/ARM3REF/io.o.ref differ
Binary files linux.orig/arch/arm/lib/ARM3REF/iputils.o.ref and linux.arm/arch/arm/lib/ARM3REF/iputils.o.ref differ
Binary files linux.orig/arch/arm/lib/ARM3REF/irqs.o.ref and linux.arm/arch/arm/lib/ARM3REF/irqs.o.ref differ
Binary files linux.orig/arch/arm/lib/ARM3REF/ll_char_wr.o.ref and linux.arm/arch/arm/lib/ARM3REF/ll_char_wr.o.ref differ
Binary files linux.orig/arch/arm/lib/ARM3REF/memfastset.o.ref and linux.arm/arch/arm/lib/ARM3REF/memfastset.o.ref differ
Binary files linux.orig/arch/arm/lib/ARM3REF/prints.o.ref and linux.arm/arch/arm/lib/ARM3REF/prints.o.ref differ
Binary files linux.orig/arch/arm/lib/ARM3REF/segment.o.ref and linux.arm/arch/arm/lib/ARM3REF/segment.o.ref differ
Binary files linux.orig/arch/arm/lib/ARM3REF/string.o.ref and linux.arm/arch/arm/lib/ARM3REF/string.o.ref differ
Binary files linux.orig/arch/arm/lib/ARM3REF/system.o.ref and linux.arm/arch/arm/lib/ARM3REF/system.o.ref differ
Binary files linux.orig/arch/arm/lib/ARM3REF/traps.o.ref and linux.arm/arch/arm/lib/ARM3REF/traps.o.ref differ
diff -r -u -N linux.orig/arch/arm/lib/CHECKOBJS linux.arm/arch/arm/lib/CHECKOBJS
--- linux.orig/arch/arm/lib/CHECKOBJS	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/CHECKOBJS	Fri Oct 27 23:14:12 1995
@@ -0,0 +1,20 @@
+#!/bin/sh
+echo +++ bitops ok: 8 differences only!
+echo +++ traps ok: 4 differences only!
+for i in *.o; do
+if [ -f ARM3REF/$i.ref ]; then
+  echo -n Checking $i...
+  cmp -l $i ARM3REF/$i.ref > /dev/null
+  if [ $? -eq 0 ]; then
+    echo " ok"
+  else
+    objdump --disassemble $i | grep 000 > $i.dis1
+    objdump --disassemble ARM3REF/$i.ref | grep 000 > $i.dis2
+    echo " *** differences:"
+    diff -u $i.dis2 $i.dis1 > $i.diff
+    rm -f $i.dis1
+    rm -f $i.dis2
+    cat $i.diff
+  fi
+fi
+done
diff -r -u -N linux.orig/arch/arm/lib/Makefile linux.arm/arch/arm/lib/Makefile
--- linux.orig/arch/arm/lib/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/Makefile	Fri Oct 27 23:14:10 1995
@@ -0,0 +1,33 @@
+
+all: lib.o
+
+dep:
+
+modules:
+
+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
+OBJS :=$(OBJS) iputils.o
+endif
+
+lib.o: $(OBJS)
+	$(LD) -r -o lib.o $(OBJS)
+	sync
+
+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 -r -u -N linux.orig/arch/arm/lib/README linux.arm/arch/arm/lib/README
--- linux.orig/arch/arm/lib/README	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/README	Fri Oct 27 23:14:12 1995
@@ -0,0 +1,8 @@
+This directory contains various functions required by the kernel.
+
+I have already started to convert this so that it has ARM independence,
+but there is still a lot of work to be done.
+
+Once you have made changes, please recompile this directory as ARM3
+and run the CHECKOBJS script.  If any files have differences, please
+check your changes.
diff -r -u -N linux.orig/arch/arm/lib/backtrace.S linux.arm/arch/arm/lib/backtrace.S
--- linux.orig/arch/arm/lib/backtrace.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/backtrace.S	Fri Oct 27 23:14:03 1995
@@ -0,0 +1,31 @@
+#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]
+		ldr	fp, [fp, #-12]
+		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 -r -u -N linux.orig/arch/arm/lib/bitops.S linux.arm/arch/arm/lib/bitops.S
--- linux.orig/arch/arm/lib/bitops.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/bitops.S	Fri Oct 27 23:14:03 1995
@@ -0,0 +1,123 @@
+#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 -r -u -N linux.orig/arch/arm/lib/bitops.o.diff linux.arm/arch/arm/lib/bitops.o.diff
--- linux.orig/arch/arm/lib/bitops.o.diff	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/bitops.o.diff	Fri Oct 27 23:14:13 1995
@@ -0,0 +1,39 @@
+--- bitops.o.dis2	Thu Oct 26 15:00:24 1995
++++ bitops.o.dis1	Thu Oct 26 15:00:23 1995
+@@ -42,28 +42,28 @@
+ 000000a4 <findzbit1lp+10> e1520001	cmp	r2, r1
+ 000000a8 <findzbit1lp+14> 3afffff9	bcc	00000094 <findzbit1lp>
+ 000000ac <findzbit1lp+18> e2810001	add	r0, r1, #1
+-000000b0 <findzbit1lp+1c> e1a0f00e	mov	pc, lr
++000000b0 <findzbit1lp+1c> e1b0f00e	movs	pc, lr
+ 000000b4 <foundzbit> e3130001	tst	r3, #1
+ 000000b8 <foundzbit+4> 01a00002	moveq	r0, r2
+-000000bc <foundzbit+8> 01a0f00e	moveq	pc, lr
++000000bc <foundzbit+8> 01b0f00e	moveqs	pc, lr
+ 000000c0 <foundzbit+c> e3130002	tst	r3, #2
+ 000000c4 <foundzbit+10> 02820001	addeq	r0, r2, #1
+-000000c8 <foundzbit+14> 01a0f00e	moveq	pc, lr
++000000c8 <foundzbit+14> 01b0f00e	moveqs	pc, lr
+ 000000cc <foundzbit+18> e3130004	tst	r3, #4
+ 000000d0 <foundzbit+1c> 02820002	addeq	r0, r2, #2
+-000000d4 <foundzbit+20> 01a0f00e	moveq	pc, lr
++000000d4 <foundzbit+20> 01b0f00e	moveqs	pc, lr
+ 000000d8 <foundzbit+24> e3130008	tst	r3, #8
+ 000000dc <foundzbit+28> 02820003	addeq	r0, r2, #3
+-000000e0 <foundzbit+2c> 01a0f00e	moveq	pc, lr
++000000e0 <foundzbit+2c> 01b0f00e	moveqs	pc, lr
+ 000000e4 <foundzbit+30> e3130010	tst	r3, #10
+ 000000e8 <foundzbit+34> 02820004	addeq	r0, r2, #4
+-000000ec <foundzbit+38> 01a0f00e	moveq	pc, lr
++000000ec <foundzbit+38> 01b0f00e	moveqs	pc, lr
+ 000000f0 <foundzbit+3c> e3130020	tst	r3, #20
+ 000000f4 <foundzbit+40> 02820005	addeq	r0, r2, #5
+-000000f8 <foundzbit+44> 01a0f00e	moveq	pc, lr
++000000f8 <foundzbit+44> 01b0f00e	moveqs	pc, lr
+ 000000fc <foundzbit+48> e3130040	tst	r3, #40
+ 00000100 <foundzbit+4c> 02820006	addeq	r0, r2, #6
+-00000104 <foundzbit+50> 01a0f00e	moveq	pc, lr
++00000104 <foundzbit+50> 01b0f00e	moveqs	pc, lr
+ 00000108 <foundzbit+54> e2820007	add	r0, r2, #7
+ 0000010c <foundzbit+58> e1b0f00e	movs	pc, lr
+ 00000110 <_find_next_zero_bit> e2822001	add	r2, r2, #1
diff -r -u -N linux.orig/arch/arm/lib/calls.c linux.arm/arch/arm/lib/calls.c
--- linux.orig/arch/arm/lib/calls.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/calls.c	Fri Oct 27 23:14:08 1995
@@ -0,0 +1,17 @@
+#include <linux/sys.h>
+
+extern void sys_ni_syscall(void);
+
+#define sysname(name) extern void sys_##name(void);
+#include "calls.h"
+#undef sysname
+
+#define sysname(name) sys_##name ,
+
+void (*sys_call_table[])(void)=
+{
+#include "calls.h"
+  sys_ni_syscall
+};
+
+#undef sysname
diff -r -u -N linux.orig/arch/arm/lib/calls.h linux.arm/arch/arm/lib/calls.h
--- linux.orig/arch/arm/lib/calls.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/calls.h	Fri Oct 27 23:14:11 1995
@@ -0,0 +1,142 @@
+/* 0 */		sysname(setup)
+		sysname(exit)
+		sysname(fork)
+		sysname(read)
+		sysname(write)
+/* 5 */		sysname(open)
+		sysname(close)
+		sysname(waitpid)
+		sysname(creat)
+		sysname(link)
+/* 10 */	sysname(unlink)
+		sysname(execve)
+		sysname(chdir)
+		sysname(time)
+		sysname(mknod)
+/* 15 */	sysname(chmod)
+		sysname(chown)
+		sysname(break)
+		sysname(stat)
+		sysname(lseek)
+/* 20 */	sysname(getpid)
+		sysname(mount)
+		sysname(umount)
+		sysname(setuid)
+		sysname(getuid)
+/* 25 */	sysname(stime)
+		sysname(ptrace)
+		sysname(alarm)
+		sysname(fstat)
+		sysname(pause)
+/* 30 */	sysname(utime)
+		sysname(stty)
+		sysname(gtty)
+		sysname(access)
+		sysname(nice)
+/* 35 */	sysname(ftime)
+		sysname(sync)
+		sysname(kill)
+		sysname(rename)
+		sysname(mkdir)
+/* 40 */	sysname(rmdir)
+		sysname(dup)
+		sysname(pipe)
+		sysname(times)
+		sysname(prof)
+/* 45 */	sysname(brk)
+		sysname(setgid)
+		sysname(getgid)
+		sysname(signal)
+		sysname(geteuid)
+/* 50 */	sysname(getegid)
+		sysname(acct)
+		sysname(phys)
+		sysname(lock)
+		sysname(ioctl)
+/* 55 */	sysname(fcntl)
+		sysname(mpx)
+		sysname(setpgid)
+		sysname(ulimit)
+		sysname(olduname)
+/* 60 */	sysname(umask)
+		sysname(chroot)
+		sysname(ustat)
+		sysname(dup2)
+		sysname(getppid)
+/* 65 */	sysname(getpgrp)
+		sysname(setsid)
+		sysname(sigaction)
+		sysname(sgetmask)
+		sysname(ssetmask)
+/* 70 */	sysname(setreuid)
+		sysname(setregid)
+		sysname(sigsuspend)
+		sysname(sigpending)
+		sysname(sethostname)
+/* 75 */	sysname(setrlimit)
+		sysname(getrlimit)
+		sysname(getrusage)
+		sysname(gettimeofday)
+		sysname(settimeofday)
+/* 80 */	sysname(getgroups)
+		sysname(setgroups)
+		sysname(select)
+		sysname(symlink)
+		sysname(lstat)
+/* 85 */	sysname(readlink)
+		sysname(uselib)
+		sysname(swapon)
+		sysname(reboot)
+		sysname(readdir)
+/* 90 */	sysname(mmap)
+		sysname(munmap)
+		sysname(truncate)
+		sysname(ftruncate)
+		sysname(fchmod)
+/* 95 */	sysname(fchown)
+		sysname(getpriority)
+		sysname(setpriority)
+		sysname(profil)
+		sysname(statfs)
+/* 100 */	sysname(fstatfs)
+/* sysname(ioperm) */sysname(ni_syscall)/* To do */
+		sysname(socketcall)
+		sysname(syslog)
+		sysname(setitimer)
+/* 105 */	sysname(getitimer)
+		sysname(newstat)
+		sysname(newlstat)
+		sysname(newfstat)
+		sysname(uname)
+/* 110 */	sysname(iopl)
+		sysname(vhangup)
+		sysname(idle)
+/* sysname(vm86) */sysname(ni_syscall)
+		sysname(wait4)
+/* 115 */	sysname(swapoff)
+		sysname(sysinfo)
+		sysname(ipc)
+		sysname(fsync)
+		sysname(sigreturn)
+/* sysname(clone) */sysname(ni_syscall)/* To Do */
+		sysname(setdomainname)
+		sysname(newuname)
+/* sysname(modify_ldt) */sysname(ni_syscall)/* ??? */
+		sysname(adjtimex)
+/* 125 */	sysname(mprotect)
+		sysname(sigprocmask)
+		sysname(create_module)
+		sysname(init_module)
+		sysname(delete_module)
+/* 130 */	sysname(get_kernel_syms)
+/* sysname(quotactl) */sysname(ni_syscall)
+		sysname(getpgid)
+		sysname(fchdir)
+		sysname(bdflush)
+/* 135 */	sysname(sysfs)
+		sysname(personality)
+/* sysname(afs_syscall) */sysname(ni_syscall)
+		sysname(setfsuid)
+		sysname(setfsgid)
+/* 140 */	/* sysname(llseek) */sysname(ni_syscall)
+/*sysname(MEMINFO)*/
diff -r -u -N linux.orig/arch/arm/lib/constants.h linux.arm/arch/arm/lib/constants.h
--- linux.orig/arch/arm/lib/constants.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/constants.h	Fri Oct 27 23:14:12 1995
@@ -0,0 +1,18 @@
+/*
+ *  contants.h generated by getconstants
+ * DO NOT EDIT!
+ */
+#define PAGE_PRESENT	1
+#define PAGE_RW	2
+#define PAGE_USER	4
+#define PAGE_ACCESSED	32
+#define PAGE_DIRTY	64
+#define TSS_MEMMAP	1168
+#define TSS_UREGS	952
+#define TSS_SREGS	972
+#define TSS_FPESAVE	1016
+#define SYS_execve	9437195
+#define SYS_sigreturn	9437303
+#define KSWI_BASE	0x900000
+#define KSWI_SYS_BASE	0x9F0000
+#define SYS_ERROR0	0x9F0000
diff -r -u -N linux.orig/arch/arm/lib/delay.S linux.arm/arch/arm/lib/delay.S
--- linux.orig/arch/arm/lib/delay.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/delay.S	Fri Oct 27 23:14:05 1995
@@ -0,0 +1,37 @@
+#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 -r -u -N linux.orig/arch/arm/lib/floppydma.S linux.arm/arch/arm/lib/floppydma.S
--- linux.orig/arch/arm/lib/floppydma.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/floppydma.S	Fri Oct 27 23:14:05 1995
@@ -0,0 +1,36 @@
+		.text
+
+LC0:		.word	_floppyfiq_len
+
+		.global	_floppy_fiqin
+_floppy_fiqin:	ldr	r8, [pc, #LC0 - . - 8]
+		ldmia	r8, {r9, r10, r11}
+		subs	r9, r9, #1
+		ldrgtb	r12, [r11, #-4]
+		ldrleb	r12, [r11], #0
+		strb	r12, [r10], #1
+		stmia	r8, {r9, r10}
+		subs	pc, lr, #4
+
+		.global	_floppy_fiqout
+_floppy_fiqout:	ldr	r8, [pc, #LC0 - . - 8]
+		ldmia	r8, {r9, r10, r11}
+		subs	r9, r9, #1
+		ldrgeb	r12, [r10], #1
+		movlt	r12, #0
+;		strb	r12, [r11, #-4]
+;		ldrleb	r12, [r11], #0
+		strleb	r12, [r11], #0
+		strgtb	r12, [r11, #-4]
+		stmia	r8, {r9, r10}
+		subs	pc, lr, #4
+
+		.data
+
+		.global	_floppyfiq_len
+		.global	_floppyfiq_to
+		.global	_floppyfiq_from
+
+_floppyfiq_len:		.word	0
+_floppyfiq_to:		.word	0
+_floppyfiq_from:	.word	0
diff -r -u -N linux.orig/arch/arm/lib/fp_support.c linux.arm/arch/arm/lib/fp_support.c
--- linux.orig/arch/arm/lib/fp_support.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/fp_support.c	Fri Oct 27 23:14:13 1995
@@ -0,0 +1,15 @@
+#include <linux/sched.h>
+
+extern void (*fp_save)(int *);
+
+void fp_setup(void)
+{
+	struct task_struct **p;
+
+	p = task;
+	while (p++ < task + NR_TASKS)
+	{
+		if(*p)
+			fp_save((*p)->tss.fpe_save);
+	}
+}
Binary files linux.orig/arch/arm/lib/getconstants and linux.arm/arch/arm/lib/getconstants differ
diff -r -u -N linux.orig/arch/arm/lib/getconstants.c linux.arm/arch/arm/lib/getconstants.c
--- linux.orig/arch/arm/lib/getconstants.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/getconstants.c	Fri Oct 27 23:14:13 1995
@@ -0,0 +1,29 @@
+#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);
+}
+
+int main()
+{
+	struct task_struct *tsk = NULL;
+	printf("/*\n *  contants.h generated by getconstants\n * DO NOT EDIT!\n */\n");
+	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);
+	printdef("TSS_MEMMAP",		(int)&tsk->tss.memmap);
+	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+0x900000);
+	printdef("SYS_sigreturn",	__NR_sigreturn+0x900000);
+	printf("#define KSWI_BASE	0x900000\n");
+	printf("#define KSWI_SYS_BASE	0x9F0000\n");
+	printf("#define SYS_ERROR0	0x9F0000\n");
+	return 0;
+}
diff -r -u -N linux.orig/arch/arm/lib/io.S linux.arm/arch/arm/lib/io.S
--- linux.orig/arch/arm/lib/io.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/io.S	Fri Oct 27 23:14:05 1995
@@ -0,0 +1,271 @@
+#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 -r -u -N linux.orig/arch/arm/lib/ioc.c linux.arm/arch/arm/lib/ioc.c
--- linux.orig/arch/arm/lib/ioc.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/ioc.c	Fri Oct 27 23:14:10 1995
@@ -0,0 +1,7 @@
+unsigned char *ioc = (unsigned char *)0x03200000;
+
+unsigned long muldiv(unsigned long lps, unsigned long hz, int ticks)
+{
+	return lps * hz / ticks;
+}
+
diff -r -u -N linux.orig/arch/arm/lib/iputils.S linux.arm/arch/arm/lib/iputils.S
--- linux.orig/arch/arm/lib/iputils.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/iputils.S	Fri Oct 27 23:14:12 1995
@@ -0,0 +1,134 @@
+#include <asm/assembler.h>
+		.text
+
+@ Prototype: int ip_fast_csum(unsigned char *buff,int wlen)
+
+		.global	_ip_fast_csum
+_ip_fast_csum:	teq	r1, #0
+		RETINSTR(moveq,pc,lr)
+		stmfd	sp!, {lr}
+		mov	r3, #0
+		adds	r3, r3, r3
+_csumlp:	ldr	r2, [r0], #4
+		adcs	r3, r3, r2
+		sub	r1, r1, #1
+		teq	r1, #0				@ Must not affect C flag
+		bne	_csumlp
+		adc	r3, r3, #0
+		mov	r2, #0xff
+		orr	r2, r2, #0xff00
+		and	r0, r3, r2
+		add	r0, r0, r3, lsr #16
+		add	r0, r0, r0, lsr #16
+		eor	r0, r0, r2
+		and	r0, r0, r2
+		LOADREGS(fd, sp!, {pc})
+
+
+@ Prototype: int ip_compute_csum(unsigned char *buf,int len)
+@ Notes    : buf must be word aligned
+
+		.global	_ip_compute_csum
+_ip_compute_csum:
+		stmfd	sp!, {r4, r5, lr}
+		mov	r5, #0xff
+		orr	r5, r5, #0xff00
+		mov	r3, #0
+		cmp	r1, #3
+		ble	_csum_shortword
+_csum1lp:	ldr	r2, [r0], #4
+		adds	r3, r3, r2
+		adc	r3, r3, #0
+		sub	r1, r1, #4
+		cmp	r1, #3
+		bgt	_csum1lp
+		and	r4, r3, r5
+		add	r3, r4, r3, lsr #16
+_csum_shortword:
+		tst	r1, #2
+		beq	_csum_byte
+		ldr	r2, [r0], #2
+		and	r2, r2, r5
+		add	r3, r3, r2
+_csum_byte:	tst	r1, #1
+		beq	_csum_end
+		ldrb	r2, [r0], #1
+		add	r3, r3, r2
+_csum_end:	add	r3, r3, r3, lsr #16
+		eor	r3, r3, r5
+		and	r0, r3, r5
+		LOADREGS(fd, sp!, {r4, r5, pc})
+
+@ Prototype: unsigned short tcp_check(struct tcphdr *th,int len,unsigned long saddr,
+@				unsigned long daddr)
+
+#define IPPROTO_TCP	6
+#define IPPROTO_UDP	17
+
+		.global	_tcp_check
+_tcp_check:	stmfd	sp!, {r4 - r9, lr}
+		mov	r4, r0
+		mov	r5, r1
+		teq	r2, #0
+		bne	_nogetaddr
+		stmfd	sp!, {r3}
+		bl	_ip_my_addr
+		mov	r2, r0
+		ldmfd	sp!, {r3}
+_nogetaddr:	mov	r8, #IPPROTO_TCP
+		bl	_tcpudp_check
+		LOADREGS(fd, sp!, {r4 - r9, pc})
+
+@ Prototype: unsigned short udp_check(struct uhphdr *uh,int len,unsigned long saddr,
+@				unsigned long daddr)@
+
+		.global	_udp_check
+_udp_check:	stmfd	sp!, {r4 - r9, lr}
+		mov	r8, #IPPROTO_UDP
+		mov	r4, r0
+		mov	r5, r1
+		bl	_tcpudp_check
+		LOADREGS(fd, sp!, {r4 - r9, pc})
+
+_tcpudp_check:	stmfd	sp!, {lr}
+		mov	r7, #0xff
+		orr	r7, r7, #0xff00
+@ Compute initial part
+		and	r0, r5, #0x00ff
+		and	r1, r5, #0xff00
+		mov	r0, r0, lsl #24
+		orr	r0, r0, r1, lsl #8
+		add	r8, r0, r8, lsl #8
+		adds	r6, r3, r2
+		adcs	r6, r6, r8
+		adc	r6, r6, #0
+@ Compute tcp block in words (word aligned)
+		cmp	r5,#3
+		ble	_tcp_chkshort
+		mov	r8,r5, lsr #2
+		adds	r8,r4,r8, lsl #2		@ Clear carry
+_tcp_chkwds:	ldr	r0,[r4],#4
+		adcs	r6,r6,r0
+		teq	r4,r8
+		bne	_tcp_chkwds
+		adc	r6,r6,#0			@ Put in carry
+@ Convert from 32-bits to 16-bits
+@ Add in word
+_tcp_chkshort:	tst	r5,#2
+		beq	_tcp_chkbyte
+		ldr	r0,[r4],#2
+		and	r0,r0,r7
+		adds	r6,r6,r0
+		adc	r6,r6,#0
+_tcp_chkbyte:	tst	r5,#1
+		beq	_tcp_chkend
+		ldrb	r0,[r4],#1
+		adds	r6,r6,r0
+		adc	r6,r6,#0
+_tcp_chkend:	and	r0,r6,r7			@ Convert checksum to a 16-bit word
+		bic	r1,r6,r7
+		add	r6,r0,r1, lsr #16		@ checksum in low-bits
+		add	r6,r6,r6, lsr #16
+		and	r6,r6,r7
+		eor	r0,r6,r7
+		LOADREGS(fd, sp!, {pc})
diff -r -u -N linux.orig/arch/arm/lib/irqs.S linux.arm/arch/arm/lib/irqs.S
--- linux.orig/arch/arm/lib/irqs.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/irqs.S	Fri Oct 27 23:14:09 1995
@@ -0,0 +1,272 @@
+#include <asm/assembler.h>
+
+@ IRQ stubs
+
+@ IRQ stubs entered with:
+@ r1 = IOC
+@ r2 = IRQ number
+@ return 0 for normal irq
+
+		.text
+
+LC0:
+		.word	_enabled_irqs
+LC1:
+		.word	_intr_count
+
+#define BAD_IRQ8(n, v1, v2)				\
+		.global	_bad_IRQ##n##_interrupt		;\
+_bad_IRQ##n##_interrupt:				;\
+		ldrb	r0, [r4, \#0x18]		;\
+		bic	r0, r0, \# v1			;\
+		strb	r0, [r4, \#0x18]		;\
+		ldr	r3, [pc, \#LC0 - . - 8]		;\
+		ldr	r0, [r3, \#0]			;\
+		bic	r0, r0, \# v2			;\
+		str	r0, [r3, \#0]			;\
+		ldr	r3, [pc, \#LC1 - . - 8]		;\
+		ldr	r0, [r3]			;\
+		add	r0, r0, \#1			;\
+		str	r0, [r3]			;\
+		stmfd	sp !,{lr}			;\
+		bl	_bad_IRQ			;\
+		ldmfd	sp !,{lr}			;\
+		ldr	r3, [pc, \#LC1 - . - 8]		;\
+		ldr	r0, [r3]			;\
+		sub	r0, r0, \#1			;\
+		str	r0, [r3]			;\
+		mov	r0, \#1				;\
+		RETINSTR(mov,pc,lr)
+
+#define BAD_IRQ16(n, v1, v2)				\
+		.global	_bad_IRQ##n##_interrupt		;\
+_bad_IRQ##n##_interrupt:				;\
+		ldrb	r0, [r4, \#0x28]		;\
+		bic	r0, r0, \# v1			;\
+		strb	r0, [r4, \#0x28]		;\
+		ldr	r3, [pc, \#LC0 - . - 8]		;\
+		ldr	r0, [r3, \#0]			;\
+		bic	r0, r0, \# v2			;\
+		str	r0, [r3, \#0]			;\
+		ldr	r3, [pc, \#LC1 - . - 8]		;\
+		ldr	r0, [r3]			;\
+		add	r0, r0, \#1			;\
+		str	r0, [r3]			;\
+		stmfd	sp !,{lr}			;\
+		bl	_bad_IRQ			;\
+		ldmfd	sp !,{lr}			;\
+		ldr	r3, [pc, \#LC1 - . - 8]		;\
+		ldr	r0, [r3]			;\
+		sub	r0, r0, \#1			;\
+		str	r0, [r3]			;\
+		mov	r0, \#1				;\
+		RETINSTR(mov,pc,lr)
+
+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)
+
+LC2:
+		.word	_intr_count
+LC3:
+		.word	_eirq
+LC4:
+		.word	_enabled_irqs
+
+#define FAST_IRQ8(n, v1, v2)				\
+		.global	_fast_IRQ##n##_interrupt	;\
+_fast_IRQ##n##_interrupt:				;\
+		stmfd	sp!, {lr}			;\
+		ldr	r5, [pc, \#LC2 - . - 8]		;\
+		ldr	r6, [pc, \#LC3 - . - 8]		;\
+		ldr	r0, [r6]			;\
+		bic	r0, r0, \# v2			;\
+		str	r0, [r6]			;\
+		ldrb	r0, [r4, \#0x18]		;\
+		bic	r0, r0, \# v1			;\
+		strb	r0, [r4, \#0x18]		;\
+		mov	r0, \# v1			;\
+		strb	r0, [r4, \#0x14]		;\
+		ldr	r0, [r5]			;\
+		add	r0, r0, \#1			;\
+		str	r0, [r5]			;\
+		mov	r0, \#n				;\
+		bl	_do_fast_IRQ			;\
+		DISABLEIRQS				;\
+		ldr	r0, [r5]			;\
+		sub	r0, r0, \#1			;\
+		str	r0, [r5]			;\
+		ldr	r3, [pc, \#LC4 - . - 8]		;\
+		ldrb	r3, [r3, \#0]			;\
+		ldrb	r2, [r6]			;\
+		orr	r2, r2, \# v1			;\
+		strb	r2, [r6]			;\
+		and	r3, r3, r2			;\
+		strb	r3, [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}			;\
+		ldr	r5, [pc, \#LC2 - . - 8]		;\
+		ldr	r6, [pc, \#LC3 - . - 8]		;\
+		ldr	r0, [r6]			;\
+		bic	r0, r0, \# v2			;\
+		str	r0, [r6]			;\
+		ldrb	r0, [r4, \#0x28]		;\
+		bic	r0, r0, \# v1			;\
+		strb	r0, [r4, \#0x28]		;\
+		ldr	r0, [r5]			;\
+		add	r0, r0, \#1			;\
+		str	r0, [r5]			;\
+		mov	r0, \#n				;\
+		bl	_do_fast_IRQ			;\
+		DISABLEIRQS				;\
+		ldr	r0, [r5]			;\
+		sub	r0, r0, \#1			;\
+		str	r0, [r5]			;\
+		ldr	r3, [pc, \#LC4 - . - 8]		;\
+		ldrb	r3, [r3, \#1]			;\
+		ldrb	r2, [r6, \#1]			;\
+		orr	r2, r2, \# v1			;\
+		strb	r2, [r6, \#1]			;\
+		and	r3, r3, r2			;\
+		strb	r3, [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)
+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)
+
+LC5:
+		.word	_intr_count
+LC6:
+		.word	_eirq
+LC7:
+		.word	_enabled_irqs
+
+#define NORM_IRQ8(n, v1, v2)				\
+		.global	_IRQ##n##_interrupt		;\
+_IRQ##n##_interrupt:					;\
+		stmfd	sp!, {lr}			;\
+		ldr	r5, [pc, \#LC5 - . - 8]		;\
+		ldr	r6, [pc, \#LC6 - . - 8]		;\
+		ldr	r0, [r6]			;\
+		bic	r0, r0, \# v2			;\
+		str	r0, [r6]			;\
+		ldrb	r0, [r4, \#0x18]		;\
+		bic	r0, r0, \# v1			;\
+		strb	r0, [r4, \#0x18]		;\
+		mov	r0, \# v1			;\
+		strb	r0, [r4, \#0x14]		;\
+		ldr	r0, [r5]			;\
+		add	r0, r0, \#1			;\
+		str	r0, [r5]			;\
+		ENABLEIRQS				;\
+		mov	r0, \#n				;\
+		bl	_do_IRQ				;\
+		DISABLEIRQS				;\
+		ldr	r0, [r5]			;\
+		sub	r0, r0, \#1			;\
+		str	r0, [r5]			;\
+		ldr	r3, [pc, \#LC7 - . - 8]		;\
+		ldrb	r3, [r3, \#0]			;\
+		ldrb	r2, [r6]			;\
+		orr	r2, r2, \# v1			;\
+		strb	r2, [r6]			;\
+		and	r3, r3, r2			;\
+		strb	r3, [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}			;\
+		ldr	r5, [pc, \#LC5 - . - 8]		;\
+		ldr	r6, [pc, \#LC6 - . - 8]		;\
+		ldr	r0, [r6]			;\
+		bic	r0, r0, \# v2			;\
+		str	r0, [r6]			;\
+		ldrb	r0, [r4, \#0x28]		;\
+		bic	r0, r0, \# v1			;\
+		strb	r0, [r4, \#0x28]		;\
+		ldr	r0, [r5]			;\
+		add	r0, r0, \#1			;\
+		str	r0, [r5]			;\
+		ENABLEIRQS				;\
+		mov	r0, \#n				;\
+		bl	_do_IRQ				;\
+		DISABLEIRQS				;\
+		ldr	r0, [r5]			;\
+		sub	r0, r0, \#1			;\
+		str	r0, [r5]			;\
+		ldr	r3, [pc, \#LC7 - . - 8]		;\
+		ldrb	r3, [r3, \#1]			;\
+		ldrb	r2, [r6, \#1]			;\
+		orr	r2, r2, \# v1			;\
+		strb	r2, [r6, \#1]			;\
+		and	r3, r3, r2			;\
+		strb	r3, [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)
+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
+
+		.data
+
+_eirq:
+		.word	0xFFFFFFFF
diff -r -u -N linux.orig/arch/arm/lib/ll_char_wr.S linux.arm/arch/arm/lib/ll_char_wr.S
--- linux.orig/arch/arm/lib/ll_char_wr.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/ll_char_wr.S	Fri Oct 27 23:14:10 1995
@@ -0,0 +1,123 @@
+#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 -r -u -N linux.orig/arch/arm/lib/memfastset.S linux.arm/arch/arm/lib/memfastset.S
--- linux.orig/arch/arm/lib/memfastset.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/memfastset.S	Fri Oct 27 23:14:08 1995
@@ -0,0 +1,34 @@
+#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 -r -u -N linux.orig/arch/arm/lib/prints.S linux.arm/arch/arm/lib/prints.S
--- linux.orig/arch/arm/lib/prints.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/prints.S	Fri Oct 27 23:14:05 1995
@@ -0,0 +1,54 @@
+#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
+		str	r2, [sp]
+		mov	r2, sp
+		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, #0x2000
+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, #0x2000
+dly2lp:		SUB	r3, r3, #1
+		TEQ	r3, #0
+		BNE	dly2lp
+		B	out2lp
+
diff -r -u -N linux.orig/arch/arm/lib/segment.S linux.arm/arch/arm/lib/segment.S
--- linux.orig/arch/arm/lib/segment.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/segment.S	Fri Oct 27 23:14:08 1995
@@ -0,0 +1,592 @@
+#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,r4,r5,r6,r7,r8,r9,ip}
+		STMDB	r0!,{r3,r4,r5,r6,r7,r8,r9,ip}
+		SUBS	r2,r2,#32
+		BGE	down_cpy_8_lp
+
+down_not_long_copy:
+		CMN	r2,#16
+		LDMGEDB	r1!,{r3,r4,r5,r6}
+		STMGEDB	r0!,{r3,r4,r5,r6}
+		SUBGE	r2,r2,#16
+		ADDS	r2,r2,#20
+		LDMGEDB	r1!,{r3,r4,r5}
+		STMGEDB	r0!,{r3,r4,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 -r -u -N linux.orig/arch/arm/lib/string.S linux.arm/arch/arm/lib/string.S
--- linux.orig/arch/arm/lib/string.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/string.S	Fri Oct 27 23:14:13 1995
@@ -0,0 +1,370 @@
+#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:	subs	r2, r2, #32
+		bmi	memsetl32
+		orr	ip, r1, r1, lsl #8
+		orr	ip, ip, ip, lsl #16
+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})
+
+# END OF FILE!
+
+#if 0
+# Prototype: char *strcpy(char *dest,const char *src);
+
+		.global	strcpy
+strcpy:
+		STMFD	sp!,{lr}
+		MOV	r3,r0
+strcpylp:
+		LDRB	r2,[r1],#1
+		STRB	r2,[r3],#1
+		TEQ	r2,#0
+		BNE	strcpylp
+		LDMFD	sp!,{pc}^
+
+# Prototype: char *strncpy(char *dest,const char *src,size_t count);
+
+		.global	strncpy
+strncpy:
+		STMFD	sp!,{r4,lr}
+		MOV	r4,r0
+strncpylp:
+		CMP	r2,#0
+		LDRGTB	r3,[r1],#1
+		STRGTB	r3,[r4],#1
+		SUBGT	r2,r2,#1
+		TEQ	r3,#0
+		BGT	strncpylp
+		LDMFD	sp!,{r4,pc}^
+
+# Prototype: char *strcat(char *dest,const char *src)
+
+		.global	strcat
+strcat:
+		STMFD	sp!,{lr}
+		MOV	r3,r0
+strcatlp1:
+		LDRB	r2,[r3],#1
+		TEQ	r2,#0
+		BNE	strcatlp1
+		SUB	r3,r3,#1
+		B	strcpylp
+
+# Prototype: char *strncat(char *dest,const char *src,size_t count);
+
+		.global	strncat
+strncat:
+		STMFD	sp!,{r4,lr}
+		MOV	r4,r0
+strncat1lp:
+		LDRB	r3,[r4],#1
+		TEQ	r3,#0
+		BNE	strncat1lp
+		SUB	r4,r4,#1
+		B	strncpylp
+
+# Prototype: int strcmp(const char *cs,const char *ct);
+
+		.global	strcmp
+strcmp:
+		STMFD	sp!,{lr}
+strcmplp:
+		LDRB	r2,[r0],#1
+		LDRB	r3,[r1],#1
+		SUBS	r3,r2,r3
+		MOVNE	r0,r3
+		LDMNEFD	sp!,{pc}^
+		TEQ	r2,#0
+		BNE	strcmplp
+		MOV	r0,#0
+		LDMFD	sp!,{pc}^
+
+# Prototype: int strncmp(const char *cs,const char *ct,size_t count);
+
+		.global	strncmp
+strncmp:
+		STMFD	sp!,{r4,r5,lr}
+strncmplp:
+		SUBS	r2,r2,#1
+		MOVLT	r0,#0
+		LDMLTFD	sp!,{r4,r5,pc}^
+		LDRB	r3,[r0],#1
+		LDRB	r4,[r1],#1
+		SUBS	r4,r3,r4
+		MOVNE	r0,r4
+		LDMNEFD	sp!,{r4,r5,pc}^
+		TEQ	r3,#0
+		BNE	strncmplp
+		MOV	r0,r4
+		LDMFD	sp!,{r4,r5,pc}^
+
+# Prototype: char *strchr(const char *s,char c);
+
+		.global	strchr
+strchr:
+		STMFD	sp!,{lr}
+strchrlp:
+		LDRB	r2,[r0],#1
+		TEQ	r2,r1
+		BEQ	strchrf
+		TEQ	r2,#0
+		BNE	strchrlp
+		MOV	r0,#0
+		LDMFD	sp!,{pc}^
+strchrf:
+		SUB	r0,r0,#1
+		LDMFD	sp!,{pc}^
+
+# Prototype: size_t strspn(const char *cs,const char *ct);
+
+		.global	strspn
+strspn:
+		STMFD	sp!,{r4,r5,lr}
+		MOV	r4,#0
+		MOV	r5,r1
+		B	strspn1lp
+strspn1lp1:
+		ADD	r4,r4,#1
+		MOV	r1,r5
+strspn1lp:
+		LDRB	r2,[r0,r4]
+		TEQ	r2,#0
+		MOVEQ	r0,r4
+		LDMEQFD	sp!,{r4,r5,pc}^
+strspn2lp:
+		LDRB	r3,[r1],#1
+		TEQ	r2,r3
+		BEQ	strspn1lp1
+		TEQ	r3,#0
+		BNE	strspn2lp
+		MOV	r0,r4
+		LDMFD	sp!,{r4,r5,pc}^
+
+# Prototype: size_t strcspn(const char *cs,const char *ct);
+
+		.global	strcspn
+strcspn:
+		STMFD	sp!,{r4,r5,lr}
+		MOV	r4,#0
+		MOV	r5,r1
+		B	strcspn1lp
+strcspn1lp1:
+		ADD	r4,r4,#1
+		MOV	r1,r5
+strcspn1lp:
+		LDRB	r2,[r0,r4]
+		TEQ	r2,#0
+		MOVEQ	r0,r4
+		LDMEQFD	sp!,{r4,r5,pc}^
+strcspn2lp:
+		LDRB	r3,[r1],#1
+		TEQ	r3,#0
+		BEQ	strcspn1lp1
+		TEQ	r2,r3
+		BNE	strcspn2lp
+		MOV	r0,r4
+		LDMFD	sp!,{r4,r5,pc}^
+
+# Prototype: char *strpbrk(const char *cs,const char *ct);
+
+		.global	strpbrk
+strpbrk:
+		STMFD	sp!,{r4,r5,lr}
+		MOV	r5,r1
+strpbrk1lp:
+		LDRB	r2,[r0],#1
+		TEQ	r2,#0
+		MOVEQ	r0,#0
+		LDMEQFD	sp!,{r4,r5,pc}^
+strpbrk2lp:
+		LDRB	r3,[r1],#1
+		TEQ	r2,r3
+		SUBEQ	r0,r0,#1
+		LDMEQFD	sp!,{r4,r5,pc}^
+		TEQ	r3,#0
+		BNE	strpbrk2lp
+		MOV	r1,r5
+		B	strpbrk1lp
+
+# Prototype: char *strstr(const char *cs,const char *ct);
+
+# Prototype: char *strtok(char *s,const char *ct)
+
+		.global	strtok
+strtok:
+		stmfd	sp!, {r4-r6, lr}
+		teq	r0, #0
+		ldr	r4, =|___strtok|
+		ldreq	r0, [r4]
+srchnlp:
+		ldrb	r2, [r0], #1
+		teq	r2, #0
+		moveq	r0, #0
+		ldmeqfd	sp!, {r4-r6, pc}^
+		bl	str_toksrch
+		bne	srchnlp
+		sub	r5, r0, #1
+srchn2lp:
+		ldrb	r2, [r0], #1
+		teq	r2,#0
+		beq	strtokend
+		bl	str_toksrch
+		beq	srchn2lp
+		str	r0,[r4]
+		sub	r0,r0,#1
+		mov	r4,#0
+		strb	r4,[r0]
+		mov	r0,r5
+		ldmfd	sp!,{r4-r6,pc}^
+strtokend:
+		sub	r0,r0,#1
+		str	r0,[r4]
+		mov	r0,r5
+		ldmfd	sp!,{r4-r6,pc}^
+
+str_toksrch:
+		mov	r6,r1
+str_toksrchlp:
+		ldrb	r3,[r6],#1
+		teq	r3,#0
+		moveq	pc,lr
+		teq	r3,r2
+		bne	str_toksrchlp
+		movs	r3,#1
+		mov	pc,lr
+
+# Prototype: size_t strlen(const char *s)
+
+		.global	strlen
+strlen:
+		STMFD	sp!,{lr}
+		ADD	r2,r0,#1
+strlenlp:
+		LDRB	r1,[r0],#1
+		TEQ	r1,#0
+		BNE	strlenlp
+		SUB	r0,r0,r2
+		LDMFD	sp!,{pc}
+
+# Prototype: int memcmp(const void *cs,const void *ct,size_t n);
+
+		.global	memcmp
+memcmp:
+		STMFD	sp!,{r4,lr}
+memcmplp:
+		SUBS	r2,r2,#1
+		MOVLT	r0,#0
+		LDMLTFD	sp!,{r4,pc}^
+		LDRB	r3,[r0],#1
+		LDRB	r4,[r1],#1
+		SUBS	r4,r3,r4
+		MOVNE	r0,r4
+		LDMNEFD	sp!,{r4,pc}^
+		B	memcmplp
+
+# Prototype: void *memchr(const void *cs,char c,size_t n);
+
+		.global	memchr
+memchr:
+		STMFD	sp!,{lr}
+memchrlp:
+		LDRB	r3,[r0],#1
+		TEQ	r3,r1
+		SUBEQ	r0,r0,#1
+		LDMEQFD	sp!,{pc}^
+		SUBS	r2,r2,#1
+		BGE	memchrlp
+		MOV	r0,#0
+		LDMFD	sp!,{pc}^
+
+# Prototype: void *memset(void *s,char c,size_t n);
+
+# Prototype: void memfastset(void *d,unsigned long c,size_t n);
+
+		.global	memfastset
+memfastset:
+		STMFD	sp!,{lr}
+memfastsetlp:
+		SUBS	r2,r2,#4
+		STRGE	r1,[r0],#4
+		BGT	memfastsetlp
+		LDMFD	sp!,{pc}^
+
+# Prototype: char *memscan(void *addr,unsigned char c,int size);
+
+		.global	memscan
+memscan:
+		TEQ	r2,#0
+		MOVEQS	pc,lr
+		SUB	r2,r2,#1
+		LDRB	r3,[r0],#1
+		TEQ	r3,r1
+		BNE	memscan
+		MOVS	pc,lr
+
+		.data
+
+___strtok:	DCD	0
+#endif
diff -r -u -N linux.orig/arch/arm/lib/system.S linux.arm/arch/arm/lib/system.S
--- linux.orig/arch/arm/lib/system.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/system.S	Fri Oct 27 23:14:10 1995
@@ -0,0 +1,218 @@
+#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
+		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
+
+# 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
+		ldr	r0, [r4, #TSS_MEMMAP]
+		bl	_remap_task
+		add	r0, r4, #TSS_UREGS+3*4
+		ldmia	r0!, {r13,r14}^			@ Get user registers
+		add	r0, r4, #TSS_FPESAVE		@ Get address of FPE save
+		bl	_fpe_restore
+		SAVEIRQS
+		DISABLEIRQS
+		str	r4, [r5]
+		add	r0, r4, #TSS_SREGS
+#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 -r -u -N linux.orig/arch/arm/lib/traps.S linux.arm/arch/arm/lib/traps.S
--- linux.orig/arch/arm/lib/traps.S	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/traps.S	Fri Oct 27 23:14:11 1995
@@ -0,0 +1,668 @@
+#include <asm/assembler.h>
+		.text
+#define ARM	300
+@ Offsets into current task structure
+@ -----------------------------------
+@   +0		state (0 = running)
+#define STATE		0
+@   +4		counter
+#define COUNTER		4
+@   +8		priority
+#define PRIORITY	8
+@   +12		signal
+#define SIGNAL		12
+@   +16		blocked
+#define BLOCKED		16
+@   +20		flags
+#define FLAGS		20
+@   +24		errno
+#define ERRNO		24
+
+@ 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
+@ This first one will eventually disappear
+#define OS_NUMBER_OLD	0
+#define OS_NUMBER	9
+
+#define S_OLD_R0	64
+#define S_PC		60
+#define S_LR		56
+#define S_SP		52
+#define S_FP		48
+#define S_IP		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
+@ -------------------------------------------------------------------------------
+LC1:
+		.word	_current
+LC2:
+		.word	_sys_call_table
+
+vector_swi:	USER_SAVE_ALL
+		bic	lr, lr, #0xfc000003
+		ldr	r12, [lr, #-4]
+		teqp	pc, #0x00000003
+		and	r10, r12,#0x00f00000
+		teq	r10, #OS_NUMBER<<20
+		bne	_bad
+retry:		bic	r12, r12, #0xFF000000
+		bic	r12, r12, #0x00F00000
+		cmp	r12, #(KSWI_SYS_BASE - KSWI_BASE)
+		bcs	_arm_sys_call
+		cmp	r12, #NSYS_CALL
+		bcs	_swi_bad_call
+@		ldr	r10, [pc, #LC1 - . - 8]
+@		mov	r12, #0
+@		str	r12, [r10,#ERRNO]
+		ldr	r10, [pc, #LC2 - . - 8]
+		mov	r9, sp
+		mov	lr, pc
+		ldr	pc, [r10,r12,ASL#2]
+		str	r0, [sp,#0]		@ returned r0
+@		ldr	r10, [pc, #LC1 - . - 8]
+@		ldr	r12, [r10,#ERRNO]
+@		rsbs	r12, r12, #0
+@		beq	_ret_from_sys_call
+@		str	r12, [sp, #0]
+		b	_ret_from_sys_call
+
+_swi_bad_call:	BL	_sys_ni_syscall
+		STR	r0,[sp,#0]		@ returned r0
+		B	_ret_from_sys_call
+
+_bad:		STMFD	sp!,{r0-r3,r12}
+		BIC	r0,r12,#0xFF000000
+		BL	_deferred
+		LDMFD	sp!,{r0-r3,r12}
+		B	retry
+
+_arm_sys_call:	BIC	r0,r12,#0x000F0000
+		MOV	r1,sp
+		BL	_arm_syscall
+		B	_ret_from_sys_call
+
+@ ===============================================================================
+@ Undefined instruction handler
+@ -------------------------------------------------------------------------------
+
+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
+
+_undefinstr:	ldr	r3, [pc, #LC9 - . - 8]
+		ldr	pc, [r3]
+
+_fpundefinstr:	mov	r1,sp
+		ENABLEIRQS
+		mov	r2,#0
+		bl	_do_undefinstr
+_fpreturn:	b	_ret_from_sys_call
+
+undef_not_user:
+		SVC_SAVE_ALL
+		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
+		teqeqp	pc, #0x00000003
+		bic	r0, lr, #0xFC000003
+		add	r1, r0, #4
+		mov	r2, #0x100
+		mov	r3, sp
+		bl	_address_abort
+		teq	r0, #0
+		bne	_ret_from_sys_call
+	ADR	r0,t
+	BL	_printk
+		ldr	lr, [sp,#S_PC]
+		b	_undefinstr
+t:	.ascii "undef\r\n\0"
+	.align
+
+prefetch_not_user:
+		SVC_SAVE_ALL
+		mov	r0, sp
+		mov	r1, #BAD_PREFETCH
+		and	r2, lr, #3
+		bl	_bad_mode
+		SVC_RESTORE_ALL			@ Doesnt actually return
+
+@ ===============================================================================
+@ Address exception handler
+@ -------------------------------------------------------------------------------
+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
+		BIC	r0,lr,#0xFC000003
+		MOV	r1,sp
+		ORR	r2,r2,#0x400
+		BL	_do_excpt
+		LDMIA	sp,{r0-lr}
+		ADD	sp,sp,#15*4
+		MOVS	pc,lr
+		SVC_RESTORE_ALL
+
+addrexcptn_illegal_mode:
+		mov	r0,sp
+		str	lr, [sp, #-4]!
+		orr	r1, r2, #0x0c000000
+		teqp	r1, #0			@ change into mode
+		mov	r0, r0
+		mov	r1, r8
+		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
+		bl	_bad_mode
+		add	sp, sp, #16*4
+		SVC_RESTORE_ALL			@ Doesnt actually return
+
+@ ===============================================================================
+@ Trap initialisation
+@ -------------------------------------------------------------------------------
+
+swi_sigreturn:	SWI	SYS_sigreturn		@ SWI SYS_sigreturn
+		SWI	SYS_ERROR0
+		ldr	pc, .+8
+
+		.global	_trap_init
+_trap_init:	STMFD	sp !,{r0-r9,lr}		@ Save link register
+		TEQP	pc,#0x0C000003
+		MOV	r0,#0			@ Lowest location
+		MOV	r9,#0xEA000000		@ B instruction
+		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
+@		ADR	r8,vector_FIQ-36
+		ldr	r8, swi_sigreturn+8
+		LDR	r1, swi_sigreturn+4
+		ORR	r2,r9,r2,LSR#2
+		ORR	r3,r9,r3,LSR#2
+		ORR	r4,r9,r4,LSR#2
+		ORR	r5,r9,r5,LSR#2
+		ORR	r6,r9,r6,LSR#2
+		ORR	r7,r9,r7,LSR#2
+@		ORR	r8,r9,r8,LSR#2
+		LDR	r9,swi_sigreturn
+		STMIA	r0,{r1-r9}		@ Save all into page 0 ram
+		TEQP	pc,#0x08000002
+#if ARM < 300
+		MOV	r0,r0
+#endif
+		LDR	r13,[pc, #LC4 - . - 8]
+		TEQP	pc,#0x08000001
+#if ARM < 300
+		MOV	r0,r0
+#endif
+		MOV	r13,#0x8000		@ max 32*4 bytes
+		TEQP	pc,#0x08000003
+#if ARM < 300
+		MOV	r0,r0
+#endif
+		LDMFD	sp!,{r0-r9,pc}^		@ Return
+
+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
+#if ARM < 300
+		MOV	r0,r0
+#endif
+		LDR	lr,[pc, #LC4 - . - 8]
+		LDR	lr,[lr]
+		USER_SAVE_ALL
+urepeat:	MOV	r1,sp
+		MOV	r4,#IOC			@ point at IOC
+		LDRB	r2,[r4,#0x24]		@ get high priority first
+		ADR	r3,irq_prio_h
+		TEQ	r2,#0
+		LDREQB	r2,[r4,#0x14]		@ get low priority
+		ADREQ	r3,irq_prio_l
+		TEQ	r2,#0
+		BEQ	no_irq
+		LDRB	r2,[r3,r2]		@ Get IRQ number
+		LDR	r3,[pc, #LC5 - . - 8]
+		MOV	lr,pc
+		LDR	pc,[r3,r2,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
+no_irq:		B	_ret_from_sys_call
+
+IRQ_not_user:	TEQP	pc,#0x08000003
+#if ARM < 300
+		MOV	r0,r0
+#endif
+		SVC_IRQ_SAVE_ALL
+                AND	r2,lr,#3
+		TEQ	r2,#3
+		BNE	irq_illegal_mode
+srepeat:	MOV	r1,sp
+		MOV	r4,#IOC			@ point at IOC
+		LDRB	r2,[r4,#0x24]		@ get high priority first
+		ADR	r3,irq_prio_h
+		TEQ	r2,#0
+		LDREQB	r2,[r4,#0x14]		@ get low priority
+		ADREQ	r3,irq_prio_l
+		TEQ	r2,#0
+		BEQ	no_irq2
+		LDRB	r2,[r3,r2]		@ Get IRQ number
+		LDR	r3,[pc, #LC5 - . - 8]
+		MOV	lr,pc
+		LDR	pc,[r3,r2,LSL#2]
+		MOV	r2,#1
+		TEQ	r0,#0			@ Check to see if it is a fast IRQ
+		BLEQ	_ret_from_slow_irq
+		B	srepeat
+no_irq2:	SVC_RESTORE_ALL
+
+irq_illegal_mode:
+		MOV	r0,sp
+		MOV	r1,#BAD_IRQ
+		BL	_bad_mode
+		SVC_RESTORE_ALL			@ shouldnt return
+
+@ ===============================================================================
+@ FIQ handler code
+@ -------------------------------------------------------------------------------
+vector_FIQ:	MOV	r8,#0x03200000
+		LDRB	r8,[r8,#0x34]
+		AND	r8,r8,#15
+		ADR	r9,fiqtable
+		LDRB	r9,[r9,r8]
+		TEQ	r9,#0
+		LDRNE	r8,[pc, #LC5 - . - 8]
+		LDRNE	pc,[r8,r9,LSL#2]
+		SUBS	pc,lr,#4
+
+@ ===============================================================================
+@ 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
+		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
+		BIC	r0,lr,#0xFC000003
+		MOV	r3,sp
+		BL	data_do
+		SVC_RESTORE_ALL
+
+data_illegal_mode:
+		MOV	r0,sp
+		MOV	r1,#BAD_DATA
+		BL	_bad_mode
+		SVC_RESTORE_ALL			@ Doesnt actually return
+
+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
+
+fiqtable:	.byte	 0,16,17,16,18,16, 0,16,19,16, 0,16, 0,16, 0,16
+
+@ ----------------------------------------------------------------------------------------------
+
+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	_address_abort
+
+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	_address_abort
+
+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	_address_abort
+
+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	_address_abort
+
+instrldcstc:	AND	r7,r5,#255		@ Get offset
+		ORR	r2,r2,#0x600
+		B	ldrstrsubadd
+
+@ ===============================================================================
+
+LC6:
+		.word	_intr_count		@ -8
+		.word	_bh_mask		@ -4
+		.word	_bh_active		@ -0
+		.word	_need_resched		@ +4
+		.word	_current		@ +8
+		.word	_init_task		@ +12
+
+_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
+
+reschedule:	BL	_schedule
+		B	_ret_from_sys_call
+
+_ret_from_slow_irq:
+		LDR	r0,[pc, #LC6 - . -8]
+		LDR	r1,[r0]
+		TEQ	r1,#0
+		MOVNES	pc,lr
+		STMFD	sp!,{r4-r8,lr}
+		MOV	r4,r0
+		MOV	r5,r1
+		LDR	r6,[pc, #LC6 - . -4]
+		LDR	r7,[pc, #LC6 - . -0]
+recheck_bh2:	LDR	r0,[r6]
+		LDR	r1,[r7]
+		TST	r0,r1
+		LDMEQFD	sp!,{r4-r8,pc}^
+handle_bottom_half2:
+		ADD	r0,r5,#1
+		STR	r0,[r4]
+		MOV	r8,pc
+		TEQP	pc,#0x00000003
+#if ARM < 300
+		MOV	r0,r0			@ *** CHECK ***
+#endif
+		BL	_do_bottom_half
+		TEQP	r8,#0
+		STR	r5,[r4]
+		B	recheck_bh2
+
+		.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 -r -u -N linux.orig/arch/arm/lib/traps.o.diff linux.arm/arch/arm/lib/traps.o.diff
--- linux.orig/arch/arm/lib/traps.o.diff	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/lib/traps.o.diff	Fri Oct 27 23:14:13 1995
@@ -0,0 +1,29 @@
+--- traps.o.dis2	Thu Oct 26 15:00:35 1995
++++ traps.o.dis1	Thu Oct 26 15:00:34 1995
+@@ -45,7 +45,7 @@
+ 000000b8 <vector_undefinstr+14> e8cd7fff	stmia	sp, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, sl, fp, ip, sp, lr}^
+ 000000bc <vector_undefinstr+18> e1a00000	mov	r0, r0
+ 000000c0 <vector_undefinstr+1c> e3a0b000	mov	fp, #0
+-000000c4 <vector_undefinstr+20> e33ff003	teqp	pc, #3
++000000c4 <vector_undefinstr+20> e33ff3c2	teqp	pc, #8000003
+ 000000c8 <_undefinstr> e51f3038	ldr	r3, 00000098 <LC9>
+ 000000cc <_undefinstr+4> e493f000	ldr	pc, [r3]
+ 000000d0 <_fpundefinstr> e1a0100d	mov	r1, sp
+@@ -165,7 +165,7 @@
+ 00000298 <addrexcptn_illegal_mode+54> e8ddffff	ldmia	sp, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, sl, fp, ip, sp, lr, pc}^
+ 0000029c <swi_sigreturn> ef900077	swi	0x00900077
+ 000002a0 <swi_sigreturn+4> ef9f0000	swi	0x009f0000
+-000002a4 <L0> e51ff000	ldr	pc, 000002ac <_trap_init+4>
++000002a4 <L0> e59ff000	ldr	pc, 000002ac <_trap_init+4>
+ 000002a8 <_trap_init> e92d43ff	stmdb	sp!, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, lr}
+ 000002ac <_trap_init+4> e33ff3c3	teqp	pc, #c000003
+ 000002b0 <_trap_init+8> e3a00000	mov	r0, #0
+@@ -187,7 +187,7 @@
+ 000002f0 <_trap_init+48> e51f905c	ldr	r9, 0000029c <swi_sigreturn>
+ 000002f4 <_trap_init+4c> e88003fe	stmia	r0!, {r1, r2, r3, r4, r5, r6, r7, r8, r9}
+ 000002f8 <_trap_init+50> e33ff382	teqp	pc, #8000002
+-000002fc <_trap_init+54> e51fd00c	ldr	sp, 000002f8 <_trap_init+50>
++000002fc <_trap_init+54> e59fd00c	ldr	sp, 00000310 <LC4>
+ 00000300 <_trap_init+58> e33ff342	teqp	pc, #8000001
+ 00000304 <_trap_init+5c> e3a0dc80	mov	sp, #8000
+ 00000308 <_trap_init+60> e33ff3c2	teqp	pc, #8000003
diff -r -u -N linux.orig/arch/arm/mm/Makefile linux.arm/arch/arm/mm/Makefile
--- linux.orig/arch/arm/mm/Makefile	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/mm/Makefile	Fri Oct 27 23:14:13 1995
@@ -0,0 +1,34 @@
+#
+# 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...
+
+.c.o:
+	$(CC) $(CFLAGS) -c $<
+.s.o:
+	$(AS) -o $*.o $<
+.c.s:
+	$(CC) $(CFLAGS) -S $<
+
+OBJS	= init.o fault.o
+
+mm.o: $(OBJS)
+	$(LD) -r -o mm.o $(OBJS)
+#	stripper mm.o mm2.o
+	sync
+
+modules:
+
+dep:
+	$(CPP) -M *.c > .depend
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff -r -u -N linux.orig/arch/arm/mm/fault.c linux.arm/arch/arm/mm/fault.c
--- linux.orig/arch/arm/mm/fault.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/mm/fault.c	Fri Oct 27 23:14:13 1995
@@ -0,0 +1,256 @@
+/*
+ *  linux/arch/arm/mm/fault.c
+ *
+ *  Copyright (C) 1995  Linus Torvalds
+ */
+
+#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(vma, address, error_code & 2);
+		return;
+	}
+
+	do_no_page(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)
+ */
+int address_abort (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, 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, 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 -r -u -N linux.orig/arch/arm/mm/init.c linux.arm/arch/arm/mm/init.c
--- linux.orig/arch/arm/mm/init.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/mm/init.c	Fri Oct 27 23:14:14 1995
@@ -0,0 +1,274 @@
+/*
+ *  linux/arch/arm/mm/init.c
+ *
+ *  Copyright (C) 1995  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] & MAP_PAGE_RESERVED)
+			reserved++;
+		else if (!mem_map[i])
+			free++;
+		else
+			shared += mem_map[i]-1;
+	}
+	printk("%d pages of RAM\n",total);
+	printk("%d free pages\n",free);
+	printk("%d reserved pages\n",reserved);
+	printk("%d pages shared\n",shared);
+	show_buffers();
+#ifdef CONFIG_NET
+	show_net_buffers();
+#endif
+}
+
+extern unsigned long free_area_init(unsigned long, unsigned long);
+
+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 = pgd_offset(&init_task, i);
+		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)]=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);
+	
+	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;
+
+	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] = MAP_PAGE_RESERVED;
+
+	while(i < MAP_NR(end_mem))
+		mem_map[i++] = 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)]) {
+			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)] = 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] & MAP_PAGE_RESERVED)
+			continue;
+		val->totalram++;
+		if (!mem_map[i])
+			continue;
+		val->sharedram += mem_map[i]-1;
+	}
+	val->totalram <<= PAGE_SHIFT;
+	val->sharedram <<= PAGE_SHIFT;
+	return;
+}
diff -r -u -N linux.orig/arch/arm/mm/init2.c linux.arm/arch/arm/mm/init2.c
--- linux.orig/arch/arm/mm/init2.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/mm/init2.c	Fri Oct 27 23:14:14 1995
@@ -0,0 +1,269 @@
+/*
+ *  linux/arch/arm/mm/init.c
+ *
+ *  Copyright (C) 1995  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;
+pgd_t swapper_pg_dir[1024];
+int page_shift = 15;
+
+/*
+ * 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)
+{
+        memset(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)
+{
+        memset(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_SHIFT;
+	while (i-- > 0) {
+		total++;
+		if (mem_map[i] & MAP_PAGE_RESERVED)
+			reserved++;
+		else if (!mem_map[i])
+			free++;
+		else
+			shared += mem_map[i]-1;
+	}
+	printk("%d pages of RAM\n",total);
+	printk("%d free pages\n",free);
+	printk("%d reserved pages\n",reserved);
+	printk("%d pages shared\n",shared);
+	show_buffers();
+#ifdef CONFIG_NET
+	show_net_buffers();
+#endif
+}
+
+extern unsigned long free_area_init(unsigned long, unsigned long);
+
+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;
+	pgd_t *pg_dir;
+	pmd_t *pm_dir;
+	pte_t *pt_entry;
+
+	if(updated)
+		return 0;
+
+	address=LOGICAL_END;
+
+	for(i=32512*1024; i<LOGICAL_END; i+=PAGE_SIZE)
+	{
+		pg_dir = pgd_offset(&init_task, i);
+		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<(256*1024+LOGICAL_END); address += PAGE_SIZE)
+		{
+			mem_map[MAP_NR(address)]=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;
+	
+	extern int _end;
+
+	low_memory = PAGE_ALIGN(start_mem);
+
+	page_nr = MAP_NR(end_mem);
+	
+	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));
+	}
+	address = map_screen_mem(start_mem - 256*1024,0);
+
+	/* 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; i<((int)&_end)>>PAGE_SHIFT; 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(start_mem, end_mem);
+}
+
+void mem_init(unsigned long start_mem, unsigned long end_mem)
+{
+	int codepages = 0;
+	int reservedpages = 0;
+	int datapages = 0;
+	unsigned long tmp;
+	extern int etext;
+	int i;
+
+	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] = MAP_PAGE_RESERVED;
+
+	while(i < MAP_NR(end_mem))
+		mem_map[i++] = 0;
+
+#ifdef CONFIG_SCSI
+	scsi_mem_init(high_memory);
+#endif
+#ifdef CONFIG_SOUND
+	sound_mem_init();
+#endif
+	for (tmp = low_memory ; tmp < high_memory ; tmp += PAGE_SIZE) {
+		if (mem_map[MAP_NR(tmp)]) {
+			if (tmp < low_memory + (256*1024+32*1024))
+				reservedpages++;
+			else if (tmp < (unsigned long) &etext)
+				codepages++;
+			else
+				datapages++;
+			continue;
+		}
+		mem_map[MAP_NR(tmp)] = 1;
+		free_page(tmp);
+	}
+	tmp = nr_free_pages << PAGE_SHIFT;
+	printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data)\n",
+		tmp >> 10,
+		high_memory >> 10,
+		codepages << (PAGE_SHIFT-10),
+		reservedpages << (PAGE_SHIFT-10),
+		datapages << (PAGE_SHIFT-10));
+}
+
+void si_meminfo(struct sysinfo *val)
+{
+	int i;
+
+	i = high_memory >> PAGE_SHIFT;
+	val->totalram = 0;
+	val->sharedram = 0;
+	val->freeram = nr_free_pages << PAGE_SHIFT;
+	val->bufferram = buffermem;
+	while (i-- > 0)  {
+		if (mem_map[i] & MAP_PAGE_RESERVED)
+			continue;
+		val->totalram++;
+		if (!mem_map[i])
+			continue;
+		val->sharedram += mem_map[i]-1;
+	}
+	val->totalram <<= PAGE_SHIFT;
+	val->sharedram <<= PAGE_SHIFT;
+	return;
+}
diff -r -u -N linux.orig/arch/arm/mm/memory.c linux.arm/arch/arm/mm/memory.c
--- linux.orig/arch/arm/mm/memory.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/arch/arm/mm/memory.c	Fri Oct 27 23:14:15 1995
@@ -0,0 +1,35 @@
+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(updated)
+    return 0;
+
+  address=PHYS_START;
+  log_start >>= PAGE_SHIFT;
+
+  for(i=(32512*1024)>>PAGE_SHIFT; i<PHYS_START>>PAGE_SHIFT; i++)
+  {
+    if(i>=log_start)
+    {
+      init_task.tss.memmap[i]=address|PAGE_ACCESSED|PAGE_USER|PAGE_RW|PAGE_PRESENT;
+      address+=PAGE_SIZE;
+    }
+    else
+      init_task.tss.memmap[i]=0;
+  }
+  if(update)
+  {
+    for(; address<(256*1024+0x02000000); address += PAGE_SIZE)
+    {
+      mem_map[MAP_NR(address)]=1;
+      free_page(address);
+    }
+    updated=1;
+    invalidate();
+  }
+  return address;
+}
diff -r -u -N linux.orig/arch/i386/Makefile linux.arm/arch/i386/Makefile
--- linux.orig/arch/i386/Makefile	Fri Oct 27 23:08:13 1995
+++ linux.arm/arch/i386/Makefile	Fri Oct 27 23:13:37 1995
@@ -39,7 +39,7 @@
 
 HEAD := arch/i386/kernel/head.o
 
-SUBDIRS := $(SUBDIRS) arch/i386/kernel arch/i386/mm
+SUBDIRS := drivers $(SUBDIRS) arch/i386/kernel arch/i386/mm
 ARCHIVES := arch/i386/kernel/kernel.o arch/i386/mm/mm.o $(ARCHIVES)
 
 ifdef CONFIG_IBCS
diff -r -u -N linux.orig/arch/mips/Makefile linux.arm/arch/mips/Makefile
--- linux.orig/arch/mips/Makefile	Fri Oct 27 23:08:16 1995
+++ linux.arm/arch/mips/Makefile	Fri Oct 27 23:13:59 1995
@@ -40,7 +40,7 @@
 
 HEAD := arch/mips/kernel/head.o
 
-SUBDIRS := $(SUBDIRS) arch/mips/kernel arch/mips/mm
+SUBDIRS := drivers $(SUBDIRS) arch/mips/kernel arch/mips/mm
 ARCHIVES := arch/mips/kernel/kernel.o arch/mips/mm/mm.o $(ARCHIVES)
 
 MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
diff -r -u -N linux.orig/arch/sparc/Makefile linux.arm/arch/sparc/Makefile
--- linux.orig/arch/sparc/Makefile	Fri Oct 27 23:08:18 1995
+++ linux.arm/arch/sparc/Makefile	Fri Oct 27 23:13:56 1995
@@ -24,7 +24,7 @@
 
 HEAD := arch/sparc/kernel/head.o
 
-SUBDIRS := $(SUBDIRS) arch/sparc/kernel arch/sparc/lib arch/sparc/mm
+SUBDIRS := drivers $(SUBDIRS) arch/sparc/kernel arch/sparc/lib arch/sparc/mm
 ARCHIVES := arch/sparc/kernel/kernel.o arch/sparc/mm/mm.o $(ARCHIVES)
 LIBS := $(TOPDIR)/lib/lib.a $(LIBS) $(TOPDIR)/arch/sparc/lib/lib.a
 
diff -r -u -N linux.orig/drivers/char/diffs linux.arm/drivers/char/diffs
--- linux.orig/drivers/char/diffs	Thu Jan  1 01:00:00 1970
+++ linux.arm/drivers/char/diffs	Fri Oct 27 23:12:51 1995
@@ -0,0 +1,657 @@
+--- /usr/src/linux/arch/arm/drivers/char/tty_io.c	Fri Jul 14 00:07:27 1995
++++ tty_io.c	Sun Sep 24 11:09:57 1995
+@@ -19,7 +19,7 @@
+  * 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
++ * 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
+@@ -52,6 +52,7 @@
+ #include <linux/mm.h>
+ #include <linux/string.h>
+ #include <linux/malloc.h>
++#include <linux/config.h>
+ 
+ #include <asm/segment.h>
+ #include <asm/system.h>
+@@ -59,18 +60,19 @@
+ 
+ #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
+ 
+-extern int set_selection(const int arg, struct tty_struct *tty);
+-extern int paste_selection(struct tty_struct *tty);
+-extern int sel_loadlut(const int arg);
+-extern int mouse_reporting(void);
+-extern int shift_state;
+-extern int do_screendump(int arg);
++#define TTY_PARANOIA_CHECK
++#define CHECK_TTY_COUNT
++
++extern void do_blank_screen(int nopowersave);
++extern void do_unblank_screen(void);
++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 */
+@@ -78,10 +80,12 @@
+ 
+ /*
+  * 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;
+ 
+@@ -123,8 +127,6 @@
+ 	return(_tty_name(tty, buf));
+ }
+ 
+-#define TTY_PARANOIA_CHECK
+-
+ inline int tty_paranoia_check(struct tty_struct *tty, dev_t device,
+ 			      const char *routine)
+ {
+@@ -146,18 +148,45 @@
+ 	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 (%d, %d) tty->count(%d) != #fd's(%d) in %s\n",
++		       MAJOR(tty->device), MINOR(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;
+ }
+ 
+@@ -175,6 +204,8 @@
+ 		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);
+@@ -212,7 +243,7 @@
+ {
+ 	int	major, minor;
+ 	struct tty_driver *p;
+-
++	
+ 	minor = MINOR(device);
+ 	major = MAJOR(device);
+ 
+@@ -313,6 +344,7 @@
+ 
+ 	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;
+@@ -325,7 +357,7 @@
+ 		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)
+@@ -352,18 +384,22 @@
+ 				       -i);
+ 		}
+ 	}
+-
+-	if (tty->session > 0) {
+-		kill_sl(tty->session,SIGHUP,1);
+-		kill_sl(tty->session,SIGCONT,1);
+-	}
+-	tty->flags = 0;
+-	tty->session = 0;
+-	tty->pgrp = -1;
++	
+  	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)
+@@ -406,13 +442,19 @@
+ 	struct tty_struct *tty = current->tty;
+ 	struct task_struct *p;
+ 
+-	if (!tty)
++	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;
+ 
+@@ -466,6 +508,7 @@
+                 return;
+         if (!vc_cons_allocated(new_console))
+                 return;
++	last_console = fg_console;
+ 
+ 	/*
+ 	 * If we're switching, we could be going from KD_GRAPHICS to
+@@ -511,11 +554,9 @@
+ 	if (old_vc_mode != vt_cons[new_console]->vc_mode)
+ 	{
+ 		if (vt_cons[new_console]->vc_mode == KD_TEXT)
+-			unblank_screen();
+-		else {
+-			timer_active &= ~(1<<BLANK_TIMER);
+-			blank_screen();
+-		}
++			do_unblank_screen();
++		else
++			do_blank_screen(1);
+ 	}
+ 
+ 	/*
+@@ -715,7 +756,7 @@
+ 	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;
++	struct tty_driver *driver;	
+ 	int retval;
+ 	int idx;
+ 
+@@ -733,6 +774,7 @@
+ 	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 &&
+@@ -741,8 +783,7 @@
+ 		goto end_init;
+ 	retval = -ENOMEM;
+ 	if (!*tty_loc && !tty) {
+-		if (!(tty = (struct tty_struct*) kmalloc(sizeof(struct tty_struct),
+-							GFP_KERNEL)))
++		if (!(tty = (struct tty_struct*) get_free_page(GFP_KERNEL)))
+ 			goto end_init;
+ 		initialize_tty_struct(tty);
+ 		tty->device = device;
+@@ -772,9 +813,9 @@
+ 
+ 		if (!*o_tty_loc && !o_tty) {
+ 			dev_t 	o_device;
++			
+ 			o_tty = (struct tty_struct *)
+-				kmalloc(sizeof(struct tty_struct),
+-					GFP_KERNEL);
++				get_free_page(GFP_KERNEL);
+ 			if (!o_tty)
+ 				goto end_init;
+ 			o_device = MKDEV(driver->other->major,
+@@ -800,7 +841,7 @@
+ 			memset(o_ltp, 0, sizeof(struct termios));
+ 			goto repeat;
+ 		}
+-
++		
+ 	}
+ 	/* Now we have allocated all the structures: update all the pointers.. */
+ 	if (!*tp_loc) {
+@@ -826,8 +867,15 @@
+ 			}
+ 		}
+ 		tty = NULL;
+-	} else
++	} 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;
+@@ -862,9 +910,9 @@
+ 	retval = 0;
+ end_init:
+ 	if (tty)
+-		kfree_s(tty,sizeof(struct tty_struct));
++		free_page((unsigned long) tty);
+ 	if (o_tty)
+-		kfree_s(o_tty,sizeof(struct tty_struct));
++		free_page((unsigned long) o_tty);
+ 	if (tp)
+ 		kfree_s(tp, sizeof(struct termios));
+ 	if (o_tp)
+@@ -887,12 +935,13 @@
+ 	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;
+@@ -955,7 +1004,7 @@
+ 		}
+ 	}
+ #endif
+-
++	
+ 	if (tty->driver.close)
+ 		tty->driver.close(tty, filp);
+ 	if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
+@@ -974,6 +1023,9 @@
+ 	if (tty->count)
+ 		return;
+ 
++	/*
++	 * We're committed; at this point, we must not block!
++	 */
+ 	if (o_tty) {
+ 		if (o_tty->count)
+ 			return;
+@@ -981,10 +1033,11 @@
+ 		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
+@@ -1007,7 +1060,12 @@
+ 		(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;
+@@ -1036,11 +1094,12 @@
+ 	sti();
+ 	tty->magic = 0;
+ 	(*tty->driver.refcount)--;
+-	kfree_s(tty,sizeof(struct tty_struct));
++	free_page((unsigned long) tty);
++	filp->private_data = 0;
+ 	if (o_tty) {
+ 		o_tty->magic = 0;
+ 		(*o_tty->driver.refcount)--;
+-		kfree_s(o_tty,sizeof(struct tty_struct));
++		free_page((unsigned long) o_tty);
+ 	}
+ }
+ 
+@@ -1077,11 +1136,12 @@
+ 		noctty = 1;
+ 	}
+ 	minor = MINOR(device);
+-
++	
+ 	retval = init_dev(device, &tty);
+-	filp->private_data = 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;
+@@ -1118,6 +1178,7 @@
+ 	    !current->tty &&
+ 	    tty->session == 0) {
+ 		current->tty = tty;
++		current->tty_old_pgrp = 0;
+ 		tty->session = current->session;
+ 		tty->pgrp = current->pgrp;
+ 	}
+@@ -1190,13 +1251,14 @@
+ 		if (!tty->fasync && !tty->read_wait)
+ 			tty->minimum_to_wake = N_TTY_BUF_SIZE;
+ 	}
+-	return 0;
++	return 0;	
+ }
+ 
++#if 0
+ /*
+  * XXX does anyone use this anymore?!?
+  */
+-static int do_get_ps_info(int arg)
++static int do_get_ps_info(unsigned long arg)
+ {
+ 	struct tstruct {
+ 		int flag;
+@@ -1207,7 +1269,7 @@
+ 	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;
+@@ -1220,10 +1282,11 @@
+ 				put_fs_byte(*c++, d++);
+ 			put_fs_long(1, (unsigned long *)(ts->present+n));
+ 		}
+-		else
++		else	
+ 			put_fs_long(0, (unsigned long *)(ts->present+n));
+-	return(0);
++	return(0);			
+ }
++#endif
+ 
+ static int tty_ioctl(struct inode * inode, struct file * file,
+ 		     unsigned int cmd, unsigned long arg)
+@@ -1235,7 +1298,7 @@
+ 	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;
+@@ -1268,7 +1331,7 @@
+ 			retval = verify_area(VERIFY_READ, (void *) arg,
+ 					     sizeof (struct winsize));
+ 			if (retval)
+-				return retval;
++				return retval;			
+ 			memcpy_fromfs(&tmp_ws, (struct winsize *) arg,
+ 				      sizeof (struct winsize));
+ 			if (memcmp(&tmp_ws, &tty->winsize,
+@@ -1344,6 +1407,7 @@
+ 					return -EPERM;
+ 			}
+ 			current->tty = tty;
++			current->tty_old_pgrp = 0;
+ 			tty->session = current->session;
+ 			tty->pgrp = current->pgrp;
+ 			return 0;
+@@ -1389,39 +1453,61 @@
+ 			arg = get_fs_long((unsigned long *) 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 (get_fs_byte((char *)arg))
++			switch (retval = get_fs_byte((char *)arg))
+ 			{
+ 				case 0:
+-					return do_screendump(arg);
++				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:
+-					unblank_screen();
++					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_fs_byte(0, arg);
+-/* 					put_fs_byte(shift_state,arg); */
++			/*
++			 * 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_fs_byte(shift_state,arg);
+ 					return 0;
+ 				case 7:
+ 					put_fs_byte(mouse_reporting(),arg);
+ 					return 0;
+-				default:
++				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,
+@@ -1445,7 +1531,7 @@
+  * 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
+@@ -1461,7 +1547,7 @@
+ 	int session;
+ 	int		i;
+ 	struct file	*filp;
+-
++	
+ 	if (!tty)
+ 		return;
+ 	session  = tty->session;
+@@ -1520,7 +1606,7 @@
+ 	count = tty->flip.count;
+ 	tty->flip.count = 0;
+ 	sti();
+-
++	
+ #if 0
+ 	if (count > tty->max_flip_cnt)
+ 		tty->max_flip_cnt = count;
+@@ -1569,10 +1655,10 @@
+ 
+ 	if (!driver->put_char)
+ 		driver->put_char = tty_default_put_char;
+-
++	
+ 	driver->prev = 0;
+ 	driver->next = tty_drivers;
+-	tty_drivers->prev = driver;
++	if (tty_drivers) tty_drivers->prev = driver;
+ 	tty_drivers = driver;
+ 	return error;
+ }
+@@ -1585,31 +1671,32 @@
+ 	int	retval;
+ 	struct tty_driver *p;
+ 	int	found = 0;
+-	int	major_inuse = 0;
+-
+-	if (driver->refcount)
++	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)
+-			major_inuse++;
++			othername = p->name;
+ 	}
+ 
+-	if (!major_inuse) {
++	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 = driver->next->prev;
++		driver->next->prev = driver->prev;
+ 
+ 	return 0;
+ }
+@@ -1628,20 +1715,19 @@
+ 	(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
+ 
+ 	/*
+-	 * Set up the standard termios.  Individual tty drivers may
++	 * 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
++	 * set up the console device so that later boot sequences can 
+ 	 * inform about problems etc..
+ 	 */
+ 	return con_init(kmem_start);
+@@ -1662,6 +1748,10 @@
+ 
+ 	kmem_start = kbd_init(kmem_start);
+ 	kmem_start = rs_init(kmem_start);
++#ifdef CONFIG_CYCLADES
++	kmem_start = cy_init(kmem_start);
++#endif
+ 	kmem_start = pty_init(kmem_start);
++	kmem_start = vcs_init(kmem_start);
+ 	return kmem_start;
+ }
diff -r -u -N linux.orig/drivers/net/Makefile linux.arm/drivers/net/Makefile
--- linux.orig/drivers/net/Makefile	Fri Oct 27 23:08:31 1995
+++ linux.arm/drivers/net/Makefile	Fri Oct 27 23:12:05 1995
@@ -8,7 +8,7 @@
 include CONFIG
 
 # Build MODULES by appending to this string for every driver below
-MODULES :=
+#MODULES :=
 
 NETDRV_OBJS := Space.o auto_irq.o net_init.o loopback.o
 override CFLAGS := $(CFLAGS) -I../../net/inet
@@ -263,9 +263,9 @@
 
 dep:
 	$(CPP) -M $(NETDRV_OBJS:.o=.c) > .depend
-ifdef MODULES
-	$(CPP) -M -DMODULE $(MODULES:.o=.c) >> .depend
-endif
+#ifdef MODULES
+#	$(CPP) -M -DMODULE $(MODULES:.o=.c) >> .depend
+#endif
 
 tar:
 
diff -r -u -N linux.orig/drivers/net/plip.c linux.arm/drivers/net/plip.c
--- linux.orig/drivers/net/plip.c	Fri Oct 27 23:08:31 1995
+++ linux.arm/drivers/net/plip.c	Fri Oct 27 23:12:01 1995
@@ -173,7 +173,7 @@
 			unsigned char msb;
 			unsigned char lsb;
 #else
-#error	"Please fix the endianness defines in <asm/byteorder.h>"
+#error	"Please fix endianness defines in <asm/byteorder.h>"
 #endif						
 		} b;
 		unsigned short h;
diff -r -u -N linux.orig/fs/Makefile linux.arm/fs/Makefile
--- linux.orig/fs/Makefile	Fri Oct 27 23:08:49 1995
+++ linux.arm/fs/Makefile	Fri Oct 27 23:09:17 1995
@@ -11,8 +11,8 @@
 
 ifdef CONFIG_MINIX_FS
 FS_SUBDIRS := $(FS_SUBDIRS) minix
-else
-MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) minix
+#else
+#MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) minix
 endif
 
 ifdef CONFIG_EXT_FS
@@ -25,8 +25,8 @@
 
 ifdef CONFIG_MSDOS_FS
 FS_SUBDIRS := $(FS_SUBDIRS) msdos
-else
-MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) msdos
+#else
+#MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) msdos
 endif
 
 ifdef CONFIG_PROC_FS
@@ -47,20 +47,20 @@
 
 ifdef CONFIG_XIA_FS
 FS_SUBDIRS := $(FS_SUBDIRS) xiafs
-else
-MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) xiafs
+#else
+#MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) xiafs
 endif
 
 ifdef CONFIG_UMSDOS_FS
 FS_SUBDIRS := $(FS_SUBDIRS) umsdos
-else
-MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) umsdos
+#else
+#MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) umsdos
 endif
 
 ifdef CONFIG_SYSV_FS
 FS_SUBDIRS := $(FS_SUBDIRS) sysv
-else
-MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) sysv
+#else
+#MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) sysv
 endif
 
 ifdef CONFIG_HPFS_FS
@@ -69,8 +69,8 @@
 
 ifdef CONFIG_BINFMT_ELF
 BINFMTS := $(BINFMTS) binfmt_elf.o
-else
-MODULE_OBJS := $(MODULE_OBJS) binfmt_elf.o
+#else
+#MODULE_OBJS := $(MODULE_OBJS) binfmt_elf.o
 endif
 
 .c.s:
@@ -80,14 +80,18 @@
 .s.o:
 	$(AS) -o $*.o $<
 
-OBJS=	open.o read_write.o inode.o devices.o file_table.o buffer.o super.o \
-	block_dev.o stat.o exec.o pipe.o namei.o fcntl.o ioctl.o \
+#OBJS=	open.o read_write.o inode.o devices.o file_table.o buffer.o super.o \
+#	block_dev.o stat.o exec.o pipe.o namei.o fcntl.o ioctl.o \
+#	select.o fifo.o locks.o filesystems.o dcache.o $(BINFMTS)
+OBJS=	exec.o open.o read_write.o inode.o devices.o file_table.o buffer.o super.o \
+	block_dev.o stat.o pipe.o namei.o fcntl.o ioctl.o \
 	select.o fifo.o locks.o filesystems.o dcache.o $(BINFMTS)
 
 all: fs.o filesystems.a
 
 fs.o: $(OBJS)
 	$(LD) -r -o fs.o $(OBJS)
+#	stripper fs.o fs2.o
 
 filesystems.a: dummy
 	rm -f filesystems.a
@@ -95,11 +99,19 @@
 	  test ! -d $$i || \
 	    { $(MAKE) -C $$i; $(AR) rcs filesystems.a $$i/$$i.o; }; done
 
+#ifdef MODULE_OBJS
+
 modules: $(MODULE_OBJS)
 	for i in $(MODULE_FS_SUBDIRS); do $(MAKE) -C $$i modules; done
 	cd ../modules;for i in $(MODULE_OBJS); do ln -sf ../fs/$$i .; done
 	cd ../modules;for i in $(MODULE_FS_SUBDIRS); \
 	do echo $$i.o; ln -sf ../fs/$$i/$$i.o .; done > ../modules/FS_MODULES
+
+#else
+ifdef not_defined
+modules:
+
+endif
 
 depend dep:
 	$(CPP) -M *.c > .depend
diff -r -u -N linux.orig/fs/buffer.c linux.arm/fs/buffer.c
--- linux.orig/fs/buffer.c	Fri Oct 27 23:08:45 1995
+++ linux.arm/fs/buffer.c	Fri Oct 27 23:09:20 1995
@@ -1617,6 +1617,7 @@
 	int i;
         int isize = BUFSIZE_INDEX(BLOCK_SIZE);
 
+#ifndef __arm__
 	if (high_memory >= 4*1024*1024) {
 		if(high_memory >= 16*1024*1024)
 			 nr_hash = 16381;
@@ -1625,6 +1626,9 @@
 	} else {
 		nr_hash = 997;
 	};
+#else
+	nr_hash = 4093;
+#endif
 	
 	hash_table = (struct buffer_head **) vmalloc(nr_hash * 
 						     sizeof(struct buffer_head *));
diff -r -u -N linux.orig/fs/exec.c linux.arm/fs/exec.c
--- linux.orig/fs/exec.c	Fri Oct 27 23:08:45 1995
+++ linux.arm/fs/exec.c	Fri Oct 27 23:09:21 1995
@@ -237,14 +237,25 @@
 	set_fs(USER_DS);
 /* Dump the data area */
 	if (dump.u_dsize != 0) {
+#ifndef __arm__
 		dump_start = dump.u_tsize << 12;
 		dump_size = dump.u_dsize << 12;
+#else
+		dump_start = (dump.u_tsize << PAGE_SHIFT) + 32768;
+		dump_size = dump.u_dsize << PAGE_SHIFT;
+		verify_area (VERIFY_READ, 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 << 12;
+#else
+		dump_start = dump.start_stack;
+		dump_size = dump.u_ssize << PAGE_SHIFT;
+#endif
 		DUMP_WRITE(dump_start,dump_size);
 	}
 /* Finally dump the task struct.  Not be used by gdb, but could be useful */
@@ -307,7 +318,11 @@
 	if (mpnt) {
 		mpnt->vm_task = current;
 		mpnt->vm_start = PAGE_MASK & (unsigned long) p;
+#ifndef __arm__
 		mpnt->vm_end = TASK_SIZE;
+#else
+		mpnt->vm_end = 24 * 1024 * 1024;
+#endif
 		mpnt->vm_page_prot = PAGE_COPY;
 		mpnt->vm_flags = VM_STACK_FLAGS;
 		mpnt->vm_ops = NULL;
@@ -400,14 +415,23 @@
 	while (argc-- > 0) {
 		if (from_kmem == 1)
 			set_fs(new_fs);
+#ifndef __arm__
 		if (!(tmp = (char *)get_fs_long(((unsigned long *)argv)+argc)))
+#else
+		if (!(tmp = (char *)(from_kmem&3?*(((unsigned long *)argv)+argc):
+				get_fs_long(((unsigned long *)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_fs_byte(tmp++));
+#else
+		} while (from_kmem&2?*tmp++:get_fs_byte(tmp++));
+#endif
 		if (p < len) {	/* this shouldn't happen - 128kB */
 			set_fs(old_fs);
 			return 0;
@@ -426,7 +450,11 @@
 					set_fs(new_fs);
 
 			}
+#ifndef __arm__
 			*(pag + offset) = get_fs_byte(tmp);
+#else
+			*(pag + offset) = (from_kmem&2?*tmp:get_fs_byte(tmp));
+#endif
 		}
 	}
 	if (from_kmem==2)
@@ -442,7 +470,10 @@
 	code_limit = TASK_SIZE;
 	data_limit = TASK_SIZE;
 	code_base = data_base = 0;
+#ifndef __arm__
+	/* load_aout_binary seems to set this up! */
 	current->mm->start_code = code_base;
+#endif
 	data_base += data_limit;
 	for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) {
 		data_base -= PAGE_SIZE;
@@ -484,7 +515,11 @@
  			goto close_readexec;
 	} else
 		file.f_pos = offset;
+#ifndef __arm__
 	if (get_fs() == USER_DS) {
+#else
+	if ((unsigned long)addr < LOGICAL_END) {
+#endif
 		result = verify_area(VERIFY_WRITE, addr, count);
 		if (result)
 			goto close_readexec;
@@ -713,8 +748,14 @@
 exec_error2:
 	iput(bprm.inode);
 exec_error1:
+#ifndef __arm__
 	for (i=0 ; i<MAX_ARG_PAGES ; i++)
 		free_page(bprm.page[i]);
+#else
+	for (i=0 ; i<MAX_ARG_PAGES ; i++)
+		if(bprm.page[i])
+			free_page(bprm.page[i]);
+#endif
 	return(retval);
 }
 
@@ -741,9 +782,15 @@
 	int fd, error;
 	unsigned long p = bprm->p;
 	unsigned long fd_offset;
+#ifdef __arm__
+	int mach_type_arm;
+#endif
 
 	ex = *((struct exec *) bprm->buf);		/* exec-header */
-	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC && 
+	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC &&
+#ifdef __arm__
+	     N_MAGIC(ex) != NMAGIC &&
+#endif
 	     N_MAGIC(ex) != QMAGIC) ||
 	    ex.a_trsize || ex.a_drsize ||
 	    bprm->inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {
@@ -770,20 +817,29 @@
 		(current->mm->start_brk =
 		(current->mm->end_data = ex.a_data +
 		(current->mm->end_code = ex.a_text +
-		(current->mm->start_code = N_TXTADDR(ex)))));
+		(current->mm->start_code = 0x8000/*N_TXTADDR(ex)*/))));
 	current->mm->rss = 0;
 	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);
+#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);
+#endif
 	} else {
 		if (ex.a_text & 0xfff || ex.a_data & 0xfff)
 			printk(KERN_NOTICE "executable not page aligned\n");
-		
+
 		fd = open_inode(bprm->inode, O_RDONLY);
 		
 		if (fd < 0)
@@ -798,18 +854,28 @@
 				  (char *) N_TXTADDR(ex), ex.a_text+ex.a_data);
 			goto beyond_if;
 		}
-
+#ifndef __arm__
 		error = do_mmap(file, N_TXTADDR(ex), ex.a_text,
 			PROT_READ | PROT_EXEC,
 			MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
 			fd_offset);
-
 		if (error != N_TXTADDR(ex)) {
 			sys_close(fd);
 			send_sig(SIGKILL, current, 0);
 			return error;
 		}
-		
+#else
+		error = do_mmap(file, 0x8000, ex.a_text,
+			PROT_READ | PROT_EXEC,
+			MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
+			(mach_type_arm ? fd_offset : 0x8000));
+		if (error != 0x8000) {
+			sys_close(fd);
+			send_sig(SIGKILL, current, 0);
+			return error;
+		}
+#endif
+#ifndef __arm__
  		error = do_mmap(file, N_TXTADDR(ex) + ex.a_text, ex.a_data,
 				PROT_READ | PROT_WRITE | PROT_EXEC,
 				MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
@@ -819,6 +885,17 @@
 			send_sig(SIGKILL, current, 0);
 			return error;
 		}
+#else
+ 		error = do_mmap(file, 0x8000 + ex.a_text, ex.a_data,
+				PROT_READ | PROT_WRITE | PROT_EXEC,
+				MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
+				(mach_type_arm ? fd_offset : 0x8000) + ex.a_text);
+		sys_close(fd);
+		if (error != 0x8000 + ex.a_text) {
+			send_sig(SIGKILL, current, 0);
+			return error;
+		}
+#endif
 	}
 beyond_if:
 	if (current->exec_domain && current->exec_domain->use_count)
@@ -843,7 +920,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->R0;
+#endif
 }
 
 
@@ -854,7 +935,7 @@
 	struct  inode * inode;
 	unsigned int len;
 	unsigned int bss;
-	unsigned int start_addr;
+	unsigned int start_addr, offset;
 	int error;
 	
 	file = current->files->fd[fd];
@@ -877,19 +958,41 @@
 		printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n");
 		return -ENOEXEC;
 	}
-	
+
 	if (N_FLAGS(ex)) return -ENOEXEC;
 
 	/* 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;
+	offset = N_TXTOFF(ex);
+#else
+	if (N_MAGIC(ex) == QMAGIC)
+	{
+		if ((ex.a_entry & 0x7000) == 0x7000)
+		{
+			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);
+	}
+#endif
 
 	/* 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,
-			N_TXTOFF(ex));
+			offset);
+
 	if (error != start_addr)
 		return error;
 	len = PAGE_ALIGN(ex.a_text + ex.a_data);
diff -r -u -N linux.orig/fs/ext2/Makefile linux.arm/fs/ext2/Makefile
--- linux.orig/fs/ext2/Makefile	Fri Oct 27 23:08:46 1995
+++ linux.arm/fs/ext2/Makefile	Fri Oct 27 23:09:49 1995
@@ -19,6 +19,7 @@
 
 ext2.o: $(OBJS)
 	$(LD) -r -o ext2.o $(OBJS)
+#	stripper ext2.o ext22.o
 
 dep:
 	$(CPP) -M *.c > .depend
diff -r -u -N linux.orig/fs/isofs/Makefile linux.arm/fs/isofs/Makefile
--- linux.orig/fs/isofs/Makefile	Fri Oct 27 23:08:48 1995
+++ linux.arm/fs/isofs/Makefile	Fri Oct 27 23:09:40 1995
@@ -14,12 +14,25 @@
 .s.o:
 	$(AS) -o $*.o $<
 
-OBJS=	namei.o inode.o file.o dir.o util.o rock.o symlink.o
+ifdef CONFIG_ARM
+ROCK=rock-arm.o
+else
+ROCK=rock.o
+endif
+
+OBJS=	namei.o inode.o file.o dir.o util.o $(ROCK) symlink.o
 
 isofs.o: $(OBJS)
 	$(LD) -r -o isofs.o $(OBJS)
 
+ifdef CONFIG_ARM
+isofs-module.o:
+	$(LD) -r -o isofs-module.o $(OBJS) `gcc --print-libgcc-file-name`
+
+modules: isofs-module.o
+else
 modules: isofs.o
+endif
 	ln -sf ../fs/isofs/isofs.o $(TOPDIR)/modules
 
 dep:
diff -r -u -N linux.orig/fs/isofs/rock-arm.c linux.arm/fs/isofs/rock-arm.c
--- linux.orig/fs/isofs/rock-arm.c	Thu Jan  1 01:00:00 1970
+++ linux.arm/fs/isofs/rock-arm.c	Fri Oct 27 23:09:43 1995
@@ -0,0 +1,542 @@
+/*
+ *  linux/fs/isofs/rock.c
+ *
+ *  (C) 1992, 1993  Eric Youngdale
+ *
+ *  Rock Ridge Extensions to iso9660
+ */
+#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>
+
+#undef SP /* CLASH HERE! */
+
+#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 -r -u -N linux.orig/fs/isofs/rock-arm.h linux.arm/fs/isofs/rock-arm.h
--- linux.orig/fs/isofs/rock-arm.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/fs/isofs/rock-arm.h	Fri Oct 27 23:09:43 1995
@@ -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 -r -u -N linux.orig/fs/msdos/Makefile linux.arm/fs/msdos/Makefile
--- linux.orig/fs/msdos/Makefile	Fri Oct 27 23:08:49 1995
+++ linux.arm/fs/msdos/Makefile	Fri Oct 27 23:09:26 1995
@@ -18,6 +18,7 @@
 
 msdos.o: $(OBJS)
 	$(LD) -r -o msdos.o $(OBJS)
+#	stripper msdos.o msdos2.o
 
 modules: msdos.o
 	ln -sf ../fs/msdos/msdos.o $(TOPDIR)/modules
diff -r -u -N linux.orig/fs/msdos/inode.c linux.arm/fs/msdos/inode.c
--- linux.orig/fs/msdos/inode.c	Fri Oct 27 23:08:49 1995
+++ linux.arm/fs/msdos/inode.c	Fri Oct 27 23:09:27 1995
@@ -226,7 +226,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;
@@ -239,9 +244,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 -r -u -N linux.orig/fs/proc/Makefile linux.arm/fs/proc/Makefile
--- linux.orig/fs/proc/Makefile	Fri Oct 27 23:08:52 1995
+++ linux.arm/fs/proc/Makefile	Fri Oct 27 23:09:32 1995
@@ -18,6 +18,7 @@
 
 proc.o: $(OBJS)
 	$(LD) -r -o proc.o $(OBJS)
+#	stripper proc.o proc2.o
 
 dep:
 	$(CPP) -M *.c > .depend
diff -r -u -N linux.orig/fs/proc/array.c linux.arm/fs/proc/array.c
--- linux.orig/fs/proc/array.c	Fri Oct 27 23:08:52 1995
+++ linux.arm/fs/proc/array.c	Fri Oct 27 23:09:33 1995
@@ -319,6 +319,12 @@
 			      x86_capability & 256 ? "yes" : "no",
 		              loops_per_sec/500000, (loops_per_sec/5000) % 100
 			      );
+#elif defined(__arm__)
+	return sprintf(buffer,	"cpu\t\t: arm%d\n"
+				"BogoMips\t: %lu.%02lu\n",
+				3,
+				loops_per_sec/500000, (loops_per_sec/5000) % 100
+				);
 #else
 	return 0;
 #endif
@@ -419,7 +425,6 @@
 
 static unsigned long get_wchan(struct task_struct *p)
 {
-#ifdef __i386__
 	unsigned long ebp, eip;
 	unsigned long stack_page;
 	int count = 0;
@@ -429,6 +434,7 @@
 	stack_page = p->kernel_stack_page;
 	if (!stack_page)
 		return 0;
+#ifdef __i386__
 	ebp = p->tss.ebp;
 	do {
 		if (ebp < stack_page || ebp >= 4092+stack_page)
@@ -439,12 +445,36 @@
 			return eip;
 		ebp = *(unsigned long *) ebp;
 	} while (count++ < 16);
+#elif defined(__arm__)
+	ebp = p->tss.sregs[11-4]; /* get current fp (should be schedule) */
+	do {
+		if (ebp < stack_page || ebp > 4092+stack_page)
+			return 0;
+#if defined(__arm2__) || defined(__arm3__)
+		eip = ((*(unsigned long *) ebp) - 16) & ~0xfc000003;
+#else
+		eip = (*(unsigned long *) ebp) - 16;
+#endif
+		if ((void *)eip != sleep_on &&
+		    (void *)eip != interruptible_sleep_on &&
+		    (void *)eip != schedule)
+			return eip;
+		ebp = *(unsigned long *) (ebp - 12);
+	} while (count++ < 16);
 #endif
 	return 0;
 }
 
+#ifdef __i386__
 #define	KSTK_EIP(stack)	(((unsigned long *)stack)[1019])
 #define	KSTK_ESP(stack)	(((unsigned long *)stack)[1022])
+#elif defined (__arm__)
+#define KSTK_EIP(stack) (((unsigned long *)stack)[1022])
+#define KSTK_ESP(stack) (((unsigned long *)stack)[1020])
+#else
+#define KSTK_EIP(stack) 0
+#define KSTK_ESP(stack) 0
+#endif
 
 static int get_stat(int pid, char * buffer)
 {
@@ -509,7 +539,11 @@
 		(*p)->it_real_value,
 		(*p)->start_time,
 		vsize,
+#ifndef __arm__
 		(*p)->mm->rss, /* you might want to shift this left 3 */
+#else
+		(*p)->mm->rss * 8,
+#endif
 		(*p)->rlim[RLIMIT_RSS].rlim_cur,
 		(*p)->mm->start_code,
 		(*p)->mm->end_code,
@@ -599,7 +633,7 @@
 static int get_statm(int pid, char * buffer)
 {
 	struct task_struct ** p = get_task(pid);
-	int size=0, resident=0, share=0, trs=0, lrs=0, drs=0, dt=0;
+	int size=0, resident=0, share=0, trs=0, lrs=0, drs=0, dt=0, t=0;
 
 	if (!p || !*p)
 		return 0;
@@ -619,17 +653,127 @@
 				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;
+t += sprintf(buffer+t, "%d %d %d %d %d %d %d\n", total, pages, shared, trs, lrs, drs, dirty);
 			vma = vma->vm_next;
 		}
 	}
-	return sprintf(buffer,"%d %d %d %d %d %d %d\n",
+	return t+sprintf(buffer+t,"%d %d %d %d %d %d %d\n",
 		       size, resident, share, trs, lrs, drs, dt);
 }
 
+static int mmaps_pgd(pgd_t *pgd, char *line, int lineno)
+{
+	pmd_t *pmd;
+	pte_t *pte;
+	int len;
+	
+	if (pgd_none(*pgd))
+		return 0;
+
+	if (pgd_bad(*pgd))
+		return 0;
+			
+	pmd = pmd_offset(pgd, lineno << 15);
+
+	if (pmd_none (*pmd))
+		return 0;
+
+	if (pmd_bad (*pmd))
+		return 0;
+
+	pte = pte_offset(pmd, lineno << 15);
+
+	if (pte_none (*pte))
+		return 0;
+
+	len = sprintf(line, "%08X = %08X ", lineno << 15, pte_val(*pte));
+	if (pte_present (*pte))
+		len += sprintf(line + len, "[%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))]);
+	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;
+
+	if (lineno < (0x02000000 >> 15))
+	{
+	
+		do
+		{
+			pgd_t *pgd = pgd_offset(*p, lineno << 15);
+			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 ++;
+			}
+
+		}
+		while (count && lineno < (0x02000000 >> 15));
+	}
+
+	file->f_pos = (lineno << 10) + column;
+	
+	return destptr - buf;
+}
+
 /*
  * The way we support synthetic files > 4K
  * - without storing their contents in some buffer and
@@ -906,6 +1050,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 -r -u -N linux.orig/fs/proc/array.c.old linux.arm/fs/proc/array.c.old
--- linux.orig/fs/proc/array.c.old	Thu Jan  1 01:00:00 1970
+++ linux.arm/fs/proc/array.c.old	Fri Oct 27 23:09:38 1995
@@ -0,0 +1,941 @@
+#define THREE_LEVEL
+/*
+ *  linux/fs/proc/array.c
+ *
+ *  Copyright (C) 1992  by Linus Torvalds
+ *  based on ideas by Darren Senn
+ *
+ * Fixes:
+ * Michael. K. Johnson: stat,statm extensions.
+ *                      <johnsonm@stolaf.edu>
+ *
+ * Pauline Middelink :  Made cmdline,envline only break at '\0's, to
+ *                      make sure SET_PROCTITLE works. Also removed
+ *                      bad '!' which forced address recalculation for
+ *                      EVERY character on the current page.
+ *                      <middelin@polyware.iaf.nl>
+ *
+ * Danny ter Haar    :	Some minor additions for cpuinfo
+ *			<danny@ow.nl>
+ *
+ * Alessandro Rubini :  profile extension.
+ *                      <rubini@ipvvis.unipv.it>
+ *
+ * Jeff Tranter      :  added BogoMips field to cpuinfo
+ *                      <Jeff_Tranter@Mitel.COM>
+ *
+ * Bruno Haible      :  remove 4K limit for the maps file
+ * <haible@ma2s2.mathematik.uni-karlsruhe.de>
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/tty.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/string.h>
+#include <linux/mman.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+
+#include <asm/segment.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+
+#define LOAD_INT(x) ((x) >> FSHIFT)
+#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
+
+#ifdef CONFIG_DEBUG_MALLOC
+int get_malloc(char * buffer);
+#endif
+
+
+static int read_core(struct inode * inode, struct file * file,char * buf, int count)
+{
+	unsigned long p = file->f_pos;
+	int read;
+	int count1;
+	char * pnt;
+	struct user dump;
+
+	memset(&dump, 0, sizeof(struct user));
+	dump.magic = CMAGIC;
+	dump.u_dsize = high_memory >> 12;
+
+	if (count < 0)
+		return -EINVAL;
+	if (p >= high_memory + PAGE_SIZE)
+		return 0;
+	if (count > high_memory + PAGE_SIZE - p)
+		count = high_memory + PAGE_SIZE - p;
+	read = 0;
+
+	if (p < sizeof(struct user) && count > 0) {
+		count1 = count;
+		if (p + count1 > sizeof(struct user))
+			count1 = sizeof(struct user)-p;
+		pnt = (char *) &dump + p;
+		memcpy_tofs(buf,(void *) pnt, count1);
+		buf += count1;
+		p += count1;
+		count -= count1;
+		read += count1;
+	}
+
+	while (p < 2*PAGE_SIZE && count > 0) {
+		put_fs_byte(0,buf);
+		buf++;
+		p++;
+		count--;
+		read++;
+	}
+	memcpy_tofs(buf,(void *) (p - PAGE_SIZE),count);
+	read += count;
+	file->f_pos += read;
+	return read;
+}
+
+static struct file_operations proc_kcore_operations = {
+	NULL,           /* lseek */
+	read_core,
+};
+
+struct inode_operations proc_kcore_inode_operations = {
+	&proc_kcore_operations, 
+};
+
+
+#ifdef CONFIG_PROFILE
+
+extern unsigned long prof_len;
+extern unsigned long * prof_buffer;
+/*
+ * This function accesses profiling information. The returned data is
+ * binary: the sampling step and the actual contents of the profile
+ * buffer. Use of the program readprofile is recommended in order to
+ * get meaningful info out of these data.
+ */
+static int read_profile(struct inode *inode, struct file *file, char *buf, int count)
+{
+    unsigned long p = file->f_pos;
+	int read;
+	char * pnt;
+	unsigned long sample_step = 1 << CONFIG_PROFILE_SHIFT;
+
+	if (count < 0)
+	    return -EINVAL;
+	if (p >= (prof_len+1)*sizeof(unsigned long))
+	    return 0;
+	if (count > (prof_len+1)*sizeof(unsigned long) - p)
+	    count = (prof_len+1)*sizeof(unsigned long) - p;
+    read = 0;
+
+    while (p < sizeof(unsigned long) && count > 0) {
+        put_fs_byte(*((char *)(&sample_step)+p),buf);
+		buf++; p++; count--; read++;
+    }
+    pnt = (char *)prof_buffer + p - sizeof(unsigned long);
+	memcpy_tofs(buf,(void *)pnt,count);
+	read += count;
+	file->f_pos += read;
+	return read;
+}
+
+/* Writing to /proc/profile resets the counters */
+static int write_profile(struct inode * inode, struct file * file, char * buf, int count)
+{
+    int i=prof_len;
+
+    while (i--)
+	    prof_buffer[i]=0UL;
+    return count;
+}
+
+static struct file_operations proc_profile_operations = {
+	NULL,           /* lseek */
+	read_profile,
+	write_profile,
+};
+
+struct inode_operations proc_profile_inode_operations = {
+	&proc_profile_operations, 
+};
+
+#endif /* CONFIG_PROFILE */
+
+
+static int get_loadavg(char * buffer)
+{
+	int a, b, c;
+
+	a = avenrun[0] + (FIXED_1/200);
+	b = avenrun[1] + (FIXED_1/200);
+	c = avenrun[2] + (FIXED_1/200);
+	return sprintf(buffer,"%d.%02d %d.%02d %d.%02d\n",
+		LOAD_INT(a), LOAD_FRAC(a),
+		LOAD_INT(b), LOAD_FRAC(b),
+		LOAD_INT(c), LOAD_FRAC(c));
+}
+
+static int get_kstat(char * buffer)
+{
+	int i, len;
+	unsigned sum = 0;
+
+	for (i = 0 ; i < 16 ; i++)
+		sum += kstat.interrupts[i];
+	len = sprintf(buffer,
+		"cpu  %u %u %u %lu\n"
+		"disk %u %u %u %u\n"
+		"page %u %u\n"
+		"swap %u %u\n"
+		"intr %u",
+		kstat.cpu_user,
+		kstat.cpu_nice,
+		kstat.cpu_system,
+		jiffies - (kstat.cpu_user + kstat.cpu_nice + kstat.cpu_system),
+		kstat.dk_drive[0],
+		kstat.dk_drive[1],
+		kstat.dk_drive[2],
+		kstat.dk_drive[3],
+		kstat.pgpgin,
+		kstat.pgpgout,
+		kstat.pswpin,
+		kstat.pswpout,
+		sum);
+	for (i = 0 ; i < 16 ; i++)
+		len += sprintf(buffer + len, " %u", kstat.interrupts[i]);
+	len += sprintf(buffer + len,
+		"\nctxt %u\n"
+		"btime %lu\n",
+		kstat.context_swtch,
+		xtime.tv_sec - jiffies / HZ);
+	return len;
+}
+
+
+static int get_uptime(char * buffer)
+{
+	unsigned long uptime;
+	unsigned long idle;
+
+	uptime = jiffies;
+	idle = task[0]->utime + task[0]->stime;
+
+	/* The formula for the fraction parts really is ((t * 100) / HZ) % 100, but
+	   that would overflow about every five days at HZ == 100.
+	   Therefore the identity a = (a / b) * b + a % b is used so that it is
+	   calculated as (((t / HZ) * 100) + ((t % HZ) * 100) / HZ) % 100.
+	   The part in front of the '+' always evaluates as 0 (mod 100). All divisions
+	   in the above formulas are truncating. For HZ being a power of 10, the
+	   calculations simplify to the version in the #else part (if the printf
+	   format is adapted to the same number of digits as zeroes in HZ.
+	 */
+#if HZ!=100
+	return sprintf(buffer,"%lu.%02lu %lu.%02lu\n",
+		uptime / HZ,
+		(((uptime % HZ) * 100) / HZ) % 100,
+		idle / HZ,
+		(((idle % HZ) * 100) / HZ) % 100);
+#else
+	return sprintf(buffer,"%lu.%02lu %lu.%02lu\n",
+		uptime / HZ,
+		uptime % HZ,
+		idle / HZ,
+		idle % HZ);
+#endif
+}
+
+static int get_meminfo(char * buffer)
+{
+	struct sysinfo i;
+
+	si_meminfo(&i);
+	si_swapinfo(&i);
+	return sprintf(buffer, "        total:   used:    free:   shared:  buffers:\n"
+		"Mem:  %8lu %8lu %8lu %8lu %8lu\n"
+		"Swap: %8lu %8lu %8lu\n",
+		i.totalram, i.totalram-i.freeram, i.freeram, i.sharedram, i.bufferram,
+		i.totalswap, i.totalswap-i.freeswap, i.freeswap);
+}
+
+static int get_version(char * buffer)
+{
+	extern char *linux_banner;
+
+	strcpy(buffer, linux_banner);
+	return strlen(buffer);
+}
+
+static int get_cpuinfo(char * buffer)
+{
+#ifdef __i386__
+	char *model[2][9]={{"DX","SX","DX/2","4","SX/2","6",
+				"DX/2-WB","DX/4"},
+			{"Pentium 60/66","Pentium 90/100","3",
+				"4","5","6","7","8"}};
+	char mask[2];
+	mask[0] = x86_mask+'@';
+	mask[1] = '\0';
+	return sprintf(buffer,"cpu\t\t: %c86\n"
+			      "model\t\t: %s\n"
+			      "mask\t\t: %s\n"
+			      "vid\t\t: %s\n"
+			      "fdiv_bug\t: %s\n"
+			      "math\t\t: %s\n"
+			      "hlt\t\t: %s\n"
+			      "wp\t\t: %s\n"
+			      "Integrated NPU\t: %s\n"
+			      "Enhanced VM86\t: %s\n"
+			      "IO Breakpoints\t: %s\n"
+			      "4MB Pages\t: %s\n"
+			      "TS Counters\t: %s\n"
+			      "Pentium MSR\t: %s\n"
+			      "Mach. Ch. Exep.\t: %s\n"
+			      "CMPXCHGB8B\t: %s\n"
+		              "BogoMips\t: %lu.%02lu\n",
+			      x86+'0', 
+			      x86_model ? model[x86-4][x86_model-1] : "Unknown",
+			      x86_mask ? mask : "Unknown",
+			      x86_vendor_id,
+			      fdiv_bug ? "yes" : "no",
+			      hard_math ? "yes" : "no",
+			      hlt_works_ok ? "yes" : "no",
+			      wp_works_ok ? "yes" : "no",
+			      x86_capability & 1 ? "yes" : "no",
+			      x86_capability & 2 ? "yes" : "no",
+			      x86_capability & 4 ? "yes" : "no",
+			      x86_capability & 8 ? "yes" : "no",
+			      x86_capability & 16 ? "yes" : "no",
+			      x86_capability & 32 ? "yes" : "no",
+			      x86_capability & 128 ? "yes" : "no",
+			      x86_capability & 256 ? "yes" : "no",
+		              loops_per_sec/500000, (loops_per_sec/5000) % 100
+			      );
+#else
+	return 0;
+#endif
+}
+
+static struct task_struct ** get_task(pid_t pid)
+{
+	struct task_struct ** p;
+
+	p = task;
+	while (++p < task+NR_TASKS) {
+		if (*p && (*p)->pid == pid)
+			return p;
+	}
+	return NULL;
+}
+
+static unsigned long get_phys_addr(struct task_struct * p, unsigned long ptr)
+{
+	pgd_t *page_dir;
+	pmd_t *page_middle;
+	pte_t pte;
+
+	if (!p || ptr >= TASK_SIZE)
+		return 0;
+	page_dir = pgd_offset(p,ptr);
+	if (pgd_none(*page_dir))
+		return 0;
+	if (pgd_bad(*page_dir)) {
+		printk("bad page directory entry %08lx\n", pgd_val(*page_dir));
+		pgd_clear(page_dir);
+		return 0;
+	}
+	page_middle = pmd_offset(page_dir,ptr);
+	if (pmd_none(*page_middle))
+		return 0;
+	if (pmd_bad(*page_middle)) {
+		printk("bad page middle entry %08lx\n", pmd_val(*page_middle));
+		pmd_clear(page_middle);
+		return 0;
+	}
+	pte = *pte_offset(page_middle,ptr);
+	if (!pte_present(pte))
+		return 0;
+	return pte_page(pte) + (ptr & ~PAGE_MASK);
+}
+
+static int get_array(struct task_struct ** p, unsigned long start, unsigned long end, char * buffer)
+{
+	unsigned long addr;
+	int size = 0, result = 0;
+	char c;
+
+	if (start >= end)
+		return result;
+	for (;;) {
+		addr = get_phys_addr(*p, start);
+		if (!addr)
+			goto ready;
+		do {
+			c = *(char *) addr;
+			if (!c)
+				result = size;
+			if (size < PAGE_SIZE)
+				buffer[size++] = c;
+			else
+				goto ready;
+			addr++;
+			start++;
+			if (!c && start >= end)
+				goto ready;
+		} while (addr & ~PAGE_MASK);
+	}
+ready:
+	/* remove the trailing blanks, used to fill out argv,envp space */
+	while (result>0 && buffer[result-1]==' ')
+		result--;
+	return result;
+}
+
+static int get_env(int pid, char * buffer)
+{
+	struct task_struct ** p = get_task(pid);
+
+	if (!p || !*p)
+		return 0;
+	return get_array(p, (*p)->mm->env_start, (*p)->mm->env_end, buffer);
+}
+
+static int get_arg(int pid, char * buffer)
+{
+	struct task_struct ** p = get_task(pid);
+
+	if (!p || !*p)
+		return 0;
+	return get_array(p, (*p)->mm->arg_start, (*p)->mm->arg_end, buffer);
+}
+
+static unsigned long get_wchan(struct task_struct *p)
+{
+#ifdef __i386__
+	unsigned long ebp, eip;
+	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;
+	ebp = p->tss.ebp;
+	do {
+		if (ebp < stack_page || ebp >= 4092+stack_page)
+			return 0;
+		eip = *(unsigned long *) (ebp+4);
+		if ((void *)eip != sleep_on &&
+		    (void *)eip != interruptible_sleep_on)
+			return eip;
+		ebp = *(unsigned long *) ebp;
+	} while (count++ < 16);
+#endif
+	return 0;
+}
+
+#define	KSTK_EIP(stack)	(((unsigned long *)stack)[1019])
+#define	KSTK_ESP(stack)	(((unsigned long *)stack)[1022])
+
+static int get_stat(int pid, char * buffer)
+{
+	struct task_struct ** p = get_task(pid);
+	unsigned long sigignore=0, sigcatch=0, bit=1, wchan;
+	unsigned long vsize, eip, esp;
+	int i,tty_pgrp;
+	char state;
+
+	if (!p || !*p)
+		return 0;
+	if ((*p)->state < 0 || (*p)->state > 5)
+		state = '.';
+	else
+		state = "RSDZTD"[(*p)->state];
+	eip = esp = 0;
+	vsize = (*p)->kernel_stack_page;
+	if (vsize) {
+		eip = KSTK_EIP(vsize);
+		esp = KSTK_ESP(vsize);
+		vsize = (*p)->mm->brk - (*p)->mm->start_code + PAGE_SIZE-1;
+		if (esp)
+			vsize += TASK_SIZE - esp;
+	}
+	wchan = get_wchan(*p);
+	for(i=0; i<32; ++i) {
+		switch((unsigned long) (*p)->sigaction[i].sa_handler) {
+		case 1: sigignore |= bit; break;
+		case 0: break;
+		default: sigcatch |= bit;
+		} bit <<= 1;
+	}
+	if ((*p)->tty)
+		tty_pgrp = (*p)->tty->pgrp;
+	else
+		tty_pgrp = -1;
+	return sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \
+%lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu \
+%lu %lu %lu %lu\n",
+		pid,
+		(*p)->comm,
+		state,
+		(*p)->p_pptr->pid,
+		(*p)->pgrp,
+		(*p)->session,
+	        (*p)->tty ? (*p)->tty->device : 0,
+		tty_pgrp,
+		(*p)->flags,
+		(*p)->mm->min_flt,
+		(*p)->mm->cmin_flt,
+		(*p)->mm->maj_flt,
+		(*p)->mm->cmaj_flt,
+		(*p)->utime,
+		(*p)->stime,
+		(*p)->cutime,
+		(*p)->cstime,
+		(*p)->counter,  /* this is the kernel priority ---
+				   subtract 30 in your user-level program. */
+		(*p)->priority, /* this is the nice value ---
+				   subtract 15 in your user-level program. */
+		(*p)->timeout,
+		(*p)->it_real_value,
+		(*p)->start_time,
+		vsize,
+		(*p)->mm->rss, /* you might want to shift this left 3 */
+		(*p)->rlim[RLIMIT_RSS].rlim_cur,
+		(*p)->mm->start_code,
+		(*p)->mm->end_code,
+		(*p)->mm->start_stack,
+		esp,
+		eip,
+		(*p)->signal,
+		(*p)->blocked,
+		sigignore,
+		sigcatch,
+		wchan);
+}
+		
+static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned long size,
+	int * pages, int * shared, int * dirty, int * total)
+{
+	pte_t * pte;
+	unsigned long end;
+
+	if (pmd_none(*pmd))
+		return;
+	if (pmd_bad(*pmd)) {
+		printk("statm_pte_range: bad pmd (%08lx)\n", pmd_val(*pmd));
+		pmd_clear(pmd);
+		return;
+	}
+	pte = pte_offset(pmd, address);
+	address &= ~PMD_MASK;
+	end = address + size;
+	if (end > PMD_SIZE)
+		end = PMD_SIZE;
+	do {
+		pte_t page = *pte;
+
+		address += PAGE_SIZE;
+		pte++;
+		if (pte_none(page))
+			continue;
+		++*total;
+		if (!pte_present(page))
+			continue;
+		++*pages;
+		if (pte_dirty(page))
+			++*dirty;
+		if (pte_page(page) >= high_memory)
+			continue;
+		if (mem_map[MAP_NR(pte_page(page))] > 1)
+			++*shared;
+	} while (address < end);
+}
+
+static inline void statm_pmd_range(pgd_t * pgd, unsigned long address, unsigned long size,
+	int * pages, int * shared, int * dirty, int * total)
+{
+	pmd_t * pmd;
+	unsigned long end;
+
+	if (pgd_none(*pgd))
+		return;
+	if (pgd_bad(*pgd)) {
+		printk("statm_pmd_range: bad pgd (%08lx)\n", pgd_val(*pgd));
+		pgd_clear(pgd);
+		return;
+	}
+	pmd = pmd_offset(pgd, address);
+	address &= ~PGDIR_MASK;
+	end = address + size;
+	if (end > PGDIR_SIZE)
+		end = PGDIR_SIZE;
+	do {
+		statm_pte_range(pmd, address, end - address, pages, shared, dirty, total);
+		address = (address + PMD_SIZE) & PMD_MASK;
+		pmd++;
+	} while (address < end);
+}
+
+static void statm_pgd_range(pgd_t * pgd, unsigned long address, unsigned long end,
+	int * pages, int * shared, int * dirty, int * total)
+{
+	while (address < end) {
+		statm_pmd_range(pgd, address, end - address, pages, shared, dirty, total);
+		address = (address + PGDIR_SIZE) & PGDIR_MASK;
+		pgd++;
+	}
+}
+
+static int get_statm(int pid, char * buffer)
+{
+	struct task_struct ** p = get_task(pid);
+	int size=0, resident=0, share=0, trs=0, lrs=0, drs=0, dt=0;
+
+	if (!p || !*p)
+		return 0;
+	if ((*p)->state != TASK_ZOMBIE) {
+		struct vm_area_struct * vma = (*p)->mm->mmap;
+
+		while (vma) {
+			pgd_t *pgd = pgd_offset(*p, vma->vm_start);
+			int pages = 0, shared = 0, dirty = 0, total = 0;
+
+			statm_pgd_range(pgd, vma->vm_start, vma->vm_end, &pages, &shared, &dirty, &total);
+			resident += pages;
+			share += shared;
+			dt += dirty;
+			size += total;
+			if (vma->vm_flags & VM_EXECUTABLE)
+				trs += pages;	/* text */
+			else if (vma->vm_flags & VM_GROWSDOWN)
+				drs += pages;	/* stack */
+			else if (vma->vm_end > 0x60000000)
+				lrs += pages;	/* library */
+			else
+				drs += pages;
+			vma = vma->vm_next;
+		}
+	}
+	return sprintf(buffer,"%d %d %d %d %d %d %d\n",
+		       size, resident, share, trs, lrs, drs, dt);
+}
+
+/*
+ * The way we support synthetic files > 4K
+ * - without storing their contents in some buffer and
+ * - without walking through the entire synthetic file until we reach the
+ *   position of the requested data
+ * is to cleverly encode the current position in the file's f_pos field.
+ * There is no requirement that a read() call which returns `count' bytes
+ * of data increases f_pos by exactly `count'.
+ *
+ * This idea is Linus' one. Bruno implemented it.
+ */
+
+/*
+ * For the /proc/<pid>/maps file, we use fixed length records, each containing
+ * a single line.
+ */
+#define MAPS_LINE_LENGTH	1024
+#define MAPS_LINE_SHIFT		10
+/*
+ * f_pos = (number of the vma in the task->mm->mmap list) * MAPS_LINE_LENGTH
+ *         + (index into the line)
+ */
+#define MAPS_LINE_FORMAT	  "%08lx-%08lx %s %08lx %02x:%02x %lu\n"
+#define MAPS_LINE_MAX	49 /* sum of 8  1  8  1 4 1 8  1  2 1  2 1 10 1 */
+
+static int read_maps (int pid, struct file * file, char * buf, int count)
+{
+	struct task_struct ** p = get_task(pid);
+	char * destptr;
+	loff_t lineno;
+	int column;
+	struct vm_area_struct * map;
+	int i;
+
+	if (!p || !*p)
+		return -EINVAL;
+
+	if (count == 0)
+		return 0;
+
+	/* decode f_pos */
+	lineno = file->f_pos >> MAPS_LINE_SHIFT;
+	column = file->f_pos & (MAPS_LINE_LENGTH-1);
+
+	/* quickly go to line lineno */
+	for (map = (*p)->mm->mmap, i = 0; map && (i < lineno); map = map->vm_next, i++)
+		continue;
+
+	destptr = buf;
+
+	for ( ; map ; ) {
+		/* produce the next line */
+		char line[MAPS_LINE_MAX+1];
+		char str[5], *cp = str;
+		int flags;
+		dev_t dev;
+		unsigned long ino;
+		int len;
+
+		flags = map->vm_flags;
+
+		*cp++ = flags & VM_READ ? 'r' : '-';
+		*cp++ = flags & VM_WRITE ? 'w' : '-';
+		*cp++ = flags & VM_EXEC ? 'x' : '-';
+		*cp++ = flags & VM_MAYSHARE ? 's' : 'p';
+		*cp++ = 0;
+
+		if (map->vm_inode != NULL) {
+			dev = map->vm_inode->i_dev;
+			ino = map->vm_inode->i_ino;
+		} else {
+			dev = 0;
+			ino = 0;
+		}
+
+		len = sprintf(line, MAPS_LINE_FORMAT,
+			      map->vm_start, map->vm_end, str, map->vm_offset,
+			      MAJOR(dev),MINOR(dev), ino);
+
+		if (column >= len) {
+			column = 0; /* continue with next line at column 0 */
+			lineno++;
+			map = map->vm_next;
+			continue;
+		}
+
+		i = len-column;
+		if (i > count)
+			i = count;
+		memcpy_tofs(destptr, line+column, i);
+		destptr += i; count -= i;
+		column += i;
+		if (column >= len) {
+			column = 0; /* next time: next line at column 0 */
+			lineno++;
+			map = map->vm_next;
+		}
+
+		/* done? */
+		if (count == 0)
+			break;
+
+		/* By writing to user space, we might have slept.
+		 * Stop the loop, to avoid a race condition.
+		 */
+		if (*p != current)
+			break;
+	}
+
+	/* encode f_pos */
+	file->f_pos = (lineno << MAPS_LINE_SHIFT) + column;
+
+	return destptr-buf;
+}
+
+extern int get_module_list(char *);
+extern int get_device_list(char *);
+extern int get_filesystem_list(char *);
+extern int get_ksyms_list(char *);
+extern int get_irq_list(char *);
+extern int get_dma_list(char *);
+extern int get_cpuinfo(char *);
+extern int get_pci_list(char*);
+
+static int get_root_array(char * page, int type)
+{
+	switch (type) {
+		case PROC_LOADAVG:
+			return get_loadavg(page);
+
+		case PROC_UPTIME:
+			return get_uptime(page);
+
+		case PROC_MEMINFO:
+			return get_meminfo(page);
+
+#ifdef CONFIG_PCI
+  	        case PROC_PCI:
+			return get_pci_list(page);
+#endif
+			
+		case PROC_CPUINFO:
+			return get_cpuinfo(page);
+
+		case PROC_VERSION:
+			return get_version(page);
+
+#ifdef CONFIG_DEBUG_MALLOC
+		case PROC_MALLOC:
+			return get_malloc(page);
+#endif
+
+		case PROC_MODULES:
+			return get_module_list(page);
+
+		case PROC_STAT:
+			return get_kstat(page);
+
+		case PROC_DEVICES:
+			return get_device_list(page);
+
+		case PROC_INTERRUPTS:
+			return get_irq_list(page);
+
+		case PROC_FILESYSTEMS:
+			return get_filesystem_list(page);
+
+		case PROC_KSYMS:
+			return get_ksyms_list(page);
+
+		case PROC_DMA:
+			return get_dma_list(page);
+
+		case PROC_IOPORTS:
+			return get_ioport_list(page);
+	}
+	return -EBADF;
+}
+
+static int get_process_array(char * page, int pid, int type)
+{
+	switch (type) {
+		case PROC_PID_ENVIRON:
+			return get_env(pid, page);
+		case PROC_PID_CMDLINE:
+			return get_arg(pid, page);
+		case PROC_PID_STAT:
+			return get_stat(pid, page);
+		case PROC_PID_STATM:
+			return get_statm(pid, page);
+	}
+	return -EBADF;
+}
+
+
+static inline int fill_array(char * page, int pid, int type)
+{
+	if (pid)
+		return get_process_array(page, pid, type);
+	return get_root_array(page, type);
+}
+
+static int array_read(struct inode * inode, struct file * file,char * buf, int count)
+{
+	unsigned long page;
+	int length;
+	int end;
+	unsigned int type, pid;
+
+	if (count < 0)
+		return -EINVAL;
+	if (!(page = __get_free_page(GFP_KERNEL)))
+		return -ENOMEM;
+	type = inode->i_ino;
+	pid = type >> 16;
+	type &= 0x0000ffff;
+	length = fill_array((char *) page, pid, type);
+	if (length < 0) {
+		free_page(page);
+		return length;
+	}
+	if (file->f_pos >= length) {
+		free_page(page);
+		return 0;
+	}
+	if (count + file->f_pos > length)
+		count = length - file->f_pos;
+	end = count + file->f_pos;
+	memcpy_tofs(buf, (char *) page + file->f_pos, count);
+	free_page(page);
+	file->f_pos = end;
+	return count;
+}
+
+static struct file_operations proc_array_operations = {
+	NULL,		/* array_lseek */
+	array_read,
+	NULL,		/* array_write */
+	NULL,		/* array_readdir */
+	NULL,		/* array_select */
+	NULL,		/* array_ioctl */
+	NULL,		/* mmap */
+	NULL,		/* no special open code */
+	NULL,		/* no special release code */
+	NULL		/* can't fsync */
+};
+
+struct inode_operations proc_array_inode_operations = {
+	&proc_array_operations,	/* default base directory file-ops */
+	NULL,			/* create */
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL			/* permission */
+};
+
+static int arraylong_read (struct inode * inode, struct file * file, char * buf, int count)
+{
+	unsigned int pid = inode->i_ino >> 16;
+	unsigned int type = inode->i_ino & 0x0000ffff;
+
+	if (count < 0)
+		return -EINVAL;
+
+	switch (type) {
+		case PROC_PID_MAPS:
+			return read_maps(pid, file, buf, count);
+	}
+	return -EINVAL;
+}
+
+static struct file_operations proc_arraylong_operations = {
+	NULL,		/* array_lseek */
+	arraylong_read,
+	NULL,		/* array_write */
+	NULL,		/* array_readdir */
+	NULL,		/* array_select */
+	NULL,		/* array_ioctl */
+	NULL,		/* mmap */
+	NULL,		/* no special open code */
+	NULL,		/* no special release code */
+	NULL		/* can't fsync */
+};
+
+struct inode_operations proc_arraylong_inode_operations = {
+	&proc_arraylong_operations,	/* default base directory file-ops */
+	NULL,			/* create */
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL			/* permission */
+};
diff -r -u -N linux.orig/fs/proc/base.c linux.arm/fs/proc/base.c
--- linux.orig/fs/proc/base.c	Fri Oct 27 23:08:52 1995
+++ linux.arm/fs/proc/base.c	Fri Oct 27 23:09:32 1995
@@ -62,7 +62,8 @@
 	{ PROC_PID_CMDLINE,	7, "cmdline" },
 	{ PROC_PID_STAT,	4, "stat" },
 	{ PROC_PID_STATM,	5, "statm" },
-	{ PROC_PID_MAPS,	4, "maps" }
+	{ PROC_PID_MAPS,	4, "maps" },
+	{ PROC_PID_MAPS+1,	5, "mmaps" }
 };
 
 #define NR_BASE_DIRENTRY ((sizeof (base_dir))/(sizeof (base_dir[0])))
diff -r -u -N linux.orig/fs/proc/inode.c linux.arm/fs/proc/inode.c
--- linux.orig/fs/proc/inode.c	Fri Oct 27 23:08:52 1995
+++ linux.arm/fs/proc/inode.c	Fri Oct 27 23:09:33 1995
@@ -232,6 +232,10 @@
 			inode->i_mode = S_IFIFO | S_IRUGO;
 			inode->i_op = &proc_arraylong_inode_operations;
 			return;
+		case PROC_PID_MAPS+1:
+			inode->i_mode = S_IFREG | S_IRUGO;
+			inode->i_op = &proc_arraylong_inode_operations;
+			return;
 	}
 	switch (ino >> 8) {
 		case PROC_PID_FD_DIR:
diff -r -u -N linux.orig/fs/super.c linux.arm/fs/super.c
--- linux.orig/fs/super.c	Fri Oct 27 23:08:53 1995
+++ linux.arm/fs/super.c	Fri Oct 27 23:09:22 1995
@@ -557,6 +557,10 @@
 	char * t;
 	unsigned long flags = 0;
 	unsigned long page = 0;
+#ifdef __arm__
+	data = (void *)(((unsigned long *)new_flags)[1]);
+	new_flags = *((unsigned long *)new_flags);
+#endif
 
 	if (!suser())
 		return -EPERM;
@@ -629,7 +633,8 @@
 		}
 	}
 	retval = do_mount(dev,dir_name,t,flags,(void *) page);
-	free_page(page);
+/*rmk*/	if(page)
+		free_page(page);
 	if (retval && fops && fops->release)
 		fops->release(inode, NULL);
 	iput(inode);
diff -r -u -N linux.orig/include/asm/arcaudio.h linux.arm/include/asm/arcaudio.h
--- linux.orig/include/asm/arcaudio.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm/arcaudio.h	Fri Oct 27 23:11:20 1995
@@ -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 -r -u -N linux.orig/include/asm/assembler.h linux.arm/include/asm/assembler.h
--- linux.orig/include/asm/assembler.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm/assembler.h	Fri Oct 27 23:11:21 1995
@@ -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 -r -u -N linux.orig/include/asm/bitops.h linux.arm/include/asm/bitops.h
--- linux.orig/include/asm/bitops.h	Fri Oct 27 23:09:01 1995
+++ linux.arm/include/asm/bitops.h	Fri Oct 27 23:11:18 1995
@@ -1,135 +1,42 @@
-#ifndef _I386_BITOPS_H
-#define _I386_BITOPS_H
+#ifndef __ASM_ARM_BITOPS_H
+#define __ASM_ARM_BITOPS_H
 
 /*
- * Copyright 1992, Linus Torvalds.
+ * Copyright 1995, Russell King
  */
 
 /*
- * These have to be done with inline assembly: that way the bit-setting
- * is guaranteed to be atomic. All bit operations return 0 if the bit
- * was cleared before the operation and != 0 if it was not.
- *
  * bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1).
  */
 
-/*
- * Some hacks to defeat gcc over-optimizations..
- */
-struct __dummy { unsigned long a[100]; };
-#define ADDR (*(struct __dummy *) addr)
-
-extern __inline__ int set_bit(int nr, void * addr)
-{
-	int oldbit;
-
-	__asm__ __volatile__("btsl %2,%1\n\tsbbl %0,%0"
-		:"=r" (oldbit),"=m" (ADDR)
-		:"r" (nr));
-	return oldbit;
-}
-
-extern __inline__ int clear_bit(int nr, void * addr)
-{
-	int oldbit;
-
-	__asm__ __volatile__("btrl %2,%1\n\tsbbl %0,%0"
-		:"=r" (oldbit),"=m" (ADDR)
-		:"r" (nr));
-	return oldbit;
-}
-
-extern __inline__ int change_bit(int nr, void * addr)
-{
-	int oldbit;
-
-	__asm__ __volatile__("btcl %2,%1\n\tsbbl %0,%0"
-		:"=r" (oldbit),"=m" (ADDR)
-		:"r" (nr));
-	return oldbit;
-}
+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 __inline__ int test_bit(int nr, void * addr)
-{
-	int oldbit;
-
-	__asm__ __volatile__("btl %2,%1\n\tsbbl %0,%0"
-		:"=r" (oldbit)
-		:"m" (ADDR),"r" (nr));
-	return oldbit;
-}
+extern int test_bit(int nr, void * addr);
 
 /*
  * Find-bit routines..
  */
-extern inline int find_first_zero_bit(void * addr, unsigned size)
-{
-	int res;
-
-	if (!size)
-		return 0;
-	__asm__("
-		cld
-		movl $-1,%%eax
-		repe; scasl
-		je 1f
-		subl $4,%%edi
-		movl (%%edi),%%eax
-		notl %%eax
-		bsfl %%eax,%%edx
-		jmp 2f
-1:		xorl %%edx,%%edx
-2:		subl %%ebx,%%edi
-		shll $3,%%edi
-		addl %%edi,%%edx"
-		:"=d" (res)
-		:"c" ((size + 31) >> 5), "D" (addr), "b" (addr)
-		:"ax", "bx", "cx", "di");
-	return res;
-}
-
-extern inline int find_next_zero_bit (void * addr, int size, int offset)
-{
-	unsigned long * p = ((unsigned long *) addr) + (offset >> 5);
-	int set = 0, bit = offset & 31, res;
-	
-	if (bit) {
-		/*
-		 * Look for zero in first byte
-		 */
-		__asm__("
-			bsfl %1,%0
-			jne 1f
-			movl $32, %0
-1:			"
-			: "=r" (set)
-			: "r" (~(*p >> bit)));
-		if (set < (32 - bit))
-			return set + offset;
-		set = 32 - bit;
-		p++;
-	}
-	/*
-	 * No zero yet, search remaining full bytes for a zero
-	 */
-	res = find_first_zero_bit (p, size - 32 * (p - (unsigned long *) addr));
-	return (offset + set + res);
-}
+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,
- * so code should check against ~0UL first..
+ * ffz = Find First Zero in word. Undefined if no zero exists.
  */
 extern inline unsigned long ffz(unsigned long word)
 {
-	__asm__("bsfl %1,%0"
-		:"=r" (word)
-		:"r" (~word));
-	return word;
+	int k;
+	
+	for (k = 0; k < 32; k++)
+		if ((word & (1<<k))==0)
+			break;
+        return k;
 }
 
-#endif /* _I386_BITOPS_H */
+#endif /* _ARM_BITOPS_H */
+
diff -r -u -N linux.orig/include/asm/bugs.h linux.arm/include/asm/bugs.h
--- linux.orig/include/asm/bugs.h	Fri Oct 27 23:09:01 1995
+++ linux.arm/include/asm/bugs.h	Fri Oct 27 23:11:18 1995
@@ -1,116 +1,17 @@
 /*
- *  include/asm-i386/bugs.h
+ *  include/asm-arm/bugs.h
  *
- *  Copyright (C) 1994  Linus Torvalds
+ *  Copyright (C) 1995  Russell King
  */
 
 /*
  * This is included by init/main.c to check for architecture-dependent bugs.
  *
  * Needs:
- *	void check_bugs(void);
+ *      void check_bugs(void);
  */
-
-#define CONFIG_BUGi386
-
-static void no_halt(char *s, int *ints)
-{
-	hlt_works_ok = 0;
-}
-
-static void no_387(char *s, int *ints)
+         
+static inline void check_bugs(void)
 {
-	hard_math = 0;
-	__asm__("movl %%cr0,%%eax\n\t"
-		"orl $0xE,%%eax\n\t"
-		"movl %%eax,%%cr0\n\t" : : : "ax");
 }
 
-static char fpu_error = 0;
-
-static void copro_timeout(void)
-{
-	fpu_error = 1;
-	timer_table[COPRO_TIMER].expires = jiffies+100;
-	timer_active |= 1<<COPRO_TIMER;
-	printk("387 failed: trying to reset\n");
-	send_sig(SIGFPE, last_task_used_math, 1);
-	outb_p(0,0xf1);
-	outb_p(0,0xf0);
-}
-
-static void check_fpu(void)
-{
-	static double x = 4195835.0;
-	static double y = 3145727.0;
-	unsigned short control_word;
-
-	if (!hard_math) {
-#ifndef CONFIG_MATH_EMULATION
-		printk("No coprocessor found and no math emulation present.\n");
-		printk("Giving up.\n");
-		for (;;) ;
-#endif
-		return;
-	}
-	/*
-	 * check if exception 16 works correctly.. This is truly evil
-	 * code: it disables the high 8 interrupts to make sure that
-	 * the irq13 doesn't happen. But as this will lead to a lockup
-	 * if no exception16 arrives, it depends on the fact that the
-	 * high 8 interrupts will be re-enabled by the next timer tick.
-	 * So the irq13 will happen eventually, but the exception 16
-	 * should get there first..
-	 */
-	printk("Checking 386/387 coupling... ");
-	timer_table[COPRO_TIMER].expires = jiffies+50;
-	timer_table[COPRO_TIMER].fn = copro_timeout;
-	timer_active |= 1<<COPRO_TIMER;
-	__asm__("clts ; fninit ; fnstcw %0 ; fwait":"=m" (*&control_word));
-	control_word &= 0xffc0;
-	__asm__("fldcw %0 ; fwait": :"m" (*&control_word));
-	outb_p(inb_p(0x21) | (1 << 2), 0x21);
-	__asm__("fldz ; fld1 ; fdiv %st,%st(1) ; fwait");
-	timer_active &= ~(1<<COPRO_TIMER);
-	if (fpu_error)
-		return;
-	if (!ignore_irq13) {
-		printk("Ok, fpu using old IRQ13 error reporting\n");
-		return;
-	}
-	__asm__("fninit\n\t"
-		"fldl %1\n\t"
-		"fdivl %2\n\t"
-		"fmull %2\n\t"
-		"fldl %1\n\t"
-		"fsubp %%st,%%st(1)\n\t"
-		"fistpl %0\n\t"
-		"fwait\n\t"
-		"fninit"
-		: "=m" (*&fdiv_bug)
-		: "m" (*&x), "m" (*&y));
-	if (!fdiv_bug) {
-		printk("Ok, fpu using exception 16 error reporting.\n");
-		return;
-
-	}
-	printk("Hmm, FDIV bug i%c86 system\n", '0'+x86);
-}
-
-static void check_hlt(void)
-{
-	printk("Checking 'hlt' instruction... ");
-	if (!hlt_works_ok) {
-		printk("disabled\n");
-		return;
-	}
-	__asm__ __volatile__("hlt ; hlt ; hlt ; hlt");
-	printk("Ok.\n");
-}
-
-static void check_bugs(void)
-{
-	check_fpu();
-	check_hlt();
-	system_utsname.machine[1] = '0' + x86;
-}
diff -r -u -N linux.orig/include/asm/byteorder.h linux.arm/include/asm/byteorder.h
--- linux.orig/include/asm/byteorder.h	Fri Oct 27 23:09:01 1995
+++ linux.arm/include/asm/byteorder.h	Fri Oct 27 23:11:18 1995
@@ -1,5 +1,5 @@
-#ifndef _I386_BYTEORDER_H
-#define _I386_BYTEORDER_H
+#ifndef __ASM_ARM_BYTEORDER_H
+#define __ASM_ARM_BYTEORDER_H
 
 #undef ntohl
 #undef ntohs
@@ -14,23 +14,25 @@
 #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 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)
 {
-	__asm__("xchgb %b0,%h0\n\t"	/* swap lower bytes	*/
-		"rorl $16,%0\n\t"	/* swap words		*/
-		"xchgb %b0,%h0"		/* swap higher bytes	*/
-		:"=q" (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;
 }
@@ -44,15 +46,18 @@
 extern __inline__ unsigned short int
 __ntohs(unsigned short int x)
 {
-	__asm__("xchgb %b0,%h0"		/* swap bytes		*/
-		: "=q" (x)
-		:  "0" (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))) \
+			      (((unsigned short int)(x) & 0xff00) >> 8)))
 
 #define __htonl(x) __ntohl(x)
 #define __htons(x) __ntohs(x)
@@ -79,3 +84,4 @@
 #endif
 
 #endif
+
diff -r -u -N linux.orig/include/asm/byteorder.h.2 linux.arm/include/asm/byteorder.h.2
--- linux.orig/include/asm/byteorder.h.2	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm/byteorder.h.2	Fri Oct 27 23:11:20 1995
@@ -0,0 +1,59 @@
+#ifndef __ASM_ARM_BYTEORDER_H
+#define __ASM_ARM_BYTEORDER_H
+
+#undef ntohl
+#undef ntohs
+#undef htonl
+#undef htons
+
+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 unsigned long int __ntohl(unsigned long int 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 unsigned short int __ntohs(unsigned short int 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 -r -u -N linux.orig/include/asm/delay.h linux.arm/include/asm/delay.h
--- linux.orig/include/asm/delay.h	Fri Oct 27 23:09:01 1995
+++ linux.arm/include/asm/delay.h	Fri Oct 27 23:11:18 1995
@@ -1,16 +1,13 @@
-#ifndef _I386_DELAY_H
-#define _I386_DELAY_H
+#ifndef __ASM_ARM_DELAY_H
+#define __ASM_ARM_DELAY_H
 
 /*
- * Copyright (C) 1993 Linus Torvalds
+ * Copyright (C) 1995 Russell King
  *
  * Delay routines, using a pre-computed "loops_per_second" value.
  */
 
-extern __inline__ void __delay(int loops)
-{
-	__asm__(".align 2,0x90\n1:\tdecl %0\n\tjns 1b": :"a" (loops):"ax");
-}
+extern void __delay(int loops);
 
 /*
  * division by multiplication: you don't have to worry about
@@ -22,25 +19,9 @@
  * first constant multiplications gets optimized away if the delay is
  * a constant)
  */
-extern __inline__ void udelay(unsigned long usecs)
-{
-	usecs *= 0x000010c6;		/* 2**32 / 1000000 */
-	__asm__("mull %0"
-		:"=d" (usecs)
-		:"a" (usecs),"0" (loops_per_sec)
-		:"ax");
-	__delay(usecs);
-}
-
-extern __inline__ unsigned long muldiv(unsigned long a, unsigned long b, unsigned long c)
-{
-	__asm__("mull %1 ; divl %2"
-		:"=a" (a)
-		:"d" (b),
-		 "r" (c),
-		 "0" (a)
-		:"dx");
-	return a;
-}
+extern void udelay(unsigned long usecs);
+
+extern unsigned long muldiv(unsigned long a, unsigned long b, unsigned long c);
+
+#endif /* defined(_ARM_DELAY_H) */
 
-#endif /* defined(_I386_DELAY_H) */
diff -r -u -N linux.orig/include/asm/dma.h linux.arm/include/asm/dma.h
--- linux.orig/include/asm/dma.h	Fri Oct 27 23:09:01 1995
+++ linux.arm/include/asm/dma.h	Fri Oct 27 23:11:18 1995
@@ -1,149 +1,32 @@
-/* $Id: dma.h,v 1.7 1992/12/14 00:29:34 root Exp root $
- * linux/include/asm/dma.h: Defines for using and allocating dma channels.
- * Written by Hennus Bergman, 1992.
- * High DMA channel support & info by Hannu Savolainen
- * and John Boyd, Nov. 1992.
- */
-
-#ifndef _ASM_DMA_H
-#define _ASM_DMA_H
-
-#include <asm/io.h>		/* need byte IO */
-
-
-#ifdef HAVE_REALLY_SLOW_DMA_CONTROLLER
-#define dma_outb	outb_p
-#else
-#define dma_outb	outb
-#endif
+#ifndef __ASM_ARM_DMA_H
+#define __ASM_ARM_DMA_H
 
-#define dma_inb		inb
-
-/*
- * NOTES about DMA transfers:
- *
- *  controller 1: channels 0-3, byte operations, ports 00-1F
- *  controller 2: channels 4-7, word operations, ports C0-DF
- *
- *  - ALL registers are 8 bits only, regardless of transfer size
- *  - channel 4 is not used - cascades 1 into 2.
- *  - channels 0-3 are byte - addresses/counts are for physical bytes
- *  - channels 5-7 are word - addresses/counts are for physical words
- *  - transfers must not cross physical 64K (0-3) or 128K (5-7) boundaries
- *  - transfer count loaded to registers is 1 less than actual count
- *  - controller 2 offsets are all even (2x offsets for controller 1)
- *  - page registers for 5-7 don't use data bit 0, represent 128K pages
- *  - page registers for 0-3 use bit 0, represent 64K pages
- *
- * DMA transfers are limited to the lower 16MB of _physical_ memory.  
- * Note that addresses loaded into registers must be _physical_ addresses,
- * not logical addresses (which may differ if paging is active).
- *
- *  Address mapping for channels 0-3:
- *
- *   A23 ... A16 A15 ... A8  A7 ... A0    (Physical addresses)
- *    |  ...  |   |  ... |   |  ... |
- *    |  ...  |   |  ... |   |  ... |
- *    |  ...  |   |  ... |   |  ... |
- *   P7  ...  P0  A7 ... A0  A7 ... A0   
- * |    Page    | Addr MSB | Addr LSB |   (DMA registers)
- *
- *  Address mapping for channels 5-7:
- *
- *   A23 ... A17 A16 A15 ... A9 A8 A7 ... A1 A0    (Physical addresses)
- *    |  ...  |   \   \   ... \  \  \  ... \  \
- *    |  ...  |    \   \   ... \  \  \  ... \  (not used)
- *    |  ...  |     \   \   ... \  \  \  ... \
- *   P7  ...  P1 (0) A7 A6  ... A0 A7 A6 ... A0   
- * |      Page      |  Addr MSB   |  Addr LSB  |   (DMA registers)
- *
- * Again, channels 5-7 transfer _physical_ words (16 bits), so addresses
- * and counts _must_ be word-aligned (the lowest address bit is _ignored_ at
- * the hardware level, so odd-byte transfers aren't possible).
- *
- * Transfer count (_not # bytes_) is limited to 64K, represented as actual
- * count - 1 : 64K => 0xFFFF, 1 => 0x0000.  Thus, count is always 1 or more,
- * and up to 128K bytes may be transferred on channels 5-7 in one operation. 
- *
- */
+#include <asm/irq.h>
 
 #define MAX_DMA_CHANNELS	8
 
-/* The maximum address that we can perform a DMA transfer to on this platform */
-#define MAX_DMA_ADDRESS      0x1000000
+#define MAX_DMA_ADDRESS		0
 
-/* 8237 DMA controllers */
-#define IO_DMA1_BASE	0x00	/* 8 bit slave DMA, channels 0..3 */
-#define IO_DMA2_BASE	0xC0	/* 16 bit master DMA, ch 4(=slave input)..7 */
-
-/* DMA controller registers */
-#define DMA1_CMD_REG		0x08	/* command register (w) */
-#define DMA1_STAT_REG		0x08	/* status register (r) */
-#define DMA1_REQ_REG            0x09    /* request register (w) */
-#define DMA1_MASK_REG		0x0A	/* single-channel mask (w) */
-#define DMA1_MODE_REG		0x0B	/* mode register (w) */
-#define DMA1_CLEAR_FF_REG	0x0C	/* clear pointer flip-flop (w) */
-#define DMA1_TEMP_REG           0x0D    /* Temporary Register (r) */
-#define DMA1_RESET_REG		0x0D	/* Master Clear (w) */
-#define DMA1_CLR_MASK_REG       0x0E    /* Clear Mask */
-#define DMA1_MASK_ALL_REG       0x0F    /* all-channels mask (w) */
-
-#define DMA2_CMD_REG		0xD0	/* command register (w) */
-#define DMA2_STAT_REG		0xD0	/* status register (r) */
-#define DMA2_REQ_REG            0xD2    /* request register (w) */
-#define DMA2_MASK_REG		0xD4	/* single-channel mask (w) */
-#define DMA2_MODE_REG		0xD6	/* mode register (w) */
-#define DMA2_CLEAR_FF_REG	0xD8	/* clear pointer flip-flop (w) */
-#define DMA2_TEMP_REG           0xDA    /* Temporary Register (r) */
-#define DMA2_RESET_REG		0xDA	/* Master Clear (w) */
-#define DMA2_CLR_MASK_REG       0xDC    /* Clear Mask */
-#define DMA2_MASK_ALL_REG       0xDE    /* all-channels mask (w) */
-
-#define DMA_ADDR_0              0x00    /* DMA address registers */
-#define DMA_ADDR_1              0x02
-#define DMA_ADDR_2              0x04
-#define DMA_ADDR_3              0x06
-#define DMA_ADDR_4              0xC0
-#define DMA_ADDR_5              0xC4
-#define DMA_ADDR_6              0xC8
-#define DMA_ADDR_7              0xCC
-
-#define DMA_CNT_0               0x01    /* DMA count registers */
-#define DMA_CNT_1               0x03
-#define DMA_CNT_2               0x05
-#define DMA_CNT_3               0x07
-#define DMA_CNT_4               0xC2
-#define DMA_CNT_5               0xC6
-#define DMA_CNT_6               0xCA
-#define DMA_CNT_7               0xCE
-
-#define DMA_PAGE_0              0x87    /* DMA page registers */
-#define DMA_PAGE_1              0x83
-#define DMA_PAGE_2              0x81
-#define DMA_PAGE_3              0x82
-#define DMA_PAGE_5              0x8B
-#define DMA_PAGE_6              0x89
-#define DMA_PAGE_7              0x8A
-
-#define DMA_MODE_READ	0x44	/* I/O to memory, no autoinit, increment, single mode */
-#define DMA_MODE_WRITE	0x48	/* memory to I/O, no autoinit, increment, single mode */
-#define DMA_MODE_CASCADE 0xC0   /* pass thru DREQ->HRQ, DACK<-HLDA only */
+#define DMA_MODE_READ		0x44
+#define DMA_MODE_WRITE		0x48
 
 /* enable/disable a specific DMA channel */
 static __inline__ void enable_dma(unsigned int dmanr)
 {
-	if (dmanr<=3)
-		dma_outb(dmanr,  DMA1_MASK_REG);
-	else
-		dma_outb(dmanr & 3,  DMA2_MASK_REG);
+	switch(dmanr)
+	{
+		case 2:  enable_irq(16); break;
+		default: printk("enable_dma: dma %d not supported\n", dmanr); break;
+	}
 }
 
 static __inline__ void disable_dma(unsigned int dmanr)
 {
-	if (dmanr<=3)
-		dma_outb(dmanr | 4,  DMA1_MASK_REG);
-	else
-		dma_outb((dmanr & 3) | 4,  DMA2_MASK_REG);
+	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'.
@@ -155,19 +38,31 @@
  */
 static __inline__ void clear_dma_ff(unsigned int dmanr)
 {
-	if (dmanr<=3)
-		dma_outb(0,  DMA1_CLEAR_FF_REG);
-	else
-		dma_outb(0,  DMA2_CLEAR_FF_REG);
+	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 */
 static __inline__ void set_dma_mode(unsigned int dmanr, char mode)
 {
-	if (dmanr<=3)
-		dma_outb(mode | dmanr,  DMA1_MODE_REG);
-	else
-		dma_outb(mode | (dmanr&3),  DMA2_MODE_REG);
+	extern void floppy_fiqin(void);
+	extern void floppy_fiqout(void);
+	switch(dmanr)
+	{
+		case 2:
+			if(mode == DMA_MODE_READ)
+				*((unsigned long *)0x00000024) = (unsigned long)floppy_fiqin;
+			else
+			if(mode == DMA_MODE_WRITE)
+				*((unsigned long *)0x00000024) = (unsigned long)floppy_fiqout;
+			else
+				printk("set_dma_mode: %s: invalid mode %02X\n", "floppy", mode);
+			break;
+		default: printk("set_dma_mode: dma %d not supported\n", dmanr); break;
+	}
 }
 
 /* Set only the page register bits of the transfer address.
@@ -177,29 +72,7 @@
  */
 static __inline__ void set_dma_page(unsigned int dmanr, char pagenr)
 {
-	switch(dmanr) {
-		case 0:
-			dma_outb(pagenr, DMA_PAGE_0);
-			break;
-		case 1:
-			dma_outb(pagenr, DMA_PAGE_1);
-			break;
-		case 2:
-			dma_outb(pagenr, DMA_PAGE_2);
-			break;
-		case 3:
-			dma_outb(pagenr, DMA_PAGE_3);
-			break;
-		case 5:
-			dma_outb(pagenr & 0xfe, DMA_PAGE_5);
-			break;
-		case 6:
-			dma_outb(pagenr & 0xfe, DMA_PAGE_6);
-			break;
-		case 7:
-			dma_outb(pagenr & 0xfe, DMA_PAGE_7);
-			break;
-	}
+	printk("set_dma_page: dma %d not supported\n", dmanr);
 }
 
 
@@ -208,35 +81,28 @@
  */
 static __inline__ void set_dma_addr(unsigned int dmanr, unsigned int a)
 {
-	set_dma_page(dmanr, a>>16);
-	if (dmanr <= 3)  {
-	    dma_outb( a & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE );
-            dma_outb( (a>>8) & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE );
-	}  else  {
-	    dma_outb( (a>>1) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE );
-	    dma_outb( (a>>9) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE );
+	extern unsigned char *floppyfiq_to;
+	switch(dmanr)
+	{
+		case 2:  floppyfiq_to = (unsigned char *)a; break;
+		default: printk("set_dma_addr: dma %d not supported\n", dmanr); break;
 	}
 }
 
 
-/* Set transfer size (max 64k for DMA1..3, 128k for DMA5..7) for
- * a specific DMA channel.
- * You must ensure the parameters are valid.
- * NOTE: from a manual: "the number of transfers is one more
- * than the initial word count"! This is taken into account.
- * Assumes dma flip-flop is clear.
- * NOTE 2: "count" represents _bytes_ and must be even for channels 5-7.
+/* Set transfer size for a specific DMA channel.
  */
 static __inline__ void set_dma_count(unsigned int dmanr, unsigned int count)
 {
-        count--;
-	if (dmanr <= 3)  {
-	    dma_outb( count & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE );
-	    dma_outb( (count>>8) & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE );
-        } else {
-	    dma_outb( (count>>1) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE );
-	    dma_outb( (count>>9) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE );
-        }
+	extern unsigned int floppyfiq_len;
+	extern unsigned char *floppyfiq_from;
+	switch(dmanr)
+	{
+		case 2:  floppyfiq_from = (unsigned char *)0x0302a000;
+			 floppyfiq_len  = count;
+			 break;
+		default: printk("set_dma_count: dma %d not supported\n", dmanr); break;
+	}
 }
 
 
@@ -250,22 +116,19 @@
  */
 static __inline__ int get_dma_residue(unsigned int dmanr)
 {
-	unsigned int io_port = (dmanr<=3)? ((dmanr&3)<<1) + 1 + IO_DMA1_BASE
-					 : ((dmanr&3)<<2) + 2 + IO_DMA2_BASE;
-
-	/* using short to get 16-bit wrap around */
-	unsigned short count;
-
-	count = 1 + dma_inb(io_port);
-	count += dma_inb(io_port) << 8;
-	
-	return (dmanr<=3)? count : (count<<1);
+	extern unsigned int floppyfiq_len;
+	switch(dmanr)
+	{
+		case 2:  return floppyfiq_len;
+		default: printk("get_dma_residue: dma %d not supported\n", dmanr); break;
+	}
+	return -1;
 }
 
 
 /* These are in kernel/dma.c: */
-extern int request_dma(unsigned int dmanr, char * device_id);	/* reserve a DMA channel */
-extern void free_dma(unsigned int dmanr);	/* release it again */
+extern int request_dma(unsigned int dmanr, char * device_id);   /* reserve a DMA channel */
+extern void free_dma(unsigned int dmanr);       /* release it again */
 
+#endif /* _ARM_DMA_H */
 
-#endif /* _ASM_DMA_H */
diff -r -u -N linux.orig/include/asm/ecard.h linux.arm/include/asm/ecard.h
--- linux.orig/include/asm/ecard.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm/ecard.h	Fri Oct 27 23:11:20 1995
@@ -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 -r -u -N linux.orig/include/asm/io.h linux.arm/include/asm/io.h
--- linux.orig/include/asm/io.h	Fri Oct 27 23:09:01 1995
+++ linux.arm/include/asm/io.h	Fri Oct 27 23:11:19 1995
@@ -1,159 +1,168 @@
-#ifndef _ASM_IO_H
-#define _ASM_IO_H
+#ifndef __ASM_ARM_IO_H
+#define __ASM_ARM_IO_H
 
-/*
- * This file contains the definitions for the x86 IO instructions
- * inb/inw/inl/outb/outw/outl and the "string versions" of the same
- * (insb/insw/insl/outsb/outsw/outsl). You can also use "pausing"
- * versions of the single-IO instructions (inb_p/inw_p/..).
- *
- * This file is not meant to be obfuscating: it's just complicated
- * to (a) handle it all in a way that makes gcc able to optimize it
- * as well as possible and (b) trying to avoid writing the same thing
- * over and over again with slight variations and possibly making a
- * mistake somewhere.
- */
-
-/*
- * Thanks to James van Artsdalen for a better timing-fix than
- * the two short jumps: using outb's to a nonexistent port seems
- * to guarantee better timings even on fast machines.
- *
- * On the other hand, I'd like to be sure of a non-existent port:
- * I feel a bit unsafe about using 0x80 (should be safe, though)
- *
- *		Linus
- */
+#undef USE_INLINE
 
-#ifdef SLOW_IO_BY_JUMPING
-#define __SLOW_DOWN_IO __asm__ __volatile__("jmp 1f\n1:\tjmp 1f\n1:")
+#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 __SLOW_DOWN_IO __asm__ __volatile__("outb %al,$0x80")
+
+#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
 
-#ifdef REALLY_SLOW_IO
-#define SLOW_DOWN_IO { __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; }
+#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 SLOW_DOWN_IO __SLOW_DOWN_IO
+
+#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
 
-/*
- * Talk about misusing macros..
- */
-
-#define __OUT1(s,x) \
-extern inline void __out##s(unsigned x value, unsigned short port) {
-
-#define __OUT2(s,s1,s2) \
-__asm__ __volatile__ ("out" #s " %" s1 "0,%" s2 "1"
-
-#define __OUT(s,s1,x) \
-__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); } \
-__OUT1(s##c,x) __OUT2(s,s1,"") : : "a" (value), "i" (port)); } \
-__OUT1(s##_p,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); SLOW_DOWN_IO; } \
-__OUT1(s##c_p,x) __OUT2(s,s1,"") : : "a" (value), "i" (port)); SLOW_DOWN_IO; }
-
-#define __IN1(s) \
-extern inline unsigned int __in##s(unsigned short port) { unsigned int _v;
-
-#define __IN2(s,s1,s2) \
-__asm__ __volatile__ ("in" #s " %" s2 "1,%" s1 "0"
-
-#define __IN(s,s1,i...) \
-__IN1(s) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); return _v; } \
-__IN1(s##c) __IN2(s,s1,"") : "=a" (_v) : "i" (port) ,##i ); return _v; } \
-__IN1(s##_p) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); SLOW_DOWN_IO; return _v; } \
-__IN1(s##c_p) __IN2(s,s1,"") : "=a" (_v) : "i" (port) ,##i ); SLOW_DOWN_IO; return _v; }
-
-#define __INS(s) \
-extern inline void ins##s(unsigned short port, void * addr, unsigned long count) \
-{ __asm__ __volatile__ ("cld ; rep ; ins" #s \
-: "=D" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); }
-
-#define __OUTS(s) \
-extern inline void outs##s(unsigned short port, const void * addr, unsigned long count) \
-{ __asm__ __volatile__ ("cld ; rep ; outs" #s \
-: "=S" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); }
-
-__IN(b,"b","0" (0))
-__IN(w,"w","0" (0))
-__IN(l,"")
-
-__OUT(b,"b",char)
-__OUT(w,"w",short)
-__OUT(l,,int)
-
-__INS(b)
-__INS(w)
-__INS(l)
-
-__OUTS(b)
-__OUTS(w)
-__OUTS(l)
-
-/*
- * Note that due to the way __builtin_constant_p() works, you
- *  - can't use it inside a inline function (it will never be true)
- *  - you don't have to worry about side effects within the __builtin..
- */
-#define outb(val,port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__outbc((val),(port)) : \
-	__outb((val),(port)))
-
-#define inb(port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__inbc(port) : \
-	__inb(port))
-
-#define outb_p(val,port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__outbc_p((val),(port)) : \
-	__outb_p((val),(port)))
-
-#define inb_p(port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__inbc_p(port) : \
-	__inb_p(port))
-
-#define outw(val,port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__outwc((val),(port)) : \
-	__outw((val),(port)))
-
-#define inw(port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__inwc(port) : \
-	__inw(port))
-
-#define outw_p(val,port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__outwc_p((val),(port)) : \
-	__outw_p((val),(port)))
-
-#define inw_p(port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__inwc_p(port) : \
-	__inw_p(port))
-
-#define outl(val,port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__outlc((val),(port)) : \
-	__outl((val),(port)))
-
-#define inl(port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__inlc(port) : \
-	__inl(port))
-
-#define outl_p(val,port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__outlc_p((val),(port)) : \
-	__outl_p((val),(port)))
-
-#define inl_p(port) \
-((__builtin_constant_p((port)) && (port) < 256) ? \
-	__inlc_p(port) : \
-	__inl_p(port))
+#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 -r -u -N linux.orig/include/asm/irq-no.h linux.arm/include/asm/irq-no.h
--- linux.orig/include/asm/irq-no.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm/irq-no.h	Fri Oct 27 23:11:20 1995
@@ -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 -r -u -N linux.orig/include/asm/irq.h linux.arm/include/asm/irq.h
--- linux.orig/include/asm/irq.h	Fri Oct 27 23:09:01 1995
+++ linux.arm/include/asm/irq.h	Fri Oct 27 23:11:19 1995
@@ -1,11 +1,5 @@
-#ifndef _ASM_IRQ_H
-#define _ASM_IRQ_H
-
-/*
- *	linux/include/asm/irq.h
- *
- *	(C) 1992, 1993 Linus Torvalds
- */
+#ifndef __ASM_ARM_IRQ_H
+#define __ASM_ARM_IRQ_H
 
 #include <linux/linkage.h>
 #include <asm/segment.h>
@@ -15,151 +9,6 @@
 
 #define __STR(x) #x
 #define STR(x) __STR(x)
- 
-#define SAVE_ALL \
-	"cld\n\t" \
-	"push %gs\n\t" \
-	"push %fs\n\t" \
-	"push %es\n\t" \
-	"push %ds\n\t" \
-	"pushl %eax\n\t" \
-	"pushl %ebp\n\t" \
-	"pushl %edi\n\t" \
-	"pushl %esi\n\t" \
-	"pushl %edx\n\t" \
-	"pushl %ecx\n\t" \
-	"pushl %ebx\n\t" \
-	"movl $" STR(KERNEL_DS) ",%edx\n\t" \
-	"mov %dx,%ds\n\t" \
-	"mov %dx,%es\n\t" \
-	"movl $" STR(USER_DS) ",%edx\n\t" \
-	"mov %dx,%fs\n\t"   \
-	"movl $0,%edx\n\t"  \
-	"movl %edx,%db7\n\t"
-
-/*
- * SAVE_MOST/RESTORE_MOST is used for the faster version of IRQ handlers,
- * installed by using the SA_INTERRUPT flag. These kinds of IRQ's don't
- * call the routines that do signal handling etc on return, and can have
- * more relaxed register-saving etc. They are also atomic, and are thus
- * suited for small, fast interrupts like the serial lines or the harddisk
- * drivers, which don't actually need signal handling etc.
- *
- * Also note that we actually save only those registers that are used in
- * C subroutines (%eax, %edx and %ecx), so if you do something weird,
- * you're on your own. The only segments that are saved (not counting the
- * automatic stack and code segment handling) are %ds and %es, and they
- * point to kernel space. No messing around with %fs here.
- */
-#define SAVE_MOST \
-	"cld\n\t" \
-	"push %es\n\t" \
-	"push %ds\n\t" \
-	"pushl %eax\n\t" \
-	"pushl %edx\n\t" \
-	"pushl %ecx\n\t" \
-	"movl $" STR(KERNEL_DS) ",%edx\n\t" \
-	"mov %dx,%ds\n\t" \
-	"mov %dx,%es\n\t"
-
-#define RESTORE_MOST \
-	"popl %ecx\n\t" \
-	"popl %edx\n\t" \
-	"popl %eax\n\t" \
-	"pop %ds\n\t" \
-	"pop %es\n\t" \
-	"iret"
-
-/*
- * The "inb" instructions are not needed, but seem to change the timings
- * a bit - without them it seems that the harddisk driver won't work on
- * all hardware. Arghh.
- */
-#define ACK_FIRST(mask) \
-	"inb $0x21,%al\n\t" \
-	"jmp 1f\n" \
-	"1:\tjmp 1f\n" \
-	"1:\torb $" #mask ",_cache_21\n\t" \
-	"movb _cache_21,%al\n\t" \
-	"outb %al,$0x21\n\t" \
-	"jmp 1f\n" \
-	"1:\tjmp 1f\n" \
-	"1:\tmovb $0x20,%al\n\t" \
-	"outb %al,$0x20\n\t"
-
-#define ACK_SECOND(mask) \
-	"inb $0xA1,%al\n\t" \
-	"jmp 1f\n" \
-	"1:\tjmp 1f\n" \
-	"1:\torb $" #mask ",_cache_A1\n\t" \
-	"movb _cache_A1,%al\n\t" \
-	"outb %al,$0xA1\n\t" \
-	"jmp 1f\n" \
-	"1:\tjmp 1f\n" \
-	"1:\tmovb $0x20,%al\n\t" \
-	"outb %al,$0xA0\n\t" \
-	"jmp 1f\n" \
-	"1:\tjmp 1f\n" \
-	"1:\toutb %al,$0x20\n\t"
-
-#define UNBLK_FIRST(mask) \
-	"inb $0x21,%al\n\t" \
-	"jmp 1f\n" \
-	"1:\tjmp 1f\n" \
-	"1:\tandb $~(" #mask "),_cache_21\n\t" \
-	"movb _cache_21,%al\n\t" \
-	"outb %al,$0x21\n\t"
-
-#define UNBLK_SECOND(mask) \
-	"inb $0xA1,%al\n\t" \
-	"jmp 1f\n" \
-	"1:\tjmp 1f\n" \
-	"1:\tandb $~(" #mask "),_cache_A1\n\t" \
-	"movb _cache_A1,%al\n\t" \
-	"outb %al,$0xA1\n\t"
-
-#define IRQ_NAME2(nr) nr##_interrupt(void)
-#define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr)
-#define FAST_IRQ_NAME(nr) IRQ_NAME2(fast_IRQ##nr)
-#define BAD_IRQ_NAME(nr) IRQ_NAME2(bad_IRQ##nr)
-	
-#define BUILD_IRQ(chip,nr,mask) \
-asmlinkage void IRQ_NAME(nr); \
-asmlinkage void FAST_IRQ_NAME(nr); \
-asmlinkage void BAD_IRQ_NAME(nr); \
-__asm__( \
-"\n.align 4\n" \
-"_IRQ" #nr "_interrupt:\n\t" \
-	"pushl $-"#nr"-2\n\t" \
-	SAVE_ALL \
-	ACK_##chip(mask) \
-	"incl _intr_count\n\t"\
-	"sti\n\t" \
-	"movl %esp,%ebx\n\t" \
-	"pushl %ebx\n\t" \
-	"pushl $" #nr "\n\t" \
-	"call _do_IRQ\n\t" \
-	"addl $8,%esp\n\t" \
-	"cli\n\t" \
-	UNBLK_##chip(mask) \
-	"decl _intr_count\n\t" \
-	"jmp ret_from_sys_call\n" \
-"\n.align 4\n" \
-"_fast_IRQ" #nr "_interrupt:\n\t" \
-	SAVE_MOST \
-	ACK_##chip(mask) \
-	"incl _intr_count\n\t" \
-	"pushl $" #nr "\n\t" \
-	"call _do_fast_IRQ\n\t" \
-	"addl $4,%esp\n\t" \
-	"cli\n\t" \
-	UNBLK_##chip(mask) \
-	"decl _intr_count\n\t" \
-	RESTORE_MOST \
-"\n\n.align 4\n" \
-"_bad_IRQ" #nr "_interrupt:\n\t" \
-	SAVE_MOST \
-	ACK_##chip(mask) \
-	RESTORE_MOST);
 
 #endif
+
diff -r -u -N linux.orig/include/asm/page.h linux.arm/include/asm/page.h
--- linux.orig/include/asm/page.h	Fri Oct 27 23:09:01 1995
+++ linux.arm/include/asm/page.h	Fri Oct 27 23:11:19 1995
@@ -1,10 +1,25 @@
-#ifndef _I386_PAGE_H
-#define _I386_PAGE_H
+#ifndef __ASM_ARM_PAGE_H
+#define __ASM_ARM_PAGE_H
 
 /* PAGE_SHIFT determines the page size */
-#define PAGE_SHIFT	12
-#define PAGE_SIZE	(1UL << PAGE_SHIFT)
-#define PAGE_MASK	(~(PAGE_SIZE-1))
+/* 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
+
+extern void remap_task(unsigned long memmap);
+
+#define LOGICAL_END	0x02000000
+
+#define PAGE_SIZE       (1UL << PAGE_SHIFT)
+#define PAGE_MASK       (~(PAGE_SIZE-1))
 
 #ifdef __KERNEL__
 
@@ -19,15 +34,15 @@
 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) } )
+#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
 /*
@@ -38,31 +53,32 @@
 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)
+#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
 
 #define invalidate() \
-__asm__ __volatile__("movl %%cr3,%%eax\n\tmovl %%eax,%%cr3": : :"ax")
+	remap_task(current->tss.memmap)
 
 /* 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		0
-#define MAP_NR(addr)		(((unsigned long)(addr)) >> PAGE_SHIFT)
+#define PAGE_OFFSET		0x02000000
+#define MAP_NR(addr)		(((unsigned long)(addr) - PAGE_OFFSET) >> PAGE_SHIFT)
 #define MAP_PAGE_RESERVED	(1<<15)
 
 typedef unsigned short mem_map_t;
 
 #endif /* __KERNEL__ */
 
-#endif /* _I386_PAGE_H */
+#endif /* _ARM_PAGE_H */
+
diff -r -u -N linux.orig/include/asm/param.h linux.arm/include/asm/param.h
--- linux.orig/include/asm/param.h	Fri Oct 27 23:09:01 1995
+++ linux.arm/include/asm/param.h	Fri Oct 27 23:11:19 1995
@@ -1,20 +1,21 @@
-#ifndef _ASMi386_PARAM_H
-#define _ASMi386_PARAM_H
+#ifndef __ASM_ARM_PARAM_H
+#define __ASM_ARM_PARAM_H
 
 #ifndef HZ
 #define HZ 100
 #endif
 
-#define EXEC_PAGESIZE	4096
+#define EXEC_PAGESIZE   32768
 
 #ifndef NGROUPS
-#define NGROUPS		32
+#define NGROUPS         32
 #endif
 
 #ifndef NOGROUP
-#define NOGROUP		(-1)
+#define NOGROUP         (-1)
 #endif
 
-#define MAXHOSTNAMELEN	64	/* max length of hostname */
+#define MAXHOSTNAMELEN  64      /* max length of hostname */
 
 #endif
+
diff -r -u -N linux.orig/include/asm/pgtable.h linux.arm/include/asm/pgtable.h
--- linux.orig/include/asm/pgtable.h	Fri Oct 27 23:09:01 1995
+++ linux.arm/include/asm/pgtable.h	Fri Oct 27 23:11:19 1995
@@ -1,33 +1,25 @@
-#ifndef _I386_PGTABLE_H
-#define _I386_PGTABLE_H
+#ifndef __ASM_ARM_PGTABLE_H
+#define __ASM_ARM_PGTABLE_H
 
-/*
- * The Linux memory management assumes a three-level page table setup. On
- * the i386, we use that, but "fold" the mid level into the top-level page
- * table, so that we physically have the same two-level page table as the
- * i386 mmu expects.
- *
- * This file contains the functions and defines necessary to modify and use
- * the i386 page table tree.
- */
+#include <linux/malloc.h>
 
 /* PMD_SHIFT determines the size of the area a second-level page table can map */
-#define PMD_SHIFT	22
-#define PMD_SIZE	(1UL << PMD_SHIFT)
-#define PMD_MASK	(~(PMD_SIZE-1))
+#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	22
-#define PGDIR_SIZE	(1UL << PGDIR_SHIFT)
-#define PGDIR_MASK	(~(PGDIR_SIZE-1))
+#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	1024
-#define PTRS_PER_PMD	1
-#define PTRS_PER_PGD	1024
+#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
@@ -36,48 +28,47 @@
  * The vmalloc() routines leaves a hole of 4kB between each vmalloced
  * area for the same reason. ;)
  */
-#define VMALLOC_OFFSET	(8*1024*1024)
-#define VMALLOC_START ((high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
-#define VMALLOC_VMADDR(x) (TASK_SIZE + (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)
+#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 __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:
@@ -86,105 +77,97 @@
  */
 #undef CONFIG_TEST_VERIFY_AREA
 
+#ifndef not_yet_checked
 /* page table for 0-4MB for everybody */
 extern unsigned long pg0[1024];
+#endif
 
 /*
- * BAD_PAGETABLE is used when we need a bogus page-table, while
  * BAD_PAGE is used for a bogus page.
  *
  * ZERO_PAGE is a global shared page that is always zero: used
  * for zero-mapped memory areas etc..
  */
 extern pte_t __bad_page(void);
-extern pte_t * __bad_pagetable(void);
-
 extern unsigned long __zero_page(void);
 
-#define BAD_PAGETABLE __bad_pagetable()
 #define BAD_PAGE __bad_page()
 #define ZERO_PAGE __zero_page()
 
 /* number of bits that fit into a memory pointer */
-#define BITS_PER_PTR			(8*sizeof(unsigned long))
+#define BITS_PER_PTR                    (8*sizeof(unsigned long))
 
 /* to align the pointer to a pointer address */
-#define PTR_MASK			(~(sizeof(void*)-1))
+#define PTR_MASK                        (~(sizeof(void*)-1))
 
 /* sizeof(void*)==1<<SIZEOF_PTR_LOG2 */
 /* 64-bit machines, beware!  SRB. */
-#define SIZEOF_PTR_LOG2			2
-
-/* to find an entry in a page-table */
-#define PAGE_PTR(address) \
-((unsigned long)(address)>>(PAGE_SHIFT-SIZEOF_PTR_LOG2)&PTR_MASK&~PAGE_MASK)
+#define SIZEOF_PTR_LOG2                 2
 
 /* to set the page-dir */
 #define SET_PAGE_DIR(tsk,pgdir) \
 do { \
-	(tsk)->tss.cr3 = (unsigned long) (pgdir); \
+	(tsk)->tss.memmap = (unsigned long) (pgdir); \
 	if ((tsk) == current) \
-		__asm__ __volatile__("movl %0,%%cr3": :"a" ((tsk)->tss.cr3)); \
+		remap_task(current->tss.memmap); \
 } while (0)
 
-extern unsigned long high_memory;
+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 0; }
+extern inline void pte_clear(pte_t *ptep)       { pte_val(*ptep) = 0; }
+extern inline void pte_reuse(pte_t * ptep)	{ }
+
+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)      { }
 
-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)] != 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)] & MAP_PAGE_RESERVED))
-		mem_map[MAP_NR(ptep)]++;
-}
-
-extern inline int pmd_none(pmd_t pmd)		{ return !pmd_val(pmd); }
-extern inline int pmd_bad(pmd_t pmd)		{ return (pmd_val(pmd) & ~PAGE_MASK) != _PAGE_TABLE || pmd_val(pmd) > high_memory; }
-extern inline int pmd_present(pmd_t pmd)	{ return pmd_val(pmd) & _PAGE_PRESENT; }
-extern inline int pmd_inuse(pmd_t *pmdp)	{ return 0; }
-extern inline void pmd_clear(pmd_t * pmdp)	{ pmd_val(*pmdp) = 0; }
-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 mem_map[MAP_NR(pgdp)] != 1; }
-extern inline void pgd_clear(pgd_t * pgdp)	{ }
+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)      { }
 extern inline void pgd_reuse(pgd_t * pgdp)
 {
-	if (!(mem_map[MAP_NR(pgdp)] & MAP_PAGE_RESERVED))
-		mem_map[MAP_NR(pgdp)]++;
+        if (!(mem_map[MAP_NR(pgdp)] & MAP_PAGE_RESERVED))
+                mem_map[MAP_NR(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; }
+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,
@@ -205,19 +188,19 @@
 /* to find an entry in a page-table-directory */
 extern inline pgd_t * pgd_offset(struct task_struct * tsk, unsigned long address)
 {
-	return (pgd_t *) tsk->tss.cr3 + (address >> PGDIR_SHIFT);
+        return (pgd_t *) tsk->tss.memmap + (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;
+        return (pmd_t *) dir;
 }
 
-/* Find an entry in the third-level page table.. */ 
+/* Find an entry in the third-level page table.. */
 extern inline pte_t * pte_offset(pmd_t * dir, unsigned long address)
 {
-	return (pte_t *) pmd_page(*dir) + ((address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1));
+        return (pte_t *) dir;
 }
 
 /*
@@ -227,32 +210,11 @@
  */
 extern inline void pte_free_kernel(pte_t * pte)
 {
-	mem_map[MAP_NR(pte)] = 1;
-	free_page((unsigned long) pte);
 }
 
 extern inline pte_t * pte_alloc_kernel(pmd_t * pmd, unsigned long address)
 {
-	address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
-	if (pmd_none(*pmd)) {
-		pte_t * page = (pte_t *) get_free_page(GFP_KERNEL);
-		if (pmd_none(*pmd)) {
-			if (page) {
-				pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) page;
-				mem_map[MAP_NR(page)] = MAP_PAGE_RESERVED;
-				return page + address;
-			}
-			pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) BAD_PAGETABLE;
-			return NULL;
-		}
-		free_page((unsigned long) page);
-	}
-	if (pmd_bad(*pmd)) {
-		printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
-		pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) BAD_PAGETABLE;
-		return NULL;
-	}
-	return (pte_t *) pmd_page(*pmd) + address;
+	return (pte_t *) pmd;
 }
 
 /*
@@ -265,35 +227,16 @@
 
 extern inline pmd_t * pmd_alloc_kernel(pgd_t * pgd, unsigned long address)
 {
-	return (pmd_t *) pgd;
+        return (pmd_t *) pgd;
 }
 
 extern inline void pte_free(pte_t * pte)
 {
-	free_page((unsigned long) pte);
 }
 
 extern inline pte_t * pte_alloc(pmd_t * pmd, unsigned long address)
 {
-	address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
-	if (pmd_none(*pmd)) {
-		pte_t * page = (pte_t *) get_free_page(GFP_KERNEL);
-		if (pmd_none(*pmd)) {
-			if (page) {
-				pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) page;
-				return page + address;
-			}
-			pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) BAD_PAGETABLE;
-			return NULL;
-		}
-		free_page((unsigned long) page);
-	}
-	if (pmd_bad(*pmd)) {
-		printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
-		pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) BAD_PAGETABLE;
-		return NULL;
-	}
-	return (pte_t *) pmd_page(*pmd) + address;
+	return (pte_t *) pmd;
 }
 
 /*
@@ -306,17 +249,22 @@
 
 extern inline pmd_t * pmd_alloc(pgd_t * pgd, unsigned long address)
 {
-	return (pmd_t *) pgd;
+        return (pmd_t *) pgd;
 }
 
 extern inline void pgd_free(pgd_t * pgd)
 {
-	free_page((unsigned long) pgd);
+	kfree_s((void *)pgd, 32*1024*1024*4/PAGE_SIZE);
 }
 
 extern inline pgd_t * pgd_alloc(void)
 {
-	return (pgd_t *) get_free_page(GFP_KERNEL);
+	pgd_t *pgd;
+	
+        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];
@@ -326,8 +274,9 @@
  * tables contain all the necessary information.
  */
 extern inline void update_mmu_cache(struct vm_area_struct * vma,
-	unsigned long address, pte_t pte)
+        unsigned long address, pte_t pte)
 {
 }
 
-#endif /* _I386_PAGE_H */
+#endif /* _ARM_PAGE_H */
+
diff -r -u -N linux.orig/include/asm/processor.h linux.arm/include/asm/processor.h
--- linux.orig/include/asm/processor.h	Fri Oct 27 23:09:01 1995
+++ linux.arm/include/asm/processor.h	Fri Oct 27 23:11:19 1995
@@ -1,141 +1,97 @@
 /*
- * include/asm-i386/processor.h
+ * include/asm-arm/processor.h
  *
- * Copyright (C) 1994 Linus Torvalds
+ * Copyright (C) 1995 Russell King
  */
 
-#ifndef __ASM_I386_PROCESSOR_H
-#define __ASM_I386_PROCESSOR_H
+#ifndef __ASM_ARM_PROCESSOR_H
+#define __ASM_ARM_PROCESSOR_H
 
 /*
- * System setup and hardware bug flags..
+ * Bus types
  */
-extern char hard_math;
-extern char x86;		/* lower 4 bits */
-extern char x86_vendor_id[13];
-extern char x86_model;		/* lower 4 bits */
-extern char x86_mask;		/* lower 4 bits */
-extern int  x86_capability;	/* field of flags */
-extern int  fdiv_bug;		
-extern char ignore_irq13;
-extern char wp_works_ok;		/* doesn't work on a 386 */
-extern char hlt_works_ok;	/* problems on some 486Dx4's and old 386's */
-
-#define start_bh_atomic() \
-__asm__ __volatile__("incl _intr_count")
-
-#define end_bh_atomic() \
-__asm__ __volatile__("decl _intr_count")
-
-/*
- * Bus types (default is ISA, but people can check others with these..)
- * MCA_bus hardcoded to 0 for now.
- */
-extern int EISA_bus;
+#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 */
 
-/*
- * 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	(0xC0000000UL)
+#define ARM_bus	1
+#define ARM_bus__is_a_macro
 
 /*
- * Size of io_bitmap in longwords: 32 is ports 0-0x3ff.
+ * on arm2,3 wp does not work ok
  */
-#define IO_BITMAP_SIZE	32
+#define wp_works_ok 0
+#define wp_works_ok__is_a_macro /* for versions in ksyms.c */
 
-struct i387_hard_struct {
-	long	cwd;
-	long	swd;
-	long	twd;
-	long	fip;
-	long	fcs;
-	long	foo;
-	long	fos;
-	long	st_space[20];	/* 8*10 bytes for each FP-reg = 80 bytes */
-};
+extern inline void start_bh_atomic(void)
+{
+  unsigned long dummy1, dummy2, *dummy3 = &intr_count;
+  __asm__ __volatile__(
+  " mov		%0,pc\n"
+  " teqp	pc,#0x08000003\n"
+  " ldr		%1,[%2]\n"
+  " add		%1,%1,#1\n"
+  " str		%1,[%2]\n"
+  " teqp	pc,%0\n"
+  : "=r" (dummy1), "=r" (dummy2)
+  : "r" (dummy3) : "memory");
+}
 
-struct i387_soft_struct {
-	long	cwd;
-	long	swd;
-	long	twd;
-	long	fip;
-	long	fcs;
-	long	foo;
-	long	fos;
-	long    top;
-	struct fpu_reg	regs[8];	/* 8*16 bytes for each FP-reg = 128 bytes */
-	unsigned char	lookahead;
-	struct info	*info;
-	unsigned long	entry_eip;
-};
+extern inline void end_bh_atomic(void)
+{
+  unsigned long dummy1, dummy2, *dummy3 = &intr_count;
+  __asm__ __volatile__(
+  " mov		%0,pc\n"
+  " teqp	pc,#0x08000003\n"
+  " ldr		%1,[%2]\n"
+  " sub		%1,%1,#1\n"
+  " str		%1,[%2]\n"
+  " teqp	pc,%0\n"
+  : "=r" (dummy1), "=r" (dummy2)
+  : "r" (dummy3) : "memory");
+}
 
-union i387_union {
-	struct i387_hard_struct hard;
-	struct i387_soft_struct soft;
-};
+/*
+ * 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 short	back_link,__blh;
-	unsigned long	esp0;
-	unsigned short	ss0,__ss0h;
-	unsigned long	esp1;
-	unsigned short	ss1,__ss1h;
-	unsigned long	esp2;
-	unsigned short	ss2,__ss2h;
-	unsigned long	cr3;
-	unsigned long	eip;
-	unsigned long	eflags;
-	unsigned long	eax,ecx,edx,ebx;
-	unsigned long	esp;
-	unsigned long	ebp;
-	unsigned long	esi;
-	unsigned long	edi;
-	unsigned short	es, __esh;
-	unsigned short	cs, __csh;
-	unsigned short	ss, __ssh;
-	unsigned short	ds, __dsh;
-	unsigned short	fs, __fsh;
-	unsigned short	gs, __gsh;
-	unsigned short	ldt, __ldth;
-	unsigned short	trace, bitmap;
-	unsigned long	io_bitmap[IO_BITMAP_SIZE+1];
-	unsigned long	tr;
-	unsigned long	cr2, trap_no, error_code;
-/* floating point info */
-	union i387_union i387;
-/* virtual 86 mode info */
-	struct vm86_struct * vm86_info;
-	unsigned long screen_bitmap;
-	unsigned long v86flags, v86mask, v86mode;
+  	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_task, 0, 0x40000000, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC }
+#define INIT_MMAP { &init_task, 0, 0x02000000, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC }
 
 #define INIT_TSS  { \
-	0,0, \
-	sizeof(init_kernel_stack) + (long) &init_kernel_stack, \
-	KERNEL_DS, 0, \
-	0,0,0,0,0,0, \
+	{ 0, }, \
+	{ 0, }, \
+	{ 0, }, \
+	0,0,0, \
 	(long) &swapper_pg_dir, \
-	0,0,0,0,0,0,0,0,0,0, \
-	USER_DS,0,USER_DS,0,USER_DS,0,USER_DS,0,USER_DS,0,USER_DS,0, \
-	_LDT(0),0, \
-	0, 0x8000, \
-	{~0, }, /* ioperm */ \
-	_TSS(0), 0, 0,0, \
-	{ { 0, }, },  /* 387 state */ \
-	NULL, 0, 0, 0, 0 /* vm86_info */ \
+	1 \
 }
 
-static inline void start_thread(struct pt_regs * regs, unsigned long eip, unsigned long esp)
+static inline void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp)
 {
-	regs->cs = USER_CS;
-	regs->ds = regs->es = regs->ss = regs->fs = regs->gs = USER_DS;
-	regs->eip = eip;
-	regs->esp = esp;
+	int i;
+	unsigned long *stack = (unsigned long *)sp;
+	for(i=3; i <15; i++)	/* Initialise all registers to zero */
+	  regs->uregs[i] = 0;
+	regs->PC = pc;		/* pc */
+	regs->SP = sp;		/* sp */
+	regs->R2 = stack[2];	/* r2 (envp) */
+	regs->R1 = stack[1];	/* r1 (argv) */
+	regs->R0 = stack[0];	/* r0 (argc) */
 }
 
-#endif /* __ASM_I386_PROCESSOR_H */
+#endif /* __ASM_ARM_PROCESSOR_H */
+
diff -r -u -N linux.orig/include/asm/processor.h.2 linux.arm/include/asm/processor.h.2
--- linux.orig/include/asm/processor.h.2	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm/processor.h.2	Fri Oct 27 23:11:20 1995
@@ -0,0 +1,95 @@
+/*
+ * 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 */
+
+extern inline void start_bh_atomic(void)
+{
+  unsigned long dummy1, dummy2;
+  __asm__ __volatile__(
+  " mov		%0,pc\n"
+  " teqp	pc,#0x08000003\n"
+  " ldr		%1,[%2]\n"
+  " add		%1,%1,#1\n"
+  " str		%1,[%2]\n"
+  " teqp	pc,%0\n"
+  : "=r" (dummy1), "=r" (dummy2)
+  : "r" (&intr_count) : "memory");
+}
+
+extern inline void end_bh_atomic(void)
+{
+  unsigned long dummy1, dummy2;
+  __asm__ __volatile__(
+  " mov		%0,pc\n"
+  " teqp	pc,#0x08000003\n"
+  " ldr		%1,[%2]\n"
+  " sub		%1,%1,#1\n"
+  " str		%1,[%2]\n"
+  " teqp	pc,%0\n"
+  : "=r" (dummy1), "=r" (dummy2)
+  : "r" (&intr_count) : "memory");
+}
+
+/*
+ * 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 */
+};
+
+#define INIT_MMAP { &init_task, 0, 0x40000000, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC }
+
+#define INIT_TSS  { \
+	{ 0, }, \
+	{ 0, }, \
+	{ 0, }, \
+	0,0,0, \
+	(long) &swapper_pg_dir \
+}
+
+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->PC = pc;		/* pc */
+	regs->SP = sp;		/* sp */
+	regs->R2 = stack[2];	/* r2 (envp) */
+	regs->R1 = stack[1];	/* r1 (argv) */
+	regs->R0 = stack[0];	/* r0 (argc) */
+}
+
+#endif /* __ASM_ARM_PROCESSOR_H */
+
diff -r -u -N linux.orig/include/asm/ptrace.h linux.arm/include/asm/ptrace.h
--- linux.orig/include/asm/ptrace.h	Fri Oct 27 23:09:01 1995
+++ linux.arm/include/asm/ptrace.h	Fri Oct 27 23:11:19 1995
@@ -1,51 +1,36 @@
-#ifndef _I386_PTRACE_H
-#define _I386_PTRACE_H
+#ifndef __ASM_ARM_PTRACE_H
+#define __ASM_ARM_PTRACE_H
 
-#define EBX 0
-#define ECX 1
-#define EDX 2
-#define ESI 3
-#define EDI 4
-#define EBP 5
-#define EAX 6
-#define DS 7
-#define ES 8
-#define FS 9
-#define GS 10
-#define ORIG_EAX 11
-#define EIP 12
-#define CS  13
-#define EFL 14
-#define UESP 15
-#define SS   16
 
-
-/* this struct defines the way the registers are stored on the 
+/* this struct defines the way the registers are stored on the
    stack during a system call. */
 
 struct pt_regs {
-	long ebx;
-	long ecx;
-	long edx;
-	long esi;
-	long edi;
-	long ebp;
-	long eax;
-	unsigned short ds, __dsu;
-	unsigned short es, __esu;
-	unsigned short fs, __fsu;
-	unsigned short gs, __gsu;
-	long orig_eax;
-	long eip;
-	unsigned short cs, __csu;
-	long eflags;
-	long esp;
-	unsigned short ss, __ssu;
+	long uregs[17];
 };
 
+#define PC	uregs[15]
+#define LR	uregs[14]
+#define SP	uregs[13]
+#define FP	uregs[12]
+#define IP	uregs[11]
+#define R10	uregs[10]
+#define R9	uregs[9]
+#define R8	uregs[8]
+#define R7	uregs[7]
+#define R6	uregs[6]
+#define R5	uregs[5]
+#define R4	uregs[4]
+#define R3	uregs[3]
+#define R2	uregs[2]
+#define R1	uregs[1]
+#define R0	uregs[0]
+#define ORIG_R0	uregs[16] /* -1 */
+
 #ifdef __KERNEL__
-#define user_mode(regs) ((VM_MASK & (regs)->eflags) || (3 & (regs)->cs))
+#define user_mode(regs) (((regs)->uregs[15] & 3) == 0)
 extern void show_regs(struct pt_regs *);
 #endif
 
 #endif
+
diff -r -u -N linux.orig/include/asm/segment.h linux.arm/include/asm/segment.h
--- linux.orig/include/asm/segment.h	Fri Oct 27 23:09:01 1995
+++ linux.arm/include/asm/segment.h	Fri Oct 27 23:11:19 1995
@@ -1,229 +1,108 @@
-#ifndef _ASM_SEGMENT_H
-#define _ASM_SEGMENT_H
+#ifndef __ASM_ARM_SEGMENT_H
+#define __ASM_ARM_SEGMENT_H
 
-#define KERNEL_CS	0x10
-#define KERNEL_DS	0x18
+#ifndef __ASSEMBLY__
 
-#define USER_CS		0x23
-#define USER_DS		0x2B
+#include <linux/sched.h>
 
-#ifndef __ASSEMBLY__
+#define KERNEL_CS   0x0
+#define KERNEL_DS   0x0
+
+#define USER_CS     0x1
+#define USER_DS     0x1
 
-static inline unsigned char get_user_byte(const char * addr)
+#define IS_USER_SEG (current->tss.fs != KERNEL_DS)
+
+extern unsigned char get_user_byte(const char * addr);
+static inline unsigned char get_mfs_byte(const char *addr)
 {
-	register unsigned char _v;
+	if (IS_USER_SEG)
+		return get_user_byte(addr);
+	else
+		return *addr;
+}
+#define get_fs_byte(addr) get_mfs_byte((char *)(addr))
 
-	__asm__ ("movb %%fs:%1,%0":"=q" (_v):"m" (*addr));
-	return _v;
+extern unsigned short get_user_word(const short *addr);
+static inline unsigned short get_mfs_word(const short *addr)
+{
+	if (IS_USER_SEG)
+		return get_user_word(addr);
+	else
+		return *addr;
 }
+#define get_fs_word(addr) get_mfs_word((short *)(addr))
 
-#define get_fs_byte(addr) get_user_byte((char *)(addr))
-
-static inline unsigned short get_user_word(const short *addr)
-{
-	unsigned short _v;
-
-	__asm__ ("movw %%fs:%1,%0":"=r" (_v):"m" (*addr));
-	return _v;
-}
-
-#define get_fs_word(addr) get_user_word((short *)(addr))
-
-static inline unsigned long get_user_long(const int *addr)
-{
-	unsigned long _v;
-
-	__asm__ ("movl %%fs:%1,%0":"=r" (_v):"m" (*addr)); \
-	return _v;
-}
-
-#define get_fs_long(addr) get_user_long((int *)(addr))
-
-static inline void put_user_byte(char val,char *addr)
-{
-__asm__ ("movb %0,%%fs:%1": /* no outputs */ :"iq" (val),"m" (*addr));
-}
-
-#define put_fs_byte(x,addr) put_user_byte((x),(char *)(addr))
-
-static inline void put_user_word(short val,short * addr)
-{
-__asm__ ("movw %0,%%fs:%1": /* no outputs */ :"ir" (val),"m" (*addr));
-}
-
-#define put_fs_word(x,addr) put_user_word((x),(short *)(addr))
-
-static inline void put_user_long(unsigned long val,int * addr)
-{
-__asm__ ("movl %0,%%fs:%1": /* no outputs */ :"ir" (val),"m" (*addr));
-}
-
-#define put_fs_long(x,addr) put_user_long((x),(int *)(addr))
-
-static inline void __generic_memcpy_tofs(void * to, const void * from, unsigned long n)
-{
-__asm__("cld\n\t"
-	"push %%es\n\t"
-	"push %%fs\n\t"
-	"pop %%es\n\t"
-	"testb $1,%%cl\n\t"
-	"je 1f\n\t"
-	"movsb\n"
-	"1:\ttestb $2,%%cl\n\t"
-	"je 2f\n\t"
-	"movsw\n"
-	"2:\tshrl $2,%%ecx\n\t"
-	"rep ; movsl\n\t"
-	"pop %%es"
-	: /* no outputs */
-	:"c" (n),"D" ((long) to),"S" ((long) from)
-	:"cx","di","si");
-}
-
-static inline void __constant_memcpy_tofs(void * to, const void * from, unsigned long n)
-{
-	switch (n) {
-		case 0:
-			return;
-		case 1:
-			put_user_byte(*(const char *) from, (char *) to);
-			return;
-		case 2:
-			put_user_word(*(const short *) from, (short *) to);
-			return;
-		case 3:
-			put_user_word(*(const short *) from, (short *) to);
-			put_user_byte(*(2+(const char *) from), 2+(char *) to);
-			return;
-		case 4:
-			put_user_long(*(const int *) from, (int *) to);
-			return;
-	}
-#define COMMON(x) \
-__asm__("cld\n\t" \
-	"push %%es\n\t" \
-	"push %%fs\n\t" \
-	"pop %%es\n\t" \
-	"rep ; movsl\n\t" \
-	x \
-	"pop %%es" \
-	: /* no outputs */ \
-	:"c" (n/4),"D" ((long) to),"S" ((long) from) \
-	:"cx","di","si")
-
-	switch (n % 4) {
-		case 0:
-			COMMON("");
-			return;
-		case 1:
-			COMMON("movsb\n\t");
-			return;
-		case 2:
-			COMMON("movsw\n\t");
-			return;
-		case 3:
-			COMMON("movsw\n\tmovsb\n\t");
-			return;
-	}
-#undef COMMON
-}
-
-static inline void __generic_memcpy_fromfs(void * to, const void * from, unsigned long n)
-{
-__asm__("cld\n\t"
-	"testb $1,%%cl\n\t"
-	"je 1f\n\t"
-	"fs ; movsb\n"
-	"1:\ttestb $2,%%cl\n\t"
-	"je 2f\n\t"
-	"fs ; movsw\n"
-	"2:\tshrl $2,%%ecx\n\t"
-	"rep ; fs ; movsl"
-	: /* no outputs */
-	:"c" (n),"D" ((long) to),"S" ((long) from)
-	:"cx","di","si","memory");
-}
-
-static inline void __constant_memcpy_fromfs(void * to, const void * from, unsigned long n)
-{
-	switch (n) {
-		case 0:
-			return;
-		case 1:
-			*(char *)to = get_user_byte((const char *) from);
-			return;
-		case 2:
-			*(short *)to = get_user_word((const short *) from);
-			return;
-		case 3:
-			*(short *) to = get_user_word((const short *) from);
-			*((char *) to + 2) = get_user_byte(2+(const char *) from);
-			return;
-		case 4:
-			*(int *) to = get_user_long((const int *) from);
-			return;
-	}
-#define COMMON(x) \
-__asm__("cld\n\t" \
-	"rep ; fs ; movsl\n\t" \
-	x \
-	: /* no outputs */ \
-	:"c" (n/4),"D" ((long) to),"S" ((long) from) \
-	:"cx","di","si","memory")
-
-	switch (n % 4) {
-		case 0:
-			COMMON("");
-			return;
-		case 1:
-			COMMON("fs ; movsb");
-			return;
-		case 2:
-			COMMON("fs ; movsw");
-			return;
-		case 3:
-			COMMON("fs ; movsw\n\tfs ; movsb");
-			return;
-	}
-#undef COMMON
-}
-
-#define memcpy_fromfs(to, from, n) \
-(__builtin_constant_p(n) ? \
- __constant_memcpy_fromfs((to),(from),(n)) : \
- __generic_memcpy_fromfs((to),(from),(n)))
-
-#define memcpy_tofs(to, from, n) \
-(__builtin_constant_p(n) ? \
- __constant_memcpy_tofs((to),(from),(n)) : \
- __generic_memcpy_tofs((to),(from),(n)))
-
-/*
- * Someone who knows GNU asm better than I should double check the following.
- * It seems to work, but I don't know if I'm doing something subtly wrong.
- * --- TYT, 11/24/91
- * [ nothing wrong here, Linus: I just changed the ax to be any reg ]
- */
+extern unsigned long get_user_long(const long *addr);
+static inline unsigned long get_mfs_long(const long *addr)
+{
+	if (IS_USER_SEG)
+		return get_user_long(addr);
+	else
+		return *addr;
+}
+#define get_fs_long(addr) get_mfs_long((long *)(addr))
+
+extern void put_user_byte(char val,char *addr);
+static inline void put_mfs_byte(char val, char *addr)
+{
+	if (IS_USER_SEG)
+		put_user_byte(val, addr);
+	else
+		*addr = val;
+}
+#define put_fs_byte(x,addr) put_mfs_byte((x),(char *)(addr))
+
+extern void put_user_word(short val,short * addr);
+static inline void put_mfs_word(short val, short *addr)
+{
+	if (IS_USER_SEG)
+		put_user_word(val, addr);
+	else
+		*addr = val;
+}
+#define put_fs_word(x,addr) put_mfs_word((x),(short *)(addr))
+
+extern void put_user_long(long val, long *addr);
+static inline void put_mfs_long(long val, long *addr)
+{
+	if (IS_USER_SEG)
+		put_user_long(val, addr);
+	else
+		*addr = val;
+}
+#define put_fs_long(x,addr) put_mfs_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
+#ifndef __need_memcpy_tofs
+inline
+#endif
+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)
 {
-	unsigned long _v;
-	__asm__("mov %%fs,%w0":"=r" (_v):"0" (0));
-	return _v;
+	return current->tss.fs;
 }
 
 static inline unsigned long get_ds(void)
 {
-	unsigned long _v;
-	__asm__("mov %%ds,%w0":"=r" (_v):"0" (0));
-	return _v;
+	return KERNEL_DS;
 }
 
 static inline void set_fs(unsigned long val)
 {
-	__asm__ __volatile__("mov %w0,%%fs": /* no output */ :"r" (val));
+	current->tss.fs = val;
 }
 
 #endif /* __ASSEMBLY__ */
 
-#endif /* _ASM_SEGMENT_H */
+#endif /* __ASM_ARM_SEGMENT_H */
+
diff -r -u -N linux.orig/include/asm/segment.h.2 linux.arm/include/asm/segment.h.2
--- linux.orig/include/asm/segment.h.2	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm/segment.h.2	Fri Oct 27 23:11:20 1995
@@ -0,0 +1,87 @@
+#ifndef __ASM_ARM_SEGMENT_H
+#define __ASM_ARM_SEGMENT_H
+
+#ifndef __ASSEMBLY__
+
+#define KERNEL_CS   0x0
+#define KERNEL_DS   0x0
+
+#define USER_CS     0x1
+#define USER_DS     0x1
+
+unsigned char inline get_user_byte(const char * addr)
+{
+	unsigned char ret;
+	__asm__ __volatile__(
+		"ldrbt\t%0,[%1],#0\n\t" :
+		"=r" (ret) :
+		"r" (addr));
+	return ret;
+}
+#define get_fs_byte(addr) get_user_byte((char *)(addr))
+
+unsigned short inline get_user_word(const short *addr)
+{
+	unsigned short ret, __dummy__;
+	__asm__ __volatile__(
+		"ldrbt\t%0,[%2],#1\n\t"
+		"ldrbt\t%1,[%2],#0\n\t"
+		"orr\t%0,%0,%1,LSL#8\n\t":
+		"=r" (ret), "=r" (__dummy__):
+		"r" (addr));
+	return ret;
+}
+#define get_fs_word(addr) get_user_word((short *)(addr))
+
+unsigned long inline get_user_long(const long *addr)
+{
+	unsigned long ret, __dummy__;
+	
+	__asm__ __volatile__(
+		"ands	%1, %2, #3\n\t"
+		"bic	%2, %2, #3\n\t"
+		"ldrt	%0, [%2], #4\n\t"
+		"beq	L0\n\t"
+		"ldrt	%2, [%2], #0\n\t"
+		"mov	%1, %1, LSL#3\n\t"
+		"mov	%0, %0, LSR %1\n\t"
+		"rsb	%1, %1, #32\n\t"
+		"orr	%0, %0, %2, lsl %1\n"
+		"L0:\t\n\t" :
+		"=r" (ret), "=r" (__dummy__) :
+		"r" (addr));
+	return ret;
+}
+		
+#define get_fs_long(addr) get_user_long((int *)(addr))
+
+extern void put_user_byte(char val,char *addr);
+#define put_fs_byte(x,addr) put_user_byte((x),(char *)(addr))
+
+extern void put_user_word(short val,short * addr);
+#define put_fs_word(x,addr) put_user_word((x),(short *)(addr))
+
+extern void put_user_long(unsigned long val,int * addr);
+#define put_fs_long(x,addr) put_user_long((x),(int *)(addr))
+
+extern void memcpy_tofs(void * to, const void * from, unsigned long n);
+extern void memcpy_fromfs(void * to, const void * from, unsigned long n);
+  
+static inline unsigned long get_fs(void)
+{
+	return 0;
+}
+
+static inline unsigned long get_ds(void)
+{
+	return 0; /* CHECK */
+}
+
+static inline void set_fs(unsigned long val)
+{
+}
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ASM_ARM_SEGMENT_H */
+
diff -r -u -N linux.orig/include/asm/signal.h linux.arm/include/asm/signal.h
--- linux.orig/include/asm/signal.h	Fri Oct 27 23:09:01 1995
+++ linux.arm/include/asm/signal.h	Fri Oct 27 23:11:19 1995
@@ -1,29 +1,13 @@
-#ifndef _ASMi386_SIGNAL_H
-#define _ASMi386_SIGNAL_H
+#ifndef __ASM_ARM_SIGNAL_H
+#define __ASM_ARM_SIGNAL_H
 
 struct sigcontext_struct {
-	unsigned short gs, __gsh;
-	unsigned short fs, __fsh;
-	unsigned short es, __esh;
-	unsigned short ds, __dsh;
-	unsigned long edi;
-	unsigned long esi;
-	unsigned long ebp;
-	unsigned long esp;
-	unsigned long ebx;
-	unsigned long edx;
-	unsigned long ecx;
-	unsigned long eax;
-	unsigned long trapno;
-	unsigned long err;
-	unsigned long eip;
-	unsigned short cs, __csh;
-	unsigned long eflags;
-	unsigned long esp_at_signal;
-	unsigned short ss, __ssh;
-	unsigned long i387;
-	unsigned long oldmask;
-	unsigned long cr2;
+  unsigned long magic;
+  unsigned long reg[17];
+  unsigned long trap_no;
+  unsigned long error_code;
+  unsigned long oldmask;
 };
 
 #endif
+
diff -r -u -N linux.orig/include/asm/string.h linux.arm/include/asm/string.h
--- linux.orig/include/asm/string.h	Fri Oct 27 23:09:01 1995
+++ linux.arm/include/asm/string.h	Fri Oct 27 23:11:20 1995
@@ -1,550 +1,31 @@
-#ifndef _I386_STRING_H_
-#define _I386_STRING_H_
-
-/*
- * This string-include defines all string functions as inline
- * functions. Use gcc. It also assumes ds=es=data space, this should be
- * normal. Most of the string-functions are rather heavily hand-optimized,
- * see especially strtok,strstr,str[c]spn. They should work, but are not
- * very easy to understand. Everything is done entirely within the register
- * set, making the functions fast and clean. String instructions have been
- * used through-out, making for "slightly" unclear code :-)
- *
- *		Copyright (C) 1991, 1992 Linus Torvalds
- */
- 
-extern inline char * strcpy(char * dest,const char *src)
-{
-__asm__ __volatile__(
-	"cld\n"
-	"1:\tlodsb\n\t"
-	"stosb\n\t"
-	"testb %%al,%%al\n\t"
-	"jne 1b"
-	: /* no output */
-	:"S" (src),"D" (dest):"si","di","ax","memory");
-return dest;
-}
-
-extern inline char * strncpy(char * dest,const char *src,size_t count)
-{
-__asm__ __volatile__(
-	"cld\n"
-	"1:\tdecl %2\n\t"
-	"js 2f\n\t"
-	"lodsb\n\t"
-	"stosb\n\t"
-	"testb %%al,%%al\n\t"
-	"jne 1b\n\t"
-	"rep\n\t"
-	"stosb\n"
-	"2:"
-	: /* no output */
-	:"S" (src),"D" (dest),"c" (count):"si","di","ax","cx","memory");
-return dest;
-}
-
-extern inline char * strcat(char * dest,const char * src)
-{
-__asm__ __volatile__(
-	"cld\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"decl %1\n"
-	"1:\tlodsb\n\t"
-	"stosb\n\t"
-	"testb %%al,%%al\n\t"
-	"jne 1b"
-	: /* no output */
-	:"S" (src),"D" (dest),"a" (0),"c" (0xffffffff):"si","di","ax","cx");
-return dest;
-}
-
-extern inline char * strncat(char * dest,const char * src,size_t count)
-{
-__asm__ __volatile__(
-	"cld\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"decl %1\n\t"
-	"movl %4,%3\n"
-	"1:\tdecl %3\n\t"
-	"js 2f\n\t"
-	"lodsb\n\t"
-	"stosb\n\t"
-	"testb %%al,%%al\n\t"
-	"jne 1b\n"
-	"2:\txorl %2,%2\n\t"
-	"stosb"
-	: /* no output */
-	:"S" (src),"D" (dest),"a" (0),"c" (0xffffffff),"g" (count)
-	:"si","di","ax","cx","memory");
-return dest;
-}
-
-extern inline int strcmp(const char * cs,const char * ct)
-{
-register int __res;
-__asm__ __volatile__(
-	"cld\n"
-	"1:\tlodsb\n\t"
-	"scasb\n\t"
-	"jne 2f\n\t"
-	"testb %%al,%%al\n\t"
-	"jne 1b\n\t"
-	"xorl %%eax,%%eax\n\t"
-	"jmp 3f\n"
-	"2:\tsbbl %%eax,%%eax\n\t"
-	"orb $1,%%eax\n"
-	"3:"
-	:"=a" (__res):"S" (cs),"D" (ct):"si","di");
-return __res;
-}
-
-extern inline int strncmp(const char * cs,const char * ct,size_t count)
-{
-register int __res;
-__asm__ __volatile__(
-	"cld\n"
-	"1:\tdecl %3\n\t"
-	"js 2f\n\t"
-	"lodsb\n\t"
-	"scasb\n\t"
-	"jne 3f\n\t"
-	"testb %%al,%%al\n\t"
-	"jne 1b\n"
-	"2:\txorl %%eax,%%eax\n\t"
-	"jmp 4f\n"
-	"3:\tsbbl %%eax,%%eax\n\t"
-	"orb $1,%%al\n"
-	"4:"
-	:"=a" (__res):"S" (cs),"D" (ct),"c" (count):"si","di","cx");
-return __res;
-}
-
-extern inline char * strchr(const char * s, int c)
-{
-register char * __res;
-__asm__ __volatile__(
-	"cld\n\t"
-	"movb %%al,%%ah\n"
-	"1:\tlodsb\n\t"
-	"cmpb %%ah,%%al\n\t"
-	"je 2f\n\t"
-	"testb %%al,%%al\n\t"
-	"jne 1b\n\t"
-	"movl $1,%1\n"
-	"2:\tmovl %1,%0\n\t"
-	"decl %0"
-	:"=a" (__res):"S" (s),"0" (c):"si");
-return __res;
-}
-
-extern inline char * strrchr(const char * s, int c)
-{
-register char * __res;
-__asm__ __volatile__(
-	"cld\n\t"
-	"movb %%al,%%ah\n"
-	"1:\tlodsb\n\t"
-	"cmpb %%ah,%%al\n\t"
-	"jne 2f\n\t"
-	"leal -1(%%esi),%0\n"
-	"2:\ttestb %%al,%%al\n\t"
-	"jne 1b"
-	:"=d" (__res):"0" (0),"S" (s),"a" (c):"ax","si");
-return __res;
-}
-
-extern inline size_t strspn(const char * cs, const char * ct)
-{
-register char * __res;
-__asm__ __volatile__(
-	"cld\n\t"
-	"movl %4,%%edi\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"notl %%ecx\n\t"
-	"decl %%ecx\n\t"
-	"movl %%ecx,%%edx\n"
-	"1:\tlodsb\n\t"
-	"testb %%al,%%al\n\t"
-	"je 2f\n\t"
-	"movl %4,%%edi\n\t"
-	"movl %%edx,%%ecx\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"je 1b\n"
-	"2:\tdecl %0"
-	:"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
-	:"ax","cx","dx","di");
-return __res-cs;
-}
-
-extern inline size_t strcspn(const char * cs, const char * ct)
-{
-register char * __res;
-__asm__ __volatile__(
-	"cld\n\t"
-	"movl %4,%%edi\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"notl %%ecx\n\t"
-	"decl %%ecx\n\t"
-	"movl %%ecx,%%edx\n"
-	"1:\tlodsb\n\t"
-	"testb %%al,%%al\n\t"
-	"je 2f\n\t"
-	"movl %4,%%edi\n\t"
-	"movl %%edx,%%ecx\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"jne 1b\n"
-	"2:\tdecl %0"
-	:"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
-	:"ax","cx","dx","di");
-return __res-cs;
-}
-
-extern inline char * strpbrk(const char * cs,const char * ct)
-{
-register char * __res;
-__asm__ __volatile__(
-	"cld\n\t"
-	"movl %4,%%edi\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"notl %%ecx\n\t"
-	"decl %%ecx\n\t"
-	"movl %%ecx,%%edx\n"
-	"1:\tlodsb\n\t"
-	"testb %%al,%%al\n\t"
-	"je 2f\n\t"
-	"movl %4,%%edi\n\t"
-	"movl %%edx,%%ecx\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"jne 1b\n\t"
-	"decl %0\n\t"
-	"jmp 3f\n"
-	"2:\txorl %0,%0\n"
-	"3:"
-	:"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
-	:"ax","cx","dx","di");
-return __res;
-}
-
-extern inline char * strstr(const char * cs,const char * ct)
-{
-register char * __res;
-__asm__ __volatile__(
-	"cld\n\t" \
-	"movl %4,%%edi\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"notl %%ecx\n\t"
-	"decl %%ecx\n\t"	/* NOTE! This also sets Z if searchstring='' */
-	"movl %%ecx,%%edx\n"
-	"1:\tmovl %4,%%edi\n\t"
-	"movl %%esi,%%eax\n\t"
-	"movl %%edx,%%ecx\n\t"
-	"repe\n\t"
-	"cmpsb\n\t"
-	"je 2f\n\t"		/* also works for empty string, see above */
-	"xchgl %%eax,%%esi\n\t"
-	"incl %%esi\n\t"
-	"cmpb $0,-1(%%eax)\n\t"
-	"jne 1b\n\t"
-	"xorl %%eax,%%eax\n\t"
-	"2:"
-	:"=a" (__res):"0" (0),"c" (0xffffffff),"S" (cs),"g" (ct)
-	:"cx","dx","di","si");
-return __res;
-}
-
-extern inline size_t strlen(const char * s)
-{
-register int __res;
-__asm__ __volatile__(
-	"cld\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"notl %0\n\t"
-	"decl %0"
-	:"=c" (__res):"D" (s),"a" (0),"0" (0xffffffff):"di");
-return __res;
-}
+#ifndef __ASM_ARM_STRING_H
+#define __ASM_ARM_STRING_H
 
+extern  char * strcpy(char * dest,const char *src);
+extern  char * strncpy(char * dest,const char *src,size_t count);
+extern  char * strcat(char * dest,const char * src);
+extern  char * strncat(char * dest,const char * src,size_t count);
+extern  int strcmp(const char * cs,const char * ct);
+extern  int strncmp(const char * cs,const char * ct,size_t count);
+extern  char * strchr(const char * s, int c);
+extern  char * strrchr(const char * s, int c);
+extern  size_t strspn(const char * cs, const char * ct);
+extern  size_t strcspn(const char * cs, const char * ct);
+extern  char * strpbrk(const char * cs,const char * ct);
+extern  char * strstr(const char * cs,const char * ct);
+extern  size_t strlen(const char * s);
 extern char * ___strtok;
-
-extern inline char * strtok(char * s,const char * ct)
-{
-register char * __res;
-__asm__ __volatile__(
-	"testl %1,%1\n\t"
-	"jne 1f\n\t"
-	"testl %0,%0\n\t"
-	"je 8f\n\t"
-	"movl %0,%1\n"
-	"1:\txorl %0,%0\n\t"
-	"movl $-1,%%ecx\n\t"
-	"xorl %%eax,%%eax\n\t"
-	"cld\n\t"
-	"movl %4,%%edi\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"notl %%ecx\n\t"
-	"decl %%ecx\n\t"
-	"je 7f\n\t"			/* empty delimiter-string */
-	"movl %%ecx,%%edx\n"
-	"2:\tlodsb\n\t"
-	"testb %%al,%%al\n\t"
-	"je 7f\n\t"
-	"movl %4,%%edi\n\t"
-	"movl %%edx,%%ecx\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"je 2b\n\t"
-	"decl %1\n\t"
-	"cmpb $0,(%1)\n\t"
-	"je 7f\n\t"
-	"movl %1,%0\n"
-	"3:\tlodsb\n\t"
-	"testb %%al,%%al\n\t"
-	"je 5f\n\t"
-	"movl %4,%%edi\n\t"
-	"movl %%edx,%%ecx\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"jne 3b\n\t"
-	"decl %1\n\t"
-	"cmpb $0,(%1)\n\t"
-	"je 5f\n\t"
-	"movb $0,(%1)\n\t"
-	"incl %1\n\t"
-	"jmp 6f\n"
-	"5:\txorl %1,%1\n"
-	"6:\tcmpb $0,(%0)\n\t"
-	"jne 7f\n\t"
-	"xorl %0,%0\n"
-	"7:\ttestl %0,%0\n\t"
-	"jne 8f\n\t"
-	"movl %0,%1\n"
-	"8:"
-	:"=b" (__res),"=S" (___strtok)
-	:"0" (___strtok),"1" (s),"g" (ct)
-	:"ax","cx","dx","di","memory");
-return __res;
-}
-
-extern inline void * __memcpy(void * to, const void * from, size_t n)
-{
-__asm__ __volatile__(
-	"cld\n\t"
-	"movl %%edx, %%ecx\n\t"
-	"shrl $2,%%ecx\n\t"
-	"rep ; movsl\n\t"
-	"testb $1,%%dl\n\t"
-	"je 1f\n\t"
-	"movsb\n"
-	"1:\ttestb $2,%%dl\n\t"
-	"je 2f\n\t"
-	"movsw\n"
-	"2:\n"
-	: /* no output */
-	:"d" (n),"D" ((long) to),"S" ((long) from)
-	: "cx","di","si","memory");
-return (to);
-}
-
-/*
- * This looks horribly ugly, but the compiler can optimize it totally,
- * as the count is constant.
- */
-extern inline void * __constant_memcpy(void * to, const void * from, size_t n)
-{
-	switch (n) {
-		case 0:
-			return to;
-		case 1:
-			*(unsigned char *)to = *(unsigned char *)from;
-			return to;
-		case 2:
-			*(unsigned short *)to = *(unsigned short *)from;
-			return to;
-		case 3:
-			*(unsigned short *)to = *(unsigned short *)from;
-			*(2+(unsigned char *)to) = *(2+(unsigned char *)from);
-			return to;
-		case 4:
-			*(unsigned long *)to = *(unsigned long *)from;
-			return to;
-	}
-#define COMMON(x) \
-__asm__("cld\n\t" \
-	"rep ; movsl" \
-	x \
-	: /* no outputs */ \
-	: "c" (n/4),"D" ((long) to),"S" ((long) from) \
-	: "cx","di","si","memory");
-
-	switch (n % 4) {
-		case 0: COMMON(""); return to;
-		case 1: COMMON("\n\tmovsb"); return to;
-		case 2: COMMON("\n\tmovsw"); return to;
-		case 3: COMMON("\n\tmovsw\n\tmovsb"); return to;
-	}
-#undef COMMON
-}
-
-#define memcpy(t, f, n) \
-(__builtin_constant_p(n) ? \
- __constant_memcpy((t),(f),(n)) : \
- __memcpy((t),(f),(n)))
-
-extern inline void * memmove(void * dest,const void * src, size_t n)
-{
-if (dest<src)
-__asm__ __volatile__(
-	"cld\n\t"
-	"rep\n\t"
-	"movsb"
-	: /* no output */
-	:"c" (n),"S" (src),"D" (dest)
-	:"cx","si","di");
-else
-__asm__ __volatile__(
-	"std\n\t"
-	"rep\n\t"
-	"movsb\n\t"
-	"cld"
-	: /* no output */
-	:"c" (n),
-	 "S" (n-1+(const char *)src),
-	 "D" (n-1+(char *)dest)
-	:"cx","si","di","memory");
-return dest;
-}
-
-extern inline int memcmp(const void * cs,const void * ct,size_t count)
-{
-register int __res;
-__asm__ __volatile__(
-	"cld\n\t"
-	"repe\n\t"
-	"cmpsb\n\t"
-	"je 1f\n\t"
-	"sbbl %%eax,%%eax\n\t"
-	"orb $1,%%al\n"
-	"1:"
-	:"=a" (__res):"0" (0),"S" (cs),"D" (ct),"c" (count)
-	:"si","di","cx");
-return __res;
-}
-
-extern inline void * memchr(const void * cs,int c,size_t count)
-{
-register void * __res;
-if (!count)
-	return NULL;
-__asm__ __volatile__(
-	"cld\n\t"
-	"repne\n\t"
-	"scasb\n\t"
-	"je 1f\n\t"
-	"movl $1,%0\n"
-	"1:\tdecl %0"
-	:"=D" (__res):"a" (c),"D" (cs),"c" (count)
-	:"cx");
-return __res;
-}
-
-extern inline void * __memset_generic(void * s, char c,size_t count)
-{
-__asm__ __volatile__(
-	"cld\n\t"
-	"rep\n\t"
-	"stosb"
-	: /* no output */
-	:"a" (c),"D" (s),"c" (count)
-	:"cx","di","memory");
-return s;
-}
-
-/* we might want to write optimized versions of these later */
-#define __constant_c_memset(s,c,count) __memset_generic((s),(unsigned char)(c),(count))
-#define __constant_count_memset(s,c,count) __memset_generic((s),(c),(count))
-
-/*
- * This looks horribly ugly, but the compiler can optimize it totally,
- * as we by now know that both pattern and count is constant..
- */
-extern inline void * __constant_c_and_count_memset(void * s, unsigned long pattern, size_t count)
-{
-	switch (count) {
-		case 0:
-			return s;
-		case 1:
-			*(unsigned char *)s = pattern;
-			return s;
-		case 2:
-			*(unsigned short *)s = pattern;
-			return s;
-		case 3:
-			*(unsigned short *)s = pattern;
-			*(2+(unsigned char *)s) = pattern;
-			return s;
-		case 4:
-			*(unsigned long *)s = pattern;
-			return s;
-	}
-#define COMMON(x) \
-__asm__("cld\n\t" \
-	"rep ; stosl" \
-	x \
-	: /* no outputs */ \
-	: "a" (pattern),"c" (count/4),"D" ((long) s) \
-	: "cx","di","memory")
-
-	switch (count % 4) {
-		case 0: COMMON(""); return s;
-		case 1: COMMON("\n\tstosb"); return s;
-		case 2: COMMON("\n\tstosw"); return s;
-		case 3: COMMON("\n\tstosw\n\tstosb"); return s;
-	}
-#undef COMMON
-}
-
-#define __constant_c_x_memset(s, c, count) \
-(__builtin_constant_p(count) ? \
- __constant_c_and_count_memset((s),(c),(count)) : \
- __constant_c_memset((s),(c),(count)))
-
-#define __memset(s, c, count) \
-(__builtin_constant_p(count) ? \
- __constant_count_memset((s),(c),(count)) : \
- __memset_generic((s),(c),(count)))
-
-#define memset(s, c, count) \
-(__builtin_constant_p(c) ? \
- __constant_c_x_memset((s),(0x01010101UL*(unsigned char)c),(count)) : \
- __memset((s),(c),(count)))
+extern  char * strtok(char * s,const char * ct);
+extern  void * memcpy(void * to, const void * from, size_t n);
+extern  void * memmove(void * dest,const void * src, size_t n);
+extern  int memcmp(const void * cs,const void * ct,size_t count);
+extern  void * memchr(const void * cs,int c,size_t count);
+extern  void * memset(void * s, char c,size_t count);
 
 /*
  * find the first occurrence of byte 'c', or 1 past the area if none
  */
-extern inline void * memscan(void * addr, int c, size_t size)
-{
-	if (!size)
-		return addr;
-	__asm__("cld
-		repnz; scasb
-		jnz 1f
-		dec %%edi
-1:		"
-		: "=D" (addr), "=c" (size)
-		: "0" (addr), "1" (size), "a" (c));
-	return addr;
-}
+extern  void * memscan(void * addr, int c, size_t size);
 
 #endif
+ 
diff -r -u -N linux.orig/include/asm/system.h linux.arm/include/asm/system.h
--- linux.orig/include/asm/system.h	Fri Oct 27 23:09:01 1995
+++ linux.arm/include/asm/system.h	Fri Oct 27 23:11:20 1995
@@ -1,244 +1,156 @@
-#ifndef __ASM_SYSTEM_H
-#define __ASM_SYSTEM_H
+#ifndef __ASM_ARM_SYSTEM_H
+#define __ASM_ARM_SYSTEM_H
 
-#include <asm/segment.h>
-
-/*
- * Entry into gdt where to find first TSS. GDT layout:
- *   0 - nul
- *   1 - kernel code segment
- *   2 - kernel data segment
- *   3 - user code segment
- *   4 - user data segment
- * ...
- *   8 - TSS #0
- *   9 - LDT #0
- *  10 - TSS #1
- *  11 - LDT #1
- */
-#define FIRST_TSS_ENTRY 8
-#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1)
-#define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3))
-#define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3))
-#define load_TR(n) __asm__("ltr %%ax": /* no output */ :"a" (_TSS(n)))
-#define load_ldt(n) __asm__("lldt %%ax": /* no output */ :"a" (_LDT(n)))
-#define store_TR(n) \
-__asm__("str %%ax\n\t" \
-	"subl %2,%%eax\n\t" \
-	"shrl $4,%%eax" \
-	:"=a" (n) \
-	:"0" (0),"i" (FIRST_TSS_ENTRY<<3))
+#include <linux/kernel.h>
 
 /* This special macro can be used to load a debugging register */
 
-#define loaddebug(register) \
-		__asm__("movl %0,%%edx\n\t" \
-			"movl %%edx,%%db" #register "\n\t" \
-			: /* no output */ \
-			:"m" (current->debugreg[register]) \
-			:"dx");
-
+#define loaddebug(register)
 
 /*
- *	switch_to(n) should switch tasks to task nr n, first
+ *      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.
- * This also clears the TS-flag if the task we switched to has used
- * the math co-processor latest.
  *
  * It also reloads the debug regs if necessary..
  */
-#define switch_to(tsk) do { \
-__asm__("cli\n\t" \
-	"xchgl %%ecx,_current\n\t" \
-	"ljmp %0\n\t" \
-	"sti\n\t" \
-	"cmpl %%ecx,_last_task_used_math\n\t" \
-	"jne 1f\n\t" \
-	"clts\n" \
-	"1:" \
-	: /* no output */ \
-	:"m" (*(((char *)&tsk->tss.tr)-4)), \
-	 "c" (tsk) \
-	:"cx"); \
-	/* Now maybe reload the debug registers */ \
-	if(current->debugreg[7]){ \
-		loaddebug(0); \
-		loaddebug(1); \
-		loaddebug(2); \
-		loaddebug(3); \
-		loaddebug(6); \
-	} \
-} while (0)
-
-#define _set_base(addr,base) \
-__asm__("movw %%dx,%0\n\t" \
-	"rorl $16,%%edx\n\t" \
-	"movb %%dl,%1\n\t" \
-	"movb %%dh,%2" \
-	: /* no output */ \
-	:"m" (*((addr)+2)), \
-	 "m" (*((addr)+4)), \
-	 "m" (*((addr)+7)), \
-	 "d" (base) \
-	:"dx")
-
-#define _set_limit(addr,limit) \
-__asm__("movw %%dx,%0\n\t" \
-	"rorl $16,%%edx\n\t" \
-	"movb %1,%%dh\n\t" \
-	"andb $0xf0,%%dh\n\t" \
-	"orb %%dh,%%dl\n\t" \
-	"movb %%dl,%1" \
-	: /* no output */ \
-	:"m" (*(addr)), \
-	 "m" (*((addr)+6)), \
-	 "d" (limit) \
-	:"dx")
-
-#define set_base(ldt,base) _set_base( ((char *)&(ldt)) , base )
-#define set_limit(ldt,limit) _set_limit( ((char *)&(ldt)) , (limit-1)>>12 )
-
-static inline unsigned long _get_base(char * addr)
-{
-	unsigned long __base;
-	__asm__("movb %3,%%dh\n\t"
-		"movb %2,%%dl\n\t"
-		"shll $16,%%edx\n\t"
-		"movw %1,%%dx"
-		:"=&d" (__base)
-		:"m" (*((addr)+2)),
-		 "m" (*((addr)+4)),
-		 "m" (*((addr)+7)));
-	return __base;
-}
-
-#define get_base(ldt) _get_base( ((char *)&(ldt)) )
-
-static inline unsigned long get_limit(unsigned long segment)
-{
-	unsigned long __limit;
-	__asm__("lsll %1,%0"
-		:"=r" (__limit):"r" (segment));
-	return __limit+1;
-}
 
-#define nop() __asm__ __volatile__ ("nop")
-
-/*
- * Clear and set 'TS' bit respectively
- */
-#define clts() __asm__ __volatile__ ("clts")
-#define stts() \
-__asm__ __volatile__ ( \
-	"movl %%cr0,%%eax\n\t" \
-	"orl $8,%%eax\n\t" \
-	"movl %%eax,%%cr0" \
-	: /* no outputs */ \
-	: /* no inputs */ \
-	:"ax")
+extern void switch_to(void *);
 
+#define CHECK_ALIGN(w, n, s) \
+  if (((long)w) & (n-1)) \
+    printk("Mal-aligned pointer in " s ": %p (in file %s, line %d)\n", w, __FILE__, __LINE__);
 
 extern inline unsigned long xchg_u8(char * m, unsigned long val)
 {
-	__asm__("xchgb %b0,%1":"=q" (val),"=m" (*m):"0" (val):"memory");
+#if __arm__ > 2
+	__asm__ __volatile__(
+	" swpb	%0,%1,[%2]\n"
+	: "=r" (val)
+	: "r" (val), "r" (m)
+	: "memory");
+#else
+	unsigned long temp0, temp1;
+	__asm__ __volatile__(
+	" mov   %0, pc\n"
+	" orr	%1, %0, #0x08000000\n"
+	" teqp  %1, #0\n"
+	" mov   %1, %3\n"		/* Safe: in case it uses same reg */
+	" ldrb	%2, [%4]\n"
+	" strb  %1, [%4]\n"
+	" teqp  %0, #0\n"
+	: "=r" (temp0), "=r" (temp1), "=r" (val)
+	: "r" (val), "r" (m)
+	: "memory");
+#endif
 	return val;
 }
 
 extern inline unsigned long xchg_u16(short * m, unsigned long val)
 {
-	__asm__("xchgw %w0,%1":"=r" (val),"=m" (*m):"0" (val):"memory");
-	return val;
+	unsigned long temp0, temp1;
+	CHECK_ALIGN(m, 2, "xchg_u16");
+	__asm__ __volatile__(
+	" mov   %0, pc\n"
+	" orr	%1, %0, #0x08000000\n"
+	" teqp  %1, #0\n"
+	" mov   %1, %3\n"		/* Safe: in case it uses same reg */
+	" 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" (val)
+	: "r" (val), "r" (m)
+	: "memory");
+	return val & 0xffff;
 }
 
 extern inline unsigned long xchg_u32(int * m, unsigned long val)
 {
-	__asm__("xchgl %0,%1":"=r" (val),"=m" (*m):"0" (val):"memory");
+#if __arm__ > 2
+	CHECK_ALIGN(m, 4, "xchg_u32");
+	__asm__ __volatile__(
+	" swp	%0,%1,[%2]\n"
+	: "=r" (val)
+	: "r" (val), "r" (m)
+	: "memory");
+#else
+	unsigned long temp0, temp1;
+	CHECK_ALIGN(m, 4, "xchg_u32");
+	__asm__ __volatile__(
+	" mov   %0, pc\n"
+	" orr	%1, %0, #0x08000000\n"
+	" teqp  %1, #0\n"
+	" mov   %1, %3\n"		/* Safe: in case it uses same reg */
+	" ldr	%2, [%4]\n"
+	" str   %1, [%4]\n"
+	" teqp  %0, #0\n"
+	: "=r" (temp0), "=r" (temp1), "=r" (val)
+	: "r" (val), "r" (m)
+	: "memory");
+#endif
 	return val;
 }
 
 extern inline int tas(char * m)
 {
-	return xchg_u8(m,1);
+        return xchg_u8(m,1);
 }
 
 extern inline void * xchg_ptr(void * m, void * val)
 {
-	return (void *) xchg_u32(m, (unsigned long) val);
+        return (void *) xchg_u32(m, (unsigned long) val);
 }
 
-#define sti() __asm__ __volatile__ ("sti": : :"memory")
-#define cli() __asm__ __volatile__ ("cli": : :"memory")
-
-#define save_flags(x) \
-__asm__ __volatile__("pushfl ; popl %0":"=r" (x): /* no input */ :"memory")
-
-#define restore_flags(x) \
-__asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"r" (x):"memory")
-
-#define iret() __asm__ __volatile__ ("iret": : :"memory")
-
-#define _set_gate(gate_addr,type,dpl,addr) \
-__asm__ __volatile__ ("movw %%dx,%%ax\n\t" \
-	"movw %2,%%dx\n\t" \
-	"movl %%eax,%0\n\t" \
-	"movl %%edx,%1" \
-	:"=m" (*((long *) (gate_addr))), \
-	 "=m" (*(1+(long *) (gate_addr))) \
-	:"i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
-	 "d" ((char *) (addr)),"a" (KERNEL_CS << 16) \
-	:"ax","dx")
-
-#define set_intr_gate(n,addr) \
-	_set_gate(&idt[n],14,0,addr)
-
-#define set_trap_gate(n,addr) \
-	_set_gate(&idt[n],15,0,addr)
-
-#define set_system_gate(n,addr) \
-	_set_gate(&idt[n],15,3,addr)
-
-#define set_call_gate(a,addr) \
-	_set_gate(a,12,3,addr)
-
-#define _set_seg_desc(gate_addr,type,dpl,base,limit) {\
-	*((gate_addr)+1) = ((base) & 0xff000000) | \
-		(((base) & 0x00ff0000)>>16) | \
-		((limit) & 0xf0000) | \
-		((dpl)<<13) | \
-		(0x00408000) | \
-		((type)<<8); \
-	*(gate_addr) = (((base) & 0x0000ffff)<<16) | \
-		((limit) & 0x0ffff); }
-
-#define _set_tssldt_desc(n,addr,limit,type) \
-__asm__ __volatile__ ("movw $" #limit ",%1\n\t" \
-	"movw %%ax,%2\n\t" \
-	"rorl $16,%%eax\n\t" \
-	"movb %%al,%3\n\t" \
-	"movb $" type ",%4\n\t" \
-	"movb $0x00,%5\n\t" \
-	"movb %%ah,%6\n\t" \
-	"rorl $16,%%eax" \
-	: /* no output */ \
-	:"a" (addr+0xc0000000), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), \
-	 "m" (*(n+5)), "m" (*(n+6)), "m" (*(n+7)) \
-	)
-
-#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),((int)(addr)),235,"0x89")
-#define set_ldt_desc(n,addr,size) \
-	_set_tssldt_desc(((char *) (n)),((int)(addr)),((size << 3) - 1),"0x82")
+#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)
 
-/*
- * This is the ldt that every process will get unless we need
- * something other than this.
- */
-extern struct desc_struct default_ldt;
+#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)			\
+  : "psr");			\
+} while (0)
 
-/*
- * disable hlt during certain critical i/o operations
- */
-#define HAVE_DISABLE_HLT
-void disable_hlt(void);
-void enable_hlt(void);
+#define nop()			\
+do {				\
+ __asm__ __volatile__(		\
+ "mov r0,r0\n"			\
+ : : );				\
+} while (0)
 
 #endif
+
diff -r -u -N linux.orig/include/asm/types.h linux.arm/include/asm/types.h
--- linux.orig/include/asm/types.h	Fri Oct 27 23:09:01 1995
+++ linux.arm/include/asm/types.h	Fri Oct 27 23:11:20 1995
@@ -1,5 +1,5 @@
-#ifndef _I386_TYPES_H
-#define _I386_TYPES_H
+#ifndef __ASM_ARM_TYPES_H
+#define __ASM_ARM_TYPES_H
 
 #ifndef _SIZE_T
 #define _SIZE_T
@@ -55,3 +55,4 @@
 #endif /* __KERNEL__ */
 
 #endif
+
diff -r -u -N linux.orig/include/asm/unistd.h linux.arm/include/asm/unistd.h
--- linux.orig/include/asm/unistd.h	Fri Oct 27 23:09:01 1995
+++ linux.arm/include/asm/unistd.h	Fri Oct 27 23:11:20 1995
@@ -1,117 +1,105 @@
-#ifndef _ASM_I386_UNISTD_H_
-#define _ASM_I386_UNISTD_H_
+#ifndef __ASM_ARM_UNISTD_H
+#define __ASM_ARM_UNISTD_H
 
-/* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */
-#define _syscall0(type,name) \
-type name(void) \
-{ \
-long __res; \
-__asm__ volatile ("int $0x80" \
-	: "=a" (__res) \
-	: "0" (__NR_##name)); \
-if (__res >= 0) \
-	return (type) __res; \
-errno = -__res; \
-return -1; \
-}
-
-#define _syscall1(type,name,type1,arg1) \
-type name(type1 arg1) \
-{ \
-long __res; \
-__asm__ volatile ("int $0x80" \
-	: "=a" (__res) \
-	: "0" (__NR_##name),"b" ((long)(arg1))); \
-if (__res >= 0) \
-	return (type) __res; \
-errno = -__res; \
-return -1; \
-}
+#include <linux/unistd.h>
 
-#define _syscall2(type,name,type1,arg1,type2,arg2) \
-type name(type1 arg1,type2 arg2) \
-{ \
-long __res; \
-__asm__ volatile ("int $0x80" \
-	: "=a" (__res) \
-	: "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2))); \
-if (__res >= 0) \
-	return (type) __res; \
-errno = -__res; \
-return -1; \
+#define __sys2(x) #x
+#define __sys1(x) __sys2(x)
+#define __syscall(name) "swi 0x900000+" __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 _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
-type name(type1 arg1,type2 arg2,type3 arg3) \
-{ \
-long __res; \
-__asm__ volatile ("int $0x80" \
-	: "=a" (__res) \
-	: "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
-		  "d" ((long)(arg3))); \
-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 ("int $0x80" \
-	: "=a" (__res) \
-	: "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
-	  "d" ((long)(arg3)),"S" ((long)(arg4))); \
-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 ("int $0x80" \
-	: "=a" (__res) \
-	: "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
-	  "d" ((long)(arg3)),"S" ((long)(arg4)),"D" ((long)(arg5))); \
-if (__res>=0) \
-	return (type) __res; \
-errno=-__res; \
-return -1; \
-}
+type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4);
 
-#ifdef __KERNEL_SYSCALLS__
+#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);
 
-/*
- * we need this inline - forking from kernel space will result
- * in NO COPY ON WRITE (!!!), until an execve is executed. This
- * is no problem, but for the stack. This is handled by not letting
- * main() use the stack at all after fork(). Thus, no function
- * calls - which means inline code for fork too, as otherwise we
- * would use the stack upon exit from 'fork()'.
- *
- * Actually only pause and fork are needed inline, so that there
- * won't be any messing with the stack from main(), but we define
- * some others too.
- */
+
+#ifdef __KERNEL_SYSCALLS__
 #define __NR__exit __NR_exit
-static inline _syscall0(int,idle)
-static inline _syscall0(int,fork)
-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 _syscall0(int,idle);
+static inline _syscall0(int,fork);
+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)
 {
@@ -120,4 +108,7 @@
 
 #endif
 
-#endif /* _ASM_I386_UNISTD_H_ */
+#endif /* __ASM_ARM_UNISTD_H */
+
+
+
diff -r -u -N linux.orig/include/asm-arm/arcaudio.h linux.arm/include/asm-arm/arcaudio.h
--- linux.orig/include/asm-arm/arcaudio.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/arcaudio.h	Fri Oct 27 23:11:23 1995
@@ -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 -r -u -N linux.orig/include/asm-arm/assembler.h linux.arm/include/asm-arm/assembler.h
--- linux.orig/include/asm-arm/assembler.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/assembler.h	Fri Oct 27 23:11:23 1995
@@ -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 -r -u -N linux.orig/include/asm-arm/bitops.h linux.arm/include/asm-arm/bitops.h
--- linux.orig/include/asm-arm/bitops.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/bitops.h	Fri Oct 27 23:11:21 1995
@@ -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 -r -u -N linux.orig/include/asm-arm/bugs.h linux.arm/include/asm-arm/bugs.h
--- linux.orig/include/asm-arm/bugs.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/bugs.h	Fri Oct 27 23:11:21 1995
@@ -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 -r -u -N linux.orig/include/asm-arm/byteorder.h linux.arm/include/asm-arm/byteorder.h
--- linux.orig/include/asm-arm/byteorder.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/byteorder.h	Fri Oct 27 23:11:21 1995
@@ -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 -r -u -N linux.orig/include/asm-arm/byteorder.h.2 linux.arm/include/asm-arm/byteorder.h.2
--- linux.orig/include/asm-arm/byteorder.h.2	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/byteorder.h.2	Fri Oct 27 23:11:23 1995
@@ -0,0 +1,59 @@
+#ifndef __ASM_ARM_BYTEORDER_H
+#define __ASM_ARM_BYTEORDER_H
+
+#undef ntohl
+#undef ntohs
+#undef htonl
+#undef htons
+
+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 unsigned long int __ntohl(unsigned long int 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 unsigned short int __ntohs(unsigned short int 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 -r -u -N linux.orig/include/asm-arm/delay.h linux.arm/include/asm-arm/delay.h
--- linux.orig/include/asm-arm/delay.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/delay.h	Fri Oct 27 23:11:21 1995
@@ -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 -r -u -N linux.orig/include/asm-arm/dma.h linux.arm/include/asm-arm/dma.h
--- linux.orig/include/asm-arm/dma.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/dma.h	Fri Oct 27 23:11:21 1995
@@ -0,0 +1,134 @@
+#ifndef __ASM_ARM_DMA_H
+#define __ASM_ARM_DMA_H
+
+#include <asm/irq.h>
+
+#define MAX_DMA_CHANNELS	8
+
+#define MAX_DMA_ADDRESS		0
+
+#define DMA_MODE_READ		0x44
+#define DMA_MODE_WRITE		0x48
+
+/* enable/disable a specific DMA channel */
+static __inline__ void enable_dma(unsigned int dmanr)
+{
+	switch(dmanr)
+	{
+		case 2:  enable_irq(16); break;
+		default: printk("enable_dma: dma %d not supported\n", dmanr); break;
+	}
+}
+
+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 */
+static __inline__ void set_dma_mode(unsigned int dmanr, char mode)
+{
+	extern void floppy_fiqin(void);
+	extern void floppy_fiqout(void);
+	switch(dmanr)
+	{
+		case 2:
+			if(mode == DMA_MODE_READ)
+				*((unsigned long *)0x00000024) = (unsigned long)floppy_fiqin;
+			else
+			if(mode == DMA_MODE_WRITE)
+				*((unsigned long *)0x00000024) = (unsigned long)floppy_fiqout;
+			else
+				printk("set_dma_mode: %s: invalid mode %02X\n", "floppy", mode);
+			break;
+		default: printk("set_dma_mode: dma %d not supported\n", dmanr); break;
+	}
+}
+
+/* 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.
+ */
+static __inline__ void set_dma_addr(unsigned int dmanr, unsigned int a)
+{
+	extern unsigned char *floppyfiq_to;
+	switch(dmanr)
+	{
+		case 2:  floppyfiq_to = (unsigned char *)a; break;
+		default: printk("set_dma_addr: dma %d not supported\n", dmanr); break;
+	}
+}
+
+
+/* Set transfer size for a specific DMA channel.
+ */
+static __inline__ void set_dma_count(unsigned int dmanr, unsigned int count)
+{
+	extern unsigned int floppyfiq_len;
+	extern unsigned char *floppyfiq_from;
+	switch(dmanr)
+	{
+		case 2:  floppyfiq_from = (unsigned char *)0x0302a000;
+			 floppyfiq_len  = count;
+			 break;
+		default: printk("set_dma_count: dma %d not supported\n", dmanr); break;
+	}
+}
+
+
+/* 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.
+ */
+static __inline__ int get_dma_residue(unsigned int dmanr)
+{
+	extern unsigned int floppyfiq_len;
+	switch(dmanr)
+	{
+		case 2:  return floppyfiq_len;
+		default: printk("get_dma_residue: dma %d not supported\n", dmanr); break;
+	}
+	return -1;
+}
+
+
+/* These are in kernel/dma.c: */
+extern int request_dma(unsigned int dmanr, char * device_id);   /* reserve a DMA channel */
+extern void free_dma(unsigned int dmanr);       /* release it again */
+
+#endif /* _ARM_DMA_H */
+
diff -r -u -N linux.orig/include/asm-arm/ecard.h linux.arm/include/asm-arm/ecard.h
--- linux.orig/include/asm-arm/ecard.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/ecard.h	Fri Oct 27 23:11:23 1995
@@ -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 -r -u -N linux.orig/include/asm-arm/io.h linux.arm/include/asm-arm/io.h
--- linux.orig/include/asm-arm/io.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/io.h	Fri Oct 27 23:11:21 1995
@@ -0,0 +1,168 @@
+#ifndef __ASM_ARM_IO_H
+#define __ASM_ARM_IO_H
+
+#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 -r -u -N linux.orig/include/asm-arm/irq-no.h linux.arm/include/asm-arm/irq-no.h
--- linux.orig/include/asm-arm/irq-no.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/irq-no.h	Fri Oct 27 23:11:22 1995
@@ -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 -r -u -N linux.orig/include/asm-arm/irq.h linux.arm/include/asm-arm/irq.h
--- linux.orig/include/asm-arm/irq.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/irq.h	Fri Oct 27 23:11:21 1995
@@ -0,0 +1,14 @@
+#ifndef __ASM_ARM_IRQ_H
+#define __ASM_ARM_IRQ_H
+
+#include <linux/linkage.h>
+#include <asm/segment.h>
+
+extern void disable_irq(unsigned int);
+extern void enable_irq(unsigned int);
+
+#define __STR(x) #x
+#define STR(x) __STR(x)
+
+#endif
+
diff -r -u -N linux.orig/include/asm-arm/page.h linux.arm/include/asm-arm/page.h
--- linux.orig/include/asm-arm/page.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/page.h	Fri Oct 27 23:11:21 1995
@@ -0,0 +1,84 @@
+#ifndef __ASM_ARM_PAGE_H
+#define __ASM_ARM_PAGE_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
+
+extern void remap_task(unsigned long memmap);
+
+#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
+
+#define invalidate() \
+	remap_task(current->tss.memmap)
+
+/* 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)
+#define MAP_PAGE_RESERVED	(1<<15)
+
+typedef unsigned short mem_map_t;
+
+#endif /* __KERNEL__ */
+
+#endif /* _ARM_PAGE_H */
+
diff -r -u -N linux.orig/include/asm-arm/param.h linux.arm/include/asm-arm/param.h
--- linux.orig/include/asm-arm/param.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/param.h	Fri Oct 27 23:11:21 1995
@@ -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 -r -u -N linux.orig/include/asm-arm/pgtable.h linux.arm/include/asm-arm/pgtable.h
--- linux.orig/include/asm-arm/pgtable.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/pgtable.h	Fri Oct 27 23:11:22 1995
@@ -0,0 +1,282 @@
+#ifndef __ASM_ARM_PGTABLE_H
+#define __ASM_ARM_PGTABLE_H
+
+#include <linux/malloc.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 { \
+	(tsk)->tss.memmap = (unsigned long) (pgdir); \
+	if ((tsk) == current) \
+		remap_task(current->tss.memmap); \
+} 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 0; }
+extern inline void pte_clear(pte_t *ptep)       { pte_val(*ptep) = 0; }
+extern inline void pte_reuse(pte_t * ptep)	{ }
+
+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)      { }
+extern inline void pgd_reuse(pgd_t * pgdp)
+{
+        if (!(mem_map[MAP_NR(pgdp)] & MAP_PAGE_RESERVED))
+                mem_map[MAP_NR(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 task_struct * tsk, unsigned long address)
+{
+        return (pgd_t *) tsk->tss.memmap + (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)
+{
+}
+
+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)
+{
+	kfree_s((void *)pgd, 32*1024*1024*4/PAGE_SIZE);
+}
+
+extern inline pgd_t * pgd_alloc(void)
+{
+	pgd_t *pgd;
+	
+        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)
+{
+}
+
+#endif /* _ARM_PAGE_H */
+
diff -r -u -N linux.orig/include/asm-arm/processor.h linux.arm/include/asm-arm/processor.h
--- linux.orig/include/asm-arm/processor.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/processor.h	Fri Oct 27 23:11:22 1995
@@ -0,0 +1,97 @@
+/*
+ * 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 */
+
+extern inline void start_bh_atomic(void)
+{
+  unsigned long dummy1, dummy2, *dummy3 = &intr_count;
+  __asm__ __volatile__(
+  " mov		%0,pc\n"
+  " teqp	pc,#0x08000003\n"
+  " ldr		%1,[%2]\n"
+  " add		%1,%1,#1\n"
+  " str		%1,[%2]\n"
+  " teqp	pc,%0\n"
+  : "=r" (dummy1), "=r" (dummy2)
+  : "r" (dummy3) : "memory");
+}
+
+extern inline void end_bh_atomic(void)
+{
+  unsigned long dummy1, dummy2, *dummy3 = &intr_count;
+  __asm__ __volatile__(
+  " mov		%0,pc\n"
+  " teqp	pc,#0x08000003\n"
+  " ldr		%1,[%2]\n"
+  " sub		%1,%1,#1\n"
+  " str		%1,[%2]\n"
+  " teqp	pc,%0\n"
+  : "=r" (dummy1), "=r" (dummy2)
+  : "r" (dummy3) : "memory");
+}
+
+/*
+ * 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_task, 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->PC = pc;		/* pc */
+	regs->SP = sp;		/* sp */
+	regs->R2 = stack[2];	/* r2 (envp) */
+	regs->R1 = stack[1];	/* r1 (argv) */
+	regs->R0 = stack[0];	/* r0 (argc) */
+}
+
+#endif /* __ASM_ARM_PROCESSOR_H */
+
diff -r -u -N linux.orig/include/asm-arm/processor.h.2 linux.arm/include/asm-arm/processor.h.2
--- linux.orig/include/asm-arm/processor.h.2	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/processor.h.2	Fri Oct 27 23:11:23 1995
@@ -0,0 +1,95 @@
+/*
+ * 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 */
+
+extern inline void start_bh_atomic(void)
+{
+  unsigned long dummy1, dummy2;
+  __asm__ __volatile__(
+  " mov		%0,pc\n"
+  " teqp	pc,#0x08000003\n"
+  " ldr		%1,[%2]\n"
+  " add		%1,%1,#1\n"
+  " str		%1,[%2]\n"
+  " teqp	pc,%0\n"
+  : "=r" (dummy1), "=r" (dummy2)
+  : "r" (&intr_count) : "memory");
+}
+
+extern inline void end_bh_atomic(void)
+{
+  unsigned long dummy1, dummy2;
+  __asm__ __volatile__(
+  " mov		%0,pc\n"
+  " teqp	pc,#0x08000003\n"
+  " ldr		%1,[%2]\n"
+  " sub		%1,%1,#1\n"
+  " str		%1,[%2]\n"
+  " teqp	pc,%0\n"
+  : "=r" (dummy1), "=r" (dummy2)
+  : "r" (&intr_count) : "memory");
+}
+
+/*
+ * 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 */
+};
+
+#define INIT_MMAP { &init_task, 0, 0x40000000, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC }
+
+#define INIT_TSS  { \
+	{ 0, }, \
+	{ 0, }, \
+	{ 0, }, \
+	0,0,0, \
+	(long) &swapper_pg_dir \
+}
+
+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->PC = pc;		/* pc */
+	regs->SP = sp;		/* sp */
+	regs->R2 = stack[2];	/* r2 (envp) */
+	regs->R1 = stack[1];	/* r1 (argv) */
+	regs->R0 = stack[0];	/* r0 (argc) */
+}
+
+#endif /* __ASM_ARM_PROCESSOR_H */
+
diff -r -u -N linux.orig/include/asm-arm/ptrace.h linux.arm/include/asm-arm/ptrace.h
--- linux.orig/include/asm-arm/ptrace.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/ptrace.h	Fri Oct 27 23:11:22 1995
@@ -0,0 +1,36 @@
+#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 PC	uregs[15]
+#define LR	uregs[14]
+#define SP	uregs[13]
+#define FP	uregs[12]
+#define IP	uregs[11]
+#define R10	uregs[10]
+#define R9	uregs[9]
+#define R8	uregs[8]
+#define R7	uregs[7]
+#define R6	uregs[6]
+#define R5	uregs[5]
+#define R4	uregs[4]
+#define R3	uregs[3]
+#define R2	uregs[2]
+#define R1	uregs[1]
+#define R0	uregs[0]
+#define ORIG_R0	uregs[16] /* -1 */
+
+#ifdef __KERNEL__
+#define user_mode(regs) (((regs)->uregs[15] & 3) == 0)
+extern void show_regs(struct pt_regs *);
+#endif
+
+#endif
+
diff -r -u -N linux.orig/include/asm-arm/segment.h linux.arm/include/asm-arm/segment.h
--- linux.orig/include/asm-arm/segment.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/segment.h	Fri Oct 27 23:11:22 1995
@@ -0,0 +1,108 @@
+#ifndef __ASM_ARM_SEGMENT_H
+#define __ASM_ARM_SEGMENT_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/sched.h>
+
+#define KERNEL_CS   0x0
+#define KERNEL_DS   0x0
+
+#define USER_CS     0x1
+#define USER_DS     0x1
+
+#define IS_USER_SEG (current->tss.fs != KERNEL_DS)
+
+extern unsigned char get_user_byte(const char * addr);
+static inline unsigned char get_mfs_byte(const char *addr)
+{
+	if (IS_USER_SEG)
+		return get_user_byte(addr);
+	else
+		return *addr;
+}
+#define get_fs_byte(addr) get_mfs_byte((char *)(addr))
+
+extern unsigned short get_user_word(const short *addr);
+static inline unsigned short get_mfs_word(const short *addr)
+{
+	if (IS_USER_SEG)
+		return get_user_word(addr);
+	else
+		return *addr;
+}
+#define get_fs_word(addr) get_mfs_word((short *)(addr))
+
+extern unsigned long get_user_long(const long *addr);
+static inline unsigned long get_mfs_long(const long *addr)
+{
+	if (IS_USER_SEG)
+		return get_user_long(addr);
+	else
+		return *addr;
+}
+#define get_fs_long(addr) get_mfs_long((long *)(addr))
+
+extern void put_user_byte(char val,char *addr);
+static inline void put_mfs_byte(char val, char *addr)
+{
+	if (IS_USER_SEG)
+		put_user_byte(val, addr);
+	else
+		*addr = val;
+}
+#define put_fs_byte(x,addr) put_mfs_byte((x),(char *)(addr))
+
+extern void put_user_word(short val,short * addr);
+static inline void put_mfs_word(short val, short *addr)
+{
+	if (IS_USER_SEG)
+		put_user_word(val, addr);
+	else
+		*addr = val;
+}
+#define put_fs_word(x,addr) put_mfs_word((x),(short *)(addr))
+
+extern void put_user_long(long val, long *addr);
+static inline void put_mfs_long(long val, long *addr)
+{
+	if (IS_USER_SEG)
+		put_user_long(val, addr);
+	else
+		*addr = val;
+}
+#define put_fs_long(x,addr) put_mfs_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
+#ifndef __need_memcpy_tofs
+inline
+#endif
+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 -r -u -N linux.orig/include/asm-arm/segment.h.2 linux.arm/include/asm-arm/segment.h.2
--- linux.orig/include/asm-arm/segment.h.2	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/segment.h.2	Fri Oct 27 23:11:22 1995
@@ -0,0 +1,87 @@
+#ifndef __ASM_ARM_SEGMENT_H
+#define __ASM_ARM_SEGMENT_H
+
+#ifndef __ASSEMBLY__
+
+#define KERNEL_CS   0x0
+#define KERNEL_DS   0x0
+
+#define USER_CS     0x1
+#define USER_DS     0x1
+
+unsigned char inline get_user_byte(const char * addr)
+{
+	unsigned char ret;
+	__asm__ __volatile__(
+		"ldrbt\t%0,[%1],#0\n\t" :
+		"=r" (ret) :
+		"r" (addr));
+	return ret;
+}
+#define get_fs_byte(addr) get_user_byte((char *)(addr))
+
+unsigned short inline get_user_word(const short *addr)
+{
+	unsigned short ret, __dummy__;
+	__asm__ __volatile__(
+		"ldrbt\t%0,[%2],#1\n\t"
+		"ldrbt\t%1,[%2],#0\n\t"
+		"orr\t%0,%0,%1,LSL#8\n\t":
+		"=r" (ret), "=r" (__dummy__):
+		"r" (addr));
+	return ret;
+}
+#define get_fs_word(addr) get_user_word((short *)(addr))
+
+unsigned long inline get_user_long(const long *addr)
+{
+	unsigned long ret, __dummy__;
+	
+	__asm__ __volatile__(
+		"ands	%1, %2, #3\n\t"
+		"bic	%2, %2, #3\n\t"
+		"ldrt	%0, [%2], #4\n\t"
+		"beq	L0\n\t"
+		"ldrt	%2, [%2], #0\n\t"
+		"mov	%1, %1, LSL#3\n\t"
+		"mov	%0, %0, LSR %1\n\t"
+		"rsb	%1, %1, #32\n\t"
+		"orr	%0, %0, %2, lsl %1\n"
+		"L0:\t\n\t" :
+		"=r" (ret), "=r" (__dummy__) :
+		"r" (addr));
+	return ret;
+}
+		
+#define get_fs_long(addr) get_user_long((int *)(addr))
+
+extern void put_user_byte(char val,char *addr);
+#define put_fs_byte(x,addr) put_user_byte((x),(char *)(addr))
+
+extern void put_user_word(short val,short * addr);
+#define put_fs_word(x,addr) put_user_word((x),(short *)(addr))
+
+extern void put_user_long(unsigned long val,int * addr);
+#define put_fs_long(x,addr) put_user_long((x),(int *)(addr))
+
+extern void memcpy_tofs(void * to, const void * from, unsigned long n);
+extern void memcpy_fromfs(void * to, const void * from, unsigned long n);
+  
+static inline unsigned long get_fs(void)
+{
+	return 0;
+}
+
+static inline unsigned long get_ds(void)
+{
+	return 0; /* CHECK */
+}
+
+static inline void set_fs(unsigned long val)
+{
+}
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ASM_ARM_SEGMENT_H */
+
diff -r -u -N linux.orig/include/asm-arm/signal.h linux.arm/include/asm-arm/signal.h
--- linux.orig/include/asm-arm/signal.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/signal.h	Fri Oct 27 23:11:22 1995
@@ -0,0 +1,13 @@
+#ifndef __ASM_ARM_SIGNAL_H
+#define __ASM_ARM_SIGNAL_H
+
+struct sigcontext_struct {
+  unsigned long magic;
+  unsigned long reg[17];
+  unsigned long trap_no;
+  unsigned long error_code;
+  unsigned long oldmask;
+};
+
+#endif
+
diff -r -u -N linux.orig/include/asm-arm/string.h linux.arm/include/asm-arm/string.h
--- linux.orig/include/asm-arm/string.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/string.h	Fri Oct 27 23:11:22 1995
@@ -0,0 +1,31 @@
+#ifndef __ASM_ARM_STRING_H
+#define __ASM_ARM_STRING_H
+
+extern  char * strcpy(char * dest,const char *src);
+extern  char * strncpy(char * dest,const char *src,size_t count);
+extern  char * strcat(char * dest,const char * src);
+extern  char * strncat(char * dest,const char * src,size_t count);
+extern  int strcmp(const char * cs,const char * ct);
+extern  int strncmp(const char * cs,const char * ct,size_t count);
+extern  char * strchr(const char * s, int c);
+extern  char * strrchr(const char * s, int c);
+extern  size_t strspn(const char * cs, const char * ct);
+extern  size_t strcspn(const char * cs, const char * ct);
+extern  char * strpbrk(const char * cs,const char * ct);
+extern  char * strstr(const char * cs,const char * ct);
+extern  size_t strlen(const char * s);
+extern char * ___strtok;
+extern  char * strtok(char * s,const char * ct);
+extern  void * memcpy(void * to, const void * from, size_t n);
+extern  void * memmove(void * dest,const void * src, size_t n);
+extern  int memcmp(const void * cs,const void * ct,size_t count);
+extern  void * memchr(const void * cs,int c,size_t count);
+extern  void * memset(void * s, char c,size_t count);
+
+/*
+ * find the first occurrence of byte 'c', or 1 past the area if none
+ */
+extern  void * memscan(void * addr, int c, size_t size);
+
+#endif
+ 
diff -r -u -N linux.orig/include/asm-arm/system.h linux.arm/include/asm-arm/system.h
--- linux.orig/include/asm-arm/system.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/system.h	Fri Oct 27 23:11:22 1995
@@ -0,0 +1,156 @@
+#ifndef __ASM_ARM_SYSTEM_H
+#define __ASM_ARM_SYSTEM_H
+
+#include <linux/kernel.h>
+
+/* 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 *);
+
+#define CHECK_ALIGN(w, n, s) \
+  if (((long)w) & (n-1)) \
+    printk("Mal-aligned pointer in " s ": %p (in file %s, line %d)\n", w, __FILE__, __LINE__);
+
+extern inline unsigned long xchg_u8(char * m, unsigned long val)
+{
+#if __arm__ > 2
+	__asm__ __volatile__(
+	" swpb	%0,%1,[%2]\n"
+	: "=r" (val)
+	: "r" (val), "r" (m)
+	: "memory");
+#else
+	unsigned long temp0, temp1;
+	__asm__ __volatile__(
+	" mov   %0, pc\n"
+	" orr	%1, %0, #0x08000000\n"
+	" teqp  %1, #0\n"
+	" mov   %1, %3\n"		/* Safe: in case it uses same reg */
+	" ldrb	%2, [%4]\n"
+	" strb  %1, [%4]\n"
+	" teqp  %0, #0\n"
+	: "=r" (temp0), "=r" (temp1), "=r" (val)
+	: "r" (val), "r" (m)
+	: "memory");
+#endif
+	return val;
+}
+
+extern inline unsigned long xchg_u16(short * m, unsigned long val)
+{
+	unsigned long temp0, temp1;
+	CHECK_ALIGN(m, 2, "xchg_u16");
+	__asm__ __volatile__(
+	" mov   %0, pc\n"
+	" orr	%1, %0, #0x08000000\n"
+	" teqp  %1, #0\n"
+	" mov   %1, %3\n"		/* Safe: in case it uses same reg */
+	" 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" (val)
+	: "r" (val), "r" (m)
+	: "memory");
+	return val & 0xffff;
+}
+
+extern inline unsigned long xchg_u32(int * m, unsigned long val)
+{
+#if __arm__ > 2
+	CHECK_ALIGN(m, 4, "xchg_u32");
+	__asm__ __volatile__(
+	" swp	%0,%1,[%2]\n"
+	: "=r" (val)
+	: "r" (val), "r" (m)
+	: "memory");
+#else
+	unsigned long temp0, temp1;
+	CHECK_ALIGN(m, 4, "xchg_u32");
+	__asm__ __volatile__(
+	" mov   %0, pc\n"
+	" orr	%1, %0, #0x08000000\n"
+	" teqp  %1, #0\n"
+	" mov   %1, %3\n"		/* Safe: in case it uses same reg */
+	" ldr	%2, [%4]\n"
+	" str   %1, [%4]\n"
+	" teqp  %0, #0\n"
+	: "=r" (temp0), "=r" (temp1), "=r" (val)
+	: "r" (val), "r" (m)
+	: "memory");
+#endif
+	return val;
+}
+
+extern inline int tas(char * m)
+{
+        return xchg_u8(m,1);
+}
+
+extern inline void * xchg_ptr(void * m, void * val)
+{
+        return (void *) xchg_u32(m, (unsigned long) val);
+}
+
+#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)			\
+  : "psr");			\
+} while (0)
+
+#define nop()			\
+do {				\
+ __asm__ __volatile__(		\
+ "mov r0,r0\n"			\
+ : : );				\
+} while (0)
+
+#endif
+
diff -r -u -N linux.orig/include/asm-arm/types.h linux.arm/include/asm-arm/types.h
--- linux.orig/include/asm-arm/types.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/types.h	Fri Oct 27 23:11:22 1995
@@ -0,0 +1,58 @@
+#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
+
+/*
+ * __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__ */
+
+#endif
+
diff -r -u -N linux.orig/include/asm-arm/unistd.h linux.arm/include/asm-arm/unistd.h
--- linux.orig/include/asm-arm/unistd.h	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/asm-arm/unistd.h	Fri Oct 27 23:11:22 1995
@@ -0,0 +1,114 @@
+#ifndef __ASM_ARM_UNISTD_H
+#define __ASM_ARM_UNISTD_H
+
+#include <linux/unistd.h>
+
+#define __sys2(x) #x
+#define __sys1(x) __sys2(x)
+#define __syscall(name) "swi 0x900000+" __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);
+
+#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);
+
+
+#ifdef __KERNEL_SYSCALLS__
+#define __NR__exit __NR_exit
+static inline _syscall0(int,idle);
+static inline _syscall0(int,fork);
+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);
+}
+
+#endif
+
+#endif /* __ASM_ARM_UNISTD_H */
+
+
+
diff -r -u -N linux.orig/include/linux/a.out.h linux.arm/include/linux/a.out.h
--- linux.orig/include/linux/a.out.h	Fri Oct 27 23:09:01 1995
+++ linux.arm/include/linux/a.out.h	Fri Oct 27 23:10:45 1995
@@ -43,6 +43,7 @@
 #endif
   /* skip a bunch so we don't run into any of sun's numbers */
   M_386 = 100,
+  M_ARM = 103,
 };
 
 #if !defined (N_MAGIC)
@@ -71,7 +72,7 @@
 #define NMAGIC 0410
 /* Code indicating demand-paged executable.  */
 #define ZMAGIC 0413
-/* This indicates a demand-paged executable with the header in the text. 
+/* This indicates a demand-paged executable with the header in the text.
    The first page is unmapped to help trap NULL pointer references */
 #define QMAGIC 0314
 
diff -r -u -N linux.orig/include/linux/autoconf.h linux.arm/include/linux/autoconf.h
--- linux.orig/include/linux/autoconf.h	Fri Oct 27 23:09:07 1995
+++ linux.arm/include/linux/autoconf.h	Fri Oct 27 23:11:01 1995
@@ -1,27 +1,26 @@
 /*
  * Automatically generated C config: don't edit
  */
+#define CONFIG_ARM
+#define CONFIG_ARMMOUSE 1
 
 /*
  * General setup
  */
-#undef  CONFIG_MATH_EMULATION
+#define CONFIG_PAGE_32K 1
+#define CONFIG_MATH_EMULATION 1
 #define CONFIG_BLK_DEV_FD 1
 #define CONFIG_ST506 1
 
 /*
  * Please see drivers/block/README.ide for help/info on IDE drives
  */
-#undef  CONFIG_BLK_DEV_HD
-#define CONFIG_BLK_DEV_IDE 1
-#undef  CONFIG_BLK_DEV_IDECD
+#define CONFIG_BLK_DEV_HD 1
+#undef  CONFIG_BLK_DEV_IDE
 #undef  CONFIG_BLK_DEV_XD
 #define CONFIG_NET 1
 #undef  CONFIG_MAX_16M
-#undef  CONFIG_PCI
 #define CONFIG_SYSVIPC 1
-#define CONFIG_BINFMT_ELF 1
-#define CONFIG_M486 1
 
 /*
  * Loadable module support
@@ -32,8 +31,8 @@
  * Networking options
  */
 #define CONFIG_INET 1
-#undef  CONFIG_IP_FORWARD
-#undef  CONFIG_IP_MULTICAST
+#define CONFIG_IP_FORWARD 1
+#define CONFIG_IP_MULTICAST 1
 #undef  CONFIG_IP_FIREWALL
 #undef  CONFIG_IP_ACCT
 
@@ -49,44 +48,37 @@
 /*
  * SCSI support
  */
-#undef  CONFIG_SCSI
+#define CONFIG_SCSI 1
 
 /*
- * Skipping SCSI configuration options...
+ * SCSI support type (disk, tape, CDrom)
  */
+#define CONFIG_BLK_DEV_SD 1
+#undef  CONFIG_CHR_DEV_ST
+#define CONFIG_BLK_DEV_SR 1
+#undef  CONFIG_CHR_DEV_SG
+
+/*
+ * SCSI low-level drivers
+ */
+#define CONFIG_SCSI_ACORNSCSI_3 1
+#undef  CONFIG_SCSI_CUMANA_1
+#undef  CONFIG_SCSI_ECOSCSI
+#undef  CONFIG_SCSI_OAK
+#undef  CONFIG_SCSI_DEBUG
 
 /*
  * Network device support
  */
 #define CONFIG_NETDEVICES 1
 #define CONFIG_DUMMY 1
-#undef  CONFIG_SLIP
+#define CONFIG_SLIP 1
+#define CONFIG_SLIP_COMPRESSED 1
+#undef  SL_SLIP_LOTS
+#undef  SL_DUMP
 #undef  CONFIG_PPP
 #undef  CONFIG_PLIP
-#undef  CONFIG_NET_ALPHA
-#undef  CONFIG_NET_VENDOR_SMC
-#undef  CONFIG_LANCE
-#undef  CONFIG_NET_VENDOR_3COM
-#define CONFIG_NET_ISA 1
-#undef  CONFIG_E2100
-#undef  CONFIG_DEPCA
-#undef  CONFIG_EWRK3
-#undef  CONFIG_HPLAN_PLUS
-#undef  CONFIG_HPLAN
-#define CONFIG_NE2000 1
-#undef  CONFIG_SK_G16
-#undef  CONFIG_NET_EISA
-#undef  CONFIG_NET_POCKET
-
-/*
- * CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
- */
-#undef  CONFIG_CDU31A
-#undef  CONFIG_MCD
-#define CONFIG_SBPCD 1
-#undef  CONFIG_SBPCD2
-#undef  CONFIG_AZTCD
-#undef  CONFIG_CDU535
+#define CONFIG_ETHER3 1
 
 /*
  * Filesystems
@@ -100,28 +92,20 @@
 #define CONFIG_PROC_FS 1
 #define CONFIG_NFS_FS 1
 #define CONFIG_ISO9660_FS 1
-#undef  CONFIG_HPFS_FS
-#undef  CONFIG_SYSV_FS
 
 /*
  * character devices
  */
-#undef  CONFIG_CYCLADES
 #define CONFIG_PRINTER 1
-#define CONFIG_BUSMOUSE 1
-#undef  CONFIG_PSMOUSE
-#undef  CONFIG_MS_BUSMOUSE
-#undef  CONFIG_ATIXL_BUSMOUSE
-#undef  CONFIG_QIC02_TAPE
-#define CONFIG_FTAPE 1
-#define NR_FTAPE_BUFFERS (3)
 
 /*
  * Sound
  */
-#undef  CONFIG_SOUND
+#define CONFIG_SOUND 1
 
 /*
  * Kernel hacking
  */
-#undef  CONFIG_PROFILE
+#define CONFIG_PROFILE 1
+#define CONFIG_PROFILE_SHIFT 4
+#define CONFIG_SCSI_CONSTANTS 1
diff -r -u -N linux.orig/include/linux/binfmts.h linux.arm/include/linux/binfmts.h
--- linux.orig/include/linux/binfmts.h	Fri Oct 27 23:09:01 1995
+++ linux.arm/include/linux/binfmts.h	Fri Oct 27 23:10:53 1995
@@ -8,7 +8,11 @@
  * and envelope for the new program. 32 should suffice, this gives
  * a maximum env+arg of 128kB !
  */
-#define MAX_ARG_PAGES 32
+#ifndef __arm__
+#  define MAX_ARG_PAGES 32
+#else
+#  define MAX_ARG_PAGES 4
+#endif
 
 /*
  * This structure is used to hold the arguments that are used when loading binaries.
diff -r -u -N linux.orig/include/linux/ip.h linux.arm/include/linux/ip.h
--- linux.orig/include/linux/ip.h	Fri Oct 27 23:09:03 1995
+++ linux.arm/include/linux/ip.h	Fri Oct 27 23:10:52 1995
@@ -79,7 +79,7 @@
 	__u8	version:4,
   		ihl:4;
 #else
-#error	"Please fix <asm/byteorder.h>"
+#error "Please fix <asm/byteorder.h>"
 #endif
 	__u8	tos;
 	__u16	tot_len;
diff -r -u -N linux.orig/include/linux/kernel_stat.h linux.arm/include/linux/kernel_stat.h
--- linux.orig/include/linux/kernel_stat.h	Fri Oct 27 23:09:03 1995
+++ linux.arm/include/linux/kernel_stat.h	Fri Oct 27 23:10:57 1995
@@ -7,6 +7,17 @@
  * used by rstatd/perfmeter
  */
 
+/*
+ * Maybe this should include a file asm/irq-no that defines the
+ * number of interrupts for each architecture? - rmk (29/7/95)
+ */
+
+#ifdef __arm__
+#include <asm/irq-no.h>
+#else
+#define NUM_IRQS 16
+#endif
+
 #define DK_NDRIVE 4
 
 struct kernel_stat {
@@ -14,7 +25,7 @@
 	unsigned int dk_drive[DK_NDRIVE];
 	unsigned int pgpgin, pgpgout;
 	unsigned int pswpin, pswpout;
-	unsigned int interrupts[16];
+	unsigned int interrupts[NUM_IRQS];
 	unsigned int ipackets, opackets;
 	unsigned int ierrors, oerrors;
 	unsigned int collisions;
diff -r -u -N linux.orig/include/linux/modules/ksyms.ver linux.arm/include/linux/modules/ksyms.ver
--- linux.orig/include/linux/modules/ksyms.ver	Fri Oct 27 23:09:07 1995
+++ linux.arm/include/linux/modules/ksyms.ver	Fri Oct 27 23:11:01 1995
@@ -4,15 +4,13 @@
 #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_mmap	_set_ver(do_mmap, 2eadc92b)
 #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 insert_vm_struct	_set_ver(insert_vm_struct, 8d8644d4)
+#define merge_segments	_set_ver(merge_segments, 3fb63ac6)
 #define __get_free_pages	_set_ver(__get_free_pages, 5243d78b)
 #define free_pages	_set_ver(free_pages, 96448859)
 #define kmalloc	_set_ver(kmalloc, d31fb2cb)
@@ -21,62 +19,62 @@
 #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 __iget	_set_ver(__iget, 5e974ec6)
+#define iput	_set_ver(iput, a55d8fb5)
+#define namei	_set_ver(namei, 652eb806)
+#define lnamei	_set_ver(lnamei, 17e0909c)
+#define open_namei	_set_ver(open_namei, 68815d38)
+#define close_fp	_set_ver(close_fp, 5c3362b3)
 #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 permission	_set_ver(permission, e1795b74)
+#define inode_setattr	_set_ver(inode_setattr, 051eebb5)
+#define inode_change_ok	_set_ver(inode_change_ok, 172606f2)
+#define generic_mmap	_set_ver(generic_mmap, ee3ff7f9)
 #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 getblk	_set_ver(getblk, 2abcaad3)
+#define bread	_set_ver(bread, ada2f968)
+#define breada	_set_ver(breada, 73d640da)
+#define brelse	_set_ver(brelse, 104025bc)
+#define ll_rw_block	_set_ver(ll_rw_block, 207f5903)
+#define __wait_on_buffer	_set_ver(__wait_on_buffer, a2174488)
+#define dcache_lookup	_set_ver(dcache_lookup, 8a1a392a)
+#define dcache_add	_set_ver(dcache_add, b7748358)
+#define register_chrdev	_set_ver(register_chrdev, 31fd4bc4)
 #define unregister_chrdev	_set_ver(unregister_chrdev, 61ea5ee8)
-#define register_blkdev	_set_ver(register_blkdev, 4699a621)
+#define register_blkdev	_set_ver(register_blkdev, c1d542c9)
 #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_register_driver	_set_ver(tty_register_driver, 42e2fe51)
+#define tty_unregister_driver	_set_ver(tty_unregister_driver, 964ac99e)
 #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 block_read	_set_ver(block_read, f65ebf63)
+#define block_write	_set_ver(block_write, 084ab6ac)
+#define block_fsync	_set_ver(block_fsync, 760f6488)
+#define wait_for_request	_set_ver(wait_for_request, 4560f3af)
 #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 blk_dev	_set_ver(blk_dev, d38611b7)
 #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 bmap	_set_ver(bmap, f3adc9b7)
 #define sync_dev	_set_ver(sync_dev, 9bca536d)
-#define get_blkfops	_set_ver(get_blkfops, 83827791)
+#define get_blkfops	_set_ver(get_blkfops, c05709ef)
 #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, 0aeac7e4)
+#define tty_wait_until_sent	_set_ver(tty_wait_until_sent, da08c6d0)
+#define tty_check_change	_set_ver(tty_check_change, da32f203)
+#define tty_hung_up_p	_set_ver(tty_hung_up_p, 9629ffb6)
+#define register_filesystem	_set_ver(register_filesystem, 6fc4dd55)
+#define unregister_filesystem	_set_ver(unregister_filesystem, 9930136b)
+#define register_binfmt	_set_ver(register_binfmt, 5e145849)
+#define unregister_binfmt	_set_ver(unregister_binfmt, 98a2fa16)
+#define lookup_exec_domain	_set_ver(lookup_exec_domain, ebad6f7c)
+#define register_exec_domain	_set_ver(register_exec_domain, 5d37daa8)
+#define unregister_exec_domain	_set_ver(unregister_exec_domain, 61e67c43)
+#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)
@@ -92,17 +90,15 @@
 #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 wake_up	_set_ver(wake_up, fffabd24)
+#define wake_up_interruptible	_set_ver(wake_up_interruptible, 2e80f1cd)
+#define sleep_on	_set_ver(sleep_on, c37bd103)
+#define interruptible_sleep_on	_set_ver(interruptible_sleep_on, b9c50ea5)
 #define schedule	_set_ver(schedule, 01000e51)
-#define current	_set_ver(current, fc1cb29b)
+#define current	_set_ver(current, 86896bc3)
 #define jiffies	_set_ver(jiffies, 2f7c7437)
 #define xtime	_set_ver(xtime, e70c0be0)
 #define loops_per_sec	_set_ver(loops_per_sec, 40a14192)
@@ -117,23 +113,27 @@
 #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 do_signal	_set_ver(do_signal, 94f9d4de)
+#define send_sig	_set_ver(send_sig, 7138660e)
 #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 do_execve	_set_ver(do_execve, 945e29fb)
+#define flush_old_exec	_set_ver(flush_old_exec, d0949913)
+#define open_inode	_set_ver(open_inode, 995189bc)
+#define read_exec	_set_ver(read_exec, 29d8dd8c)
 #define si_meminfo	_set_ver(si_meminfo, bb05fc9a)
-#define sock_register	_set_ver(sock_register, d68e1649)
+#define sock_register	_set_ver(sock_register, d0688e2b)
 #define sock_unregister	_set_ver(sock_unregister, 72c332bd)
 #define inet_add_protocol	_set_ver(inet_add_protocol, ff15a03c)
 #define inet_del_protocol	_set_ver(inet_del_protocol, a9c5e9c0)
+#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 ftape_big_buffer	_set_ver(ftape_big_buffer, e349dc35)
 #define floppy_track_buffer	_set_ver(floppy_track_buffer, c6e3f7c2)
 #define register_netdev	_set_ver(register_netdev, 298819f5)
 #define unregister_netdev	_set_ver(unregister_netdev, f1aa84e4)
@@ -153,45 +153,67 @@
 #define dev_base	_set_ver(dev_base, 1d36a3cf)
 #define dev_close	_set_ver(dev_close, 98484140)
 #define arp_find	_set_ver(arp_find, d909c1c9)
-#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 set_writetime	_set_ver(set_writetime, 52131916)
+#define n_tty_ioctl	_set_ver(n_tty_ioctl, 567c6ba8)
+#define tty_register_ldisc	_set_ver(tty_register_ldisc, bb8a6111)
+#define kill_fasync	_set_ver(kill_fasync, c4a7c6b4)
+#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, 58566e5f)
+#define scsi_unregister	_set_ver(scsi_unregister, 77b294c3)
+#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 print_msg	_set_ver(print_msg, 0465f877)
+#define print_status	_set_ver(print_status, 32f84646)
+#define set_writetime	_set_ver(set_writetime, c7c904a1)
 #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, 71e8ed15)
+#define file_fsync	_set_ver(file_fsync, a4cbd91f)
+#define clear_inode	_set_ver(clear_inode, b172a030)
+#define refile_buffer	_set_ver(refile_buffer, 0427671e)
 #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, 42e0123a)
+#define super_blocks	_set_ver(super_blocks, dc59779f)
+#define chrdev_inode_operations	_set_ver(chrdev_inode_operations, 7d6b5e19)
+#define blkdev_inode_operations	_set_ver(blkdev_inode_operations, fb8e922c)
 #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, 8662d88b)
+#define get_empty_inode	_set_ver(get_empty_inode, 8a9df217)
+#define insert_inode_hash	_set_ver(insert_inode_hash, dfb34ea8)
 #define event	_set_ver(event, a6aac9c1)
-#define __down	_set_ver(__down, 75aa9e96)
-#define msdos_bmap	_set_ver(msdos_bmap, 8e11de93)
-#define msdos_create	_set_ver(msdos_create, ace9cdf9)
-#define msdos_file_read	_set_ver(msdos_file_read, cf8f73a7)
-#define msdos_file_write	_set_ver(msdos_file_write, c49052dc)
-#define msdos_lookup	_set_ver(msdos_lookup, 03996bbb)
-#define msdos_mkdir	_set_ver(msdos_mkdir, 0fa632e4)
-#define msdos_mmap	_set_ver(msdos_mmap, 7830ad51)
-#define msdos_put_inode	_set_ver(msdos_put_inode, 2318c406)
-#define msdos_put_super	_set_ver(msdos_put_super, a7cb6559)
-#define msdos_read_inode	_set_ver(msdos_read_inode, 9feaaa1d)
-#define msdos_read_super	_set_ver(msdos_read_super, 2d0bdce0)
-#define msdos_readdir	_set_ver(msdos_readdir, ed56af5d)
-#define msdos_rename	_set_ver(msdos_rename, bba2b188)
-#define msdos_rmdir	_set_ver(msdos_rmdir, 284d1911)
-#define msdos_smap	_set_ver(msdos_smap, 478aae23)
-#define msdos_statfs	_set_ver(msdos_statfs, 55cfd862)
-#define msdos_truncate	_set_ver(msdos_truncate, fdb46445)
-#define msdos_unlink	_set_ver(msdos_unlink, b484f1d7)
-#define msdos_unlink_umsdos	_set_ver(msdos_unlink_umsdos, da666045)
-#define msdos_write_inode	_set_ver(msdos_write_inode, 95b18673)
+#define __down	_set_ver(__down, 2e6812f6)
+#define msdos_bmap	_set_ver(msdos_bmap, 48c61fd6)
+#define msdos_create	_set_ver(msdos_create, 829e7546)
+#define msdos_file_read	_set_ver(msdos_file_read, f2847308)
+#define msdos_file_write	_set_ver(msdos_file_write, d22ed4b0)
+#define msdos_lookup	_set_ver(msdos_lookup, 7bae2d1e)
+#define msdos_mkdir	_set_ver(msdos_mkdir, bd09eb9d)
+#define msdos_mmap	_set_ver(msdos_mmap, 2c99e2bc)
+#define msdos_put_inode	_set_ver(msdos_put_inode, 9a3ce4f9)
+#define msdos_put_super	_set_ver(msdos_put_super, ec99bee1)
+#define msdos_read_inode	_set_ver(msdos_read_inode, 279c7b49)
+#define msdos_read_super	_set_ver(msdos_read_super, 0dfab796)
+#define msdos_readdir	_set_ver(msdos_readdir, 0da8f5d5)
+#define msdos_rename	_set_ver(msdos_rename, 666da51c)
+#define msdos_rmdir	_set_ver(msdos_rmdir, 2e10355e)
+#define msdos_smap	_set_ver(msdos_smap, dc469d79)
+#define msdos_statfs	_set_ver(msdos_statfs, 9af053ee)
+#define msdos_truncate	_set_ver(msdos_truncate, 615fae88)
+#define msdos_unlink	_set_ver(msdos_unlink, 37272b1e)
+#define msdos_unlink_umsdos	_set_ver(msdos_unlink_umsdos, 60f42665)
+#define msdos_write_inode	_set_ver(msdos_write_inode, 223b80ff)
+#define outswb	_set_ver(outswb, 67d59274)
+#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, d6ca9c02)
+#define print_msg	_set_ver(print_msg, 0465f877)
+#define invalidate_inodes	_set_ver(invalidate_inodes, 5f9574bd)
+#define resetup_one_dev	_set_ver(resetup_one_dev, 95afb007)
 #endif /* _KSYMS_VER_ */
 #endif /* CONFIG_MODVERSIONS !__GENKSYMS__ */
diff -r -u -N linux.orig/include/linux/shm.h linux.arm/include/linux/shm.h
--- linux.orig/include/linux/shm.h	Fri Oct 27 23:09:04 1995
+++ linux.arm/include/linux/shm.h	Fri Oct 27 23:10:51 1995
@@ -39,8 +39,13 @@
 };
 
 /* address range for shared memory attaches if no address passed to shmat() */
+#ifndef __arm__
 #define SHM_RANGE_START	0x50000000
 #define SHM_RANGE_END	0x60000000
+#else
+#define SHM_RANGE_START	0x00A00000
+#define SHM_RANGE_END	0x00C00000
+#endif
 
 /* format of page table entries that correspond to shared memory pages
    currently out in swap space (see also mm/swap.c):
diff -r -u -N linux.orig/include/linux/shm.h.old linux.arm/include/linux/shm.h.old
--- linux.orig/include/linux/shm.h.old	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/linux/shm.h.old	Fri Oct 27 23:11:02 1995
@@ -0,0 +1,96 @@
+#ifndef _LINUX_SHM_H_
+#define _LINUX_SHM_H_
+#include <linux/ipc.h>
+
+struct shmid_ds {
+	struct ipc_perm shm_perm;	/* operation perms */
+	int	shm_segsz;		/* size of segment (bytes) */
+	time_t	shm_atime;		/* last attach time */
+	time_t	shm_dtime;		/* last detach time */
+	time_t	shm_ctime;		/* last change time */
+	unsigned short	shm_cpid;	/* pid of creator */
+	unsigned short	shm_lpid;	/* pid of last operator */
+	short	shm_nattch;		/* no. of current attaches */
+	/* the following are private */
+	unsigned short   shm_npages;	/* size of segment (pages) */
+	unsigned long   *shm_pages;	/* array of ptrs to frames -> SHMMAX */ 
+	struct vm_area_struct *attaches; /* descriptors for attaches */
+};
+
+/* permission flag for shmget */
+#define SHM_R		0400	/* or S_IRUGO from <linux/stat.h> */
+#define SHM_W		0200	/* or S_IWUGO from <linux/stat.h> */
+
+/* mode for attach */
+#define	SHM_RDONLY	010000	/* read-only access */
+#define	SHM_RND		020000	/* round attach address to SHMLBA boundary */
+#define	SHM_REMAP	040000	/* take-over region on attach */
+
+/* super user shmctl commands */
+#define SHM_LOCK 	11
+#define SHM_UNLOCK 	12
+
+struct	shminfo {
+    int shmmax;	
+    int shmmin;	
+    int shmmni;	
+    int shmseg;	
+    int shmall;	
+};
+
+/* address range for shared memory attaches if no address passed to shmat() */
+#define SHM_RANGE_START	0x50000000
+#define SHM_RANGE_END	0x60000000
+
+/* format of page table entries that correspond to shared memory pages
+   currently out in swap space (see also mm/swap.c):
+   bit 0 (PAGE_PRESENT) is  = 0
+   bits 7..1 (SWP_TYPE) are = SHM_SWP_TYPE
+   bits 31..8 are used like this:
+   bits 14..8 (SHM_ID) the id of the shared memory segment
+   bits 29..15 (SHM_IDX) the index of the page within the shared memory segment
+                    (actually only bits 24..15 get used since SHMMAX is so low)
+*/
+
+#define SHM_ID_SHIFT	8
+/* 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_SHIFT+_SHM_ID_BITS)
+#define _SHM_IDX_BITS	15
+#define SHM_IDX_MASK	((1<<_SHM_IDX_BITS)-1)
+
+/* We must have SHM_ID_SHIFT + _SHM_ID_BITS + _SHM_IDX_BITS <= 32
+   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 (1<<(_SHM_IDX_BITS+_SHM_ID_BITS))/* max shm system wide (pages) */
+#define	SHMLBA 0x1000				/* attach addr a multiple of this */
+#define SHMSEG SHMMNI				/* max shared segs per process */
+
+#ifdef __KERNEL__
+
+/* shm_mode upper byte flags */
+#define	SHM_DEST	01000	/* segment will be destroyed on last detach */
+#define SHM_LOCKED      02000   /* segment will not be swapped */
+
+/* ipcs ctl commands */
+#define SHM_STAT 	13
+#define SHM_INFO 	14
+struct shm_info {
+	int   used_ids;
+	ulong shm_tot; /* total allocated shm */
+	ulong shm_rss; /* total resident shm */
+	ulong shm_swp; /* total swapped shm */
+	ulong swap_attempts;
+	ulong swap_successes;
+};
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_SHM_H_ */
+
diff -r -u -N linux.orig/include/linux/string.h linux.arm/include/linux/string.h
--- linux.orig/include/linux/string.h	Fri Oct 27 23:09:04 1995
+++ linux.arm/include/linux/string.h	Fri Oct 27 23:10:43 1995
@@ -26,7 +26,7 @@
 extern int strcmp(const char *,const char *);
 extern int strncmp(const char *,const char *,size_t);
 
-extern void * memset(void *,int,size_t);
+extern void * memset(void *,char,size_t);
 extern void * memcpy(void *,const void *,size_t);
 extern void * memmove(void *,const void *,size_t);
 extern void * memscan(void *,int,size_t);
diff -r -u -N linux.orig/include/linux/tcp.h linux.arm/include/linux/tcp.h
--- linux.orig/include/linux/tcp.h	Fri Oct 27 23:09:06 1995
+++ linux.arm/include/linux/tcp.h	Fri Oct 27 23:10:53 1995
@@ -46,6 +46,16 @@
 		fin:1,
 		doff:4,
 		res1:4;
+#elif defined(__arm__)
+	__u16	res1:4,
+		doff:4,
+		fin:1,
+		syn:1,
+		rst:1,
+		psh:1,
+		ack:1,
+		urg:1,
+		res2:2;
 #else
 #error	"Adjust your <asm/byteorder.h> defines"
 #endif	
diff -r -u -N linux.orig/include/linux/timex.h linux.arm/include/linux/timex.h
--- linux.orig/include/linux/timex.h	Fri Oct 27 23:09:06 1995
+++ linux.arm/include/linux/timex.h	Fri Oct 27 23:10:55 1995
@@ -62,8 +62,13 @@
 #define MINSEC 16               /* min interval between updates (s) */
 #define MAXSEC 1200             /* max interval between updates (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 /* Underlying HZ */
+#define CLOCK_TICK_FACTOR	1000000	/* Factor of both 1000000 and CLOCK_TICK_RATE */
+#endif
 #define LATCH  ((CLOCK_TICK_RATE + HZ/2) / HZ)	/* For divider */
 
 #define FINETUNE (((((LATCH * HZ - CLOCK_TICK_RATE) << SHIFT_HZ) * \
diff -r -u -N linux.orig/include/linux/timex.h.old linux.arm/include/linux/timex.h.old
--- linux.orig/include/linux/timex.h.old	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/linux/timex.h.old	Fri Oct 27 23:11:02 1995
@@ -0,0 +1,139 @@
+/*****************************************************************************
+ *                                                                           *
+ * Copyright (c) David L. Mills 1993                                         *
+ *                                                                           *
+ * Permission to use, copy, modify, and distribute this software and its     *
+ * documentation for any purpose and without fee is hereby granted, provided *
+ * that the above copyright notice appears in all copies and that both the   *
+ * copyright notice and this permission notice appear in supporting          *
+ * documentation, and that the name University of Delaware not be used in    *
+ * advertising or publicity pertaining to distribution of the software       *
+ * without specific, written prior permission.  The University of Delaware   *
+ * makes no representations about the suitability this software for any      *
+ * purpose.  It is provided "as is" without express or implied warranty.     *
+ *                                                                           *
+ *****************************************************************************/
+
+/*
+ * Modification history timex.h
+ * 
+ * 17 Sep 93    David L. Mills
+ *      Created file $NTP/include/sys/timex.h
+ * 07 Oct 93    Torsten Duwe
+ *      Derived linux/timex.h
+ */
+#ifndef _LINUX_TIMEX_H
+#define _LINUX_TIMEX_H
+
+/*
+ * The following defines establish the engineering parameters of the PLL
+ * model. The HZ variable establishes the timer interrupt frequency, 100 Hz 
+ * for the SunOS kernel, 256 Hz for the Ultrix kernel and 1024 Hz for the
+ * OSF/1 kernel. The SHIFT_HZ define expresses the same value as the
+ * nearest power of two in order to avoid hardware multiply operations.
+ */
+#define SHIFT_HZ 7		/* log2(HZ) */
+
+/*
+ * The SHIFT_KG and SHIFT_KF defines establish the damping of the PLL
+ * and are chosen by analysis for a slightly underdamped convergence
+ * characteristic. The MAXTC define establishes the maximum time constant
+ * of the PLL. With the parameters given and the default time constant of
+ * zero, the PLL will converge in about 15 minutes.
+ */
+#define SHIFT_KG 8		/* shift for phase increment */
+#define SHIFT_KF 20		/* shift for frequency increment */
+#define MAXTC 6			/* maximum time constant (shift) */
+
+/*
+ * The SHIFT_SCALE define establishes the decimal point of the time_phase
+ * variable which serves as a an extension to the low-order bits of the
+ * system clock variable. The SHIFT_UPDATE define establishes the decimal
+ * point of the time_offset variable which represents the current offset
+ * with respect to standard time. The FINEUSEC define represents 1 usec in
+ * scaled units.
+ */
+#define SHIFT_SCALE 24		/* shift for phase scale factor */
+#define SHIFT_UPDATE (SHIFT_KG + MAXTC) /* shift for offset scale factor */
+#define FINEUSEC (1 << SHIFT_SCALE) /* 1 us in scaled units */
+
+#define MAXPHASE 128000         /* max phase error (us) */
+#define MAXFREQ 100             /* max frequency error (ppm) */
+#define MINSEC 16               /* min interval between updates (s) */
+#define MAXSEC 1200             /* max interval between updates (s) */
+
+#define CLOCK_TICK_RATE	1193180 /* Underlying HZ */
+#define CLOCK_TICK_FACTOR	20	/* Factor of both 1000000 and CLOCK_TICK_RATE */
+#define LATCH  ((CLOCK_TICK_RATE + HZ/2) / HZ)	/* For divider */
+
+#define FINETUNE (((((LATCH * HZ - CLOCK_TICK_RATE) << SHIFT_HZ) * \
+	(1000000/CLOCK_TICK_FACTOR) / (CLOCK_TICK_RATE/CLOCK_TICK_FACTOR)) \
+		<< (SHIFT_SCALE-SHIFT_HZ)) / HZ)
+
+/*
+ * syscall interface - used (mainly by NTP daemon)
+ * to discipline kernel clock oscillator
+ */
+struct timex {
+	int mode;		/* mode selector */
+	long offset;		/* time offset (usec) */
+	long frequency;		/* frequency offset (scaled ppm) */
+	long maxerror;		/* maximum error (usec) */
+	long esterror;		/* estimated error (usec) */
+	int status;		/* clock command/status */
+	long time_constant;	/* pll time constant */
+	long precision;		/* clock precision (usec) (read only) */
+	long tolerance;		/* clock frequency tolerance (ppm)
+				 * (read only)
+				 */
+	struct timeval time;	/* (read only) */
+	long tick;		/* (modified) usecs between clock ticks */
+};
+
+/*
+ * Mode codes (timex.mode) 
+ */
+#define ADJ_OFFSET		0x0001	/* time offset */
+#define ADJ_FREQUENCY		0x0002	/* frequency offset */
+#define ADJ_MAXERROR		0x0004	/* maximum time error */
+#define ADJ_ESTERROR		0x0008	/* estimated time error */
+#define ADJ_STATUS		0x0010	/* clock status */
+#define ADJ_TIMECONST		0x0020	/* pll time constant */
+#define ADJ_TICK		0x4000	/* tick value */
+#define ADJ_OFFSET_SINGLESHOT	0x8001	/* old-fashioned adjtime */
+
+/*
+ * Clock command/status codes (timex.status)
+ */
+#define TIME_OK		0	/* clock synchronized */
+#define TIME_INS	1	/* insert leap second */
+#define TIME_DEL	2	/* delete leap second */
+#define TIME_OOP	3	/* leap second in progress */
+#define TIME_BAD	4	/* clock not synchronized */
+
+#ifdef __KERNEL__
+/*
+ * kernel variables
+ */
+extern long tick;                      /* timer interrupt period */
+extern int tickadj;			/* amount of adjustment per tick */
+
+/*
+ * phase-lock loop variables
+ */
+extern int time_status;		/* clock synchronization status */
+extern long time_offset;	/* time adjustment (us) */
+extern long time_constant;	/* pll time constant */
+extern long time_tolerance;	/* frequency tolerance (ppm) */
+extern long time_precision;	/* clock precision (us) */
+extern long time_maxerror;	/* maximum error */
+extern long time_esterror;	/* estimated error */
+extern long time_phase;		/* phase offset (scaled us) */
+extern long time_freq;		/* frequency offset (scaled ppm) */
+extern long time_adj;		/* tick adjust (scaled 1 / HZ) */
+extern long time_reftime;	/* time at last adjustment (s) */
+
+extern long time_adjust;	/* The amount of adjtime left */
+#endif /* KERNEL */
+
+#endif /* LINUX_TIMEX_H */
diff -r -u -N linux.orig/include/linux/types.h linux.arm/include/linux/types.h
--- linux.orig/include/linux/types.h	Fri Oct 27 23:09:06 1995
+++ linux.arm/include/linux/types.h	Fri Oct 27 23:10:44 1995
@@ -79,6 +79,8 @@
 #undef __FD_SETSIZE
 #define __FD_SETSIZE	(__FDSET_LONGS*__NFDBITS)
 
+#ifndef __arm__
+/* This should go into some asm header */
 #undef	__FD_SET
 #define __FD_SET(fd,fdsetp) \
 		__asm__ __volatile__("btsl %1,%0": \
@@ -103,6 +105,30 @@
 			:"=m" (*(fd_set *) (fdsetp)) \
 			:"a" (0), "c" (__FDSET_LONGS), \
 			"D" ((fd_set *) (fdsetp)) :"cx","di")
+#else
+#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] = 0; \
+		   ((fd_set *)fdsetp)->fds_bits[1] = 0; \
+		   ((fd_set *)fdsetp)->fds_bits[2] = 0; \
+		   ((fd_set *)fdsetp)->fds_bits[3] = 0; \
+		   ((fd_set *)fdsetp)->fds_bits[4] = 0; \
+		   ((fd_set *)fdsetp)->fds_bits[5] = 0; \
+		   ((fd_set *)fdsetp)->fds_bits[6] = 0; \
+		   ((fd_set *)fdsetp)->fds_bits[7] = 0; })
+#endif
 
 struct ustat {
 	daddr_t f_tfree;
diff -r -u -N linux.orig/include/linux/types.h.old linux.arm/include/linux/types.h.old
--- linux.orig/include/linux/types.h.old	Thu Jan  1 01:00:00 1970
+++ linux.arm/include/linux/types.h.old	Fri Oct 27 23:11:02 1995
@@ -0,0 +1,114 @@
+#ifndef _LINUX_TYPES_H
+#define _LINUX_TYPES_H
+
+#include <asm/types.h>
+
+#ifndef _TIME_T
+#define _TIME_T
+typedef long time_t;
+#endif
+
+#ifndef _CLOCK_T
+#define _CLOCK_T
+typedef long clock_t;
+#endif
+
+#ifndef NULL
+#define NULL ((void *) 0)
+#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;
+
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+#define _LOFF_T
+typedef long long loff_t;
+#endif
+
+/* bsd */
+typedef unsigned char u_char;
+typedef unsigned short u_short;
+typedef unsigned int u_int;
+typedef unsigned long u_long;
+
+/* sysv */
+typedef unsigned char unchar;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+typedef unsigned long ulong;
+
+typedef char *caddr_t;
+
+typedef unsigned char cc_t;
+typedef unsigned int speed_t;
+typedef unsigned long tcflag_t;
+
+/*
+ * This allows for 256 file descriptors: if NR_OPEN is ever grown beyond that
+ * you'll have to change this too. But 256 fd's seem to be enough even for such
+ * "real" unices like SunOS, so hopefully this is one limit that doesn't have
+ * to be changed.
+ *
+ * Note that POSIX wants the FD_CLEAR(fd,fdsetp) defines to be in <sys/time.h>
+ * (and thus <linux/time.h>) - but this is a more logical place for them. Solved
+ * by having dummy defines in <sys/time.h>.
+ */
+
+/*
+ * Those macros may have been defined in <gnu/types.h>. But we always
+ * use the ones here. 
+ */
+#undef __FDSET_LONGS
+#define __FDSET_LONGS 8
+
+typedef struct fd_set {
+	unsigned long fds_bits [__FDSET_LONGS];
+} fd_set;
+
+#undef __NFDBITS
+#define __NFDBITS	(8 * sizeof(unsigned long))
+
+#undef __FD_SETSIZE
+#define __FD_SETSIZE	(__FDSET_LONGS*__NFDBITS)
+
+#undef	__FD_SET
+#define __FD_SET(fd,fdsetp) \
+		__asm__ __volatile__("btsl %1,%0": \
+			"=m" (*(fd_set *) (fdsetp)):"r" ((int) (fd)))
+
+#undef	__FD_CLR
+#define __FD_CLR(fd,fdsetp) \
+		__asm__ __volatile__("btrl %1,%0": \
+			"=m" (*(fd_set *) (fdsetp)):"r" ((int) (fd)))
+
+#undef	__FD_ISSET
+#define __FD_ISSET(fd,fdsetp) (__extension__ ({ \
+		unsigned char __result; \
+		__asm__ __volatile__("btl %1,%2 ; setb %0" \
+			:"=q" (__result) :"r" ((int) (fd)), \
+			"m" (*(fd_set *) (fdsetp))); \
+		__result; }))
+
+#undef	__FD_ZERO
+#define __FD_ZERO(fdsetp) \
+		__asm__ __volatile__("cld ; rep ; stosl" \
+			:"=m" (*(fd_set *) (fdsetp)) \
+			:"a" (0), "c" (__FDSET_LONGS), \
+			"D" ((fd_set *) (fdsetp)) :"cx","di")
+
+struct ustat {
+	daddr_t f_tfree;
+	ino_t f_tinode;
+	char f_fname[6];
+	char f_fpack[6];
+};
+
+#endif
diff -r -u -N linux.orig/include/linux/version.h linux.arm/include/linux/version.h
--- linux.orig/include/linux/version.h	Fri Oct 27 23:09:07 1995
+++ linux.arm/include/linux/version.h	Fri Oct 27 23:11:01 1995
@@ -1,8 +1,8 @@
 #define UTS_RELEASE "1.2.8"
-#define UTS_VERSION "#4 Mon Oct 2 13:18:51 BST 1995"
-#define LINUX_COMPILE_TIME "13:18:52"
+#define UTS_VERSION "#0 Mon Oct 9 22:18:54 BST 1995"
+#define LINUX_COMPILE_TIME "22:18:56"
 #define LINUX_COMPILE_BY "root"
-#define LINUX_COMPILE_HOST "russ"
+#define LINUX_COMPILE_HOST "arm"
 #define LINUX_COMPILE_DOMAIN "levin.co.uk"
-#define LINUX_COMPILER "gcc version 2.6.3"
+#define LINUX_COMPILER "gcc version 2.6.0"
 #define LINUX_VERSION_CODE 66056
diff -r -u -N linux.orig/include/linux/vt.h linux.arm/include/linux/vt.h
--- linux.orig/include/linux/vt.h	Fri Oct 27 23:09:07 1995
+++ linux.arm/include/linux/vt.h	Fri Oct 27 23:10:42 1995
@@ -39,4 +39,10 @@
 };
 #define VT_RESIZE	0x5609	/* set kernel's idea of screensize */
 
+#ifdef __arm__
+#define VT_GETSCRINFO	0x56FD	/* get screen info */
+#define VT_GETPALETTE	0x56FE  /* get palette */
+#define VT_SETPALETTE	0x56FF  /* set palette */
+#endif
+
 #endif /* _LINUX_VT_H */
diff -r -u -N linux.orig/init/main.c linux.arm/init/main.c
--- linux.orig/init/main.c	Fri Oct 27 23:09:07 1995
+++ linux.arm/init/main.c	Fri Oct 27 23:10:17 1995
@@ -28,6 +28,9 @@
 #include <linux/mm.h>
 
 #include <asm/bugs.h>
+#ifdef __arm__
+extern char stext;
+#endif
 
 extern unsigned long * prof_buffer;
 extern unsigned long prof_len;
@@ -185,9 +188,11 @@
 #ifdef CONFIG_BLK_DEV_XD
 	{ "xd=", xd_setup },
 #endif
+#if 0
 #ifdef CONFIG_BLK_DEV_FD
 	{ "floppy=", floppy_setup },
 #endif
+#endif
 #ifdef CONFIG_MCD
 	{ "mcd=", mcd_setup },
 #endif
@@ -356,7 +361,11 @@
 #ifdef CONFIG_PROFILE
 	prof_buffer = (unsigned long *) memory_start;
 	/* only text is profiled */
+#ifndef __arm__
 	prof_len = (unsigned long) &etext;
+#else
+	prof_len = (unsigned long) &etext - ((unsigned long) &stext);
+#endif
 	prof_len >>= CONFIG_PROFILE_SHIFT;
 	memory_start += prof_len * sizeof(unsigned long);
 #endif
@@ -388,7 +397,12 @@
 	check_bugs();
 
 	printk(linux_banner);
-
+#ifdef __arm__
+	{
+		extern void move_to_user_mode(void);
+		move_to_user_mode();
+	}
+#endif
 	if (!fork())		/* we count on this going ok */
 		init();
 /*
@@ -420,9 +434,10 @@
 	int pid,i;
 
 	setup();
+
 	sprintf(term, "TERM=con%dx%d", ORIG_VIDEO_COLS, ORIG_VIDEO_LINES);
 
-	#ifdef CONFIG_UMSDOS_FS
+#	ifdef CONFIG_UMSDOS_FS
 	{
 		/*
 			When mounting a umsdos fs as root, we detect
@@ -435,7 +450,7 @@
 			current->fs->pwd  = pseudo_root;
 		}
 	}
-	#endif
+#	endif
 
 	(void) open("/dev/tty1",O_RDWR,0);
 	(void) dup(0);
diff -r -u -N linux.orig/ipc/Makefile linux.arm/ipc/Makefile
--- linux.orig/ipc/Makefile	Fri Oct 27 23:09:07 1995
+++ linux.arm/ipc/Makefile	Fri Oct 27 23:11:56 1995
@@ -24,6 +24,7 @@
 
 ipc.o: $(OBJS) 
 	$(LD) -r -o ipc.o $(OBJS) 
+#	stripper ipc.o ipc2.o
 
 dep:
 	$(CPP) -M $(SRCS) > .depend
diff -r -u -N linux.orig/kernel/.depend.old linux.arm/kernel/.depend.old
--- linux.orig/kernel/.depend.old	Thu Jan  1 01:00:00 1970
+++ linux.arm/kernel/.depend.old	Fri Oct 27 23:10:23 1995
@@ -0,0 +1,749 @@
+dma.o: dma.c /usr/src/linux/include/linux/kernel.h \
+  /usr/lib/gcc-lib/arm-linux/2.6.0/include/stdarg.h \
+  /usr/src/linux/include/linux/linkage.h \
+  /usr/src/linux/include/linux/errno.h /usr/src/linux/include/asm/dma.h \
+  /usr/src/linux/include/asm/system.h
+exec_domain.o: exec_domain.c /usr/src/linux/include/linux/personality.h \
+  /usr/src/linux/include/linux/linkage.h \
+  /usr/src/linux/include/linux/ptrace.h /usr/src/linux/include/asm/ptrace.h \
+  /usr/src/linux/include/linux/sched.h /usr/src/linux/include/asm/param.h \
+  /usr/src/linux/include/linux/binfmts.h \
+  /usr/src/linux/include/linux/tasks.h \
+  /usr/src/linux/include/linux/kernel.h \
+  /usr/lib/gcc-lib/arm-linux/2.6.0/include/stdarg.h \
+  /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/head.h \
+  /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \
+  /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \
+  /usr/src/linux/include/asm/types.h /usr/src/linux/include/linux/dirent.h \
+  /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/net.h \
+  /usr/src/linux/include/linux/socket.h \
+  /usr/src/linux/include/linux/sockios.h \
+  /usr/src/linux/include/linux/pipe_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_i.h \
+  /usr/src/linux/include/linux/ext_fs_i.h \
+  /usr/src/linux/include/linux/ext2_fs_i.h \
+  /usr/src/linux/include/linux/hpfs_fs_i.h \
+  /usr/src/linux/include/linux/msdos_fs_i.h \
+  /usr/src/linux/include/linux/umsdos_fs_i.h \
+  /usr/src/linux/include/linux/iso_fs_i.h \
+  /usr/src/linux/include/linux/nfs_fs_i.h \
+  /usr/src/linux/include/linux/nfs.h \
+  /usr/src/linux/include/linux/xia_fs_i.h \
+  /usr/src/linux/include/linux/sysv_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_sb.h \
+  /usr/src/linux/include/linux/ext_fs_sb.h \
+  /usr/src/linux/include/linux/ext2_fs_sb.h \
+  /usr/src/linux/include/linux/hpfs_fs_sb.h \
+  /usr/src/linux/include/linux/msdos_fs_sb.h \
+  /usr/src/linux/include/linux/iso_fs_sb.h \
+  /usr/src/linux/include/linux/nfs_fs_sb.h \
+  /usr/src/linux/include/linux/xia_fs_sb.h \
+  /usr/src/linux/include/linux/sysv_fs_sb.h \
+  /usr/src/linux/include/linux/signal.h /usr/src/linux/include/asm/signal.h \
+  /usr/src/linux/include/linux/time.h /usr/src/linux/include/asm/bitops.h \
+  /usr/src/linux/include/linux/string.h /usr/src/linux/include/asm/string.h \
+  /usr/src/linux/include/linux/param.h \
+  /usr/src/linux/include/linux/resource.h \
+  /usr/src/linux/include/linux/vm86.h \
+  /usr/src/linux/include/linux/math_emu.h \
+  /usr/src/linux/include/asm/processor.h /usr/src/linux/include/linux/mm.h \
+  /usr/src/linux/include/linux/errno.h /usr/src/linux/include/asm/page.h
+exit.o: exit.c /usr/src/linux/include/linux/wait.h \
+  /usr/src/linux/include/linux/errno.h \
+  /usr/src/linux/include/linux/signal.h /usr/src/linux/include/asm/signal.h \
+  /usr/src/linux/include/linux/sched.h /usr/src/linux/include/asm/param.h \
+  /usr/src/linux/include/linux/binfmts.h \
+  /usr/src/linux/include/linux/ptrace.h /usr/src/linux/include/asm/ptrace.h \
+  /usr/src/linux/include/linux/personality.h \
+  /usr/src/linux/include/linux/linkage.h \
+  /usr/src/linux/include/linux/tasks.h \
+  /usr/src/linux/include/linux/kernel.h \
+  /usr/lib/gcc-lib/arm-linux/2.6.0/include/stdarg.h \
+  /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/head.h \
+  /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \
+  /usr/src/linux/include/linux/types.h /usr/src/linux/include/asm/types.h \
+  /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \
+  /usr/src/linux/include/linux/net.h /usr/src/linux/include/linux/socket.h \
+  /usr/src/linux/include/linux/sockios.h \
+  /usr/src/linux/include/linux/pipe_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_i.h \
+  /usr/src/linux/include/linux/ext_fs_i.h \
+  /usr/src/linux/include/linux/ext2_fs_i.h \
+  /usr/src/linux/include/linux/hpfs_fs_i.h \
+  /usr/src/linux/include/linux/msdos_fs_i.h \
+  /usr/src/linux/include/linux/umsdos_fs_i.h \
+  /usr/src/linux/include/linux/iso_fs_i.h \
+  /usr/src/linux/include/linux/nfs_fs_i.h \
+  /usr/src/linux/include/linux/nfs.h \
+  /usr/src/linux/include/linux/xia_fs_i.h \
+  /usr/src/linux/include/linux/sysv_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_sb.h \
+  /usr/src/linux/include/linux/ext_fs_sb.h \
+  /usr/src/linux/include/linux/ext2_fs_sb.h \
+  /usr/src/linux/include/linux/hpfs_fs_sb.h \
+  /usr/src/linux/include/linux/msdos_fs_sb.h \
+  /usr/src/linux/include/linux/iso_fs_sb.h \
+  /usr/src/linux/include/linux/nfs_fs_sb.h \
+  /usr/src/linux/include/linux/xia_fs_sb.h \
+  /usr/src/linux/include/linux/sysv_fs_sb.h \
+  /usr/src/linux/include/linux/time.h /usr/src/linux/include/asm/bitops.h \
+  /usr/src/linux/include/linux/string.h /usr/src/linux/include/asm/string.h \
+  /usr/src/linux/include/linux/param.h \
+  /usr/src/linux/include/linux/resource.h \
+  /usr/src/linux/include/linux/vm86.h \
+  /usr/src/linux/include/linux/math_emu.h \
+  /usr/src/linux/include/asm/processor.h /usr/src/linux/include/linux/mm.h \
+  /usr/src/linux/include/asm/page.h /usr/src/linux/include/linux/tty.h \
+  /usr/src/linux/include/linux/termios.h \
+  /usr/src/linux/include/linux/tqueue.h \
+  /usr/src/linux/include/linux/tty_driver.h \
+  /usr/src/linux/include/linux/tty_ldisc.h \
+  /usr/src/linux/include/linux/malloc.h \
+  /usr/src/linux/include/linux/config.h \
+  /usr/src/linux/include/linux/autoconf.h \
+  /usr/src/linux/include/asm/segment.h
+fork.o: fork.c /usr/src/linux/include/linux/errno.h \
+  /usr/src/linux/include/linux/sched.h /usr/src/linux/include/asm/param.h \
+  /usr/src/linux/include/linux/binfmts.h \
+  /usr/src/linux/include/linux/ptrace.h /usr/src/linux/include/asm/ptrace.h \
+  /usr/src/linux/include/linux/personality.h \
+  /usr/src/linux/include/linux/linkage.h \
+  /usr/src/linux/include/linux/tasks.h \
+  /usr/src/linux/include/linux/kernel.h \
+  /usr/lib/gcc-lib/arm-linux/2.6.0/include/stdarg.h \
+  /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/head.h \
+  /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \
+  /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \
+  /usr/src/linux/include/asm/types.h /usr/src/linux/include/linux/dirent.h \
+  /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/net.h \
+  /usr/src/linux/include/linux/socket.h \
+  /usr/src/linux/include/linux/sockios.h \
+  /usr/src/linux/include/linux/pipe_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_i.h \
+  /usr/src/linux/include/linux/ext_fs_i.h \
+  /usr/src/linux/include/linux/ext2_fs_i.h \
+  /usr/src/linux/include/linux/hpfs_fs_i.h \
+  /usr/src/linux/include/linux/msdos_fs_i.h \
+  /usr/src/linux/include/linux/umsdos_fs_i.h \
+  /usr/src/linux/include/linux/iso_fs_i.h \
+  /usr/src/linux/include/linux/nfs_fs_i.h \
+  /usr/src/linux/include/linux/nfs.h \
+  /usr/src/linux/include/linux/xia_fs_i.h \
+  /usr/src/linux/include/linux/sysv_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_sb.h \
+  /usr/src/linux/include/linux/ext_fs_sb.h \
+  /usr/src/linux/include/linux/ext2_fs_sb.h \
+  /usr/src/linux/include/linux/hpfs_fs_sb.h \
+  /usr/src/linux/include/linux/msdos_fs_sb.h \
+  /usr/src/linux/include/linux/iso_fs_sb.h \
+  /usr/src/linux/include/linux/nfs_fs_sb.h \
+  /usr/src/linux/include/linux/xia_fs_sb.h \
+  /usr/src/linux/include/linux/sysv_fs_sb.h \
+  /usr/src/linux/include/linux/signal.h /usr/src/linux/include/asm/signal.h \
+  /usr/src/linux/include/linux/time.h /usr/src/linux/include/asm/bitops.h \
+  /usr/src/linux/include/linux/string.h /usr/src/linux/include/asm/string.h \
+  /usr/src/linux/include/linux/param.h \
+  /usr/src/linux/include/linux/resource.h \
+  /usr/src/linux/include/linux/vm86.h \
+  /usr/src/linux/include/linux/math_emu.h \
+  /usr/src/linux/include/asm/processor.h /usr/src/linux/include/linux/mm.h \
+  /usr/src/linux/include/asm/page.h /usr/src/linux/include/linux/stddef.h \
+  /usr/src/linux/include/linux/unistd.h /usr/src/linux/include/asm/unistd.h \
+  /usr/src/linux/include/linux/malloc.h \
+  /usr/src/linux/include/linux/config.h \
+  /usr/src/linux/include/linux/autoconf.h \
+  /usr/src/linux/include/linux/ldt.h /usr/src/linux/include/asm/segment.h
+info.o: info.c /usr/src/linux/include/asm/segment.h \
+  /usr/src/linux/include/linux/sched.h /usr/src/linux/include/asm/param.h \
+  /usr/src/linux/include/linux/binfmts.h \
+  /usr/src/linux/include/linux/ptrace.h /usr/src/linux/include/asm/ptrace.h \
+  /usr/src/linux/include/linux/personality.h \
+  /usr/src/linux/include/linux/linkage.h \
+  /usr/src/linux/include/linux/tasks.h \
+  /usr/src/linux/include/linux/kernel.h \
+  /usr/lib/gcc-lib/arm-linux/2.6.0/include/stdarg.h \
+  /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/head.h \
+  /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \
+  /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \
+  /usr/src/linux/include/asm/types.h /usr/src/linux/include/linux/dirent.h \
+  /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/net.h \
+  /usr/src/linux/include/linux/socket.h \
+  /usr/src/linux/include/linux/sockios.h \
+  /usr/src/linux/include/linux/pipe_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_i.h \
+  /usr/src/linux/include/linux/ext_fs_i.h \
+  /usr/src/linux/include/linux/ext2_fs_i.h \
+  /usr/src/linux/include/linux/hpfs_fs_i.h \
+  /usr/src/linux/include/linux/msdos_fs_i.h \
+  /usr/src/linux/include/linux/umsdos_fs_i.h \
+  /usr/src/linux/include/linux/iso_fs_i.h \
+  /usr/src/linux/include/linux/nfs_fs_i.h \
+  /usr/src/linux/include/linux/nfs.h \
+  /usr/src/linux/include/linux/xia_fs_i.h \
+  /usr/src/linux/include/linux/sysv_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_sb.h \
+  /usr/src/linux/include/linux/ext_fs_sb.h \
+  /usr/src/linux/include/linux/ext2_fs_sb.h \
+  /usr/src/linux/include/linux/hpfs_fs_sb.h \
+  /usr/src/linux/include/linux/msdos_fs_sb.h \
+  /usr/src/linux/include/linux/iso_fs_sb.h \
+  /usr/src/linux/include/linux/nfs_fs_sb.h \
+  /usr/src/linux/include/linux/xia_fs_sb.h \
+  /usr/src/linux/include/linux/sysv_fs_sb.h \
+  /usr/src/linux/include/linux/signal.h /usr/src/linux/include/asm/signal.h \
+  /usr/src/linux/include/linux/time.h /usr/src/linux/include/asm/bitops.h \
+  /usr/src/linux/include/linux/string.h /usr/src/linux/include/asm/string.h \
+  /usr/src/linux/include/linux/param.h \
+  /usr/src/linux/include/linux/resource.h \
+  /usr/src/linux/include/linux/vm86.h \
+  /usr/src/linux/include/linux/math_emu.h \
+  /usr/src/linux/include/asm/processor.h \
+  /usr/src/linux/include/linux/unistd.h /usr/src/linux/include/asm/unistd.h \
+  /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/errno.h \
+  /usr/src/linux/include/asm/page.h
+itimer.o: itimer.c /usr/src/linux/include/linux/signal.h \
+  /usr/src/linux/include/asm/signal.h /usr/src/linux/include/linux/sched.h \
+  /usr/src/linux/include/asm/param.h /usr/src/linux/include/linux/binfmts.h \
+  /usr/src/linux/include/linux/ptrace.h /usr/src/linux/include/asm/ptrace.h \
+  /usr/src/linux/include/linux/personality.h \
+  /usr/src/linux/include/linux/linkage.h \
+  /usr/src/linux/include/linux/tasks.h \
+  /usr/src/linux/include/linux/kernel.h \
+  /usr/lib/gcc-lib/arm-linux/2.6.0/include/stdarg.h \
+  /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/head.h \
+  /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \
+  /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \
+  /usr/src/linux/include/asm/types.h /usr/src/linux/include/linux/dirent.h \
+  /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/net.h \
+  /usr/src/linux/include/linux/socket.h \
+  /usr/src/linux/include/linux/sockios.h \
+  /usr/src/linux/include/linux/pipe_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_i.h \
+  /usr/src/linux/include/linux/ext_fs_i.h \
+  /usr/src/linux/include/linux/ext2_fs_i.h \
+  /usr/src/linux/include/linux/hpfs_fs_i.h \
+  /usr/src/linux/include/linux/msdos_fs_i.h \
+  /usr/src/linux/include/linux/umsdos_fs_i.h \
+  /usr/src/linux/include/linux/iso_fs_i.h \
+  /usr/src/linux/include/linux/nfs_fs_i.h \
+  /usr/src/linux/include/linux/nfs.h \
+  /usr/src/linux/include/linux/xia_fs_i.h \
+  /usr/src/linux/include/linux/sysv_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_sb.h \
+  /usr/src/linux/include/linux/ext_fs_sb.h \
+  /usr/src/linux/include/linux/ext2_fs_sb.h \
+  /usr/src/linux/include/linux/hpfs_fs_sb.h \
+  /usr/src/linux/include/linux/msdos_fs_sb.h \
+  /usr/src/linux/include/linux/iso_fs_sb.h \
+  /usr/src/linux/include/linux/nfs_fs_sb.h \
+  /usr/src/linux/include/linux/xia_fs_sb.h \
+  /usr/src/linux/include/linux/sysv_fs_sb.h \
+  /usr/src/linux/include/linux/time.h /usr/src/linux/include/asm/bitops.h \
+  /usr/src/linux/include/linux/string.h /usr/src/linux/include/asm/string.h \
+  /usr/src/linux/include/linux/param.h \
+  /usr/src/linux/include/linux/resource.h \
+  /usr/src/linux/include/linux/vm86.h \
+  /usr/src/linux/include/linux/math_emu.h \
+  /usr/src/linux/include/asm/processor.h \
+  /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/mm.h \
+  /usr/src/linux/include/asm/page.h /usr/src/linux/include/asm/segment.h
+ksyms.o: ksyms.c /usr/src/linux/include/linux/autoconf.h \
+  /usr/src/linux/include/linux/module.h \
+  /usr/src/linux/include/linux/kernel.h \
+  /usr/lib/gcc-lib/arm-linux/2.6.0/include/stdarg.h \
+  /usr/src/linux/include/linux/linkage.h /usr/src/linux/include/linux/fs.h \
+  /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \
+  /usr/src/linux/include/linux/types.h /usr/src/linux/include/asm/types.h \
+  /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \
+  /usr/src/linux/include/linux/net.h /usr/src/linux/include/linux/socket.h \
+  /usr/src/linux/include/linux/sockios.h \
+  /usr/src/linux/include/linux/pipe_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_i.h \
+  /usr/src/linux/include/linux/ext_fs_i.h \
+  /usr/src/linux/include/linux/ext2_fs_i.h \
+  /usr/src/linux/include/linux/hpfs_fs_i.h \
+  /usr/src/linux/include/linux/msdos_fs_i.h \
+  /usr/src/linux/include/linux/umsdos_fs_i.h \
+  /usr/src/linux/include/linux/iso_fs_i.h \
+  /usr/src/linux/include/linux/nfs_fs_i.h \
+  /usr/src/linux/include/linux/nfs.h \
+  /usr/src/linux/include/linux/xia_fs_i.h \
+  /usr/src/linux/include/linux/sysv_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_sb.h \
+  /usr/src/linux/include/linux/ext_fs_sb.h \
+  /usr/src/linux/include/linux/ext2_fs_sb.h \
+  /usr/src/linux/include/linux/hpfs_fs_sb.h \
+  /usr/src/linux/include/linux/msdos_fs_sb.h \
+  /usr/src/linux/include/linux/iso_fs_sb.h \
+  /usr/src/linux/include/linux/nfs_fs_sb.h \
+  /usr/src/linux/include/linux/xia_fs_sb.h \
+  /usr/src/linux/include/linux/sysv_fs_sb.h \
+  /usr/src/linux/include/linux/blkdev.h \
+  /usr/src/linux/include/linux/major.h /usr/src/linux/include/linux/sched.h \
+  /usr/src/linux/include/asm/param.h /usr/src/linux/include/linux/binfmts.h \
+  /usr/src/linux/include/linux/ptrace.h /usr/src/linux/include/asm/ptrace.h \
+  /usr/src/linux/include/linux/personality.h \
+  /usr/src/linux/include/linux/tasks.h /usr/src/linux/include/asm/system.h \
+  /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/signal.h \
+  /usr/src/linux/include/asm/signal.h /usr/src/linux/include/linux/time.h \
+  /usr/src/linux/include/asm/bitops.h /usr/src/linux/include/linux/string.h \
+  /usr/src/linux/include/asm/string.h /usr/src/linux/include/linux/param.h \
+  /usr/src/linux/include/linux/resource.h \
+  /usr/src/linux/include/linux/vm86.h \
+  /usr/src/linux/include/linux/math_emu.h \
+  /usr/src/linux/include/asm/processor.h \
+  /usr/src/linux/include/linux/genhd.h /usr/src/linux/include/linux/mm.h \
+  /usr/src/linux/include/linux/errno.h /usr/src/linux/include/asm/page.h \
+  /usr/src/linux/include/linux/malloc.h \
+  /usr/src/linux/include/linux/config.h /usr/src/linux/include/linux/sys.h \
+  /usr/src/linux/include/linux/utsname.h \
+  /usr/src/linux/include/linux/interrupt.h \
+  /usr/src/linux/include/linux/ioport.h \
+  /usr/src/linux/include/linux/timer.h \
+  /usr/src/linux/include/linux/termios.h \
+  /usr/src/linux/include/linux/tqueue.h /usr/src/linux/include/linux/tty.h \
+  /usr/src/linux/include/linux/tty_driver.h \
+  /usr/src/linux/include/linux/tty_ldisc.h \
+  /usr/src/linux/include/linux/serial.h \
+  /usr/src/linux/include/linux/locks.h /usr/src/linux/include/linux/delay.h \
+  /usr/src/linux/include/asm/delay.h \
+  /usr/src/linux/include/linux/msdos_fs.h \
+  /usr/src/linux/include/linux/stat.h /usr/src/linux/include/linux/fd.h \
+  /usr/src/linux/include/asm/irq.h \
+  /usr/src/linux/include/linux/symtab_begin.h \
+  /usr/src/linux/include/linux/symtab_end.h
+module.o: module.c /usr/src/linux/include/linux/errno.h \
+  /usr/src/linux/include/linux/kernel.h \
+  /usr/lib/gcc-lib/arm-linux/2.6.0/include/stdarg.h \
+  /usr/src/linux/include/linux/linkage.h \
+  /usr/src/linux/include/asm/segment.h /usr/src/linux/include/linux/mm.h \
+  /usr/src/linux/include/linux/sched.h /usr/src/linux/include/asm/param.h \
+  /usr/src/linux/include/linux/binfmts.h \
+  /usr/src/linux/include/linux/ptrace.h /usr/src/linux/include/asm/ptrace.h \
+  /usr/src/linux/include/linux/personality.h \
+  /usr/src/linux/include/linux/tasks.h /usr/src/linux/include/asm/system.h \
+  /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h \
+  /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \
+  /usr/src/linux/include/linux/types.h /usr/src/linux/include/asm/types.h \
+  /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \
+  /usr/src/linux/include/linux/net.h /usr/src/linux/include/linux/socket.h \
+  /usr/src/linux/include/linux/sockios.h \
+  /usr/src/linux/include/linux/pipe_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_i.h \
+  /usr/src/linux/include/linux/ext_fs_i.h \
+  /usr/src/linux/include/linux/ext2_fs_i.h \
+  /usr/src/linux/include/linux/hpfs_fs_i.h \
+  /usr/src/linux/include/linux/msdos_fs_i.h \
+  /usr/src/linux/include/linux/umsdos_fs_i.h \
+  /usr/src/linux/include/linux/iso_fs_i.h \
+  /usr/src/linux/include/linux/nfs_fs_i.h \
+  /usr/src/linux/include/linux/nfs.h \
+  /usr/src/linux/include/linux/xia_fs_i.h \
+  /usr/src/linux/include/linux/sysv_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_sb.h \
+  /usr/src/linux/include/linux/ext_fs_sb.h \
+  /usr/src/linux/include/linux/ext2_fs_sb.h \
+  /usr/src/linux/include/linux/hpfs_fs_sb.h \
+  /usr/src/linux/include/linux/msdos_fs_sb.h \
+  /usr/src/linux/include/linux/iso_fs_sb.h \
+  /usr/src/linux/include/linux/nfs_fs_sb.h \
+  /usr/src/linux/include/linux/xia_fs_sb.h \
+  /usr/src/linux/include/linux/sysv_fs_sb.h \
+  /usr/src/linux/include/linux/signal.h /usr/src/linux/include/asm/signal.h \
+  /usr/src/linux/include/linux/time.h /usr/src/linux/include/asm/bitops.h \
+  /usr/src/linux/include/linux/string.h /usr/src/linux/include/asm/string.h \
+  /usr/src/linux/include/linux/param.h \
+  /usr/src/linux/include/linux/resource.h \
+  /usr/src/linux/include/linux/vm86.h \
+  /usr/src/linux/include/linux/math_emu.h \
+  /usr/src/linux/include/asm/processor.h /usr/src/linux/include/asm/page.h \
+  /usr/src/linux/include/linux/module.h \
+  /usr/src/linux/include/linux/malloc.h \
+  /usr/src/linux/include/linux/config.h \
+  /usr/src/linux/include/linux/autoconf.h
+panic.o: panic.c /usr/lib/gcc-lib/arm-linux/2.6.0/include/stdarg.h \
+  /usr/src/linux/include/linux/kernel.h \
+  /usr/src/linux/include/linux/linkage.h \
+  /usr/src/linux/include/linux/sched.h /usr/src/linux/include/asm/param.h \
+  /usr/src/linux/include/linux/binfmts.h \
+  /usr/src/linux/include/linux/ptrace.h /usr/src/linux/include/asm/ptrace.h \
+  /usr/src/linux/include/linux/personality.h \
+  /usr/src/linux/include/linux/tasks.h /usr/src/linux/include/asm/system.h \
+  /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h \
+  /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \
+  /usr/src/linux/include/linux/types.h /usr/src/linux/include/asm/types.h \
+  /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \
+  /usr/src/linux/include/linux/net.h /usr/src/linux/include/linux/socket.h \
+  /usr/src/linux/include/linux/sockios.h \
+  /usr/src/linux/include/linux/pipe_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_i.h \
+  /usr/src/linux/include/linux/ext_fs_i.h \
+  /usr/src/linux/include/linux/ext2_fs_i.h \
+  /usr/src/linux/include/linux/hpfs_fs_i.h \
+  /usr/src/linux/include/linux/msdos_fs_i.h \
+  /usr/src/linux/include/linux/umsdos_fs_i.h \
+  /usr/src/linux/include/linux/iso_fs_i.h \
+  /usr/src/linux/include/linux/nfs_fs_i.h \
+  /usr/src/linux/include/linux/nfs.h \
+  /usr/src/linux/include/linux/xia_fs_i.h \
+  /usr/src/linux/include/linux/sysv_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_sb.h \
+  /usr/src/linux/include/linux/ext_fs_sb.h \
+  /usr/src/linux/include/linux/ext2_fs_sb.h \
+  /usr/src/linux/include/linux/hpfs_fs_sb.h \
+  /usr/src/linux/include/linux/msdos_fs_sb.h \
+  /usr/src/linux/include/linux/iso_fs_sb.h \
+  /usr/src/linux/include/linux/nfs_fs_sb.h \
+  /usr/src/linux/include/linux/xia_fs_sb.h \
+  /usr/src/linux/include/linux/sysv_fs_sb.h \
+  /usr/src/linux/include/linux/signal.h /usr/src/linux/include/asm/signal.h \
+  /usr/src/linux/include/linux/time.h /usr/src/linux/include/asm/bitops.h \
+  /usr/src/linux/include/linux/string.h /usr/src/linux/include/asm/string.h \
+  /usr/src/linux/include/linux/param.h \
+  /usr/src/linux/include/linux/resource.h \
+  /usr/src/linux/include/linux/vm86.h \
+  /usr/src/linux/include/linux/math_emu.h \
+  /usr/src/linux/include/asm/processor.h
+printk.o: printk.c /usr/lib/gcc-lib/arm-linux/2.6.0/include/stdarg.h \
+  /usr/src/linux/include/asm/segment.h /usr/src/linux/include/asm/system.h \
+  /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/sched.h \
+  /usr/src/linux/include/asm/param.h /usr/src/linux/include/linux/binfmts.h \
+  /usr/src/linux/include/linux/ptrace.h /usr/src/linux/include/asm/ptrace.h \
+  /usr/src/linux/include/linux/personality.h \
+  /usr/src/linux/include/linux/linkage.h \
+  /usr/src/linux/include/linux/tasks.h \
+  /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/head.h \
+  /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \
+  /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \
+  /usr/src/linux/include/asm/types.h /usr/src/linux/include/linux/dirent.h \
+  /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/net.h \
+  /usr/src/linux/include/linux/socket.h \
+  /usr/src/linux/include/linux/sockios.h \
+  /usr/src/linux/include/linux/pipe_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_i.h \
+  /usr/src/linux/include/linux/ext_fs_i.h \
+  /usr/src/linux/include/linux/ext2_fs_i.h \
+  /usr/src/linux/include/linux/hpfs_fs_i.h \
+  /usr/src/linux/include/linux/msdos_fs_i.h \
+  /usr/src/linux/include/linux/umsdos_fs_i.h \
+  /usr/src/linux/include/linux/iso_fs_i.h \
+  /usr/src/linux/include/linux/nfs_fs_i.h \
+  /usr/src/linux/include/linux/nfs.h \
+  /usr/src/linux/include/linux/xia_fs_i.h \
+  /usr/src/linux/include/linux/sysv_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_sb.h \
+  /usr/src/linux/include/linux/ext_fs_sb.h \
+  /usr/src/linux/include/linux/ext2_fs_sb.h \
+  /usr/src/linux/include/linux/hpfs_fs_sb.h \
+  /usr/src/linux/include/linux/msdos_fs_sb.h \
+  /usr/src/linux/include/linux/iso_fs_sb.h \
+  /usr/src/linux/include/linux/nfs_fs_sb.h \
+  /usr/src/linux/include/linux/xia_fs_sb.h \
+  /usr/src/linux/include/linux/sysv_fs_sb.h \
+  /usr/src/linux/include/linux/signal.h /usr/src/linux/include/asm/signal.h \
+  /usr/src/linux/include/linux/time.h /usr/src/linux/include/asm/bitops.h \
+  /usr/src/linux/include/linux/string.h /usr/src/linux/include/asm/string.h \
+  /usr/src/linux/include/linux/param.h \
+  /usr/src/linux/include/linux/resource.h \
+  /usr/src/linux/include/linux/vm86.h \
+  /usr/src/linux/include/linux/math_emu.h \
+  /usr/src/linux/include/asm/processor.h /usr/src/linux/include/linux/mm.h \
+  /usr/src/linux/include/asm/page.h
+resource.o: resource.c /usr/src/linux/include/linux/sched.h \
+  /usr/src/linux/include/asm/param.h /usr/src/linux/include/linux/binfmts.h \
+  /usr/src/linux/include/linux/ptrace.h /usr/src/linux/include/asm/ptrace.h \
+  /usr/src/linux/include/linux/personality.h \
+  /usr/src/linux/include/linux/linkage.h \
+  /usr/src/linux/include/linux/tasks.h \
+  /usr/src/linux/include/linux/kernel.h \
+  /usr/lib/gcc-lib/arm-linux/2.6.0/include/stdarg.h \
+  /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/head.h \
+  /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \
+  /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \
+  /usr/src/linux/include/asm/types.h /usr/src/linux/include/linux/dirent.h \
+  /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/net.h \
+  /usr/src/linux/include/linux/socket.h \
+  /usr/src/linux/include/linux/sockios.h \
+  /usr/src/linux/include/linux/pipe_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_i.h \
+  /usr/src/linux/include/linux/ext_fs_i.h \
+  /usr/src/linux/include/linux/ext2_fs_i.h \
+  /usr/src/linux/include/linux/hpfs_fs_i.h \
+  /usr/src/linux/include/linux/msdos_fs_i.h \
+  /usr/src/linux/include/linux/umsdos_fs_i.h \
+  /usr/src/linux/include/linux/iso_fs_i.h \
+  /usr/src/linux/include/linux/nfs_fs_i.h \
+  /usr/src/linux/include/linux/nfs.h \
+  /usr/src/linux/include/linux/xia_fs_i.h \
+  /usr/src/linux/include/linux/sysv_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_sb.h \
+  /usr/src/linux/include/linux/ext_fs_sb.h \
+  /usr/src/linux/include/linux/ext2_fs_sb.h \
+  /usr/src/linux/include/linux/hpfs_fs_sb.h \
+  /usr/src/linux/include/linux/msdos_fs_sb.h \
+  /usr/src/linux/include/linux/iso_fs_sb.h \
+  /usr/src/linux/include/linux/nfs_fs_sb.h \
+  /usr/src/linux/include/linux/xia_fs_sb.h \
+  /usr/src/linux/include/linux/sysv_fs_sb.h \
+  /usr/src/linux/include/linux/signal.h /usr/src/linux/include/asm/signal.h \
+  /usr/src/linux/include/linux/time.h /usr/src/linux/include/asm/bitops.h \
+  /usr/src/linux/include/linux/string.h /usr/src/linux/include/asm/string.h \
+  /usr/src/linux/include/linux/param.h \
+  /usr/src/linux/include/linux/resource.h \
+  /usr/src/linux/include/linux/vm86.h \
+  /usr/src/linux/include/linux/math_emu.h \
+  /usr/src/linux/include/asm/processor.h \
+  /usr/src/linux/include/linux/errno.h \
+  /usr/src/linux/include/linux/ioport.h
+sched.o: sched.c /usr/src/linux/include/linux/config.h \
+  /usr/src/linux/include/linux/autoconf.h \
+  /usr/src/linux/include/linux/signal.h /usr/src/linux/include/asm/signal.h \
+  /usr/src/linux/include/linux/sched.h /usr/src/linux/include/asm/param.h \
+  /usr/src/linux/include/linux/binfmts.h \
+  /usr/src/linux/include/linux/ptrace.h /usr/src/linux/include/asm/ptrace.h \
+  /usr/src/linux/include/linux/personality.h \
+  /usr/src/linux/include/linux/linkage.h \
+  /usr/src/linux/include/linux/tasks.h \
+  /usr/src/linux/include/linux/kernel.h \
+  /usr/lib/gcc-lib/arm-linux/2.6.0/include/stdarg.h \
+  /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/head.h \
+  /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \
+  /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \
+  /usr/src/linux/include/asm/types.h /usr/src/linux/include/linux/dirent.h \
+  /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/net.h \
+  /usr/src/linux/include/linux/socket.h \
+  /usr/src/linux/include/linux/sockios.h \
+  /usr/src/linux/include/linux/pipe_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_i.h \
+  /usr/src/linux/include/linux/ext_fs_i.h \
+  /usr/src/linux/include/linux/ext2_fs_i.h \
+  /usr/src/linux/include/linux/hpfs_fs_i.h \
+  /usr/src/linux/include/linux/msdos_fs_i.h \
+  /usr/src/linux/include/linux/umsdos_fs_i.h \
+  /usr/src/linux/include/linux/iso_fs_i.h \
+  /usr/src/linux/include/linux/nfs_fs_i.h \
+  /usr/src/linux/include/linux/nfs.h \
+  /usr/src/linux/include/linux/xia_fs_i.h \
+  /usr/src/linux/include/linux/sysv_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_sb.h \
+  /usr/src/linux/include/linux/ext_fs_sb.h \
+  /usr/src/linux/include/linux/ext2_fs_sb.h \
+  /usr/src/linux/include/linux/hpfs_fs_sb.h \
+  /usr/src/linux/include/linux/msdos_fs_sb.h \
+  /usr/src/linux/include/linux/iso_fs_sb.h \
+  /usr/src/linux/include/linux/nfs_fs_sb.h \
+  /usr/src/linux/include/linux/xia_fs_sb.h \
+  /usr/src/linux/include/linux/sysv_fs_sb.h \
+  /usr/src/linux/include/linux/time.h /usr/src/linux/include/asm/bitops.h \
+  /usr/src/linux/include/linux/string.h /usr/src/linux/include/asm/string.h \
+  /usr/src/linux/include/linux/param.h \
+  /usr/src/linux/include/linux/resource.h \
+  /usr/src/linux/include/linux/vm86.h \
+  /usr/src/linux/include/linux/math_emu.h \
+  /usr/src/linux/include/asm/processor.h \
+  /usr/src/linux/include/linux/timer.h \
+  /usr/src/linux/include/linux/kernel_stat.h \
+  /usr/src/linux/include/linux/fdreg.h /usr/src/linux/include/linux/errno.h \
+  /usr/src/linux/include/linux/delay.h /usr/src/linux/include/asm/delay.h \
+  /usr/src/linux/include/linux/interrupt.h \
+  /usr/src/linux/include/linux/tqueue.h /usr/src/linux/include/linux/mm.h \
+  /usr/src/linux/include/asm/page.h /usr/src/linux/include/asm/io.h \
+  /usr/src/linux/include/asm/segment.h /usr/src/linux/include/asm/pgtable.h \
+  /usr/src/linux/include/linux/timex.h
+signal.o: signal.c /usr/src/linux/include/linux/sched.h \
+  /usr/src/linux/include/asm/param.h /usr/src/linux/include/linux/binfmts.h \
+  /usr/src/linux/include/linux/ptrace.h /usr/src/linux/include/asm/ptrace.h \
+  /usr/src/linux/include/linux/personality.h \
+  /usr/src/linux/include/linux/linkage.h \
+  /usr/src/linux/include/linux/tasks.h \
+  /usr/src/linux/include/linux/kernel.h \
+  /usr/lib/gcc-lib/arm-linux/2.6.0/include/stdarg.h \
+  /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/head.h \
+  /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \
+  /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \
+  /usr/src/linux/include/asm/types.h /usr/src/linux/include/linux/dirent.h \
+  /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/net.h \
+  /usr/src/linux/include/linux/socket.h \
+  /usr/src/linux/include/linux/sockios.h \
+  /usr/src/linux/include/linux/pipe_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_i.h \
+  /usr/src/linux/include/linux/ext_fs_i.h \
+  /usr/src/linux/include/linux/ext2_fs_i.h \
+  /usr/src/linux/include/linux/hpfs_fs_i.h \
+  /usr/src/linux/include/linux/msdos_fs_i.h \
+  /usr/src/linux/include/linux/umsdos_fs_i.h \
+  /usr/src/linux/include/linux/iso_fs_i.h \
+  /usr/src/linux/include/linux/nfs_fs_i.h \
+  /usr/src/linux/include/linux/nfs.h \
+  /usr/src/linux/include/linux/xia_fs_i.h \
+  /usr/src/linux/include/linux/sysv_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_sb.h \
+  /usr/src/linux/include/linux/ext_fs_sb.h \
+  /usr/src/linux/include/linux/ext2_fs_sb.h \
+  /usr/src/linux/include/linux/hpfs_fs_sb.h \
+  /usr/src/linux/include/linux/msdos_fs_sb.h \
+  /usr/src/linux/include/linux/iso_fs_sb.h \
+  /usr/src/linux/include/linux/nfs_fs_sb.h \
+  /usr/src/linux/include/linux/xia_fs_sb.h \
+  /usr/src/linux/include/linux/sysv_fs_sb.h \
+  /usr/src/linux/include/linux/signal.h /usr/src/linux/include/asm/signal.h \
+  /usr/src/linux/include/linux/time.h /usr/src/linux/include/asm/bitops.h \
+  /usr/src/linux/include/linux/string.h /usr/src/linux/include/asm/string.h \
+  /usr/src/linux/include/linux/param.h \
+  /usr/src/linux/include/linux/resource.h \
+  /usr/src/linux/include/linux/vm86.h \
+  /usr/src/linux/include/linux/math_emu.h \
+  /usr/src/linux/include/asm/processor.h \
+  /usr/src/linux/include/linux/errno.h \
+  /usr/src/linux/include/linux/unistd.h /usr/src/linux/include/asm/unistd.h \
+  /usr/src/linux/include/linux/mm.h /usr/src/linux/include/asm/page.h \
+  /usr/src/linux/include/asm/segment.h
+softirq.o: softirq.c /usr/src/linux/include/linux/ptrace.h \
+  /usr/src/linux/include/asm/ptrace.h /usr/src/linux/include/linux/errno.h \
+  /usr/src/linux/include/linux/kernel_stat.h \
+  /usr/src/linux/include/linux/signal.h /usr/src/linux/include/asm/signal.h \
+  /usr/src/linux/include/linux/sched.h /usr/src/linux/include/asm/param.h \
+  /usr/src/linux/include/linux/binfmts.h \
+  /usr/src/linux/include/linux/personality.h \
+  /usr/src/linux/include/linux/linkage.h \
+  /usr/src/linux/include/linux/tasks.h \
+  /usr/src/linux/include/linux/kernel.h \
+  /usr/lib/gcc-lib/arm-linux/2.6.0/include/stdarg.h \
+  /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/head.h \
+  /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \
+  /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \
+  /usr/src/linux/include/asm/types.h /usr/src/linux/include/linux/dirent.h \
+  /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/net.h \
+  /usr/src/linux/include/linux/socket.h \
+  /usr/src/linux/include/linux/sockios.h \
+  /usr/src/linux/include/linux/pipe_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_i.h \
+  /usr/src/linux/include/linux/ext_fs_i.h \
+  /usr/src/linux/include/linux/ext2_fs_i.h \
+  /usr/src/linux/include/linux/hpfs_fs_i.h \
+  /usr/src/linux/include/linux/msdos_fs_i.h \
+  /usr/src/linux/include/linux/umsdos_fs_i.h \
+  /usr/src/linux/include/linux/iso_fs_i.h \
+  /usr/src/linux/include/linux/nfs_fs_i.h \
+  /usr/src/linux/include/linux/nfs.h \
+  /usr/src/linux/include/linux/xia_fs_i.h \
+  /usr/src/linux/include/linux/sysv_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_sb.h \
+  /usr/src/linux/include/linux/ext_fs_sb.h \
+  /usr/src/linux/include/linux/ext2_fs_sb.h \
+  /usr/src/linux/include/linux/hpfs_fs_sb.h \
+  /usr/src/linux/include/linux/msdos_fs_sb.h \
+  /usr/src/linux/include/linux/iso_fs_sb.h \
+  /usr/src/linux/include/linux/nfs_fs_sb.h \
+  /usr/src/linux/include/linux/xia_fs_sb.h \
+  /usr/src/linux/include/linux/sysv_fs_sb.h \
+  /usr/src/linux/include/linux/time.h /usr/src/linux/include/asm/bitops.h \
+  /usr/src/linux/include/linux/string.h /usr/src/linux/include/asm/string.h \
+  /usr/src/linux/include/linux/param.h \
+  /usr/src/linux/include/linux/resource.h \
+  /usr/src/linux/include/linux/vm86.h \
+  /usr/src/linux/include/linux/math_emu.h \
+  /usr/src/linux/include/asm/processor.h \
+  /usr/src/linux/include/linux/interrupt.h \
+  /usr/src/linux/include/linux/mm.h /usr/src/linux/include/asm/page.h \
+  /usr/src/linux/include/asm/io.h /usr/src/linux/include/asm/irq.h \
+  /usr/src/linux/include/linux/tqueue.h
+sys.o: sys.c /usr/src/linux/include/linux/errno.h \
+  /usr/src/linux/include/linux/sched.h /usr/src/linux/include/asm/param.h \
+  /usr/src/linux/include/linux/binfmts.h \
+  /usr/src/linux/include/linux/ptrace.h /usr/src/linux/include/asm/ptrace.h \
+  /usr/src/linux/include/linux/personality.h \
+  /usr/src/linux/include/linux/linkage.h \
+  /usr/src/linux/include/linux/tasks.h \
+  /usr/src/linux/include/linux/kernel.h \
+  /usr/lib/gcc-lib/arm-linux/2.6.0/include/stdarg.h \
+  /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/head.h \
+  /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \
+  /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \
+  /usr/src/linux/include/asm/types.h /usr/src/linux/include/linux/dirent.h \
+  /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/net.h \
+  /usr/src/linux/include/linux/socket.h \
+  /usr/src/linux/include/linux/sockios.h \
+  /usr/src/linux/include/linux/pipe_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_i.h \
+  /usr/src/linux/include/linux/ext_fs_i.h \
+  /usr/src/linux/include/linux/ext2_fs_i.h \
+  /usr/src/linux/include/linux/hpfs_fs_i.h \
+  /usr/src/linux/include/linux/msdos_fs_i.h \
+  /usr/src/linux/include/linux/umsdos_fs_i.h \
+  /usr/src/linux/include/linux/iso_fs_i.h \
+  /usr/src/linux/include/linux/nfs_fs_i.h \
+  /usr/src/linux/include/linux/nfs.h \
+  /usr/src/linux/include/linux/xia_fs_i.h \
+  /usr/src/linux/include/linux/sysv_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_sb.h \
+  /usr/src/linux/include/linux/ext_fs_sb.h \
+  /usr/src/linux/include/linux/ext2_fs_sb.h \
+  /usr/src/linux/include/linux/hpfs_fs_sb.h \
+  /usr/src/linux/include/linux/msdos_fs_sb.h \
+  /usr/src/linux/include/linux/iso_fs_sb.h \
+  /usr/src/linux/include/linux/nfs_fs_sb.h \
+  /usr/src/linux/include/linux/xia_fs_sb.h \
+  /usr/src/linux/include/linux/sysv_fs_sb.h \
+  /usr/src/linux/include/linux/signal.h /usr/src/linux/include/asm/signal.h \
+  /usr/src/linux/include/linux/time.h /usr/src/linux/include/asm/bitops.h \
+  /usr/src/linux/include/linux/string.h /usr/src/linux/include/asm/string.h \
+  /usr/src/linux/include/linux/param.h \
+  /usr/src/linux/include/linux/resource.h \
+  /usr/src/linux/include/linux/vm86.h \
+  /usr/src/linux/include/linux/math_emu.h \
+  /usr/src/linux/include/asm/processor.h \
+  /usr/src/linux/include/linux/times.h \
+  /usr/src/linux/include/linux/utsname.h \
+  /usr/src/linux/include/linux/stat.h /usr/src/linux/include/linux/mman.h \
+  /usr/src/linux/include/linux/mm.h /usr/src/linux/include/asm/page.h \
+  /usr/src/linux/include/asm/segment.h /usr/src/linux/include/asm/io.h
+time.o: time.c /usr/src/linux/include/linux/errno.h \
+  /usr/src/linux/include/linux/sched.h /usr/src/linux/include/asm/param.h \
+  /usr/src/linux/include/linux/binfmts.h \
+  /usr/src/linux/include/linux/ptrace.h /usr/src/linux/include/asm/ptrace.h \
+  /usr/src/linux/include/linux/personality.h \
+  /usr/src/linux/include/linux/linkage.h \
+  /usr/src/linux/include/linux/tasks.h \
+  /usr/src/linux/include/linux/kernel.h \
+  /usr/lib/gcc-lib/arm-linux/2.6.0/include/stdarg.h \
+  /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/head.h \
+  /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \
+  /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \
+  /usr/src/linux/include/asm/types.h /usr/src/linux/include/linux/dirent.h \
+  /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/net.h \
+  /usr/src/linux/include/linux/socket.h \
+  /usr/src/linux/include/linux/sockios.h \
+  /usr/src/linux/include/linux/pipe_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_i.h \
+  /usr/src/linux/include/linux/ext_fs_i.h \
+  /usr/src/linux/include/linux/ext2_fs_i.h \
+  /usr/src/linux/include/linux/hpfs_fs_i.h \
+  /usr/src/linux/include/linux/msdos_fs_i.h \
+  /usr/src/linux/include/linux/umsdos_fs_i.h \
+  /usr/src/linux/include/linux/iso_fs_i.h \
+  /usr/src/linux/include/linux/nfs_fs_i.h \
+  /usr/src/linux/include/linux/nfs.h \
+  /usr/src/linux/include/linux/xia_fs_i.h \
+  /usr/src/linux/include/linux/sysv_fs_i.h \
+  /usr/src/linux/include/linux/minix_fs_sb.h \
+  /usr/src/linux/include/linux/ext_fs_sb.h \
+  /usr/src/linux/include/linux/ext2_fs_sb.h \
+  /usr/src/linux/include/linux/hpfs_fs_sb.h \
+  /usr/src/linux/include/linux/msdos_fs_sb.h \
+  /usr/src/linux/include/linux/iso_fs_sb.h \
+  /usr/src/linux/include/linux/nfs_fs_sb.h \
+  /usr/src/linux/include/linux/xia_fs_sb.h \
+  /usr/src/linux/include/linux/sysv_fs_sb.h \
+  /usr/src/linux/include/linux/signal.h /usr/src/linux/include/asm/signal.h \
+  /usr/src/linux/include/linux/time.h /usr/src/linux/include/asm/bitops.h \
+  /usr/src/linux/include/linux/string.h /usr/src/linux/include/asm/string.h \
+  /usr/src/linux/include/linux/param.h \
+  /usr/src/linux/include/linux/resource.h \
+  /usr/src/linux/include/linux/vm86.h \
+  /usr/src/linux/include/linux/math_emu.h \
+  /usr/src/linux/include/asm/processor.h /usr/src/linux/include/linux/mm.h \
+  /usr/src/linux/include/asm/page.h /usr/src/linux/include/asm/segment.h \
+  /usr/src/linux/include/asm/io.h \
+  /usr/src/linux/include/linux/mc146818rtc.h \
+  /usr/src/linux/include/linux/timex.h
diff -r -u -N linux.orig/kernel/Makefile linux.arm/kernel/Makefile
--- linux.orig/kernel/Makefile	Fri Oct 27 23:09:08 1995
+++ linux.arm/kernel/Makefile	Fri Oct 27 23:10:18 1995
@@ -28,6 +28,7 @@
 
 kernel.o: $(SYMTAB_OBJS) $(OBJS)
 	$(LD) -r -o kernel.o $(SYMTAB_OBJS) $(OBJS)
+#	stripper kernel.o kernel2.o
 	sync
 
 sched.o: sched.c
diff -r -u -N linux.orig/kernel/exit.c linux.arm/kernel/exit.c
--- linux.orig/kernel/exit.c	Fri Oct 27 23:09:08 1995
+++ linux.arm/kernel/exit.c	Fri Oct 27 23:10:18 1995
@@ -4,6 +4,11 @@
  *  Copyright (C) 1991, 1992  Linus Torvalds
  */
 
+/*
+ * 08/07/1995	Russell King
+ *	For the arm, altered page free/alloc to kfree/kmalloc to reduce memory wastage.
+ */
+
 #define DEBUG_PROC_TREE
 
 #include <linux/wait.h>
@@ -96,8 +101,13 @@
 			REMOVE_LINKS(p);
 			if (STACK_MAGIC != *(unsigned long *)p->kernel_stack_page)
 				printk(KERN_ALERT "release: %s kernel stack corruption. Aiee\n", p->comm);
+#ifndef __arm__
 			free_page(p->kernel_stack_page);
 			free_page((long) p);
+#else
+			kfree((void *) p->kernel_stack_page);
+			kfree((void *) p);
+#endif
 			return;
 		}
 	panic("trying to release non-existent task");
diff -r -u -N linux.orig/kernel/fork.c linux.arm/kernel/fork.c
--- linux.orig/kernel/fork.c	Fri Oct 27 23:09:08 1995
+++ linux.arm/kernel/fork.c	Fri Oct 27 23:10:19 1995
@@ -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 free/alloc to kfree/kmalloc to reduce memory wastage.
+ */
+
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -167,12 +172,22 @@
 int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs)
 {
 	int nr;
+#ifndef __arm__
 	unsigned long new_stack;
+#else
+	unsigned long new_stack = 0;
+#endif
 	struct task_struct *p;
-
+	
+#ifndef __arm__
 	if(!(p = (struct task_struct*)__get_free_page(GFP_KERNEL)))
 		goto bad_fork;
 	new_stack = get_free_page(GFP_KERNEL);
+#else
+	if(!(p = (struct task_struct*)kmalloc(sizeof(struct task_struct), GFP_KERNEL)))
+		goto bad_fork;
+	new_stack = kmalloc(4096, GFP_KERNEL);
+#endif
 	if (!new_stack)
 		goto bad_fork_free;
 	nr = find_empty_process();
@@ -226,8 +241,14 @@
 	REMOVE_LINKS(p);
 	nr_tasks--;
 bad_fork_free:
+#ifndef __arm__
 	free_page(new_stack);
 	free_page((long) p);
+#else
+	if(new_stack)
+		kfree((void *) new_stack);
+	kfree(p);
+#endif
 bad_fork:
 	return -EAGAIN;
 }
diff -r -u -N linux.orig/kernel/ksyms.c linux.arm/kernel/ksyms.c
--- linux.orig/kernel/ksyms.c	Fri Oct 27 23:09:08 1995
+++ linux.arm/kernel/ksyms.c	Fri Oct 27 23:10:20 1995
@@ -7,6 +7,7 @@
  * - External symbol table support added (December 1994)
  * - Versions on symbols added (December 1994)
  * by Bjorn Ekwall <bj0rn@blox.se>
+ * 08-07-1995	Russell King	Added extra symbols for arm modules
  */
 
 #include <linux/autoconf.h>
@@ -71,6 +72,33 @@
 #include "../drivers/scsi/constants.h"
 #endif
 
+#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 *from, 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);
+__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\", 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");
+extern void fp_printk(void);
+extern struct task_struct *fp_current;
+extern void fp_send_sig(int);
+#endif
+
 extern int sys_tz;
 extern int request_dma(unsigned int dmanr, char * deviceID);
 extern void free_dma(unsigned int dmanr);
@@ -383,6 +411,78 @@
 	X(msdos_unlink_umsdos),
 	X(msdos_write_inode),
 #endif
+
+#ifdef __arm__
+#define M(x) { (void *) & x, "_" # x }
+	/* fp emulator - these must not be versionised for backwards compatibility */
+	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 */
+	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 */
+	M(__inw),
+	M(__inb),
+	M(__outw),
+	M(__outb),
+	X(outswb),
+	X(inswb),
+
+	/* bits */
+	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
+	/* fs */
+	X(invalidate_inodes),
+	X(resetup_one_dev),
+#endif
+
 	/********************************************************
 	 * Do not add anything below this line,
 	 * as the stacked modules depend on this!
diff -r -u -N linux.orig/kernel/ksyms.ver linux.arm/kernel/ksyms.ver
--- linux.orig/kernel/ksyms.ver	Fri Oct 27 23:09:11 1995
+++ linux.arm/kernel/ksyms.ver	Fri Oct 27 23:10:22 1995
@@ -4,15 +4,13 @@
 #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_mmap	_set_ver(do_mmap, 2eadc92b)
 #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 insert_vm_struct	_set_ver(insert_vm_struct, 8d8644d4)
+#define merge_segments	_set_ver(merge_segments, 3fb63ac6)
 #define __get_free_pages	_set_ver(__get_free_pages, 5243d78b)
 #define free_pages	_set_ver(free_pages, 96448859)
 #define kmalloc	_set_ver(kmalloc, d31fb2cb)
@@ -21,62 +19,62 @@
 #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 __iget	_set_ver(__iget, 5e974ec6)
+#define iput	_set_ver(iput, a55d8fb5)
+#define namei	_set_ver(namei, 652eb806)
+#define lnamei	_set_ver(lnamei, 17e0909c)
+#define open_namei	_set_ver(open_namei, 68815d38)
+#define close_fp	_set_ver(close_fp, 5c3362b3)
 #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 permission	_set_ver(permission, e1795b74)
+#define inode_setattr	_set_ver(inode_setattr, 051eebb5)
+#define inode_change_ok	_set_ver(inode_change_ok, 172606f2)
+#define generic_mmap	_set_ver(generic_mmap, ee3ff7f9)
 #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 getblk	_set_ver(getblk, 2abcaad3)
+#define bread	_set_ver(bread, ada2f968)
+#define breada	_set_ver(breada, 73d640da)
+#define brelse	_set_ver(brelse, 104025bc)
+#define ll_rw_block	_set_ver(ll_rw_block, 207f5903)
+#define __wait_on_buffer	_set_ver(__wait_on_buffer, a2174488)
+#define dcache_lookup	_set_ver(dcache_lookup, 8a1a392a)
+#define dcache_add	_set_ver(dcache_add, b7748358)
+#define register_chrdev	_set_ver(register_chrdev, 31fd4bc4)
 #define unregister_chrdev	_set_ver(unregister_chrdev, 61ea5ee8)
-#define register_blkdev	_set_ver(register_blkdev, 4699a621)
+#define register_blkdev	_set_ver(register_blkdev, c1d542c9)
 #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_register_driver	_set_ver(tty_register_driver, 42e2fe51)
+#define tty_unregister_driver	_set_ver(tty_unregister_driver, 964ac99e)
 #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 block_read	_set_ver(block_read, f65ebf63)
+#define block_write	_set_ver(block_write, 084ab6ac)
+#define block_fsync	_set_ver(block_fsync, 760f6488)
+#define wait_for_request	_set_ver(wait_for_request, 4560f3af)
 #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 blk_dev	_set_ver(blk_dev, d38611b7)
 #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 bmap	_set_ver(bmap, f3adc9b7)
 #define sync_dev	_set_ver(sync_dev, 9bca536d)
-#define get_blkfops	_set_ver(get_blkfops, 83827791)
+#define get_blkfops	_set_ver(get_blkfops, c05709ef)
 #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, 0aeac7e4)
+#define tty_wait_until_sent	_set_ver(tty_wait_until_sent, da08c6d0)
+#define tty_check_change	_set_ver(tty_check_change, da32f203)
+#define tty_hung_up_p	_set_ver(tty_hung_up_p, 9629ffb6)
+#define register_filesystem	_set_ver(register_filesystem, 6fc4dd55)
+#define unregister_filesystem	_set_ver(unregister_filesystem, 9930136b)
+#define register_binfmt	_set_ver(register_binfmt, 5e145849)
+#define unregister_binfmt	_set_ver(unregister_binfmt, 98a2fa16)
+#define lookup_exec_domain	_set_ver(lookup_exec_domain, ebad6f7c)
+#define register_exec_domain	_set_ver(register_exec_domain, 5d37daa8)
+#define unregister_exec_domain	_set_ver(unregister_exec_domain, 61e67c43)
+#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)
@@ -92,17 +90,15 @@
 #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 wake_up	_set_ver(wake_up, fffabd24)
+#define wake_up_interruptible	_set_ver(wake_up_interruptible, 2e80f1cd)
+#define sleep_on	_set_ver(sleep_on, c37bd103)
+#define interruptible_sleep_on	_set_ver(interruptible_sleep_on, b9c50ea5)
 #define schedule	_set_ver(schedule, 01000e51)
-#define current	_set_ver(current, fc1cb29b)
+#define current	_set_ver(current, 86896bc3)
 #define jiffies	_set_ver(jiffies, 2f7c7437)
 #define xtime	_set_ver(xtime, e70c0be0)
 #define loops_per_sec	_set_ver(loops_per_sec, 40a14192)
@@ -117,23 +113,27 @@
 #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 do_signal	_set_ver(do_signal, 94f9d4de)
+#define send_sig	_set_ver(send_sig, 7138660e)
 #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 do_execve	_set_ver(do_execve, 945e29fb)
+#define flush_old_exec	_set_ver(flush_old_exec, d0949913)
+#define open_inode	_set_ver(open_inode, 995189bc)
+#define read_exec	_set_ver(read_exec, 29d8dd8c)
 #define si_meminfo	_set_ver(si_meminfo, bb05fc9a)
-#define sock_register	_set_ver(sock_register, d68e1649)
+#define sock_register	_set_ver(sock_register, d0688e2b)
 #define sock_unregister	_set_ver(sock_unregister, 72c332bd)
 #define inet_add_protocol	_set_ver(inet_add_protocol, ff15a03c)
 #define inet_del_protocol	_set_ver(inet_del_protocol, a9c5e9c0)
+#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 ftape_big_buffer	_set_ver(ftape_big_buffer, e349dc35)
 #define floppy_track_buffer	_set_ver(floppy_track_buffer, c6e3f7c2)
 #define register_netdev	_set_ver(register_netdev, 298819f5)
 #define unregister_netdev	_set_ver(unregister_netdev, f1aa84e4)
@@ -153,45 +153,67 @@
 #define dev_base	_set_ver(dev_base, 1d36a3cf)
 #define dev_close	_set_ver(dev_close, 98484140)
 #define arp_find	_set_ver(arp_find, d909c1c9)
-#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 set_writetime	_set_ver(set_writetime, 52131916)
+#define n_tty_ioctl	_set_ver(n_tty_ioctl, 567c6ba8)
+#define tty_register_ldisc	_set_ver(tty_register_ldisc, bb8a6111)
+#define kill_fasync	_set_ver(kill_fasync, c4a7c6b4)
+#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, 58566e5f)
+#define scsi_unregister	_set_ver(scsi_unregister, 77b294c3)
+#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 print_msg	_set_ver(print_msg, 0465f877)
+#define print_status	_set_ver(print_status, 32f84646)
+#define set_writetime	_set_ver(set_writetime, c7c904a1)
 #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, 71e8ed15)
+#define file_fsync	_set_ver(file_fsync, a4cbd91f)
+#define clear_inode	_set_ver(clear_inode, b172a030)
+#define refile_buffer	_set_ver(refile_buffer, 0427671e)
 #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, 42e0123a)
+#define super_blocks	_set_ver(super_blocks, dc59779f)
+#define chrdev_inode_operations	_set_ver(chrdev_inode_operations, 7d6b5e19)
+#define blkdev_inode_operations	_set_ver(blkdev_inode_operations, fb8e922c)
 #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, 8662d88b)
+#define get_empty_inode	_set_ver(get_empty_inode, 8a9df217)
+#define insert_inode_hash	_set_ver(insert_inode_hash, dfb34ea8)
 #define event	_set_ver(event, a6aac9c1)
-#define __down	_set_ver(__down, 75aa9e96)
-#define msdos_bmap	_set_ver(msdos_bmap, 8e11de93)
-#define msdos_create	_set_ver(msdos_create, ace9cdf9)
-#define msdos_file_read	_set_ver(msdos_file_read, cf8f73a7)
-#define msdos_file_write	_set_ver(msdos_file_write, c49052dc)
-#define msdos_lookup	_set_ver(msdos_lookup, 03996bbb)
-#define msdos_mkdir	_set_ver(msdos_mkdir, 0fa632e4)
-#define msdos_mmap	_set_ver(msdos_mmap, 7830ad51)
-#define msdos_put_inode	_set_ver(msdos_put_inode, 2318c406)
-#define msdos_put_super	_set_ver(msdos_put_super, a7cb6559)
-#define msdos_read_inode	_set_ver(msdos_read_inode, 9feaaa1d)
-#define msdos_read_super	_set_ver(msdos_read_super, 2d0bdce0)
-#define msdos_readdir	_set_ver(msdos_readdir, ed56af5d)
-#define msdos_rename	_set_ver(msdos_rename, bba2b188)
-#define msdos_rmdir	_set_ver(msdos_rmdir, 284d1911)
-#define msdos_smap	_set_ver(msdos_smap, 478aae23)
-#define msdos_statfs	_set_ver(msdos_statfs, 55cfd862)
-#define msdos_truncate	_set_ver(msdos_truncate, fdb46445)
-#define msdos_unlink	_set_ver(msdos_unlink, b484f1d7)
-#define msdos_unlink_umsdos	_set_ver(msdos_unlink_umsdos, da666045)
-#define msdos_write_inode	_set_ver(msdos_write_inode, 95b18673)
+#define __down	_set_ver(__down, 2e6812f6)
+#define msdos_bmap	_set_ver(msdos_bmap, 48c61fd6)
+#define msdos_create	_set_ver(msdos_create, 829e7546)
+#define msdos_file_read	_set_ver(msdos_file_read, f2847308)
+#define msdos_file_write	_set_ver(msdos_file_write, d22ed4b0)
+#define msdos_lookup	_set_ver(msdos_lookup, 7bae2d1e)
+#define msdos_mkdir	_set_ver(msdos_mkdir, bd09eb9d)
+#define msdos_mmap	_set_ver(msdos_mmap, 2c99e2bc)
+#define msdos_put_inode	_set_ver(msdos_put_inode, 9a3ce4f9)
+#define msdos_put_super	_set_ver(msdos_put_super, ec99bee1)
+#define msdos_read_inode	_set_ver(msdos_read_inode, 279c7b49)
+#define msdos_read_super	_set_ver(msdos_read_super, 0dfab796)
+#define msdos_readdir	_set_ver(msdos_readdir, 0da8f5d5)
+#define msdos_rename	_set_ver(msdos_rename, 666da51c)
+#define msdos_rmdir	_set_ver(msdos_rmdir, 2e10355e)
+#define msdos_smap	_set_ver(msdos_smap, dc469d79)
+#define msdos_statfs	_set_ver(msdos_statfs, 9af053ee)
+#define msdos_truncate	_set_ver(msdos_truncate, 615fae88)
+#define msdos_unlink	_set_ver(msdos_unlink, 37272b1e)
+#define msdos_unlink_umsdos	_set_ver(msdos_unlink_umsdos, 60f42665)
+#define msdos_write_inode	_set_ver(msdos_write_inode, 223b80ff)
+#define outswb	_set_ver(outswb, 67d59274)
+#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, d6ca9c02)
+#define print_msg	_set_ver(print_msg, 0465f877)
+#define invalidate_inodes	_set_ver(invalidate_inodes, 5f9574bd)
+#define resetup_one_dev	_set_ver(resetup_one_dev, 95afb007)
 #endif /* _KSYMS_VER_ */
 #endif /* CONFIG_MODVERSIONS !__GENKSYMS__ */
diff -r -u -N linux.orig/kernel/module.c linux.arm/kernel/module.c
--- linux.orig/kernel/module.c	Fri Oct 27 23:09:08 1995
+++ linux.arm/kernel/module.c	Fri Oct 27 23:10:20 1995
@@ -180,11 +180,19 @@
 /*
  * Initialize a module.
  */
+#ifndef __arm__
 asmlinkage int
 sys_init_module(char *module_name, char *code, unsigned codesize,
 		struct mod_routines *routines,
 		struct symbol_table *symtab)
 {
+#else
+asmlinkage int
+sys_init_module(char *module_name, char *code, unsigned codesize, unsigned long *__p)
+{
+	struct mod_routines *routines = (struct mod_routines *)__p[0];
+	struct symbol_table *symtab = (struct symbol_table *)__p[1];
+#endif
 	struct module *mp;
 	struct symbol_table *newtab;
 	char name[MOD_MAX_NAME];
diff -r -u -N linux.orig/kernel/sched.c linux.arm/kernel/sched.c
--- linux.orig/kernel/sched.c	Fri Oct 27 23:09:09 1995
+++ linux.arm/kernel/sched.c	Fri Oct 27 23:10:18 1995
@@ -31,8 +31,11 @@
 #include <asm/io.h>
 #include <asm/segment.h>
 #include <asm/pgtable.h>
+#include <asm/irq-no.h>
 
+#ifndef TIMER_IRQ
 #define TIMER_IRQ 0
+#endif
 
 #include <linux/timex.h>
 
@@ -213,7 +216,12 @@
 {
 	struct wait_queue *tmp;
 	struct task_struct * p;
-
+#ifdef __arm__
+void *ret; __asm__("mov %0, lr\n\t": "=r" (ret));
+if ((int)q & 0xfc000003) printk("wake_up: q = %p (ret=%p)\n", q, ret);
+if ((int)*q & 0xfc000003) printk("wake_up: *q = %p (ret=%p)\n", *q, ret);
+#endif
+	
 	if (!q || !(tmp = *q))
 		return;
 	do {
@@ -316,11 +324,19 @@
 {
 	unsigned long flags;
 	struct timer_list *p;
+#ifdef __arm__
+	void *bir;
+	__asm__ ("mov\t%0, lr\n\t": "=r" (bir)::"lr");
+#endif
 
 #if SLOW_BUT_DEBUGGING_TIMERS
 	if (timer->next || timer->prev) {
 		printk("add_timer() called with non-zero list from %p\n",
+#ifndef __arm__
 			__builtin_return_address(0));
+#else
+			bir);
+#endif
 		return;
 	}
 #endif
@@ -331,11 +347,43 @@
 	do {
 		p = p->next;
 	} while (timer->expires > p->expires);
+#ifdef __arm__
+	if ((int)p & 3 || (int)p < 0x01800000)
+	{
+		printk ("add_timer(): p = %p\n", p);
+		goto bad_timer;
+	}
+#endif
 	timer->next = p;
+#ifdef __arm__
+	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);
+#ifdef __arm__
+	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) ");
+			op = p;
+		} while ( (p = p->next) != &timer_head);
+	}
+	panic ("\nwhile adding %p from %p", timer, bir);
+#endif
 }
 
 int del_timer(struct timer_list * timer)
@@ -343,6 +391,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)::"lr");
+#endif
 
 	p = &timer_head;
 	save_flags(flags);
@@ -359,7 +411,11 @@
 	}
 	if (timer->next || timer->prev)
 		printk("del_timer() called from %p with timer not initialized\n",
+#ifdef __arm__
 			__builtin_return_address(0));
+#else
+			bir);
+#endif
 	restore_flags(flags);
 	return 0;
 #else	
@@ -620,7 +676,12 @@
 			kstat.cpu_system++;
 #ifdef CONFIG_PROFILE
 		if (prof_buffer && current != task[0]) {
+#ifndef __arm__
 			unsigned long eip = regs->eip;
+#else
+			extern char stext;
+			unsigned long eip = (regs->PC & ~0xfc000003) - ((unsigned long)&stext);
+#endif
 			eip >>= CONFIG_PROFILE_SHIFT;
 			if (eip < prof_len)
 				prof_buffer[eip]++;
@@ -745,6 +806,13 @@
 		printk(" current  ");
 	else
 		printk(" %08lX ", ((unsigned long *)p->tss.esp)[3]);
+#elif defined(__arm__)
+	if (p == current)
+		printk(" current  ");
+	else
+		printk(" %08lX ", ((unsigned long *)p->kernel_stack_page)[1022]);
+#else
+	printk(" unknown  ");
 #endif
 	for (free = 1; free < 1024 ; free++) {
 		if (((unsigned long *)p->kernel_stack_page)[free])
diff -r -u -N linux.orig/kernel/sched.c.old linux.arm/kernel/sched.c.old
--- linux.orig/kernel/sched.c.old	Thu Jan  1 01:00:00 1970
+++ linux.arm/kernel/sched.c.old	Fri Oct 27 23:10:26 1995
@@ -0,0 +1,786 @@
+/*
+ *  linux/kernel/sched.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ * 'sched.c' is the main kernel file. It contains scheduling primitives
+ * (sleep_on, wakeup, schedule etc) as well as a number of simple system
+ * call functions (type getpid(), which just extracts a field from
+ * current-task
+ */
+
+#include <linux/config.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/fdreg.h>
+#include <linux/errno.h>
+#include <linux/time.h>
+#include <linux/ptrace.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/tqueue.h>
+#include <linux/resource.h>
+#include <linux/mm.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/pgtable.h>
+#include <asm/irq-no.h>
+
+#ifndef TIMER_IRQ
+#define TIMER_IRQ 0
+#endif
+
+#include <linux/timex.h>
+
+/*
+ * kernel variables
+ */
+long tick = 1000000 / HZ;               /* timer interrupt period */
+volatile struct timeval xtime;		/* The current time */
+int tickadj = 500/HZ;			/* microsecs */
+
+DECLARE_TASK_QUEUE(tq_timer);
+DECLARE_TASK_QUEUE(tq_immediate);
+
+/*
+ * phase-lock loop variables
+ */
+int time_status = TIME_BAD;     /* clock synchronization status */
+long time_offset = 0;           /* time adjustment (us) */
+long time_constant = 0;         /* pll time constant */
+long time_tolerance = MAXFREQ;  /* frequency tolerance (ppm) */
+long time_precision = 1; 	/* clock precision (us) */
+long time_maxerror = 0x70000000;/* maximum error */
+long time_esterror = 0x70000000;/* estimated error */
+long time_phase = 0;            /* phase offset (scaled us) */
+long time_freq = 0;             /* frequency offset (scaled ppm) */
+long time_adj = 0;              /* tick adjust (scaled 1 / HZ) */
+long time_reftime = 0;          /* time at last adjustment (s) */
+
+long time_adjust = 0;
+long time_adjust_step = 0;
+
+int need_resched = 0;
+unsigned long event = 0;
+
+extern int _setitimer(int, struct itimerval *, struct itimerval *);
+unsigned long * prof_buffer = NULL;
+unsigned long prof_len = 0;
+
+#define _S(nr) (1<<((nr)-1))
+
+extern void mem_use(void);
+
+extern int timer_interrupt(void);
+ 
+static unsigned long init_kernel_stack[1024] = { STACK_MAGIC, };
+unsigned long init_user_stack[1024] = { STACK_MAGIC, };
+static struct vm_area_struct init_mmap = INIT_MMAP;
+struct task_struct init_task = INIT_TASK;
+
+unsigned long volatile jiffies=0;
+
+struct task_struct *current = &init_task;
+struct task_struct *last_task_used_math = NULL;
+
+struct task_struct * task[NR_TASKS] = {&init_task, };
+
+struct kernel_stat kstat = { 0 };
+
+unsigned long itimer_ticks = 0;
+unsigned long itimer_next = ~0;
+
+/*
+ *  'schedule()' is the scheduler function. It's a very simple and nice
+ * scheduler: it's not perfect, but certainly works for most things.
+ * The one thing you might take a look at is the signal-handler code here.
+ *
+ *   NOTE!!  Task 0 is the 'idle' task, which gets called when no other
+ * tasks can run. It can not be killed, and it cannot sleep. The 'state'
+ * information in task[0] is never used.
+ *
+ * The "confuse_gcc" goto is used only to get better assembly code..
+ * Dijkstra probably hates me.
+ */
+asmlinkage void schedule(void)
+{
+	int c;
+	struct task_struct * p;
+	struct task_struct * next;
+	unsigned long ticks;
+
+/* check alarm, wake up any interruptible tasks that have got a signal */
+
+	if (intr_count) {
+		printk("Aiee: scheduling in interrupt\n");
+		intr_count = 0;
+	}
+	cli();
+	ticks = itimer_ticks;
+	itimer_ticks = 0;
+	itimer_next = ~0;
+	sti();
+	need_resched = 0;
+	p = &init_task;
+	for (;;) {
+		if ((p = p->next_task) == &init_task)
+			goto confuse_gcc1;
+		if (ticks && p->it_real_value) {
+			if (p->it_real_value <= ticks) {
+				send_sig(SIGALRM, p, 1);
+				if (!p->it_real_incr) {
+					p->it_real_value = 0;
+					goto end_itimer;
+				}
+				do {
+					p->it_real_value += p->it_real_incr;
+				} while (p->it_real_value <= ticks);
+			}
+			p->it_real_value -= ticks;
+			if (p->it_real_value < itimer_next)
+				itimer_next = p->it_real_value;
+		}
+end_itimer:
+		if (p->state != TASK_INTERRUPTIBLE)
+			continue;
+		if (p->signal & ~p->blocked) {
+			p->state = TASK_RUNNING;
+			continue;
+		}
+		if (p->timeout && p->timeout <= jiffies) {
+			p->timeout = 0;
+			p->state = TASK_RUNNING;
+		}
+	}
+confuse_gcc1:
+
+/* this is the scheduler proper: */
+#if 0
+	/* give processes that go to sleep a bit higher priority.. */
+	/* This depends on the values for TASK_XXX */
+	/* This gives smoother scheduling for some things, but */
+	/* can be very unfair under some circumstances, so.. */
+ 	if (TASK_UNINTERRUPTIBLE >= (unsigned) current->state &&
+	    current->counter < current->priority*2) {
+		++current->counter;
+	}
+#endif
+	c = -1000;
+	next = p = &init_task;
+	for (;;) {
+		if ((p = p->next_task) == &init_task)
+			goto confuse_gcc2;
+		if (p->state == TASK_RUNNING && p->counter > c)
+			c = p->counter, next = p;
+	}
+confuse_gcc2:
+	if (!c) {
+		for_each_task(p)
+			p->counter = (p->counter >> 1) + p->priority;
+	}
+	if (current == next)
+		return;
+	kstat.context_swtch++;
+	switch_to(next);
+}
+
+asmlinkage int sys_pause(void)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	schedule();
+	return -ERESTARTNOHAND;
+}
+
+/*
+ * wake_up doesn't wake up stopped processes - they have to be awakened
+ * with signals or similar.
+ *
+ * Note that this doesn't need cli-sti pairs: interrupts may not change
+ * the wait-queue structures directly, but only call wake_up() to wake
+ * a process. The process itself must remove the queue once it has woken.
+ */
+void wake_up(struct wait_queue **q)
+{
+	struct wait_queue *tmp;
+	struct task_struct * p;
+
+	if (!q || !(tmp = *q))
+		return;
+	do {
+		if ((p = tmp->task) != NULL) {
+			if ((p->state == TASK_UNINTERRUPTIBLE) ||
+			    (p->state == TASK_INTERRUPTIBLE)) {
+				p->state = TASK_RUNNING;
+				if (p->counter > current->counter + 3)
+					need_resched = 1;
+			}
+		}
+		if (!tmp->next) {
+			printk("wait_queue is bad (eip = %p)\n",
+				__builtin_return_address(0));
+			printk("        q = %p\n",q);
+			printk("       *q = %p\n",*q);
+			printk("      tmp = %p\n",tmp);
+			break;
+		}
+		tmp = tmp->next;
+	} while (tmp != *q);
+}
+
+void wake_up_interruptible(struct wait_queue **q)
+{
+	struct wait_queue *tmp;
+	struct task_struct * p;
+
+	if (!q || !(tmp = *q))
+		return;
+	do {
+		if ((p = tmp->task) != NULL) {
+			if (p->state == TASK_INTERRUPTIBLE) {
+				p->state = TASK_RUNNING;
+				if (p->counter > current->counter + 3)
+					need_resched = 1;
+			}
+		}
+		if (!tmp->next) {
+			printk("wait_queue is bad (eip = %p)\n",
+				__builtin_return_address(0));
+			printk("        q = %p\n",q);
+			printk("       *q = %p\n",*q);
+			printk("      tmp = %p\n",tmp);
+			break;
+		}
+		tmp = tmp->next;
+	} while (tmp != *q);
+}
+
+void __down(struct semaphore * sem)
+{
+	struct wait_queue wait = { current, NULL };
+	add_wait_queue(&sem->wait, &wait);
+	current->state = TASK_UNINTERRUPTIBLE;
+	while (sem->count <= 0) {
+		schedule();
+		current->state = TASK_UNINTERRUPTIBLE;
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&sem->wait, &wait);
+}
+
+static inline void __sleep_on(struct wait_queue **p, int state)
+{
+	unsigned long flags;
+	struct wait_queue wait = { current, NULL };
+
+	if (!p)
+		return;
+	if (current == task[0])
+		panic("task[0] trying to sleep");
+	current->state = state;
+	add_wait_queue(p, &wait);
+	save_flags(flags);
+	sti();
+	schedule();
+	remove_wait_queue(p, &wait);
+	restore_flags(flags);
+}
+
+void interruptible_sleep_on(struct wait_queue **p)
+{
+	__sleep_on(p,TASK_INTERRUPTIBLE);
+}
+
+void sleep_on(struct wait_queue **p)
+{
+	__sleep_on(p,TASK_UNINTERRUPTIBLE);
+}
+
+/*
+ * The head for the timer-list has a "expires" field of MAX_UINT,
+ * and the sorting routine counts on this..
+ */
+static struct timer_list timer_head = { &timer_head, &timer_head, ~0, 0, NULL };
+#define SLOW_BUT_DEBUGGING_TIMERS 1
+
+void add_timer(struct timer_list * timer)
+{
+	unsigned long flags;
+	struct timer_list *p;
+
+#if SLOW_BUT_DEBUGGING_TIMERS
+	if (timer->next || timer->prev) {
+		printk("add_timer() called with non-zero list from %p\n",
+			__builtin_return_address(0));
+		return;
+	}
+#endif
+	p = &timer_head;
+	timer->expires += jiffies;
+	save_flags(flags);
+	cli();
+	do {
+		p = p->next;
+	} while (timer->expires > p->expires);
+	timer->next = p;
+	timer->prev = p->prev;
+	p->prev = timer;
+	timer->prev->next = timer;
+	restore_flags(flags);
+}
+
+int del_timer(struct timer_list * timer)
+{
+	unsigned long flags;
+#if SLOW_BUT_DEBUGGING_TIMERS
+	struct timer_list * p;
+
+	p = &timer_head;
+	save_flags(flags);
+	cli();
+	while ((p = p->next) != &timer_head) {
+		if (p == timer) {
+			timer->next->prev = timer->prev;
+			timer->prev->next = timer->next;
+			timer->next = timer->prev = NULL;
+			restore_flags(flags);
+			timer->expires -= jiffies;
+			return 1;
+		}
+	}
+	if (timer->next || timer->prev)
+		printk("del_timer() called from %p with timer not initialized\n",
+			__builtin_return_address(0));
+	restore_flags(flags);
+	return 0;
+#else	
+	save_flags(flags);
+	cli();
+	if (timer->next) {
+		timer->next->prev = timer->prev;
+		timer->prev->next = timer->next;
+		timer->next = timer->prev = NULL;
+		restore_flags(flags);
+		timer->expires -= jiffies;
+		return 1;
+	}
+	restore_flags(flags);
+	return 0;
+#endif
+}
+
+unsigned long timer_active = 0;
+struct timer_struct timer_table[32];
+
+/*
+ * Hmm.. Changed this, as the GNU make sources (load.c) seems to
+ * imply that avenrun[] is the standard name for this kind of thing.
+ * Nothing else seems to be standardized: the fractional size etc
+ * all seem to differ on different machines.
+ */
+unsigned long avenrun[3] = { 0,0,0 };
+
+/*
+ * Nr of active tasks - counted in fixed-point numbers
+ */
+static unsigned long count_active_tasks(void)
+{
+	struct task_struct **p;
+	unsigned long nr = 0;
+
+	for(p = &LAST_TASK; p > &FIRST_TASK; --p)
+		if (*p && ((*p)->state == TASK_RUNNING ||
+			   (*p)->state == TASK_UNINTERRUPTIBLE ||
+			   (*p)->state == TASK_SWAPPING))
+			nr += FIXED_1;
+	return nr;
+}
+
+static inline void calc_load(void)
+{
+	unsigned long active_tasks; /* fixed-point */
+	static int count = LOAD_FREQ;
+
+	if (count-- > 0)
+		return;
+	count = LOAD_FREQ;
+	active_tasks = count_active_tasks();
+	CALC_LOAD(avenrun[0], EXP_1, active_tasks);
+	CALC_LOAD(avenrun[1], EXP_5, active_tasks);
+	CALC_LOAD(avenrun[2], EXP_15, active_tasks);
+}
+
+/*
+ * this routine handles the overflow of the microsecond field
+ *
+ * The tricky bits of code to handle the accurate clock support
+ * were provided by Dave Mills (Mills@UDEL.EDU) of NTP fame.
+ * They were originally developed for SUN and DEC kernels.
+ * All the kudos should go to Dave for this stuff.
+ *
+ * These were ported to Linux by Philip Gladstone.
+ */
+static void second_overflow(void)
+{
+	long ltemp;
+
+	/* Bump the maxerror field */
+	time_maxerror = (0x70000000-time_maxerror < time_tolerance) ?
+	  0x70000000 : (time_maxerror + time_tolerance);
+
+	/* Run the PLL */
+	if (time_offset < 0) {
+		ltemp = (-(time_offset+1) >> (SHIFT_KG + time_constant)) + 1;
+		time_adj = ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
+		time_offset += (time_adj * HZ) >> (SHIFT_SCALE - SHIFT_UPDATE);
+		time_adj = - time_adj;
+	} else if (time_offset > 0) {
+		ltemp = ((time_offset-1) >> (SHIFT_KG + time_constant)) + 1;
+		time_adj = ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
+		time_offset -= (time_adj * HZ) >> (SHIFT_SCALE - SHIFT_UPDATE);
+	} else {
+		time_adj = 0;
+	}
+
+	time_adj += (time_freq >> (SHIFT_KF + SHIFT_HZ - SHIFT_SCALE))
+	    + FINETUNE;
+
+	/* Handle the leap second stuff */
+	switch (time_status) {
+		case TIME_INS:
+		/* ugly divide should be replaced */
+		if (xtime.tv_sec % 86400 == 0) {
+			xtime.tv_sec--; /* !! */
+			time_status = TIME_OOP;
+			printk("Clock: inserting leap second 23:59:60 UTC\n");
+		}
+		break;
+
+		case TIME_DEL:
+		/* ugly divide should be replaced */
+		if (xtime.tv_sec % 86400 == 86399) {
+			xtime.tv_sec++;
+			time_status = TIME_OK;
+			printk("Clock: deleting leap second 23:59:59 UTC\n");
+		}
+		break;
+
+		case TIME_OOP:
+		time_status = TIME_OK;
+		break;
+	}
+}
+
+/*
+ * disregard lost ticks for now.. We don't care enough.
+ */
+static void timer_bh(void * unused)
+{
+	unsigned long mask;
+	struct timer_struct *tp;
+	struct timer_list * timer;
+
+	cli();
+	while ((timer = timer_head.next) != &timer_head && timer->expires < jiffies) {
+		void (*fn)(unsigned long) = timer->function;
+		unsigned long data = timer->data;
+		timer->next->prev = timer->prev;
+		timer->prev->next = timer->next;
+		timer->next = timer->prev = NULL;
+		sti();
+		fn(data);
+		cli();
+	}
+	sti();
+	
+	for (mask = 1, tp = timer_table+0 ; mask ; tp++,mask += mask) {
+		if (mask > timer_active)
+			break;
+		if (!(mask & timer_active))
+			continue;
+		if (tp->expires > jiffies)
+			continue;
+		timer_active &= ~mask;
+		tp->fn();
+		sti();
+	}
+}
+
+void tqueue_bh(void * unused)
+{
+	run_task_queue(&tq_timer);
+}
+
+void immediate_bh(void * unused)
+{
+	run_task_queue(&tq_immediate);
+}
+
+/*
+ * The int argument is really a (struct pt_regs *), in case the
+ * interrupt wants to know from where it was called. The timer
+ * irq uses this to decide if it should update the user or system
+ * times.
+ */
+static void do_timer(int irq, struct pt_regs * regs)
+{
+	unsigned long mask;
+	struct timer_struct *tp;
+	/* last time the cmos clock got updated */
+	static long last_rtc_update=0;
+	extern int set_rtc_mmss(unsigned long);
+
+	long ltemp, psecs;
+
+	/* Advance the phase, once it gets to one microsecond, then
+	 * advance the tick more.
+	 */
+	time_phase += time_adj;
+	if (time_phase < -FINEUSEC) {
+		ltemp = -time_phase >> SHIFT_SCALE;
+		time_phase += ltemp << SHIFT_SCALE;
+		xtime.tv_usec += tick + time_adjust_step - ltemp;
+	}
+	else if (time_phase > FINEUSEC) {
+		ltemp = time_phase >> SHIFT_SCALE;
+		time_phase -= ltemp << SHIFT_SCALE;
+		xtime.tv_usec += tick + time_adjust_step + ltemp;
+	} else
+		xtime.tv_usec += tick + time_adjust_step;
+
+	if (time_adjust)
+	{
+	    /* We are doing an adjtime thing. 
+	     *
+	     * Modify the value of the tick for next time.
+	     * Note that a positive delta means we want the clock
+	     * to run fast. This means that the tick should be bigger
+	     *
+	     * Limit the amount of the step for *next* tick to be
+	     * in the range -tickadj .. +tickadj
+	     */
+	     if (time_adjust > tickadj)
+	       time_adjust_step = tickadj;
+	     else if (time_adjust < -tickadj)
+	       time_adjust_step = -tickadj;
+	     else
+	       time_adjust_step = time_adjust;
+	     
+	    /* Reduce by this step the amount of time left  */
+	    time_adjust -= time_adjust_step;
+	}
+	else
+	    time_adjust_step = 0;
+
+	if (xtime.tv_usec >= 1000000) {
+	    xtime.tv_usec -= 1000000;
+	    xtime.tv_sec++;
+	    second_overflow();
+	}
+
+	/* 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_status != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 &&
+	    xtime.tv_usec > 500000 - (tick >> 1) &&
+	    xtime.tv_usec < 500000 + (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 */
+
+	jiffies++;
+	calc_load();
+	if (user_mode(regs)) {
+		current->utime++;
+		if (current != task[0]) {
+			if (current->priority < 15)
+				kstat.cpu_nice++;
+			else
+				kstat.cpu_user++;
+		}
+		/* Update ITIMER_VIRT for current task if not in a system call */
+		if (current->it_virt_value && !(--current->it_virt_value)) {
+			current->it_virt_value = current->it_virt_incr;
+			send_sig(SIGVTALRM,current,1);
+		}
+	} else {
+		current->stime++;
+		if(current != task[0])
+			kstat.cpu_system++;
+#ifdef CONFIG_PROFILE
+		if (prof_buffer && current != task[0]) {
+			unsigned long eip = regs->eip;
+			eip >>= CONFIG_PROFILE_SHIFT;
+			if (eip < prof_len)
+				prof_buffer[eip]++;
+		}
+#endif
+	}
+	/*
+	 * check the cpu time limit on the process.
+	 */
+	if ((current->rlim[RLIMIT_CPU].rlim_max != RLIM_INFINITY) &&
+	    (((current->stime + current->utime) / HZ) >= current->rlim[RLIMIT_CPU].rlim_max))
+		send_sig(SIGKILL, current, 1);
+	if ((current->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY) &&
+	    (((current->stime + current->utime) % HZ) == 0)) {
+		psecs = (current->stime + current->utime) / HZ;
+		/* send when equal */
+		if (psecs == current->rlim[RLIMIT_CPU].rlim_cur)
+			send_sig(SIGXCPU, current, 1);
+		/* and every five seconds thereafter. */
+		else if ((psecs > current->rlim[RLIMIT_CPU].rlim_cur) &&
+		        ((psecs - current->rlim[RLIMIT_CPU].rlim_cur) % 5) == 0)
+			send_sig(SIGXCPU, current, 1);
+	}
+
+	if (current != task[0] && 0 > --current->counter) {
+		current->counter = 0;
+		need_resched = 1;
+	}
+	/* Update ITIMER_PROF for the current task */
+	if (current->it_prof_value && !(--current->it_prof_value)) {
+		current->it_prof_value = current->it_prof_incr;
+		send_sig(SIGPROF,current,1);
+	}
+	for (mask = 1, tp = timer_table+0 ; mask ; tp++,mask += mask) {
+		if (mask > timer_active)
+			break;
+		if (!(mask & timer_active))
+			continue;
+		if (tp->expires > jiffies)
+			continue;
+		mark_bh(TIMER_BH);
+	}
+	cli();
+	itimer_ticks++;
+	if (itimer_ticks > itimer_next)
+		need_resched = 1;
+	if (timer_head.next->expires < jiffies)
+		mark_bh(TIMER_BH);
+	if (tq_timer != &tq_last)
+		mark_bh(TQUEUE_BH);
+	sti();
+}
+
+asmlinkage int sys_alarm(long seconds)
+{
+	struct itimerval it_new, it_old;
+
+	it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0;
+	it_new.it_value.tv_sec = seconds;
+	it_new.it_value.tv_usec = 0;
+	_setitimer(ITIMER_REAL, &it_new, &it_old);
+	return(it_old.it_value.tv_sec + (it_old.it_value.tv_usec / 1000000));
+}
+
+asmlinkage int sys_getpid(void)
+{
+	return current->pid;
+}
+
+asmlinkage int sys_getppid(void)
+{
+	return current->p_opptr->pid;
+}
+
+asmlinkage int sys_getuid(void)
+{
+	return current->uid;
+}
+
+asmlinkage int sys_geteuid(void)
+{
+	return current->euid;
+}
+
+asmlinkage int sys_getgid(void)
+{
+	return current->gid;
+}
+
+asmlinkage int sys_getegid(void)
+{
+	return current->egid;
+}
+
+asmlinkage int sys_nice(long increment)
+{
+	int newprio;
+
+	if (increment < 0 && !suser())
+		return -EPERM;
+	newprio = current->priority - increment;
+	if (newprio < 1)
+		newprio = 1;
+	if (newprio > 35)
+		newprio = 35;
+	current->priority = newprio;
+	return 0;
+}
+
+static void show_task(int nr,struct task_struct * p)
+{
+	unsigned long free;
+	static char * stat_nam[] = { "R", "S", "D", "Z", "T", "W" };
+
+	printk("%-8s %3d ", p->comm, (p == current) ? -nr : nr);
+	if (((unsigned) p->state) < sizeof(stat_nam)/sizeof(char *))
+		printk(stat_nam[p->state]);
+	else
+		printk(" ");
+#ifdef __i386__
+	if (p == current)
+		printk(" current  ");
+	else
+		printk(" %08lX ", ((unsigned long *)p->tss.esp)[3]);
+#endif
+	for (free = 1; free < 1024 ; free++) {
+		if (((unsigned long *)p->kernel_stack_page)[free])
+			break;
+	}
+	printk("%5lu %5d %6d ", free << 2, p->pid, p->p_pptr->pid);
+	if (p->p_cptr)
+		printk("%5d ", p->p_cptr->pid);
+	else
+		printk("      ");
+	if (p->p_ysptr)
+		printk("%7d", p->p_ysptr->pid);
+	else
+		printk("       ");
+	if (p->p_osptr)
+		printk(" %5d\n", p->p_osptr->pid);
+	else
+		printk("\n");
+}
+
+void show_state(void)
+{
+	int i;
+
+	printk("                         free                        sibling\n");
+	printk("  task             PC    stack   pid father child younger older\n");
+	for (i=0 ; i<NR_TASKS ; i++)
+		if (task[i])
+			show_task(i,task[i]);
+}
+
+void sched_init(void)
+{
+	bh_base[TIMER_BH].routine = timer_bh;
+	bh_base[TQUEUE_BH].routine = tqueue_bh;
+	bh_base[IMMEDIATE_BH].routine = immediate_bh;
+	if (request_irq(TIMER_IRQ, do_timer, 0, "timer") != 0)
+		panic("Could not allocate timer IRQ!");
+	enable_bh(TIMER_BH);
+	enable_bh(TQUEUE_BH);
+	enable_bh(IMMEDIATE_BH);
+}
diff -r -u -N linux.orig/kernel/sys.c linux.arm/kernel/sys.c
--- linux.orig/kernel/sys.c	Fri Oct 27 23:09:10 1995
+++ linux.arm/kernel/sys.c	Fri Oct 27 23:10:19 1995
@@ -388,7 +388,6 @@
 	oldbrk = PAGE_ALIGN(current->mm->brk);
 	if (oldbrk == newbrk)
 		return current->mm->brk = brk;
-
 	/*
 	 * Always allow shrinking brk
 	 */
@@ -416,11 +415,15 @@
 	 * simple, it hopefully works in most obvious cases.. Easy to
 	 * fool it, but this should catch most mistakes.
 	 */
-	freepages = buffermem >> 12;
+	freepages = buffermem >> PAGE_SHIFT;
 	freepages += nr_free_pages;
 	freepages += nr_swap_pages;
+#ifndef __arm__
 	freepages -= (high_memory - 0x100000) >> 16;
-	freepages -= (newbrk-oldbrk) >> 12;
+#else
+	freepages -= 0;
+#endif
+	freepages -= (newbrk-oldbrk) >> PAGE_SHIFT;
 	if (freepages < 0)
 		return current->mm->brk;
 #if 0
diff -r -u -N linux.orig/kernel/time.c linux.arm/kernel/time.c
--- linux.orig/kernel/time.c	Fri Oct 27 23:09:10 1995
+++ linux.arm/kernel/time.c	Fri Oct 27 23:10:21 1995
@@ -19,6 +19,8 @@
  * 1995-03-26    Markus Kuhn
  *      fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887
  *      precision CMOS clock update
+ * 1995-08-07	 Russell King
+ *	altered method of obtaining/setting time for the arm
  *
  * to do: adjtimex() has to be updated to recent (1994-12-13) revision
  *        of David Mill's kernel clock model. For more information, check
@@ -72,6 +74,7 @@
 void time_init(void)
 {
 	unsigned int year, mon, day, hour, min, sec;
+#ifndef __arm__
 	int i;
 
 	/* The Linux interpretation of the CMOS clock register contents:
@@ -103,6 +106,23 @@
 	    BCD_TO_BIN(mon);
 	    BCD_TO_BIN(year);
 	  }
+#else
+	char buf[8];
+
+	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);
+#endif
 	if ((year += 1900) < 1970)
 		year += 100;
 	xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
@@ -461,6 +481,7 @@
 int set_rtc_mmss(unsigned long nowtime)
 {
   int retval = 0;
+#ifndef __arm__
   int real_seconds, real_minutes, cmos_minutes;
   unsigned char save_control, save_freq_select;
 
@@ -507,6 +528,6 @@
    */
   CMOS_WRITE(save_control, RTC_CONTROL);
   CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
-
+#endif
   return retval;
 }
diff -r -u -N linux.orig/kernel/time.c.old linux.arm/kernel/time.c.old
--- linux.orig/kernel/time.c.old	Thu Jan  1 01:00:00 1970
+++ linux.arm/kernel/time.c.old	Fri Oct 27 23:10:26 1995
@@ -0,0 +1,511 @@
+/*
+ *  linux/kernel/time.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  This file contains the interface functions for the various
+ *  time related system calls: time, stime, gettimeofday, settimeofday,
+ *			       adjtime
+ */
+/*
+ * Modification history kernel/time.c
+ * 
+ * 1993-09-02    Philip Gladstone
+ *      Created file with time related functions from sched.c and adjtimex() 
+ * 1993-10-08    Torsten Duwe
+ *      adjtime interface update and CMOS clock write code
+ * 1994-07-02    Alan Modra
+ *	fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime
+ * 1995-03-26    Markus Kuhn
+ *      fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887
+ *      precision CMOS clock update
+ *
+ * to do: adjtimex() has to be updated to recent (1994-12-13) revision
+ *        of David Mill's kernel clock model. For more information, check
+ *        <ftp://louie.udel.edu/pub/ntp/kernel.tar.Z>. 
+ */
+
+#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/mc146818rtc.h>
+#include <linux/timex.h>
+
+/* 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;
+	int i;
+
+	/* The Linux interpretation of the CMOS clock register contents:
+	 * When the Update-In-Progress (UIP) flag goes from 1 to 0, the
+	 * RTC registers show the second which has precisely just started.
+	 * Let's hope other operating systems interpret the RTC the same way.
+	 */
+	/* read RTC exactly on falling edge of update flag */
+	for (i = 0 ; i < 1000000 ; i++)	/* may take up to 1 second... */
+		if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
+			break;
+	for (i = 0 ; i < 1000000 ; i++)	/* must try at least 2.228 ms */
+		if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
+			break;
+	do { /* Isn't this overkill ? UIP above should guarantee consistency */
+		sec = CMOS_READ(RTC_SECONDS);
+		min = CMOS_READ(RTC_MINUTES);
+		hour = CMOS_READ(RTC_HOURS);
+		day = CMOS_READ(RTC_DAY_OF_MONTH);
+		mon = CMOS_READ(RTC_MONTH);
+		year = CMOS_READ(RTC_YEAR);
+	} while (sec != CMOS_READ(RTC_SECONDS));
+	if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+	  {
+	    BCD_TO_BIN(sec);
+	    BCD_TO_BIN(min);
+	    BCD_TO_BIN(hour);
+	    BCD_TO_BIN(day);
+	    BCD_TO_BIN(mon);
+	    BCD_TO_BIN(year);
+	  }
+	if ((year += 1900) < 1970)
+		year += 100;
+	xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
+	xtime.tv_usec = 0;
+}
+
+/* 
+ * The timezone where the local system is located.  Used as a default by some
+ * programs who obtain this value by using gettimeofday.
+ */
+struct timezone sys_tz = { 0, 0};
+
+asmlinkage int sys_time(long * tloc)
+{
+	int i, error;
+
+	i = CURRENT_TIME;
+	if (tloc) {
+		error = verify_area(VERIFY_WRITE, tloc, 4);
+		if (error)
+			return error;
+		put_fs_long(i,(unsigned long *)tloc);
+	}
+	return i;
+}
+
+asmlinkage int sys_stime(unsigned long * tptr)
+{
+	int error;
+	unsigned long value;
+
+	if (!suser())
+		return -EPERM;
+	error = verify_area(VERIFY_READ, tptr, sizeof(*tptr));
+	if (error)
+		return error;
+	value = get_fs_long(tptr);
+	cli();
+	xtime.tv_sec = value;
+	xtime.tv_usec = 0;
+	time_status = TIME_BAD;
+	time_maxerror = 0x70000000;
+	time_esterror = 0x70000000;
+	sti();
+	return 0;
+}
+
+/* This function must be called with interrupts disabled 
+ * It was inspired by Steve McCanne's microtime-i386 for BSD.  -- jrs
+ * 
+ * However, the pc-audio speaker driver changes the divisor so that
+ * it gets interrupted rather more often - it loads 64 into the
+ * counter rather than 11932! This has an adverse impact on
+ * do_gettimeoffset() -- it stops working! What is also not
+ * good is that the interval that our timer function gets called
+ * is no longer 10.0002 ms, but 9.9767 ms. To get around this
+ * would require using a different timing source. Maybe someone
+ * could use the RTC - I know that this can interrupt at frequencies
+ * ranging from 8192Hz to 2Hz. If I had the energy, I'd somehow fix
+ * it so that at startup, the timer code in sched.c would select
+ * using either the RTC or the 8253 timer. The decision would be
+ * based on whether there was any other device around that needed
+ * to trample on the 8253. I'd set up the RTC to interrupt at 1024 Hz,
+ * and then do some jiggery to have a version of do_timer that 
+ * advanced the clock by 1/1024 s. Every time that reached over 1/100
+ * of a second, then do all the old code. If the time was kept correct
+ * then do_gettimeoffset could just return 0 - there is no low order
+ * divider that can be accessed.
+ *
+ * Ideally, you would be able to use the RTC for the speaker driver,
+ * but it appears that the speaker driver really needs interrupt more
+ * often than every 120 us or so.
+ *
+ * Anyway, this needs more thought....		pjsg (1993-08-28)
+ * 
+ * If you are really that interested, you should be reading
+ * comp.protocols.time.ntp!
+ */
+
+#define TICK_SIZE tick
+
+static inline unsigned long do_gettimeoffset(void)
+{
+	int count;
+	unsigned long offset = 0;
+
+	/* timer count may underflow right here */
+	outb_p(0x00, 0x43);	/* latch the count ASAP */
+	count = inb_p(0x40);	/* read the latched count */
+	count |= inb(0x40) << 8;
+	/* we know probability of underflow is always MUCH less than 1% */
+	if (count > (LATCH - LATCH/100)) {
+		/* check for pending timer interrupt */
+		outb_p(0x0a, 0x20);
+		if (inb(0x20) & 1)
+			offset = TICK_SIZE;
+	}
+	count = ((LATCH-1) - count) * TICK_SIZE;
+	count = (count + LATCH/2) / LATCH;
+	return offset + count;
+}
+
+/*
+ * This version of gettimeofday has near microsecond resolution.
+ */
+void do_gettimeofday(struct timeval *tv)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	*tv = xtime;
+#if defined (__i386__) || defined (__mips__)
+	tv->tv_usec += do_gettimeoffset();
+	if (tv->tv_usec >= 1000000) {
+		tv->tv_usec -= 1000000;
+		tv->tv_sec++;
+	}
+#endif /* !defined (__i386__) && !defined (__mips__) */
+	restore_flags(flags);
+}
+
+asmlinkage int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
+{
+	int error;
+
+	if (tv) {
+		struct timeval ktv;
+		error = verify_area(VERIFY_WRITE, tv, sizeof *tv);
+		if (error)
+			return error;
+		do_gettimeofday(&ktv);
+		put_fs_long(ktv.tv_sec, (unsigned long *) &tv->tv_sec);
+		put_fs_long(ktv.tv_usec, (unsigned long *) &tv->tv_usec);
+	}
+	if (tz) {
+		error = verify_area(VERIFY_WRITE, tz, sizeof *tz);
+		if (error)
+			return error;
+		put_fs_long(sys_tz.tz_minuteswest, (unsigned long *) tz);
+		put_fs_long(sys_tz.tz_dsttime, ((unsigned long *) tz)+1);
+	}
+	return 0;
+}
+
+/*
+ * Adjust the time obtained from the CMOS to be UTC time instead of
+ * local time.
+ * 
+ * This is ugly, but preferable to the alternatives.  Otherwise we
+ * would either need to write a program to do it in /etc/rc (and risk
+ * confusion if the program gets run more than once; it would also be 
+ * hard to make the program warp the clock precisely n hours)  or
+ * compile in the timezone information into the kernel.  Bad, bad....
+ *
+ *              				- TYT, 1992-01-01
+ *
+ * The best thing to do is to keep the CMOS clock in universal time (UTC)
+ * as real UNIX machines always do it. This avoids all headaches about
+ * daylight saving times and warping kernel clocks.
+ */
+inline static void warp_clock(void)
+{
+	cli();
+	xtime.tv_sec += sys_tz.tz_minuteswest * 60;
+	sti();
+}
+
+/*
+ * In case for some reason the CMOS clock has not already been running
+ * in UTC, but in some local time: The first time we set the timezone,
+ * we will warp the clock so that it is ticking UTC time instead of
+ * local time. Presumably, if someone is setting the timezone then we
+ * are running in an environment where the programs understand about
+ * timezones. This should be done at boot time in the /etc/rc script,
+ * as soon as possible, so that the clock can be set right. Otherwise,
+ * various programs will get confused when the clock gets warped.
+ */
+asmlinkage int sys_settimeofday(struct timeval *tv, struct timezone *tz)
+{
+	static int	firsttime = 1;
+	struct timeval	new_tv;
+	struct timezone new_tz;
+
+	if (!suser())
+		return -EPERM;
+	if (tv) {
+		int error = verify_area(VERIFY_READ, tv, sizeof(*tv));
+		if (error)
+			return error;
+		memcpy_fromfs(&new_tv, tv, sizeof(*tv));
+	}
+	if (tz) {
+		int error = verify_area(VERIFY_READ, tz, sizeof(*tz));
+		if (error)
+			return error;
+		memcpy_fromfs(&new_tz, tz, sizeof(*tz));
+	}
+	if (tz) {
+		sys_tz = new_tz;
+		if (firsttime) {
+			firsttime = 0;
+			if (!tv)
+				warp_clock();
+		}
+	}
+	if (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!
+		 */
+		new_tv.tv_usec -= do_gettimeoffset();
+
+		if (new_tv.tv_usec < 0) {
+			new_tv.tv_usec += 1000000;
+			new_tv.tv_sec--;
+		}
+
+		xtime = new_tv;
+		time_status = TIME_BAD;
+		time_maxerror = 0x70000000;
+		time_esterror = 0x70000000;
+		sti();
+	}
+	return 0;
+}
+
+/* adjtimex mainly allows reading (and writing, if superuser) of
+ * kernel time-keeping variables. used by xntpd.
+ */
+asmlinkage int sys_adjtimex(struct timex *txc_p)
+{
+        long ltemp, mtemp, save_adjust;
+	int error;
+
+	/* Local copy of parameter */
+	struct timex txc;
+
+	error = verify_area(VERIFY_WRITE, txc_p, sizeof(struct timex));
+	if (error)
+	  return error;
+
+	/* Copy the user data space into the kernel copy
+	 * structure. But bear in mind that the structures
+	 * may change
+	 */
+	memcpy_fromfs(&txc, txc_p, sizeof(struct timex));
+
+	/* In order to modify anything, you gotta be super-user! */
+	if (txc.mode && !suser())
+		return -EPERM;
+
+	/* Now we validate the data before disabling interrupts
+	 */
+
+	if (txc.mode != ADJ_OFFSET_SINGLESHOT && (txc.mode & ADJ_OFFSET))
+	  /* Microsec field limited to -131000 .. 131000 usecs */
+	  if (txc.offset <= -(1 << (31 - SHIFT_UPDATE))
+	      || txc.offset >= (1 << (31 - SHIFT_UPDATE)))
+	    return -EINVAL;
+
+	/* time_status must be in a fairly small range */
+	if (txc.mode & ADJ_STATUS)
+	  if (txc.status < TIME_OK || txc.status > TIME_BAD)
+	    return -EINVAL;
+
+	/* if the quartz is off by more than 10% something is VERY wrong ! */
+	if (txc.mode & ADJ_TICK)
+	  if (txc.tick < 900000/HZ || txc.tick > 1100000/HZ)
+	    return -EINVAL;
+
+	cli();
+
+	/* Save for later - semantics of adjtime is to return old value */
+	save_adjust = time_adjust;
+
+	/* If there are input parameters, then process them */
+	if (txc.mode)
+	{
+	    if (time_status == TIME_BAD)
+		time_status = TIME_OK;
+
+	    if (txc.mode & ADJ_STATUS)
+		time_status = txc.status;
+
+	    if (txc.mode & ADJ_FREQUENCY)
+		time_freq = txc.frequency << (SHIFT_KF - 16);
+
+	    if (txc.mode & ADJ_MAXERROR)
+		time_maxerror = txc.maxerror;
+
+	    if (txc.mode & ADJ_ESTERROR)
+		time_esterror = txc.esterror;
+
+	    if (txc.mode & ADJ_TIMECONST)
+		time_constant = txc.time_constant;
+
+	    if (txc.mode & ADJ_OFFSET)
+	      if (txc.mode == ADJ_OFFSET_SINGLESHOT)
+		{
+		  time_adjust = txc.offset;
+		}
+	      else /* XXX should give an error if other bits set */
+		{
+		  time_offset = txc.offset << SHIFT_UPDATE;
+		  mtemp = xtime.tv_sec - time_reftime;
+		  time_reftime = xtime.tv_sec;
+		  if (mtemp > (MAXSEC+2) || mtemp < 0)
+		    mtemp = 0;
+
+		  if (txc.offset < 0)
+		    time_freq -= (-txc.offset * mtemp) >>
+		      (time_constant + time_constant);
+		  else
+		    time_freq += (txc.offset * mtemp) >>
+		      (time_constant + time_constant);
+
+		  ltemp = time_tolerance << SHIFT_KF;
+
+		  if (time_freq > ltemp)
+		    time_freq = ltemp;
+		  else if (time_freq < -ltemp)
+		    time_freq = -ltemp;
+		}
+	    if (txc.mode & ADJ_TICK)
+	      tick = txc.tick;
+
+	}
+	txc.offset	   = save_adjust;
+	txc.frequency	   = ((time_freq+1) >> (SHIFT_KF - 16));
+	txc.maxerror	   = time_maxerror;
+	txc.esterror	   = time_esterror;
+	txc.status	   = time_status;
+	txc.time_constant  = time_constant;
+	txc.precision	   = time_precision;
+	txc.tolerance	   = time_tolerance;
+	txc.time	   = xtime;
+	txc.tick	   = tick;
+
+	sti();
+
+	memcpy_tofs(txc_p, &txc, sizeof(struct timex));
+	return time_status;
+}
+
+/*
+ * 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;
+  int real_seconds, real_minutes, cmos_minutes;
+  unsigned char save_control, save_freq_select;
+
+  save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */
+  CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
+
+  save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */
+  CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
+
+  cmos_minutes = CMOS_READ(RTC_MINUTES);
+  if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+    BCD_TO_BIN(cmos_minutes);
+
+  /* since we're only adjusting minutes and seconds,
+   * don't interfere with hour overflow. This avoids
+   * messing with unknown time zones but requires your
+   * RTC not to be off by more than 15 minutes
+   */
+  real_seconds = nowtime % 60;
+  real_minutes = nowtime / 60;
+  if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
+    real_minutes += 30;		/* correct for half hour time zone */
+  real_minutes %= 60;
+
+  if (abs(real_minutes - cmos_minutes) < 30)
+    {
+      if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+	{
+	  BIN_TO_BCD(real_seconds);
+	  BIN_TO_BCD(real_minutes);
+	}
+      CMOS_WRITE(real_seconds,RTC_SECONDS);
+      CMOS_WRITE(real_minutes,RTC_MINUTES);
+    }
+  else
+    retval = -1;
+
+  /* The following flags have to be released exactly in this order,
+   * otherwise the DS12887 (popular MC146818A clone with integrated
+   * battery and quartz) will not reset the oscillator and will not
+   * update precisely 500 ms later. You won't find this mentioned in
+   * the Dallas Semiconductor data sheets, but who believes data
+   * sheets anyway ...                           -- Markus Kuhn
+   */
+  CMOS_WRITE(save_control, RTC_CONTROL);
+  CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
+  return retval;
+}
diff -r -u -N linux.orig/lib/string.c linux.arm/lib/string.c
--- linux.orig/lib/string.c	Fri Oct 27 23:09:12 1995
+++ linux.arm/lib/string.c	Fri Oct 27 23:10:29 1995
@@ -46,6 +46,9 @@
 	return tmp;
 }
 
+/*
+ * altered to be ansi-compiant - terminating null is always appended
+ */
 char * strncat(char *dest, const char *src, size_t count)
 {
 	char *tmp = dest;
@@ -55,7 +58,10 @@
 			dest++;
 		while ((*dest++ = *src++)) {
 			if (--count == 0)
+			{
+				*dest++ = '\0';
 				break;
+			}
 		}
 	}
 
@@ -164,7 +170,7 @@
 	___strtok = send;
 	return (sbegin);
 }
-
+#ifndef __arm__
 void * memset(void * s,char c,size_t count)
 {
 	char *xs = (char *) s;
@@ -174,7 +180,7 @@
 
 	return s;
 }
-
+#endif
 char * bcopy(const char * src, char * dest, int count)
 {
 	char *tmp = dest;
@@ -185,6 +191,7 @@
 	return dest;
 }
 
+#ifndef __arm__
 void * memcpy(void * dest,const void *src,size_t count)
 {
 	char *tmp = (char *) dest, *s = (char *) src;
@@ -214,6 +221,7 @@
 
 	return dest;
 }
+#endif
 
 int memcmp(const void * cs,const void * ct,size_t count)
 {
diff -r -u -N linux.orig/mm/Makefile linux.arm/mm/Makefile
--- linux.orig/mm/Makefile	Fri Oct 27 23:09:12 1995
+++ linux.arm/mm/Makefile	Fri Oct 27 23:10:30 1995
@@ -8,7 +8,7 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 .c.o:
-	$(CC) $(CFLAGS) -c $<
+	$(CC) $(CFLAGS) -c $< -fno-omit-frame-pointer
 .s.o:
 	$(AS) -o $*.o $<
 .c.s:
@@ -18,6 +18,8 @@
 
 mm.o: $(OBJS)
 	$(LD) -r -o mm.o $(OBJS)
+#	stripper mm.o mm2.o
+	sync
 
 modules:
 
diff -r -u -N linux.orig/mm/filemap.c linux.arm/mm/filemap.c
--- linux.orig/mm/filemap.c	Fri Oct 27 23:09:12 1995
+++ linux.arm/mm/filemap.c	Fri Oct 27 23:10:34 1995
@@ -34,7 +34,7 @@
 {
 	struct inode * inode = area->vm_inode;
 	unsigned int block;
-	int nr[8];
+	int nr[PAGE_SIZE >> inode->i_sb->s_blocksize_bits];
 	int i, *p;
 
 	address &= PAGE_MASK;
diff -r -u -N linux.orig/mm/kmalloc.c linux.arm/mm/kmalloc.c
--- linux.orig/mm/kmalloc.c	Fri Oct 27 23:09:12 1995
+++ linux.arm/mm/kmalloc.c	Fri Oct 27 23:10:33 1995
@@ -99,7 +99,7 @@
  * For now it is unsafe to allocate bucket sizes between n & n=16 where n is
  * 4096 * any power of two
  */
-
+#ifndef __arm__
 struct size_descriptor sizes[] = { 
 	{ NULL, NULL,  32,127, 0,0,0,0, 0},
 	{ NULL, NULL,  64, 63, 0,0,0,0, 0 },
@@ -116,7 +116,24 @@
 	{ NULL, NULL,131072-16,  1, 0,0,0,0, 5 },
 	{ NULL, NULL,   0,  0, 0,0,0,0, 0 }
 };
-
+#else
+struct size_descriptor sizes[] = {
+	{ NULL, NULL,    32, 1023, 0, 0, 0, 0, 0 },
+	{ NULL, NULL,    64,  511, 0, 0, 0, 0, 0 },
+	{ NULL, NULL,   128,  255, 0, 0, 0, 0, 0 },
+	{ NULL, NULL,   256,  127, 0, 0, 0, 0, 0 },
+	{ NULL, NULL,   512,   63, 0, 0, 0, 0, 0 },
+	{ NULL, NULL,  1024,   31, 0, 0, 0, 0, 0 },
+	{ NULL, NULL,  2048,   15, 0, 0, 0, 0, 0 },
+	{ NULL, NULL,  4096,    7, 0, 0, 0, 0, 0 },
+	{ NULL, NULL,  8192,    3, 0, 0, 0, 0, 0 },
+	{ NULL, NULL, 16376,    2, 0, 0, 0, 0, 0 },
+	{ NULL, NULL, 32752,    1, 0, 0, 0, 0, 0 },
+	{ NULL, NULL, 65520,    1, 0, 0, 0, 0, 1 },
+	{ NULL, NULL,131056,    1, 0, 0, 0, 0, 2 },
+	{ NULL, NULL,     0,    0, 0, 0, 0, 0, 0 }
+};
+#endif
 
 #define NBLOCKS(order)          (sizes[order].nblocks)
 #define BLOCKSIZE(order)        (sizes[order].size)
@@ -221,7 +238,7 @@
             p->bh_length = size;
             return p+1; /* Pointer arithmetic: increments past header */
             }
-        printk ("Problem: block on freelist at %08lx isn't free.\n",(long)p);
+        printk ("Problem: block on freelist at %08lx isn't free. (page = %p)\n",(long)p, page);/*rmk*/
         return (NULL);
         }
     restore_flags(flags);
@@ -303,6 +320,12 @@
 int order;
 register struct block_header *p=((struct block_header *)ptr) -1;
 struct page_descriptor *page,*pg2;
+#ifdef __arm__
+	void *bir;
+	__asm__("ldr\t%0,[ip, #-8]\n\t": "=r" (bir));
+	if((int)ptr < LOGICAL_END)
+		printk("kfree of logical memory (%p)\nPC = %p\n", ptr, bir);
+#endif
 
 page = PAGE_DESC (p);
 order = page->order;
diff -r -u -N linux.orig/mm/kmalloc.c.old linux.arm/mm/kmalloc.c.old
--- linux.orig/mm/kmalloc.c.old	Thu Jan  1 01:00:00 1970
+++ linux.arm/mm/kmalloc.c.old	Fri Oct 27 23:10:38 1995
@@ -0,0 +1,389 @@
+/*
+ *  linux/mm/kmalloc.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds & Roger Wolff.
+ *
+ *  Written by R.E. Wolff Sept/Oct '93.
+ *
+ */
+
+/*
+ * Modified by Alex Bligh (alex@cconcepts.co.uk) 4 Apr 1994 to use multiple
+ * pages. So for 'page' throughout, read 'area'.
+ */
+
+#include <linux/mm.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+
+#define GFP_LEVEL_MASK 0xf
+
+/* I want this low enough for a while to catch errors.
+   I want this number to be increased in the near future:
+        loadable device drivers should use this function to get memory */
+
+#define MAX_KMALLOC_K ((PAGE_SIZE<<(NUM_AREA_ORDERS-1))>>10)
+
+
+/* This defines how many times we should try to allocate a free page before
+   giving up. Normally this shouldn't happen at all. */
+#define MAX_GET_FREE_PAGE_TRIES 4
+
+
+/* Private flags. */
+
+#define MF_USED 0xffaa0055
+#define MF_FREE 0x0055ffaa
+
+
+/* 
+ * Much care has gone into making these routines in this file reentrant.
+ *
+ * The fancy bookkeeping of nbytesmalloced and the like are only used to
+ * report them to the user (oooohhhhh, aaaaahhhhh....) are not 
+ * protected by cli(). (If that goes wrong. So what?)
+ *
+ * These routines restore the interrupt status to allow calling with ints
+ * off. 
+ */
+
+/* 
+ * A block header. This is in front of every malloc-block, whether free or not.
+ */
+struct block_header {
+	unsigned long bh_flags;
+	union {
+		unsigned long ubh_length;
+		struct block_header *fbh_next;
+	} vp;
+};
+
+
+#define bh_length vp.ubh_length
+#define bh_next   vp.fbh_next
+#define BH(p) ((struct block_header *)(p))
+
+
+/* 
+ * The page descriptor is at the front of every page that malloc has in use. 
+ */
+struct page_descriptor {
+	struct page_descriptor *next;
+	struct block_header *firstfree;
+	int order;
+	int nfree;
+};
+
+
+#define PAGE_DESC(p) ((struct page_descriptor *)(((unsigned long)(p)) & PAGE_MASK))
+
+
+/*
+ * A size descriptor describes a specific class of malloc sizes.
+ * Each class of sizes has its own freelist.
+ */
+struct size_descriptor {
+	struct page_descriptor *firstfree;
+	struct page_descriptor *dmafree; /* DMA-able memory */
+	int size;
+	int nblocks;
+
+	int nmallocs;
+	int nfrees;
+	int nbytesmalloced;
+	int npages;
+	unsigned long gfporder; /* number of pages in the area required */
+};
+
+/*
+ * For now it is unsafe to allocate bucket sizes between n & n=16 where n is
+ * 4096 * any power of two
+ */
+struct size_descriptor sizes[] = { 
+	{ NULL, NULL,  32,127, 0,0,0,0, 0},
+	{ NULL, NULL,  64, 63, 0,0,0,0, 0 },
+	{ NULL, NULL, 128, 31, 0,0,0,0, 0 },
+	{ NULL, NULL, 252, 16, 0,0,0,0, 0 },
+	{ NULL, NULL, 508,  8, 0,0,0,0, 0 },
+	{ NULL, NULL,1020,  4, 0,0,0,0, 0 },
+	{ NULL, NULL,2040,  2, 0,0,0,0, 0 },
+	{ NULL, NULL,4096-16,  1, 0,0,0,0, 0 },
+	{ NULL, NULL,8192-16,  1, 0,0,0,0, 1 },
+	{ NULL, NULL,16384-16,  1, 0,0,0,0, 2 },
+	{ NULL, NULL,32768-16,  1, 0,0,0,0, 3 },
+	{ NULL, NULL,65536-16,  1, 0,0,0,0, 4 },
+	{ NULL, NULL,131072-16,  1, 0,0,0,0, 5 },
+	{ NULL, NULL,   0,  0, 0,0,0,0, 0 }
+};
+
+#define NBLOCKS(order)          (sizes[order].nblocks)
+#define BLOCKSIZE(order)        (sizes[order].size)
+#define AREASIZE(order)		(PAGE_SIZE<<(sizes[order].gfporder))
+
+
+long kmalloc_init (long start_mem,long end_mem)
+{
+	int order;
+
+/* 
+ * Check the static info array. Things will blow up terribly if it's
+ * incorrect. This is a late "compile time" check.....
+ */
+for (order = 0;BLOCKSIZE(order);order++)
+    {
+    if ((NBLOCKS (order)*BLOCKSIZE(order) + sizeof (struct page_descriptor)) >
+        AREASIZE(order)) 
+        {
+        printk ("Cannot use %d bytes out of %d in order = %d block mallocs\n",
+                (int) (NBLOCKS (order) * BLOCKSIZE(order) + 
+                        sizeof (struct page_descriptor)),
+                (int) AREASIZE(order),
+                BLOCKSIZE (order));
+        panic ("This only happens if someone messes with kmalloc");
+        }
+    }
+return start_mem;
+}
+
+
+
+int get_order (int size)
+{
+	int order;
+
+	/* Add the size of the header */
+	size += sizeof (struct block_header); 
+	for (order = 0;BLOCKSIZE(order);order++)
+		if (size <= BLOCKSIZE (order))
+			return order; 
+	return -1;
+}
+
+void * kmalloc (size_t size, int priority)
+{
+	unsigned long flags;
+	int order,tries,i,sz;
+	int dma_flag;
+	struct block_header *p;
+	struct page_descriptor *page;
+
+	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("kmalloc called nonatomically from interrupt %p\n",
+				__builtin_return_address(0));
+			priority = GFP_ATOMIC;
+		}
+	}
+
+order = get_order (size);
+if (order < 0)
+    {
+    printk ("kmalloc of too large a block (%d bytes).\n",(int) size);
+    return (NULL);
+    }
+
+save_flags(flags);
+
+/* It seems VERY unlikely to me that it would be possible that this 
+   loop will get executed more than once. */
+tries = MAX_GET_FREE_PAGE_TRIES; 
+while (tries --)
+    {
+    /* Try to allocate a "recently" freed memory block */
+    cli ();
+    if ((page = (dma_flag ? sizes[order].dmafree : sizes[order].firstfree)) &&
+        (p    =  page->firstfree))
+        {
+        if (p->bh_flags == MF_FREE)
+            {
+            page->firstfree = p->bh_next;
+            page->nfree--;
+            if (!page->nfree)
+                {
+		  if(dma_flag)
+		    sizes[order].dmafree = page->next;
+		  else
+		    sizes[order].firstfree = page->next;
+		  page->next = NULL;
+                }
+            restore_flags(flags);
+
+            sizes [order].nmallocs++;
+            sizes [order].nbytesmalloced += size;
+            p->bh_flags =  MF_USED; /* As of now this block is officially in use */
+            p->bh_length = size;
+            return p+1; /* Pointer arithmetic: increments past header */
+            }
+        printk ("Problem: block on freelist at %08lx isn't free. (page = %p)\n",(long)p, page);/*rmk*/
+        return (NULL);
+        }
+    restore_flags(flags);
+
+
+    /* Now we're in trouble: We need to get a new free page..... */
+
+    sz = BLOCKSIZE(order); /* sz is the size of the blocks we're dealing with */
+
+    /* This can be done with ints on: This is private to this invocation */
+    if (dma_flag)
+      page = (struct page_descriptor *) __get_dma_pages (priority & GFP_LEVEL_MASK, sizes[order].gfporder);
+    else
+      page = (struct page_descriptor *) __get_free_pages (priority & GFP_LEVEL_MASK, sizes[order].gfporder);
+
+    if (!page) {
+        static unsigned long last = 0;
+        if (last + 10*HZ < jiffies) {
+        	last = jiffies;
+	        printk ("Couldn't get a free page.....\n");
+	}
+        return NULL;
+    }
+#if 0
+    printk ("Got page %08x to use for %d byte mallocs....",(long)page,sz);
+#endif
+    sizes[order].npages++;
+
+    /* Loop for all but last block: */
+    for (i=NBLOCKS(order),p=BH (page+1);i > 1;i--,p=p->bh_next) 
+        {
+        p->bh_flags = MF_FREE;
+        p->bh_next = BH ( ((long)p)+sz);
+        }
+    /* Last block: */
+    p->bh_flags = MF_FREE;
+    p->bh_next = NULL;
+
+    page->order = order;
+    page->nfree = NBLOCKS(order); 
+    page->firstfree = BH(page+1);
+#if 0
+    printk ("%d blocks per page\n",page->nfree);
+#endif
+    /* Now we're going to muck with the "global" freelist for this size:
+       this should be uninterruptible */
+    cli ();
+    /* 
+     * sizes[order].firstfree used to be NULL, otherwise we wouldn't be
+     * here, but you never know.... 
+     */
+    if (dma_flag) {
+      page->next = sizes[order].dmafree;
+      sizes[order].dmafree = page;
+    } else {
+      page->next = sizes[order].firstfree;
+      sizes[order].firstfree = page;
+    }
+    restore_flags(flags);
+    }
+
+/* Pray that printk won't cause this to happen again :-) */
+
+printk ("Hey. This is very funny. I tried %d times to allocate a whole\n"
+        "new page for an object only %d bytes long, but some other process\n"
+        "beat me to actually allocating it. Also note that this 'error'\n"
+        "message is soooo very long to catch your attention. I'd appreciate\n"
+        "it if you'd be so kind as to report what conditions caused this to\n"
+        "the author of this kmalloc: wolff@dutecai.et.tudelft.nl.\n"
+        "(Executive summary: This can't happen)\n", 
+                MAX_GET_FREE_PAGE_TRIES,
+                (int) size);
+return NULL;
+}
+
+void kfree_s (void *ptr,int size)
+{ unsigned long bir;
+unsigned long flags;
+int order;
+register struct block_header *p=((struct block_header *)ptr) -1;
+struct page_descriptor *page,*pg2;
+__asm__("ldr\t%0,[ip, #-8]\n\t": "=r" (bir));
+if((int)ptr < 0x02000000)
+	printk("eek! - bad ptr at %p (pc = %08lX)\n", ptr, bir);
+
+page = PAGE_DESC (p);
+order = page->order;
+if ((order < 0) || 
+    (order > sizeof (sizes)/sizeof (sizes[0])) ||
+    (((long)(page->next)) & ~PAGE_MASK) ||
+    (p->bh_flags != MF_USED))
+    {
+    printk ("kfree of non-kmalloced memory: %p, next= %p, order=%d\n",
+                p, page->next, page->order);
+    return;
+    }
+if (size &&
+    size != p->bh_length)
+    {
+    printk ("Trying to free pointer at %p with wrong size: %d instead of %lu.\n",
+        p,size,p->bh_length);
+    return;
+    }
+size = p->bh_length;
+p->bh_flags = MF_FREE; /* As of now this block is officially free */
+save_flags(flags);
+cli ();
+p->bh_next = page->firstfree;
+page->firstfree = p;
+page->nfree ++;
+
+if (page->nfree == 1)
+   { /* Page went from full to one free block: put it on the freelist.  Do not bother
+      trying to put it on the DMA list. */
+   if (page->next)
+        {
+        printk ("Page %p already on freelist dazed and confused....\n", page);
+        }
+   else
+        {
+        page->next = sizes[order].firstfree;
+        sizes[order].firstfree = page;
+        }
+   }
+
+/* If page is completely free, free it */
+if (page->nfree == NBLOCKS (page->order))
+    {
+#if 0
+    printk ("Freeing page %08x.\n", (long)page);
+#endif
+    if (sizes[order].firstfree == page)
+        {
+        sizes[order].firstfree = page->next;
+        }
+    else if (sizes[order].dmafree == page)
+        {
+        sizes[order].dmafree = page->next;
+        }
+    else
+        {
+        for (pg2=sizes[order].firstfree;
+                (pg2 != NULL) && (pg2->next != page);
+                        pg2=pg2->next)
+            /* Nothing */;
+	if (!pg2)
+	  for (pg2=sizes[order].dmafree;
+	       (pg2 != NULL) && (pg2->next != page);
+	       pg2=pg2->next)
+            /* Nothing */;
+        if (pg2 != NULL)
+            pg2->next = page->next;
+        else
+            printk ("Ooops. page %p doesn't show on freelist.\n", page);
+        }
+/* FIXME: I'm sure we should do something with npages here (like npages--) */
+    free_pages ((long)page, sizes[order].gfporder);
+    }
+restore_flags(flags);
+
+/* FIXME: ?? Are these increment & decrement operations guaranteed to be
+ *	     atomic? Could an IRQ not occur between the read & the write?
+ *	     Maybe yes on a x86 with GCC...??
+ */
+sizes[order].nfrees++;      /* Noncritical (monitoring) admin stuff */
+sizes[order].nbytesmalloced -= size;
+}
diff -r -u -N linux.orig/mm/memory.c linux.arm/mm/memory.c
--- linux.orig/mm/memory.c	Fri Oct 27 23:09:12 1995
+++ linux.arm/mm/memory.c	Fri Oct 27 23:10:31 1995
@@ -175,7 +175,11 @@
 		pgd_free(page_dir);
 		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;
@@ -323,10 +327,12 @@
 	pgd_t *new_pgd;
 
 	new_pgd = pgd_alloc();
+
 	if (!new_pgd)
 		return -ENOMEM;
 	SET_PAGE_DIR(tsk, new_pgd);
 	old_pgd = pgd_offset(current, 0);
+
 	for (i = 0 ; i < PTRS_PER_PGD ; i++) {
 		int errno = copy_one_pgd(old_pgd, new_pgd);
 		if (errno) {
diff -r -u -N linux.orig/mm/memory.c.old linux.arm/mm/memory.c.old
--- linux.orig/mm/memory.c.old	Thu Jan  1 01:00:00 1970
+++ linux.arm/mm/memory.c.old	Fri Oct 27 23:10:39 1995
@@ -0,0 +1,1135 @@
+#define THREE_LEVEL
+/*
+ *  linux/mm/memory.c
+ *
+ *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
+ */
+
+/*
+ * demand-loading started 01.12.91 - seems it is high on the list of
+ * things wanted, and it should be easy to implement. - Linus
+ */
+
+/*
+ * Ok, demand-loading was easy, shared pages a little bit tricker. Shared
+ * pages started 02.12.91, seems to work. - Linus.
+ *
+ * Tested sharing by executing about 30 /bin/sh: under the old kernel it
+ * would have taken more than the 6M I have free, but it worked well as
+ * far as I could see.
+ *
+ * Also corrected some "invalidate()"s - I wasn't doing enough of them.
+ */
+
+/*
+ * Real VM (paging to/from disk) started 18.12.91. Much more work and
+ * thought has to go into this. Oh, well..
+ * 19.12.91  -  works, somewhat. Sometimes I get faults, don't know why.
+ *		Found it. Everything seems to work now.
+ * 20.12.91  -  Ok, making the swap-device changeable like the root.
+ */
+
+/*
+ * 05.04.94  -  Multi-page memory management added for v1.1.
+ * 		Idea by Alex Bligh (alex@cconcepts.co.uk)
+ */
+
+#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>
+
+unsigned long high_memory = 0;
+
+/*
+ * The free_area_list arrays point to the queue heads of the free areas
+ * of different sizes
+ */
+int nr_swap_pages = 0;
+int nr_free_pages = 0;
+struct mem_list free_area_list[NR_MEM_LISTS];
+unsigned char * free_area_map[NR_MEM_LISTS];
+
+#define copy_page(from,to) memcpy((void *) to, (void *) from, PAGE_SIZE)
+
+#define USER_PTRS_PER_PGD (TASK_SIZE / PGDIR_SIZE)
+
+mem_map_t * mem_map = NULL;
+
+/*
+ * oom() prints a message (so that the user knows why the process died),
+ * and gives the process an untrappable SIGKILL.
+ */
+void oom(struct task_struct * task)
+{
+	printk("\nOut of memory for %s.\n", current->comm);
+	task->sigaction[SIGKILL-1].sa_handler = NULL;
+	task->blocked &= ~(1<<(SIGKILL-1));
+	send_sig(SIGKILL,task,1);
+}
+
+static inline void free_one_pte(pte_t * page_table)
+{
+	pte_t page = *page_table;
+
+	if (pte_none(page))
+		return;
+	pte_clear(page_table);
+	if (!pte_present(page)) {
+		swap_free(pte_val(page));
+		return;
+	}
+	free_page(pte_page(page));
+	return;
+}
+
+static inline void free_one_pmd(pmd_t * dir)
+{
+	int j;
+	pte_t * pte;
+
+	if (pmd_none(*dir))
+		return;
+	if (pmd_bad(*dir)) {
+		printk("free_one_pmd: bad directory entry %08lx\n", pmd_val(*dir));
+		pmd_clear(dir);
+		return;
+	}
+	pte = pte_offset(dir, 0);
+	pmd_clear(dir);
+	if (pte_inuse(pte)) {
+		pte_free(pte);
+		return;
+	}
+	for (j = 0; j < PTRS_PER_PTE ; j++)
+		free_one_pte(pte+j);
+	pte_free(pte);
+}
+
+static inline void free_one_pgd(pgd_t * dir)
+{
+	int j;
+	pmd_t * pmd;
+
+	if (pgd_none(*dir))
+		return;
+	if (pgd_bad(*dir)) {
+		printk("free_one_pgd: bad directory entry %08lx\n", pgd_val(*dir));
+		pgd_clear(dir);
+		return;
+	}
+	pmd = pmd_offset(dir, 0);
+	pgd_clear(dir);
+	if (pmd_inuse(pmd)) {
+		pmd_free(pmd);
+		return;
+	}
+	for (j = 0; j < PTRS_PER_PMD ; j++)
+		free_one_pmd(pmd+j);
+	pmd_free(pmd);
+}
+	
+
+/*
+ * This function clears all user-level page tables of a process - this
+ * is needed by execve(), so that old pages aren't in the way. Note that
+ * unlike 'free_page_tables()', this function still leaves a valid
+ * page-table-tree in memory: it just removes the user pages. The two
+ * functions are similar, but there is a fundamental difference.
+ */
+void clear_page_tables(struct task_struct * tsk)
+{
+	int i;
+	pgd_t * page_dir;
+
+	if (!tsk)
+		return;
+	if (tsk == task[0])
+		panic("task[0] (swapper) doesn't support exec()\n");
+	page_dir = pgd_offset(tsk, 0);
+	if (!page_dir || page_dir == swapper_pg_dir) {
+		printk("Trying to clear kernel page-directory: not good\n");
+		return;
+	}
+	if (pgd_inuse(page_dir)) {
+		pgd_t * new_pg;
+
+		if (!(new_pg = pgd_alloc())) {
+			oom(tsk);
+			return;
+		}
+		for (i = USER_PTRS_PER_PGD ; i < PTRS_PER_PGD ; i++)
+			new_pg[i] = page_dir[i];
+		SET_PAGE_DIR(tsk, new_pg);
+		pgd_free(page_dir);
+		return;
+	}
+	for (i = 0 ; i < USER_PTRS_PER_PGD ; i++)
+		free_one_pgd(page_dir + i);
+	invalidate();
+	return;
+}
+
+/*
+ * This function frees up all page tables of a process when it exits.
+ */
+void free_page_tables(struct task_struct * tsk)
+{
+	int i;
+	pgd_t * page_dir;
+
+	if (!tsk)
+		return;
+	if (tsk == task[0]) {
+		printk("task[0] (swapper) killed: unable to recover\n");
+		panic("Trying to free up swapper memory space");
+	}
+	page_dir = pgd_offset(tsk, 0);
+	if (!page_dir || page_dir == swapper_pg_dir) {
+		printk("Trying to free kernel page-directory: not good\n");
+		return;
+	}
+	SET_PAGE_DIR(tsk, swapper_pg_dir);
+	if (pgd_inuse(page_dir)) {
+		pgd_free(page_dir);
+		return;
+	}
+	for (i = 0 ; i < PTRS_PER_PGD ; i++)
+		free_one_pgd(page_dir + i);
+	pgd_free(page_dir);
+	invalidate();
+}
+
+/*
+ * clone_page_tables() clones the page table for a process - both
+ * processes will have the exact same pages in memory. There are
+ * probably races in the memory management with cloning, but we'll
+ * see..
+ */
+int clone_page_tables(struct task_struct * tsk)
+{
+	pgd_t * pg_dir;
+
+	pg_dir = pgd_offset(current, 0);
+	pgd_reuse(pg_dir);
+	SET_PAGE_DIR(tsk, pg_dir);
+	return 0;
+}
+
+static inline void copy_one_pte(pte_t * old_pte, pte_t * new_pte)
+{
+	pte_t pte = *old_pte;
+
+	if (pte_none(pte))
+		return;
+	if (!pte_present(pte)) {
+		swap_duplicate(pte_val(pte));
+		*new_pte = pte;
+		return;
+	}
+	if (pte_page(pte) > high_memory || (mem_map[MAP_NR(pte_page(pte))] & MAP_PAGE_RESERVED)) {
+		*new_pte = pte;
+		return;
+	}
+	if (pte_cow(pte))
+		pte = pte_wrprotect(pte);
+	if (delete_from_swap_cache(pte_page(pte)))
+		pte = pte_mkdirty(pte);
+	*new_pte = pte_mkold(pte);
+	*old_pte = pte;
+	mem_map[MAP_NR(pte_page(pte))]++;
+}
+
+static inline int copy_one_pmd(pmd_t * old_pmd, pmd_t * new_pmd)
+{
+	int j;
+	pte_t *old_pte, *new_pte;
+
+	if (pmd_none(*old_pmd))
+		return 0;
+	if (pmd_bad(*old_pmd)) {
+		printk("copy_one_pmd: bad page table: probable memory corruption\n");
+		pmd_clear(old_pmd);
+		return 0;
+	}
+	old_pte = pte_offset(old_pmd, 0);
+	if (pte_inuse(old_pte)) {
+		pte_reuse(old_pte);
+		*new_pmd = *old_pmd;
+		return 0;
+	}
+	new_pte = pte_alloc(new_pmd, 0);
+	if (!new_pte)
+		return -ENOMEM;
+	for (j = 0 ; j < PTRS_PER_PTE ; j++) {
+		copy_one_pte(old_pte, new_pte);
+		old_pte++;
+		new_pte++;
+	}
+	return 0;
+}
+
+static inline int copy_one_pgd(pgd_t * old_pgd, pgd_t * new_pgd)
+{
+	int j;
+	pmd_t *old_pmd, *new_pmd;
+
+	if (pgd_none(*old_pgd))
+		return 0;
+	if (pgd_bad(*old_pgd)) {
+		printk("copy_one_pgd: bad page table (%p: %08lx): probable memory corruption\n", old_pgd, pgd_val(*old_pgd));
+		pgd_clear(old_pgd);
+		return 0;
+	}
+	old_pmd = pmd_offset(old_pgd, 0);
+	if (pmd_inuse(old_pmd)) {
+		pmd_reuse(old_pmd);
+		*new_pgd = *old_pgd;
+		return 0;
+	}
+	new_pmd = pmd_alloc(new_pgd, 0);
+	if (!new_pmd)
+		return -ENOMEM;
+	for (j = 0 ; j < PTRS_PER_PMD ; j++) {
+		int error = copy_one_pmd(old_pmd, new_pmd);
+		if (error)
+			return error;
+		old_pmd++;
+		new_pmd++;
+	}
+	return 0;
+}
+
+/*
+ * copy_page_tables() just copies the whole process memory range:
+ * note the special handling of RESERVED (ie kernel) pages, which
+ * means that they are always shared by all processes.
+ */
+int copy_page_tables(struct task_struct * tsk)
+{
+	int i;
+	pgd_t *old_pgd;
+	pgd_t *new_pgd;
+
+	new_pgd = pgd_alloc();
+
+	if (!new_pgd)
+		return -ENOMEM;
+	SET_PAGE_DIR(tsk, new_pgd);
+	old_pgd = pgd_offset(current, 0);
+
+	for (i = 0 ; i < PTRS_PER_PGD ; i++) {
+		int errno = copy_one_pgd(old_pgd, new_pgd);
+		if (errno) {
+			free_page_tables(tsk);
+			invalidate();
+			return errno;
+		}
+		old_pgd++;
+		new_pgd++;
+	}
+	invalidate();
+	return 0;
+}
+
+static inline void forget_pte(pte_t page)
+{
+	if (pte_none(page))
+		return;
+	if (pte_present(page)) {
+		free_page(pte_page(page));
+		if (mem_map[MAP_NR(pte_page(page))] & MAP_PAGE_RESERVED)
+			return;
+		if (current->mm->rss <= 0)
+			return;
+		current->mm->rss--;
+		return;
+	}
+	swap_free(pte_val(page));
+}
+
+static inline void unmap_pte_range(pmd_t * pmd, unsigned long address, unsigned long size)
+{
+	pte_t * pte;
+	unsigned long end;
+
+	if (pmd_none(*pmd))
+		return;
+	if (pmd_bad(*pmd)) {
+		printk("unmap_pte_range: bad pmd (%08lx)\n", pmd_val(*pmd));
+		pmd_clear(pmd);
+		return;
+	}
+	pte = pte_offset(pmd, address);
+	address &= ~PMD_MASK;
+	end = address + size;
+	if (end >= PMD_SIZE)
+		end = PMD_SIZE;
+	do {
+		pte_t page = *pte;
+		pte_clear(pte);
+		forget_pte(page);
+		address += PAGE_SIZE;
+		pte++;
+	} while (address < end);
+}
+
+static inline void unmap_pmd_range(pgd_t * dir, unsigned long address, unsigned long size)
+{
+	pmd_t * pmd;
+	unsigned long end;
+
+	if (pgd_none(*dir))
+		return;
+	if (pgd_bad(*dir)) {
+		printk("unmap_pmd_range: bad pgd (%08lx)\n", pgd_val(*dir));
+		pgd_clear(dir);
+		return;
+	}
+	pmd = pmd_offset(dir, address);
+	address &= ~PGDIR_MASK;
+	end = address + size;
+	if (end > PGDIR_SIZE)
+		end = PGDIR_SIZE;
+	do {
+		unmap_pte_range(pmd, address, end - address);
+		address = (address + PMD_SIZE) & PMD_MASK; 
+		pmd++;
+	} while (address < end);
+}
+
+/*
+ * a more complete version of free_page_tables which performs with page
+ * granularity.
+ */
+int unmap_page_range(unsigned long address, unsigned long size)
+{
+	pgd_t * dir;
+	unsigned long end = address + size;
+
+	dir = pgd_offset(current, address);
+	while (address < end) {
+		unmap_pmd_range(dir, address, end - address);
+		address = (address + PGDIR_SIZE) & PGDIR_MASK;
+		dir++;
+	}
+	invalidate();
+	return 0;
+}
+
+static inline void zeromap_pte_range(pte_t * pte, unsigned long address, unsigned long size, pte_t zero_pte)
+{
+	unsigned long end;
+
+	address &= ~PMD_MASK;
+	end = address + size;
+	if (end > PMD_SIZE)
+		end = PMD_SIZE;
+	do {
+		pte_t oldpage = *pte;
+		*pte = zero_pte;
+		forget_pte(oldpage);
+		address += PAGE_SIZE;
+		pte++;
+	} while (address < end);
+}
+
+static inline int zeromap_pmd_range(pmd_t * pmd, unsigned long address, unsigned long size, pte_t zero_pte)
+{
+	unsigned long end;
+
+	address &= ~PGDIR_MASK;
+	end = address + size;
+	if (end > PGDIR_SIZE)
+		end = PGDIR_SIZE;
+	do {
+		pte_t * pte = pte_alloc(pmd, address);
+		if (!pte)
+			return -ENOMEM;
+		zeromap_pte_range(pte, address, end - address, zero_pte);
+		address = (address + PMD_SIZE) & PMD_MASK;
+		pmd++;
+	} while (address < end);
+	return 0;
+}
+
+int zeromap_page_range(unsigned long address, unsigned long size, pgprot_t prot)
+{
+	int error = 0;
+	pgd_t * dir;
+	unsigned long end = address + size;
+	pte_t zero_pte;
+
+	zero_pte = pte_wrprotect(mk_pte(ZERO_PAGE, prot));
+	dir = pgd_offset(current, address);
+	while (address < end) {
+		pmd_t *pmd = pmd_alloc(dir, address);
+		error = -ENOMEM;
+		if (!pmd)
+			break;
+		error = zeromap_pmd_range(pmd, address, end - address, zero_pte);
+		if (error)
+			break;
+		address = (address + PGDIR_SIZE) & PGDIR_MASK;
+		dir++;
+	}
+	invalidate();
+	return error;
+}
+
+/*
+ * maps a range of physical memory into the requested pages. the old
+ * mappings are removed. any references to nonexistent pages results
+ * in null mappings (currently treated as "copy-on-access")
+ */
+static inline void remap_pte_range(pte_t * pte, unsigned long address, unsigned long size,
+	unsigned long offset, pgprot_t prot)
+{
+	unsigned long end;
+
+	address &= ~PMD_MASK;
+	end = address + size;
+	if (end > PMD_SIZE)
+		end = PMD_SIZE;
+	do {
+		pte_t oldpage = *pte;
+		pte_clear(pte);
+		if (offset >= high_memory || (mem_map[MAP_NR(offset)] & MAP_PAGE_RESERVED))
+			*pte = mk_pte(offset, prot);
+		else if (mem_map[MAP_NR(offset)]) {
+			mem_map[MAP_NR(offset)]++;
+			*pte = mk_pte(offset, prot);
+		}
+		forget_pte(oldpage);
+		address += PAGE_SIZE;
+		offset += PAGE_SIZE;
+		pte++;
+	} while (address < end);
+}
+
+static inline int remap_pmd_range(pmd_t * pmd, unsigned long address, unsigned long size,
+	unsigned long offset, pgprot_t prot)
+{
+	unsigned long end;
+
+	address &= ~PGDIR_MASK;
+	end = address + size;
+	if (end > PGDIR_SIZE)
+		end = PGDIR_SIZE;
+	offset -= address;
+	do {
+		pte_t * pte = pte_alloc(pmd, address);
+		if (!pte)
+			return -ENOMEM;
+		remap_pte_range(pte, address, end - address, address + offset, prot);
+		address = (address + PMD_SIZE) & PMD_MASK;
+		pmd++;
+	} while (address < end);
+	return 0;
+}
+
+int remap_page_range(unsigned long from, unsigned long offset, unsigned long size, pgprot_t prot)
+{
+	int error = 0;
+	pgd_t * dir;
+	unsigned long end = from + size;
+
+	offset -= from;
+	dir = pgd_offset(current, from);
+	while (from < end) {
+		pmd_t *pmd = pmd_alloc(dir, from);
+		error = -ENOMEM;
+		if (!pmd)
+			break;
+		error = remap_pmd_range(pmd, from, end - from, offset + from, prot);
+		if (error)
+			break;
+		from = (from + PGDIR_SIZE) & PGDIR_MASK;
+		dir++;
+	}
+	invalidate();
+	return error;
+}
+
+/*
+ * sanity-check function..
+ */
+static void put_page(pte_t * page_table, pte_t pte)
+{
+	if (!pte_none(*page_table)) {
+		printk("put_page: page already exists %08lx\n", pte_val(*page_table));
+		free_page(pte_page(pte));
+		return;
+	}
+/* no need for invalidate */
+	*page_table = pte;
+}
+
+/*
+ * This routine is used to map in a page into an address space: needed by
+ * execve() for the initial stack and environment pages.
+ */
+unsigned long put_dirty_page(struct task_struct * tsk, unsigned long page, unsigned long address)
+{
+	pgd_t * pgd;
+	pmd_t * pmd;
+	pte_t * pte;
+
+	if (page >= high_memory)
+		printk("put_dirty_page: trying to put page %08lx at %08lx\n",page,address);
+	if (mem_map[MAP_NR(page)] != 1)
+		printk("mem_map disagrees with %08lx at %08lx\n",page,address);
+	pgd = pgd_offset(tsk,address);
+	pmd = pmd_alloc(pgd, address);
+	if (!pmd) {
+		free_page(page);
+		oom(tsk);
+		return 0;
+	}
+	pte = pte_alloc(pmd, address);
+	if (!pte) {
+		free_page(page);
+		oom(tsk);
+		return 0;
+	}
+	if (!pte_none(*pte)) {
+		printk("put_dirty_page: page already exists\n");
+		pte_clear(pte);
+		invalidate();
+	}
+	*pte = pte_mkwrite(pte_mkdirty(mk_pte(page, PAGE_COPY)));
+/* no need for invalidate */
+	return page;
+}
+
+/*
+ * This routine handles present pages, when users try to write
+ * to a shared page. It is done by copying the page to a new address
+ * and decrementing the shared-page counter for the old page.
+ *
+ * Goto-purists beware: the only reason for goto's here is that it results
+ * in better assembly code.. The "default" path will see no jumps at all.
+ *
+ * Note that this routine assumes that the protection checks have been
+ * done by the caller (the low-level page fault routine in most cases).
+ * Thus we can safely just mark it writable once we've done any necessary
+ * COW.
+ *
+ * We also mark the page dirty at this point even though the page will
+ * change only once the write actually happens. This avoids a few races,
+ * and potentially makes it more efficient.
+ */
+void do_wp_page(struct vm_area_struct * vma, unsigned long address,
+	int write_access)
+{
+	pgd_t *page_dir;
+	pmd_t *page_middle;
+	pte_t *page_table, pte;
+	unsigned long old_page, new_page;
+
+	new_page = __get_free_page(GFP_KERNEL);
+	page_dir = pgd_offset(vma->vm_task,address);
+	if (pgd_none(*page_dir))
+		goto end_wp_page;
+	if (pgd_bad(*page_dir))
+		goto bad_wp_pagedir;
+	page_middle = pmd_offset(page_dir, address);
+	if (pmd_none(*page_middle))
+		goto end_wp_page;
+	if (pmd_bad(*page_middle))
+		goto bad_wp_pagemiddle;
+	page_table = pte_offset(page_middle, address);
+	pte = *page_table;
+	if (!pte_present(pte))
+		goto end_wp_page;
+	if (pte_write(pte))
+		goto end_wp_page;
+	old_page = pte_page(pte);
+	if (old_page >= high_memory)
+		goto bad_wp_page;
+	vma->vm_task->mm->min_flt++;
+	/*
+	 * Do we need to copy?
+	 */
+	if (mem_map[MAP_NR(old_page)] != 1) {
+		if (new_page) {
+			if (mem_map[MAP_NR(old_page)] & MAP_PAGE_RESERVED)
+				++vma->vm_task->mm->rss;
+			copy_page(old_page,new_page);
+			*page_table = pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot)));
+			free_page(old_page);
+			invalidate();
+			return;
+		}
+		*page_table = BAD_PAGE;
+		free_page(old_page);
+		oom(vma->vm_task);
+		invalidate();
+		return;
+	}
+	*page_table = pte_mkdirty(pte_mkwrite(pte));
+	invalidate();
+	if (new_page)
+		free_page(new_page);
+	return;
+bad_wp_page:
+	printk("do_wp_page: bogus page at address %08lx (%08lx)\n",address,old_page);
+	send_sig(SIGKILL, vma->vm_task, 1);
+	goto end_wp_page;
+bad_wp_pagemiddle:
+	printk("do_wp_page: bogus page-middle at address %08lx (%08lx)\n", address, pmd_val(*page_middle));
+	send_sig(SIGKILL, vma->vm_task, 1);
+	goto end_wp_page;
+bad_wp_pagedir:
+	printk("do_wp_page: bogus page-dir entry at address %08lx (%08lx)\n", address, pgd_val(*page_dir));
+	send_sig(SIGKILL, vma->vm_task, 1);
+end_wp_page:
+	if (new_page)
+		free_page(new_page);
+	return;
+}
+
+/*
+ * Ugly, ugly, but the goto's result in better assembly..
+ */
+int verify_area(int type, const void * addr, unsigned long size)
+{
+	struct vm_area_struct * vma;
+	unsigned long start = (unsigned long) addr;
+
+	/* If the current user space is mapped to kernel space (for the
+	 * case where we use a fake user buffer with get_fs/set_fs()) we
+	 * don't expect to find the address in the user vm map.
+	 */
+	if (get_fs() == get_ds())
+		return 0;
+
+	vma = find_vma(current, start);
+	if (!vma)
+		goto bad_area;
+	if (vma->vm_start <= start)
+		goto good_area;
+	if (!(vma->vm_flags & VM_GROWSDOWN))
+		goto bad_area;
+	if (vma->vm_end - start > current->rlim[RLIMIT_STACK].rlim_cur)
+		goto bad_area;
+
+good_area:
+	if (type == VERIFY_WRITE)
+		goto check_write;
+	for (;;) {
+		struct vm_area_struct * next;
+		if (!(vma->vm_flags & VM_READ))
+			goto bad_area;
+		if (vma->vm_end - start >= size)
+			return 0;
+		next = vma->vm_next;
+		if (!next || vma->vm_end != next->vm_start)
+			goto bad_area;
+		vma = next;
+	}
+
+check_write:
+	if (!(vma->vm_flags & VM_WRITE))
+		goto bad_area;
+	if (!wp_works_ok)
+		goto check_wp_fault_by_hand;
+	for (;;) {
+		if (vma->vm_end - start >= size)
+			break;
+		if (!vma->vm_next || vma->vm_end != vma->vm_next->vm_start)
+			goto bad_area;
+		vma = vma->vm_next;
+		if (!(vma->vm_flags & VM_WRITE))
+			goto bad_area;
+	}
+	return 0;
+
+check_wp_fault_by_hand:
+	size--;
+	size += start & ~PAGE_MASK;
+	size >>= PAGE_SHIFT;
+	start &= PAGE_MASK;
+
+	for (;;) {
+		do_wp_page(vma, start, 1);
+		if (!size)
+			break;
+		size--;
+		start += PAGE_SIZE;
+		if (start < vma->vm_end)
+			continue;
+		vma = vma->vm_next;
+		if (!vma || vma->vm_start != start)
+			goto bad_area;
+		if (!(vma->vm_flags & VM_WRITE))
+			goto bad_area;;
+	}
+	return 0;
+
+bad_area:
+	return -EFAULT;
+}
+
+static inline void get_empty_page(struct vm_area_struct * vma, pte_t * page_table)
+{
+	unsigned long tmp;
+
+	if (!(tmp = get_free_page(GFP_KERNEL))) {
+		oom(vma->vm_task);
+		put_page(page_table, BAD_PAGE);
+		return;
+	}
+	put_page(page_table, pte_mkwrite(mk_pte(tmp, vma->vm_page_prot)));
+}
+
+/*
+ * try_to_share() checks the page at address "address" in the task "p",
+ * to see if it exists, and if it is clean. If so, share it with the current
+ * task.
+ *
+ * NOTE! This assumes we have checked that p != current, and that they
+ * share the same inode and can generally otherwise be shared.
+ */
+static int try_to_share(unsigned long to_address, struct vm_area_struct * to_area,
+	unsigned long from_address, struct vm_area_struct * from_area,
+	unsigned long newpage)
+{
+	pgd_t * from_dir, * to_dir;
+	pmd_t * from_middle, * to_middle;
+	pte_t * from_table, * to_table;
+	pte_t from, to;
+
+	from_dir = pgd_offset(from_area->vm_task,from_address);
+/* is there a page-directory at from? */
+	if (pgd_none(*from_dir))
+		return 0;
+	if (pgd_bad(*from_dir)) {
+		printk("try_to_share: bad page directory %08lx\n", pgd_val(*from_dir));
+		pgd_clear(from_dir);
+		return 0;
+	}
+	from_middle = pmd_offset(from_dir, from_address);
+/* is there a mid-directory at from? */
+	if (pmd_none(*from_middle))
+		return 0;
+	if (pmd_bad(*from_middle)) {
+		printk("try_to_share: bad mid directory %08lx\n", pmd_val(*from_middle));
+		pmd_clear(from_middle);
+		return 0;
+	}
+	from_table = pte_offset(from_middle, from_address);
+	from = *from_table;
+/* is the page present? */
+	if (!pte_present(from))
+		return 0;
+/* if it is dirty it must be from a shared mapping to be shared */
+	if (pte_dirty(from)) {
+		if (!(from_area->vm_flags & VM_SHARED))
+			return 0;
+		if (pte_write(from)) {
+			printk("nonwritable, but dirty, shared page\n");
+			return 0;
+		}
+	}
+/* is the page reasonable at all? */
+	if (pte_page(from) >= high_memory)
+		return 0;
+	if (mem_map[MAP_NR(pte_page(from))] & MAP_PAGE_RESERVED)
+		return 0;
+/* is the destination ok? */
+	to_dir = pgd_offset(to_area->vm_task,to_address);
+/* is there a page-directory at to? */
+	if (pgd_none(*to_dir))
+		return 0;
+	if (pgd_bad(*to_dir)) {
+		printk("try_to_share: bad page directory %08lx\n", pgd_val(*to_dir));
+		return 0;
+	}
+	to_middle = pmd_offset(to_dir, to_address);
+/* is there a mid-directory at to? */
+	if (pmd_none(*to_middle))
+		return 0;
+	if (pmd_bad(*to_middle)) {
+		printk("try_to_share: bad mid directory %08lx\n", pmd_val(*to_middle));
+		return 0;
+	}
+	to_table = pte_offset(to_middle, to_address);
+	to = *to_table;
+	if (!pte_none(to))
+		return 0;
+/* do we copy? */
+	if (newpage) {
+		/* if it's in the swap cache, it's dirty by implication */
+		/* so we can't use it if it's not from a shared mapping */
+		if (in_swap_cache(pte_page(from))) {
+			if (!(from_area->vm_flags & VM_SHARED))
+				return 0;
+			if (!pte_write(from)) {
+				printk("nonwritable, but dirty, shared page\n");
+				return 0;
+			}
+		}
+		copy_page(pte_page(from), newpage);
+		*to_table = mk_pte(newpage, to_area->vm_page_prot);
+		return 1;
+	}
+/*
+ * do a final swap-cache test before sharing them: if it's in the swap
+ * cache, we have to remove it now, as we get two pointers to the same
+ * physical page and the cache can't handle it. Mark the original dirty.
+ *
+ * NOTE! Even if "from" is dirty, "to" will be clean: if we get here
+ * with a dirty "from", the from-mapping is a shared map, so we can trust
+ * the page contents to be up-to-date
+ */
+	if (in_swap_cache(pte_page(from))) {
+		if (!(from_area->vm_flags & VM_SHARED))
+			return 0;
+		*from_table = pte_mkdirty(from);
+		delete_from_swap_cache(pte_page(from));
+	}
+	mem_map[MAP_NR(pte_page(from))]++;
+	*to_table = mk_pte(pte_page(from), to_area->vm_page_prot);
+/* Check if we need to do anything at all to the 'from' field */
+	if (!pte_write(from))
+		return 1;
+	if (from_area->vm_flags & VM_SHARED)
+		return 1;
+/* ok, need to mark it read-only, so invalidate any possible old TB entry */
+	*from_table = pte_wrprotect(from);
+	invalidate();
+	return 1;
+}
+
+/*
+ * share_page() tries to find a process that could share a page with
+ * the current one.
+ *
+ * We first check if it is at all feasible by checking inode->i_count.
+ * It should be >1 if there are other tasks sharing this inode.
+ */
+static int share_page(struct vm_area_struct * area, unsigned long address,
+	int write_access, unsigned long newpage)
+{
+	struct inode * inode;
+	unsigned long offset;
+	unsigned long from_address;
+	unsigned long give_page;
+	struct vm_area_struct * mpnt;
+
+	if (!area || !(inode = area->vm_inode) || inode->i_count < 2)
+		return 0;
+	/* do we need to copy or can we just share? */
+	give_page = 0;
+	if (write_access && !(area->vm_flags & VM_SHARED)) {
+		if (!newpage)
+			return 0;
+		give_page = newpage;
+	}
+	offset = address - area->vm_start + area->vm_offset;
+	/* See if there is something in the VM we can share pages with. */
+	/* Traverse the entire circular i_mmap list, except `area' itself. */
+	for (mpnt = area->vm_next_share; mpnt != area; mpnt = mpnt->vm_next_share) {
+		/* must be same inode */
+		if (mpnt->vm_inode != inode) {
+			printk("Aiee! Corrupt vm_area_struct i_mmap ring\n");
+			break;	
+		}
+		/* offsets must be mutually page-aligned */
+		if ((mpnt->vm_offset ^ area->vm_offset) & ~PAGE_MASK)
+			continue;
+		/* the other area must actually cover the wanted page.. */
+		from_address = offset + mpnt->vm_start - mpnt->vm_offset;
+		if (from_address < mpnt->vm_start || from_address >= mpnt->vm_end)
+			continue;
+		/* .. NOW we can actually try to use the same physical page */
+		if (!try_to_share(address, area, from_address, mpnt, give_page))
+			continue;
+		/* free newpage if we never used it.. */
+		if (give_page || !newpage)
+			return 1;
+		free_page(newpage);
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * fill in an empty page-table if none exists.
+ */
+static inline pte_t * get_empty_pgtable(struct task_struct * tsk,unsigned long address)
+{
+	pgd_t *pgd;
+	pmd_t *pmd;
+	pte_t *pte;
+
+	pgd = pgd_offset(tsk, address);
+	pmd = pmd_alloc(pgd, address);
+	if (!pmd) {
+		oom(tsk);
+		return NULL;
+	}
+	pte = pte_alloc(pmd, address);
+	if (!pte) {
+		oom(tsk);
+		return NULL;
+	}
+	return pte;
+}
+
+static inline void do_swap_page(struct vm_area_struct * vma, unsigned long address,
+	pte_t * page_table, pte_t entry, int write_access)
+{
+	pte_t page;
+
+	if (!vma->vm_ops || !vma->vm_ops->swapin) {
+		swap_in(vma, page_table, pte_val(entry), write_access);
+		return;
+	}
+	page = vma->vm_ops->swapin(vma, address - vma->vm_start + vma->vm_offset, pte_val(entry));
+	if (pte_val(*page_table) != pte_val(entry)) {
+		free_page(pte_page(page));
+		return;
+	}
+	if (mem_map[MAP_NR(pte_page(page))] > 1 && !(vma->vm_flags & VM_SHARED))
+		page = pte_wrprotect(page);
+	++vma->vm_task->mm->rss;
+	++vma->vm_task->mm->maj_flt;
+	*page_table = page;
+	return;
+}
+
+/*
+ * do_no_page() tries to create a new page mapping. It aggressively
+ * tries to share with existing pages, but makes a separate copy if
+ * the "write_access" parameter is true in order to avoid the next
+ * page fault.
+ */
+void do_no_page(struct vm_area_struct * vma, unsigned long address,
+	int write_access)
+{
+	pte_t * page_table;
+	pte_t entry;
+	unsigned long page;
+
+	page_table = get_empty_pgtable(vma->vm_task,address);
+	if (!page_table)
+		return;
+	entry = *page_table;
+	if (pte_present(entry))
+		return;
+	if (!pte_none(entry)) {
+		do_swap_page(vma, address, page_table, entry, write_access);
+		return;
+	}
+	address &= PAGE_MASK;
+	if (!vma->vm_ops || !vma->vm_ops->nopage) {
+		++vma->vm_task->mm->rss;
+		++vma->vm_task->mm->min_flt;
+		get_empty_page(vma, page_table);
+		return;
+	}
+	page = get_free_page(GFP_KERNEL);
+	if (share_page(vma, address, write_access, page)) {
+		++vma->vm_task->mm->min_flt;
+		++vma->vm_task->mm->rss;
+		return;
+	}
+	if (!page) {
+		oom(current);
+		put_page(page_table, BAD_PAGE);
+		return;
+	}
+	++vma->vm_task->mm->maj_flt;
+	++vma->vm_task->mm->rss;
+	/*
+	 * The fourth argument is "no_share", which tells the low-level code
+	 * to copy, not share the page even if sharing is possible.  It's
+	 * essentially an early COW detection 
+	 */
+	page = vma->vm_ops->nopage(vma, address, page,
+		write_access && !(vma->vm_flags & VM_SHARED));
+	if (share_page(vma, address, write_access, 0)) {
+		free_page(page);
+		return;
+	}
+	/*
+	 * This silly early PAGE_DIRTY setting removes a race
+	 * due to the bad i386 page protection. But it's valid
+	 * for other architectures too.
+	 *
+	 * Note that if write_access is true, we either now have
+	 * a exclusive copy of the page, or this is a shared mapping,
+	 * so we can make it writable and dirty to avoid having to
+	 * handle that later.
+	 */
+	entry = mk_pte(page, vma->vm_page_prot);
+	if (write_access) {
+		entry = pte_mkwrite(pte_mkdirty(entry));
+	} else if (mem_map[MAP_NR(page)] > 1 && !(vma->vm_flags & VM_SHARED))
+		entry = pte_wrprotect(entry);
+	put_page(page_table, entry);
+}
+
+/*
+ * The above separate functions for the no-page and wp-page
+ * cases will go away (they mostly do the same thing anyway),
+ * and we'll instead use only a general "handle_mm_fault()".
+ *
+ * These routines also need to handle stuff like marking pages dirty
+ * and/or accessed for architectures that don't do it in hardware (most
+ * RISC architectures).  The early dirtying is also good on the i386.
+ *
+ * There is also a hook called "update_mmu_cache()" that architectures
+ * with external mmu caches can use to update those (ie the Sparc or
+ * PowerPC hashed page tables that act as extended TLBs).
+ */
+static inline void handle_pte_fault(struct vm_area_struct * vma, unsigned long address,
+	int write_access, pte_t * pte)
+{
+	if (!pte_present(*pte)) {
+		do_no_page(vma, address, write_access);
+		return;
+	}
+	*pte = pte_mkyoung(*pte);
+	if (!write_access)
+		return;
+	if (pte_write(*pte)) {
+		*pte = pte_mkdirty(*pte);
+		return;
+	}
+	do_wp_page(vma, address, write_access);
+}
+
+void handle_mm_fault(struct vm_area_struct * vma, unsigned long address,
+	int write_access)
+{
+	pgd_t *pgd;
+	pmd_t *pmd;
+	pte_t *pte;
+
+	pgd = pgd_offset(vma->vm_task, address);
+	pmd = pmd_alloc(pgd, address);
+	if (!pmd)
+		goto no_memory;
+	pte = pte_alloc(pmd, address);
+	if (!pte)
+		goto no_memory;
+	handle_pte_fault(vma, address, write_access, pte);
+	update_mmu_cache(vma, address, *pte);
+	return;
+no_memory:
+	oom(vma->vm_task);
+}
diff -r -u -N linux.orig/mm/mmap.c.old linux.arm/mm/mmap.c.old
--- linux.orig/mm/mmap.c.old	Thu Jan  1 01:00:00 1970
+++ linux.arm/mm/mmap.c.old	Fri Oct 27 23:10:39 1995
@@ -0,0 +1,975 @@
+/*
+ *	linux/mm/mmap.c
+ *
+ * Written by obz.
+ */
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/shm.h>
+#include <linux/errno.h>
+#include <linux/mman.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+
+static int anon_map(struct inode *, struct file *, struct vm_area_struct *);
+
+/*
+ * description of effects of mapping type and prot in current implementation.
+ * this is due to the limited x86 page protection hardware.  The expected
+ * behavior is in parens:
+ *
+ * map_type	prot
+ *		PROT_NONE	PROT_READ	PROT_WRITE	PROT_EXEC
+ * MAP_SHARED	r: (no) no	r: (yes) yes	r: (no) yes	r: (no) yes
+ *		w: (no) no	w: (no) no	w: (yes) yes	w: (no) no
+ *		x: (no) no	x: (no) yes	x: (no) yes	x: (yes) yes
+ *		
+ * MAP_PRIVATE	r: (no) no	r: (yes) yes	r: (no) yes	r: (no) yes
+ *		w: (no) no	w: (no) no	w: (copy) copy	w: (no) no
+ *		x: (no) no	x: (no) yes	x: (no) yes	x: (yes) yes
+ *
+ */
+
+pgprot_t protection_map[16] = {
+	__P000, __P001, __P010, __P011, __P100, __P101, __P110, __P111,
+	__S000, __S001, __S010, __S011, __S100, __S101, __S110, __S111
+};
+
+unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len,
+	unsigned long prot, unsigned long flags, unsigned long off)
+{
+	int error;
+	struct vm_area_struct * vma;
+
+	if ((len = PAGE_ALIGN(len)) == 0)
+		return addr;
+
+	if (addr > TASK_SIZE || len > TASK_SIZE || addr > TASK_SIZE-len)
+		return -EINVAL;
+
+	/* offset overflow? */
+	if (off + len < off)
+		return -EINVAL;
+
+	/*
+	 * do simple checking here so the lower-level routines won't have
+	 * to. we assume access permissions have been handled by the open
+	 * of the memory object, so we don't do any here.
+	 */
+
+	if (file != NULL) {
+		switch (flags & MAP_TYPE) {
+		case MAP_SHARED:
+			if ((prot & PROT_WRITE) && !(file->f_mode & 2))
+				return -EACCES;
+			/* fall through */
+		case MAP_PRIVATE:
+			if (!(file->f_mode & 1))
+				return -EACCES;
+			break;
+
+		default:
+			return -EINVAL;
+		}
+		if ((flags & MAP_DENYWRITE) && (file->f_inode->i_wcount > 0))
+			return -ETXTBSY;
+	} else if ((flags & MAP_TYPE) != MAP_PRIVATE)
+		return -EINVAL;
+
+	/*
+	 * obtain the address to map to. we verify (or select) it and ensure
+	 * that it represents a valid section of the address space.
+	 */
+
+	if (flags & MAP_FIXED) {
+		if (addr & ~PAGE_MASK)
+			return -EINVAL;
+		if (len > TASK_SIZE || addr > TASK_SIZE - len)
+			return -EINVAL;
+	} else {
+		addr = get_unmapped_area(len);
+		if (!addr)
+			return -ENOMEM;
+	}
+
+	/*
+	 * determine the object being mapped and call the appropriate
+	 * specific mapper. the address has already been validated, but
+	 * not unmapped, but the maps are removed from the list.
+	 */
+	if (file && (!file->f_op || !file->f_op->mmap))
+		return -ENODEV;
+
+	vma = (struct vm_area_struct *)kmalloc(sizeof(struct vm_area_struct),
+		GFP_KERNEL);
+	if (!vma)
+		return -ENOMEM;
+
+	vma->vm_task = current;
+	vma->vm_start = addr;
+	vma->vm_end = addr + len;
+	vma->vm_flags = prot & (VM_READ | VM_WRITE | VM_EXEC);
+	vma->vm_flags |= flags & (VM_GROWSDOWN | VM_DENYWRITE | VM_EXECUTABLE);
+
+	if (file) {
+		if (file->f_mode & 1)
+			vma->vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
+		if (flags & MAP_SHARED) {
+			vma->vm_flags |= VM_SHARED | VM_MAYSHARE;
+			/*
+			 * This looks strange, but when we don't have the file open
+			 * for writing, we can demote the shared mapping to a simpler
+			 * private mapping. That also takes care of a security hole
+			 * with ptrace() writing to a shared mapping without write
+			 * permissions.
+			 *
+			 * We leave the VM_MAYSHARE bit on, just to get correct output
+			 * from /proc/xxx/maps..
+			 */
+			if (!(file->f_mode & 2))
+				vma->vm_flags &= ~(VM_MAYWRITE | VM_SHARED);
+		}
+	} else
+		vma->vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
+	vma->vm_page_prot = protection_map[vma->vm_flags & 0x0f];
+	vma->vm_ops = NULL;
+	vma->vm_offset = off;
+	vma->vm_inode = NULL;
+	vma->vm_pte = 0;
+
+	do_munmap(addr, len);	/* Clear old maps */
+
+	if (file)
+		error = file->f_op->mmap(file->f_inode, file, vma);
+	else
+		error = anon_map(NULL, NULL, vma);
+	
+	if (error) {
+		kfree(vma);
+		return error;
+	}
+	insert_vm_struct(current, vma);
+	merge_segments(current, vma->vm_start, vma->vm_end);
+	return addr;
+}
+
+/*
+ * Get an address range which is currently unmapped.
+ * For mmap() without MAP_FIXED and shmat() with addr=0.
+ * Return value 0 means ENOMEM.
+ */
+unsigned long get_unmapped_area(unsigned long len)
+{
+	struct vm_area_struct * vmm;
+	unsigned long gap_start = 0, gap_end;
+
+	for (vmm = current->mm->mmap; ; vmm = vmm->vm_next) {
+		if (gap_start < SHM_RANGE_START)
+			gap_start = SHM_RANGE_START;
+		if (!vmm || ((gap_end = vmm->vm_start) > SHM_RANGE_END))
+			gap_end = SHM_RANGE_END;
+		gap_start = PAGE_ALIGN(gap_start);
+		gap_end &= PAGE_MASK;
+		if ((gap_start <= gap_end) && (gap_end - gap_start >= len))
+			return gap_start;
+		if (!vmm)
+			return 0;
+		gap_start = vmm->vm_end;
+	}
+}
+
+asmlinkage int sys_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_fs_long(buffer+3);
+	if (!(flags & MAP_ANONYMOUS)) {
+		unsigned long fd = get_fs_long(buffer+4);
+		if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+			return -EBADF;
+	}
+	return do_mmap(file, get_fs_long(buffer), get_fs_long(buffer+1),
+		get_fs_long(buffer+2), flags, get_fs_long(buffer+5));
+}
+
+
+/*
+ * Searching a VMA in the linear list task->mm->mmap is horribly slow.
+ * Use an AVL (Adelson-Velskii and Landis) tree to speed up this search
+ * from O(n) to O(log n), where n is the number of VMAs of the task
+ * (typically around 6, but may reach 3000 in some cases).
+ * Written by Bruno Haible <haible@ma2s2.mathematik.uni-karlsruhe.de>.
+ */
+
+/* We keep the list and tree sorted by address. */
+#define vm_avl_key	vm_end
+#define vm_avl_key_t	unsigned long	/* typeof(vma->avl_key) */
+
+/*
+ * task->mm->mmap_avl is the AVL tree corresponding to task->mm->mmap
+ * or, more exactly, its root.
+ * A vm_area_struct has the following fields:
+ *   vm_avl_left     left son of a tree node
+ *   vm_avl_right    right son of a tree node
+ *   vm_avl_height   1+max(heightof(left),heightof(right))
+ * The empty tree is represented as NULL.
+ */
+#define avl_empty	(struct vm_area_struct *) NULL
+
+/* Since the trees are balanced, their height will never be large. */
+#define avl_maxheight	41	/* why this? a small exercise */
+#define heightof(tree)	((tree) == avl_empty ? 0 : (tree)->vm_avl_height)
+/*
+ * Consistency and balancing rules:
+ * 1. tree->vm_avl_height == 1+max(heightof(tree->vm_avl_left),heightof(tree->vm_avl_right))
+ * 2. abs( heightof(tree->vm_avl_left) - heightof(tree->vm_avl_right) ) <= 1
+ * 3. foreach node in tree->vm_avl_left: node->vm_avl_key <= tree->vm_avl_key,
+ *    foreach node in tree->vm_avl_right: node->vm_avl_key >= tree->vm_avl_key.
+ */
+
+/* Look up the first VMA which satisfies  addr < vm_end,  NULL if none. */
+struct vm_area_struct * find_vma (struct task_struct * task, unsigned long addr)
+{
+#if 0 /* equivalent, but slow */
+	struct vm_area_struct * vma;
+
+	for (vma = task->mm->mmap ; ; vma = vma->vm_next) {
+		if (!vma)
+			return NULL;
+		if (vma->vm_end > addr)
+			return vma;
+	}
+#else
+	struct vm_area_struct * result = NULL;
+	struct vm_area_struct * tree;
+
+	for (tree = task->mm->mmap_avl ; ; ) {
+		if (tree == avl_empty)
+			return result;
+		if (tree->vm_end > addr) {
+			if (tree->vm_start <= addr)
+				return tree;
+			result = tree;
+			tree = tree->vm_avl_left;
+		} else
+			tree = tree->vm_avl_right;
+	}
+#endif
+}
+
+/* Look up the first VMA which intersects the interval start_addr..end_addr-1,
+   NULL if none.  Assume start_addr < end_addr. */
+struct vm_area_struct * find_vma_intersection (struct task_struct * task, unsigned long start_addr, unsigned long end_addr)
+{
+	struct vm_area_struct * vma;
+
+#if 0 /* equivalent, but slow */
+	for (vma = task->mm->mmap; vma; vma = vma->vm_next) {
+		if (end_addr <= vma->vm_start)
+			break;
+		if (start_addr < vma->vm_end)
+			return vma;
+	}
+	return NULL;
+#else
+	vma = find_vma(task,start_addr);
+	if (!vma || end_addr <= vma->vm_start)
+		return NULL;
+	return vma;
+#endif
+}
+
+/* Look up the nodes at the left and at the right of a given node. */
+static void avl_neighbours (struct vm_area_struct * node, struct vm_area_struct * tree, struct vm_area_struct ** to_the_left, struct vm_area_struct ** to_the_right)
+{
+	vm_avl_key_t key = node->vm_avl_key;
+
+	*to_the_left = *to_the_right = NULL;
+	for (;;) {
+		if (tree == avl_empty) {
+			printk("avl_neighbours: node not found in the tree\n");
+			return;
+		}
+		if (key == tree->vm_avl_key)
+			break;
+		if (key < tree->vm_avl_key) {
+			*to_the_right = tree;
+			tree = tree->vm_avl_left;
+		} else {
+			*to_the_left = tree;
+			tree = tree->vm_avl_right;
+		}
+	}
+	if (tree != node) {
+		printk("avl_neighbours: node not exactly found in the tree\n");
+		return;
+	}
+	if (tree->vm_avl_left != avl_empty) {
+		struct vm_area_struct * node;
+		for (node = tree->vm_avl_left; node->vm_avl_right != avl_empty; node = node->vm_avl_right)
+			continue;
+		*to_the_left = node;
+	}
+	if (tree->vm_avl_right != avl_empty) {
+		struct vm_area_struct * node;
+		for (node = tree->vm_avl_right; node->vm_avl_left != avl_empty; node = node->vm_avl_left)
+			continue;
+		*to_the_right = node;
+	}
+	if ((*to_the_left && ((*to_the_left)->vm_next != node)) || (node->vm_next != *to_the_right))
+		printk("avl_neighbours: tree inconsistent with list\n");
+}
+
+/*
+ * Rebalance a tree.
+ * After inserting or deleting a node of a tree we have a sequence of subtrees
+ * nodes[0]..nodes[k-1] such that
+ * nodes[0] is the root and nodes[i+1] = nodes[i]->{vm_avl_left|vm_avl_right}.
+ */
+static void avl_rebalance (struct vm_area_struct *** nodeplaces_ptr, int count)
+{
+	for ( ; count > 0 ; count--) {
+		struct vm_area_struct ** nodeplace = *--nodeplaces_ptr;
+		struct vm_area_struct * node = *nodeplace;
+		struct vm_area_struct * nodeleft = node->vm_avl_left;
+		struct vm_area_struct * noderight = node->vm_avl_right;
+		int heightleft = heightof(nodeleft);
+		int heightright = heightof(noderight);
+		if (heightright + 1 < heightleft) {
+			/*                                                      */
+			/*                            *                         */
+			/*                          /   \                       */
+			/*                       n+2      n                     */
+			/*                                                      */
+			struct vm_area_struct * nodeleftleft = nodeleft->vm_avl_left;
+			struct vm_area_struct * nodeleftright = nodeleft->vm_avl_right;
+			int heightleftright = heightof(nodeleftright);
+			if (heightof(nodeleftleft) >= heightleftright) {
+				/*                                                        */
+				/*                *                    n+2|n+3            */
+				/*              /   \                  /    \             */
+				/*           n+2      n      -->      /   n+1|n+2         */
+				/*           / \                      |    /    \         */
+				/*         n+1 n|n+1                 n+1  n|n+1  n        */
+				/*                                                        */
+				node->vm_avl_left = nodeleftright; nodeleft->vm_avl_right = node;
+				nodeleft->vm_avl_height = 1 + (node->vm_avl_height = 1 + heightleftright);
+				*nodeplace = nodeleft;
+			} else {
+				/*                                                        */
+				/*                *                     n+2               */
+				/*              /   \                 /     \             */
+				/*           n+2      n      -->    n+1     n+1           */
+				/*           / \                    / \     / \           */
+				/*          n  n+1                 n   L   R   n          */
+				/*             / \                                        */
+				/*            L   R                                       */
+				/*                                                        */
+				nodeleft->vm_avl_right = nodeleftright->vm_avl_left;
+				node->vm_avl_left = nodeleftright->vm_avl_right;
+				nodeleftright->vm_avl_left = nodeleft;
+				nodeleftright->vm_avl_right = node;
+				nodeleft->vm_avl_height = node->vm_avl_height = heightleftright;
+				nodeleftright->vm_avl_height = heightleft;
+				*nodeplace = nodeleftright;
+			}
+		}
+		else if (heightleft + 1 < heightright) {
+			/* similar to the above, just interchange 'left' <--> 'right' */
+			struct vm_area_struct * noderightright = noderight->vm_avl_right;
+			struct vm_area_struct * noderightleft = noderight->vm_avl_left;
+			int heightrightleft = heightof(noderightleft);
+			if (heightof(noderightright) >= heightrightleft) {
+				node->vm_avl_right = noderightleft; noderight->vm_avl_left = node;
+				noderight->vm_avl_height = 1 + (node->vm_avl_height = 1 + heightrightleft);
+				*nodeplace = noderight;
+			} else {
+				noderight->vm_avl_left = noderightleft->vm_avl_right;
+				node->vm_avl_right = noderightleft->vm_avl_left;
+				noderightleft->vm_avl_right = noderight;
+				noderightleft->vm_avl_left = node;
+				noderight->vm_avl_height = node->vm_avl_height = heightrightleft;
+				noderightleft->vm_avl_height = heightright;
+				*nodeplace = noderightleft;
+			}
+		}
+		else {
+			int height = (heightleft<heightright ? heightright : heightleft) + 1;
+			if (height == node->vm_avl_height)
+				break;
+			node->vm_avl_height = height;
+		}
+	}
+}
+
+/* Insert a node into a tree. */
+static void avl_insert (struct vm_area_struct * new_node, struct vm_area_struct ** ptree)
+{
+	vm_avl_key_t key = new_node->vm_avl_key;
+	struct vm_area_struct ** nodeplace = ptree;
+	struct vm_area_struct ** stack[avl_maxheight];
+	int stack_count = 0;
+	struct vm_area_struct *** stack_ptr = &stack[0]; /* = &stack[stackcount] */
+	for (;;) {
+		struct vm_area_struct * node = *nodeplace;
+		if (node == avl_empty)
+			break;
+		*stack_ptr++ = nodeplace; stack_count++;
+		if (key < node->vm_avl_key)
+			nodeplace = &node->vm_avl_left;
+		else
+			nodeplace = &node->vm_avl_right;
+	}
+	new_node->vm_avl_left = avl_empty;
+	new_node->vm_avl_right = avl_empty;
+	new_node->vm_avl_height = 1;
+	*nodeplace = new_node;
+	avl_rebalance(stack_ptr,stack_count);
+}
+
+/* Insert a node into a tree, and
+ * return the node to the left of it and the node to the right of it.
+ */
+static void avl_insert_neighbours (struct vm_area_struct * new_node, struct vm_area_struct ** ptree,
+	struct vm_area_struct ** to_the_left, struct vm_area_struct ** to_the_right)
+{
+	vm_avl_key_t key = new_node->vm_avl_key;
+	struct vm_area_struct ** nodeplace = ptree;
+	struct vm_area_struct ** stack[avl_maxheight];
+	int stack_count = 0;
+	struct vm_area_struct *** stack_ptr = &stack[0]; /* = &stack[stackcount] */
+	*to_the_left = *to_the_right = NULL;
+	for (;;) {
+		struct vm_area_struct * node = *nodeplace;
+		if (node == avl_empty)
+			break;
+		*stack_ptr++ = nodeplace; stack_count++;
+		if (key < node->vm_avl_key) {
+			*to_the_right = node;
+			nodeplace = &node->vm_avl_left;
+		} else {
+			*to_the_left = node;
+			nodeplace = &node->vm_avl_right;
+		}
+	}
+	new_node->vm_avl_left = avl_empty;
+	new_node->vm_avl_right = avl_empty;
+	new_node->vm_avl_height = 1;
+	*nodeplace = new_node;
+	avl_rebalance(stack_ptr,stack_count);
+}
+
+/* Removes a node out of a tree. */
+static void avl_remove (struct vm_area_struct * node_to_delete, struct vm_area_struct ** ptree)
+{
+	vm_avl_key_t key = node_to_delete->vm_avl_key;
+	struct vm_area_struct ** nodeplace = ptree;
+	struct vm_area_struct ** stack[avl_maxheight];
+	int stack_count = 0;
+	struct vm_area_struct *** stack_ptr = &stack[0]; /* = &stack[stackcount] */
+	struct vm_area_struct ** nodeplace_to_delete;
+	for (;;) {
+		struct vm_area_struct * node = *nodeplace;
+		if (node == avl_empty) {
+			/* what? node_to_delete not found in tree? */
+			printk("avl_remove: node to delete not found in tree\n");
+			return;
+		}
+		*stack_ptr++ = nodeplace; stack_count++;
+		if (key == node->vm_avl_key)
+			break;
+		if (key < node->vm_avl_key)
+			nodeplace = &node->vm_avl_left;
+		else
+			nodeplace = &node->vm_avl_right;
+	}
+	nodeplace_to_delete = nodeplace;
+	/* Have to remove node_to_delete = *nodeplace_to_delete. */
+	if (node_to_delete->vm_avl_left == avl_empty) {
+		*nodeplace_to_delete = node_to_delete->vm_avl_right;
+		stack_ptr--; stack_count--;
+	} else {
+		struct vm_area_struct *** stack_ptr_to_delete = stack_ptr;
+		struct vm_area_struct ** nodeplace = &node_to_delete->vm_avl_left;
+		struct vm_area_struct * node;
+		for (;;) {
+			node = *nodeplace;
+			if (node->vm_avl_right == avl_empty)
+				break;
+			*stack_ptr++ = nodeplace; stack_count++;
+			nodeplace = &node->vm_avl_right;
+		}
+		*nodeplace = node->vm_avl_left;
+		/* node replaces node_to_delete */
+		node->vm_avl_left = node_to_delete->vm_avl_left;
+		node->vm_avl_right = node_to_delete->vm_avl_right;
+		node->vm_avl_height = node_to_delete->vm_avl_height;
+		*nodeplace_to_delete = node; /* replace node_to_delete */
+		*stack_ptr_to_delete = &node->vm_avl_left; /* replace &node_to_delete->vm_avl_left */
+	}
+	avl_rebalance(stack_ptr,stack_count);
+}
+
+#ifdef DEBUG_AVL
+
+/* print a list */
+static void printk_list (struct vm_area_struct * vma)
+{
+	printk("[");
+	while (vma) {
+		printk("%08lX-%08lX", vma->vm_start, vma->vm_end);
+		vma = vma->vm_next;
+		if (!vma)
+			break;
+		printk(" ");
+	}
+	printk("]");
+}
+
+/* print a tree */
+static void printk_avl (struct vm_area_struct * tree)
+{
+	if (tree != avl_empty) {
+		printk("(");
+		if (tree->vm_avl_left != avl_empty) {
+			printk_avl(tree->vm_avl_left);
+			printk("<");
+		}
+		printk("%08lX-%08lX", tree->vm_start, tree->vm_end);
+		if (tree->vm_avl_right != avl_empty) {
+			printk(">");
+			printk_avl(tree->vm_avl_right);
+		}
+		printk(")");
+	}
+}
+
+static char *avl_check_point = "somewhere";
+
+/* check a tree's consistency and balancing */
+static void avl_checkheights (struct vm_area_struct * tree)
+{
+	int h, hl, hr;
+
+	if (tree == avl_empty)
+		return;
+	avl_checkheights(tree->vm_avl_left);
+	avl_checkheights(tree->vm_avl_right);
+	h = tree->vm_avl_height;
+	hl = heightof(tree->vm_avl_left);
+	hr = heightof(tree->vm_avl_right);
+	if ((h == hl+1) && (hr <= hl) && (hl <= hr+1))
+		return;
+	if ((h == hr+1) && (hl <= hr) && (hr <= hl+1))
+		return;
+	printk("%s: avl_checkheights: heights inconsistent\n",avl_check_point);
+}
+
+/* check that all values stored in a tree are < key */
+static void avl_checkleft (struct vm_area_struct * tree, vm_avl_key_t key)
+{
+	if (tree == avl_empty)
+		return;
+	avl_checkleft(tree->vm_avl_left,key);
+	avl_checkleft(tree->vm_avl_right,key);
+	if (tree->vm_avl_key < key)
+		return;
+	printk("%s: avl_checkleft: left key %lu >= top key %lu\n",avl_check_point,tree->vm_avl_key,key);
+}
+
+/* check that all values stored in a tree are > key */
+static void avl_checkright (struct vm_area_struct * tree, vm_avl_key_t key)
+{
+	if (tree == avl_empty)
+		return;
+	avl_checkright(tree->vm_avl_left,key);
+	avl_checkright(tree->vm_avl_right,key);
+	if (tree->vm_avl_key > key)
+		return;
+	printk("%s: avl_checkright: right key %lu <= top key %lu\n",avl_check_point,tree->vm_avl_key,key);
+}
+
+/* check that all values are properly increasing */
+static void avl_checkorder (struct vm_area_struct * tree)
+{
+	if (tree == avl_empty)
+		return;
+	avl_checkorder(tree->vm_avl_left);
+	avl_checkorder(tree->vm_avl_right);
+	avl_checkleft(tree->vm_avl_left,tree->vm_avl_key);
+	avl_checkright(tree->vm_avl_right,tree->vm_avl_key);
+}
+
+/* all checks */
+static void avl_check (struct task_struct * task, char *caller)
+{
+	avl_check_point = caller;
+/*	printk("task \"%s\", %s\n",task->comm,caller); */
+/*	printk("task \"%s\" list: ",task->comm); printk_list(task->mm->mmap); printk("\n"); */
+/*	printk("task \"%s\" tree: ",task->comm); printk_avl(task->mm->mmap_avl); printk("\n"); */
+	avl_checkheights(task->mm->mmap_avl);
+	avl_checkorder(task->mm->mmap_avl);
+}
+
+#endif
+
+
+/*
+ * Normal function to fix up a mapping
+ * This function is the default for when an area has no specific
+ * function.  This may be used as part of a more specific routine.
+ * This function works out what part of an area is affected and
+ * adjusts the mapping information.  Since the actual page
+ * manipulation is done in do_mmap(), none need be done here,
+ * though it would probably be more appropriate.
+ *
+ * By the time this function is called, the area struct has been
+ * removed from the process mapping list, so it needs to be
+ * reinserted if necessary.
+ *
+ * The 4 main cases are:
+ *    Unmapping the whole area
+ *    Unmapping from the start of the segment to a point in it
+ *    Unmapping from an intermediate point to the end
+ *    Unmapping between to intermediate points, making a hole.
+ *
+ * Case 4 involves the creation of 2 new areas, for each side of
+ * the hole.
+ */
+void unmap_fixup(struct vm_area_struct *area,
+		 unsigned long addr, size_t len)
+{
+	struct vm_area_struct *mpnt;
+	unsigned long end = addr + len;
+
+	if (addr < area->vm_start || addr >= area->vm_end ||
+	    end <= area->vm_start || end > area->vm_end ||
+	    end < addr)
+	{
+		printk("unmap_fixup: area=%lx-%lx, unmap %lx-%lx!!\n",
+		       area->vm_start, area->vm_end, addr, end);
+		return;
+	}
+
+	/* Unmapping the whole area */
+	if (addr == area->vm_start && end == area->vm_end) {
+		if (area->vm_ops && area->vm_ops->close)
+			area->vm_ops->close(area);
+		if (area->vm_inode)
+			iput(area->vm_inode);
+		return;
+	}
+
+	/* Work out to one of the ends */
+	if (end == area->vm_end)
+		area->vm_end = addr;
+	else
+	if (addr == area->vm_start) {
+		area->vm_offset += (end - area->vm_start);
+		area->vm_start = end;
+	}
+	else {
+	/* Unmapping a hole: area->vm_start < addr <= end < area->vm_end */
+		/* Add end mapping -- leave beginning for below */
+		mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL);
+
+		if (!mpnt)
+			return;
+		*mpnt = *area;
+		mpnt->vm_offset += (end - area->vm_start);
+		mpnt->vm_start = end;
+		if (mpnt->vm_inode)
+			mpnt->vm_inode->i_count++;
+		if (mpnt->vm_ops && mpnt->vm_ops->open)
+			mpnt->vm_ops->open(mpnt);
+		area->vm_end = addr;	/* Truncate area */
+		insert_vm_struct(current, mpnt);
+	}
+
+	/* construct whatever mapping is needed */
+	mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL);
+	if (!mpnt)
+		return;
+	*mpnt = *area;
+	if (mpnt->vm_ops && mpnt->vm_ops->open)
+		mpnt->vm_ops->open(mpnt);
+	if (area->vm_ops && area->vm_ops->close) {
+		area->vm_end = area->vm_start;
+		area->vm_ops->close(area);
+	}
+	insert_vm_struct(current, mpnt);
+}
+
+asmlinkage int sys_munmap(unsigned long addr, size_t len)
+{
+	return do_munmap(addr, len);
+}
+
+/*
+ * Munmap is split into 2 main parts -- this part which finds
+ * what needs doing, and the areas themselves, which do the
+ * work.  This now handles partial unmappings.
+ * Jeremy Fitzhardine <jeremy@sw.oz.au>
+ */
+int do_munmap(unsigned long addr, size_t len)
+{
+	struct vm_area_struct *mpnt, *prev, *next, **npp, *free;
+
+	if ((addr & ~PAGE_MASK) || addr > TASK_SIZE || len > TASK_SIZE-addr)
+		return -EINVAL;
+
+	if ((len = PAGE_ALIGN(len)) == 0)
+		return 0;
+
+	/*
+	 * Check if this memory area is ok - put it on the temporary
+	 * list if so..  The checks here are pretty simple --
+	 * every area affected in some way (by any overlap) is put
+	 * on the list.  If nothing is put on, nothing is affected.
+	 */
+	mpnt = find_vma(current, addr);
+	if (!mpnt)
+		return 0;
+	avl_neighbours(mpnt, current->mm->mmap_avl, &prev, &next);
+	/* we have  prev->vm_next == mpnt && mpnt->vm_next = next */
+	/* and  addr < mpnt->vm_end  */
+
+	npp = (prev ? &prev->vm_next : &current->mm->mmap);
+	free = NULL;
+	for ( ; mpnt && mpnt->vm_start < addr+len; mpnt = *npp) {
+		*npp = mpnt->vm_next;
+		mpnt->vm_next = free;
+		free = mpnt;
+		avl_remove(mpnt, &current->mm->mmap_avl);
+	}
+
+	if (free == NULL)
+		return 0;
+
+	/*
+	 * Ok - we have the memory areas we should free on the 'free' list,
+	 * so release them, and unmap the page range..
+	 * If the one of the segments is only being partially unmapped,
+	 * it will put new vm_area_struct(s) into the address space.
+	 */
+	while (free) {
+		unsigned long st, end;
+
+		mpnt = free;
+		free = free->vm_next;
+
+		remove_shared_vm_struct(mpnt);
+
+		st = addr < mpnt->vm_start ? mpnt->vm_start : addr;
+		end = addr+len;
+		end = end > mpnt->vm_end ? mpnt->vm_end : end;
+
+		if (mpnt->vm_ops && mpnt->vm_ops->unmap)
+			mpnt->vm_ops->unmap(mpnt, st, end-st);
+
+		unmap_fixup(mpnt, st, end-st);
+		kfree(mpnt);
+	}
+
+	unmap_page_range(addr, len);
+	return 0;
+}
+
+/* Build the AVL tree corresponding to the VMA list. */
+void build_mmap_avl(struct task_struct * task)
+{
+	struct vm_area_struct * vma;
+
+	task->mm->mmap_avl = NULL;
+	for (vma = task->mm->mmap; vma; vma = vma->vm_next)
+		avl_insert(vma, &task->mm->mmap_avl);
+}
+
+/* Release all mmaps. */
+void exit_mmap(struct task_struct * task)
+{
+	struct vm_area_struct * mpnt;
+
+	mpnt = task->mm->mmap;
+	task->mm->mmap = NULL;
+	task->mm->mmap_avl = NULL;
+	while (mpnt) {
+		struct vm_area_struct * next = mpnt->vm_next;
+		if (mpnt->vm_ops && mpnt->vm_ops->close)
+			mpnt->vm_ops->close(mpnt);
+		remove_shared_vm_struct(mpnt);
+		if (mpnt->vm_inode)
+			iput(mpnt->vm_inode);
+		kfree(mpnt);
+		mpnt = next;
+	}
+}
+
+/*
+ * Insert vm structure into process list sorted by address
+ * and into the inode's i_mmap ring.
+ */
+void insert_vm_struct(struct task_struct *t, struct vm_area_struct *vmp)
+{
+	struct vm_area_struct *share;
+	struct inode * inode;
+
+#if 0 /* equivalent, but slow */
+	struct vm_area_struct **p, *mpnt;
+
+	p = &t->mm->mmap;
+	while ((mpnt = *p) != NULL) {
+		if (mpnt->vm_start > vmp->vm_start)
+			break;
+		if (mpnt->vm_end > vmp->vm_start)
+			printk("insert_vm_struct: overlapping memory areas\n");
+		p = &mpnt->vm_next;
+	}
+	vmp->vm_next = mpnt;
+	*p = vmp;
+#else
+	struct vm_area_struct * prev, * next;
+
+	avl_insert_neighbours(vmp, &t->mm->mmap_avl, &prev, &next);
+	if ((prev ? prev->vm_next : t->mm->mmap) != next)
+		printk("insert_vm_struct: tree inconsistent with list\n");
+	if (prev)
+		prev->vm_next = vmp;
+	else
+		t->mm->mmap = vmp;
+	vmp->vm_next = next;
+#endif
+
+	inode = vmp->vm_inode;
+	if (!inode)
+		return;
+
+	/* insert vmp into inode's circular share list */
+	if ((share = inode->i_mmap)) {
+		vmp->vm_next_share = share->vm_next_share;
+		vmp->vm_next_share->vm_prev_share = vmp;
+		share->vm_next_share = vmp;
+		vmp->vm_prev_share = share;
+	} else
+		inode->i_mmap = vmp->vm_next_share = vmp->vm_prev_share = vmp;
+}
+
+/*
+ * Remove one vm structure from the inode's i_mmap ring.
+ */
+void remove_shared_vm_struct(struct vm_area_struct *mpnt)
+{
+	struct inode * inode = mpnt->vm_inode;
+
+	if (!inode)
+		return;
+
+	if (mpnt->vm_next_share == mpnt) {
+		if (inode->i_mmap != mpnt)
+			printk("Inode i_mmap ring corrupted\n");
+		inode->i_mmap = NULL;
+		return;
+	}
+
+	if (inode->i_mmap == mpnt)
+		inode->i_mmap = mpnt->vm_next_share;
+
+	mpnt->vm_prev_share->vm_next_share = mpnt->vm_next_share;
+	mpnt->vm_next_share->vm_prev_share = mpnt->vm_prev_share;
+}
+
+/*
+ * Merge the list of memory segments if possible.
+ * Redundant vm_area_structs are freed.
+ * This assumes that the list is ordered by address.
+ * We don't need to traverse the entire list, only those segments
+ * which intersect or are adjacent to a given interval.
+ */
+void merge_segments (struct task_struct * task, unsigned long start_addr, unsigned long end_addr)
+{
+	struct vm_area_struct *prev, *mpnt, *next;
+
+	mpnt = find_vma(task, start_addr);
+	if (!mpnt)
+		return;
+	avl_neighbours(mpnt, task->mm->mmap_avl, &prev, &next);
+	/* we have  prev->vm_next == mpnt && mpnt->vm_next = next */
+
+	if (!prev) {
+		prev = mpnt;
+		mpnt = next;
+	}
+
+	/* prev and mpnt cycle through the list, as long as
+	 * start_addr < mpnt->vm_end && prev->vm_start < end_addr
+	 */
+	for ( ; mpnt && prev->vm_start < end_addr ; prev = mpnt, mpnt = next) {
+#if 0
+		printk("looping in merge_segments, mpnt=0x%lX\n", (unsigned long) mpnt);
+#endif
+
+		next = mpnt->vm_next;
+
+		/*
+		 * To share, we must have the same inode, operations.. 
+		 */
+		if (mpnt->vm_inode != prev->vm_inode)
+			continue;
+		if (mpnt->vm_pte != prev->vm_pte)
+			continue;
+		if (mpnt->vm_ops != prev->vm_ops)
+			continue;
+		if (mpnt->vm_flags != prev->vm_flags)
+			continue;
+		if (prev->vm_end != mpnt->vm_start)
+			continue;
+		/*
+		 * and if we have an inode, the offsets must be contiguous..
+		 */
+		if ((mpnt->vm_inode != NULL) || (mpnt->vm_flags & VM_SHM)) {
+			if (prev->vm_offset + prev->vm_end - prev->vm_start != mpnt->vm_offset)
+				continue;
+		}
+
+		/*
+		 * merge prev with mpnt and set up pointers so the new
+		 * big segment can possibly merge with the next one.
+		 * The old unused mpnt is freed.
+		 */
+		avl_remove(mpnt, &task->mm->mmap_avl);
+		prev->vm_end = mpnt->vm_end;
+		prev->vm_next = mpnt->vm_next;
+		if (mpnt->vm_ops && mpnt->vm_ops->close) {
+			mpnt->vm_offset += mpnt->vm_end - mpnt->vm_start;
+			mpnt->vm_start = mpnt->vm_end;
+			mpnt->vm_ops->close(mpnt);
+		}
+		remove_shared_vm_struct(mpnt);
+		if (mpnt->vm_inode)
+			mpnt->vm_inode->i_count--;
+		kfree_s(mpnt, sizeof(*mpnt));
+		mpnt = prev;
+	}
+}
+
+/*
+ * Map memory not associated with any file into a process
+ * address space.  Adjacent memory is merged.
+ */
+static int anon_map(struct inode *ino, struct file * file, struct vm_area_struct * vma)
+{
+	if (zeromap_page_range(vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot))
+		return -ENOMEM;
+	return 0;
+}
diff -r -u -N linux.orig/mm/swap.c linux.arm/mm/swap.c
--- linux.orig/mm/swap.c	Fri Oct 27 23:09:12 1995
+++ linux.arm/mm/swap.c	Fri Oct 27 23:10:32 1995
@@ -29,10 +29,14 @@
 
 #define SWP_USED	1
 #define SWP_WRITEOK	3
-
+/* rmk */
 #define SWP_TYPE(entry) (((entry) >> 1) & 0x7f)
-#define SWP_OFFSET(entry) ((entry) >> 12)
-#define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 12))
+#define SWP_OFFSET(entry) ((entry) >> PAGE_SHIFT)
+#define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << PAGE_SHIFT))
+
+#ifdef __arm__
+int min_free_sec_pages = 8;
+#endif
 
 int min_free_pages = 20;
 
@@ -388,6 +392,7 @@
  * (C) 1993 Kai Petzke, wpp@marie.physik.tu-berlin.de
  */
 
+#ifndef __arm__
 /*
  * These are the minimum and maximum number of pages to swap from one process,
  * before proceeding to the next:
@@ -400,6 +405,11 @@
  * SWAP_RATIO / (number of recent major page faults)
  */
 #define SWAP_RATIO	128
+#else
+#define SWAP_MIN	1
+#define SWAP_MAX	2
+#define SWAP_RATIO	4
+#endif
 
 static inline int swap_out_pmd(struct vm_area_struct * vma, pmd_t *dir,
 	unsigned long address, unsigned long end)
@@ -601,7 +611,7 @@
 			if (shm_swap(i))
 				return 1;
 			state = 2;
-		default:
+		default: /*rmk*/ state = 0; /* try to reduce buffers! */
 			if (swap_out(i))
 				return 1;
 			state = 0;
@@ -678,6 +688,17 @@
 
 void free_pages(unsigned long addr, unsigned long order)
 {
+#ifdef __arm__
+	void *bir; __asm__ ("ldr %0, [ip,#-8]\n\t": "=r" (bir));
+	if (addr == 0)	/* ignore non-allocated pages */
+		return;
+
+	if (addr < LOGICAL_END) /* trap logical memory free's */
+	{
+		printk("Trying to free logical memory (%p)\nPC = %p\n", addr, bir);
+		return;
+	}
+#endif
 	if (addr < high_memory) {
 		unsigned long flag;
 		mem_map_t * map = mem_map + MAP_NR(addr);
@@ -696,7 +717,12 @@
 			return;
 		}
 		printk("Trying to free free memory (%08lx): memory probably corrupted\n",addr);
+#ifndef __arm__
 		printk("PC = %p\n", __builtin_return_address(0));
+#else
+		/* oh I wish __builtin_return_address worked!! */
+		printk("PC = %p\n", bir);
+#endif
 		return;
 	}
 }
@@ -750,7 +776,11 @@
 	}
 	reserved_pages = 5;
 	if (priority != GFP_NFS)
+#ifndef __arm__
 		reserved_pages = min_free_pages;
+#else
+		reserved_pages = min_free_sec_pages;
+#endif
 	save_flags(flags);
 repeat:
 	cli();
@@ -1114,7 +1144,11 @@
 		goto bad_swap;
 	}
 	read_swap_page(SWP_ENTRY(type,0), (char *) p->swap_lockmap);
+#ifndef __arm__
 	if (memcmp("SWAP-SPACE",p->swap_lockmap+4086,10)) {
+#else
+	if (memcmp("SWAP-SPACE",p->swap_lockmap+PAGE_SIZE-10,10)) {
+#endif
 		printk("Unable to find swap-space signature\n");
 		error = -EINVAL;
 		goto bad_swap;
@@ -1153,7 +1187,11 @@
 	p->flags = SWP_WRITEOK;
 	p->pages = j;
 	nr_swap_pages += j;
+#ifndef __arm__
 	printk("Adding Swap: %dk swap-space\n",j<<2);
+#else
+	printk("Adding Swap: %dk swap-space\n", j<<(PAGE_SHIFT - 10));
+#endif
 	return 0;
 bad_swap:
 	if(filp.f_op && filp.f_op->release)
@@ -1199,6 +1237,8 @@
  *   - mark all memory queues empty
  *   - clear the memory bitmaps
  */
+
+
 unsigned long free_area_init(unsigned long start_mem, unsigned long end_mem)
 {
 	mem_map_t * p;
@@ -1209,7 +1249,12 @@
 	 * select nr of pages we try to keep free for important stuff
 	 * with a minimum of 16 pages. This is totally arbitrary
 	 */
+#ifndef __arm__
 	i = (end_mem - PAGE_OFFSET) >> (PAGE_SHIFT+6);
+#else
+	i = (end_mem - PAGE_OFFSET) >> (PAGE_SHIFT+3);
+	min_free_sec_pages = 8;
+#endif
 	if (i < 16)
 		i = 16;
 	min_free_pages = i;
@@ -1232,5 +1277,6 @@
 		memset((void *) start_mem, 0, bitmap_size);
 		start_mem += bitmap_size;
 	}
+
 	return start_mem;
 }
diff -r -u -N linux.orig/mm/swap.c.old linux.arm/mm/swap.c.old
--- linux.orig/mm/swap.c.old	Thu Jan  1 01:00:00 1970
+++ linux.arm/mm/swap.c.old	Fri Oct 27 23:10:38 1995
@@ -0,0 +1,1240 @@
+#define THREE_LEVEL
+/*
+ *  linux/mm/swap.c
+ *
+ *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
+ */
+
+/*
+ * This file should contain most things doing the swapping from/to disk.
+ * Started 18.12.91
+ */
+
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/head.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/fs.h>
+
+#include <asm/dma.h>
+#include <asm/system.h> /* for cli()/sti() */
+#include <asm/bitops.h>
+#include <asm/pgtable.h>
+
+#define MAX_SWAPFILES 8
+
+#define SWP_USED	1
+#define SWP_WRITEOK	3
+/* rmk */
+#define SWP_TYPE(entry) (((entry) >> 1) & 0x7f)
+#define SWP_OFFSET(entry) ((entry) >> PAGE_SHIFT)
+#define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << PAGE_SHIFT))
+
+int min_free_pages = 20;
+
+static int nr_swapfiles = 0;
+static struct wait_queue * lock_queue = NULL;
+
+static struct swap_info_struct {
+	unsigned long flags;
+	struct inode * swap_file;
+	unsigned int swap_device;
+	unsigned char * swap_map;
+	unsigned char * swap_lockmap;
+	int pages;
+	int lowest_bit;
+	int highest_bit;
+	unsigned long max;
+} swap_info[MAX_SWAPFILES];
+
+extern int shm_swap (int);
+
+unsigned long *swap_cache;
+
+#ifdef SWAP_CACHE_INFO
+unsigned long swap_cache_add_total = 0;
+unsigned long swap_cache_add_success = 0;
+unsigned long swap_cache_del_total = 0;
+unsigned long swap_cache_del_success = 0;
+unsigned long swap_cache_find_total = 0;
+unsigned long swap_cache_find_success = 0;
+
+extern inline void show_swap_cache_info(void)
+{
+	printk("Swap cache: add %ld/%ld, delete %ld/%ld, find %ld/%ld\n",
+		swap_cache_add_total, swap_cache_add_success, 
+		swap_cache_del_total, swap_cache_del_success,
+		swap_cache_find_total, swap_cache_find_success);
+}
+#endif
+
+static int add_to_swap_cache(unsigned long addr, unsigned long entry)
+{
+	struct swap_info_struct * p = &swap_info[SWP_TYPE(entry)];
+
+#ifdef SWAP_CACHE_INFO
+	swap_cache_add_total++;
+#endif
+	if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) {
+		entry = (unsigned long) xchg_ptr(swap_cache + MAP_NR(addr), (void *) entry);
+		if (entry)  {
+			printk("swap_cache: replacing non-NULL entry\n");
+		}
+#ifdef SWAP_CACHE_INFO
+		swap_cache_add_success++;
+#endif
+		return 1;
+	}
+	return 0;
+}
+
+static unsigned long init_swap_cache(unsigned long mem_start,
+	unsigned long mem_end)
+{
+	unsigned long swap_cache_size;
+
+	mem_start = (mem_start + 15) & ~15;
+	swap_cache = (unsigned long *) mem_start;
+	swap_cache_size = MAP_NR(mem_end);
+	memset(swap_cache, 0, swap_cache_size * sizeof (unsigned long));
+	return (unsigned long) (swap_cache + swap_cache_size);
+}
+
+void rw_swap_page(int rw, unsigned long entry, char * buf)
+{
+	unsigned long type, offset;
+	struct swap_info_struct * p;
+
+	type = SWP_TYPE(entry);
+	if (type >= nr_swapfiles) {
+		printk("Internal error: bad swap-device\n");
+		return;
+	}
+	p = &swap_info[type];
+	offset = SWP_OFFSET(entry);
+	if (offset >= p->max) {
+		printk("rw_swap_page: weirdness\n");
+		return;
+	}
+	if (p->swap_map && !p->swap_map[offset]) {
+		printk("Hmm.. Trying to use unallocated swap (%08lx)\n", entry);
+		return;
+	}
+	if (!(p->flags & SWP_USED)) {
+		printk("Trying to swap to unused swap-device\n");
+		return;
+	}
+	while (set_bit(offset,p->swap_lockmap))
+		sleep_on(&lock_queue);
+	if (rw == READ)
+		kstat.pswpin++;
+	else
+		kstat.pswpout++;
+	if (p->swap_device) {
+		ll_rw_page(rw,p->swap_device,offset,buf);
+	} else if (p->swap_file) {
+		struct inode *swapf = p->swap_file;
+		unsigned int zones[8];
+		int i;
+		if (swapf->i_op->bmap == NULL
+			&& swapf->i_op->smap != NULL){
+			/*
+				With MsDOS, we use msdos_smap which return
+				a sector number (not a cluster or block number).
+				It is a patch to enable the UMSDOS project.
+				Other people are working on better solution.
+
+				It sounds like ll_rw_swap_file defined
+				it operation size (sector size) based on
+				PAGE_SIZE and the number of block to read.
+				So using bmap or smap should work even if
+				smap will require more blocks.
+			*/
+			int j;
+			unsigned int block = offset << 3;
+
+			for (i=0, j=0; j< PAGE_SIZE ; i++, j += 512){
+				if (!(zones[i] = swapf->i_op->smap(swapf,block++))) {
+					printk("rw_swap_page: bad swap file\n");
+					return;
+				}
+			}
+		}else{
+			int j;
+			unsigned int block = offset
+				<< (12 - swapf->i_sb->s_blocksize_bits);
+
+			for (i=0, j=0; j< PAGE_SIZE ; i++, j +=swapf->i_sb->s_blocksize)
+				if (!(zones[i] = bmap(swapf,block++))) {
+					printk("rw_swap_page: bad swap file\n");
+					return;
+				}
+		}
+		ll_rw_swap_file(rw,swapf->i_dev, zones, i,buf);
+	} else
+		printk("re_swap_page: no swap file or device\n");
+	if (offset && !clear_bit(offset,p->swap_lockmap))
+		printk("rw_swap_page: lock already cleared\n");
+	wake_up(&lock_queue);
+}
+
+unsigned int get_swap_page(void)
+{
+	struct swap_info_struct * p;
+	unsigned int offset, type;
+
+	p = swap_info;
+	for (type = 0 ; type < nr_swapfiles ; type++,p++) {
+		if ((p->flags & SWP_WRITEOK) != SWP_WRITEOK)
+			continue;
+		for (offset = p->lowest_bit; offset <= p->highest_bit ; offset++) {
+			if (p->swap_map[offset])
+				continue;
+			if (test_bit(offset, p->swap_lockmap))
+				continue;
+			p->swap_map[offset] = 1;
+			nr_swap_pages--;
+			if (offset == p->highest_bit)
+				p->highest_bit--;
+			p->lowest_bit = offset;
+			return SWP_ENTRY(type,offset);
+		}
+	}
+	return 0;
+}
+
+void swap_duplicate(unsigned long entry)
+{
+	struct swap_info_struct * p;
+	unsigned long offset, type;
+
+	if (!entry)
+		return;
+	offset = SWP_OFFSET(entry);
+	type = SWP_TYPE(entry);
+	if (type == SHM_SWP_TYPE)
+		return;
+	if (type >= nr_swapfiles) {
+		printk("Trying to duplicate nonexistent swap-page\n");
+		return;
+	}
+	p = type + swap_info;
+	if (offset >= p->max) {
+		printk("swap_duplicate: weirdness\n");
+		return;
+	}
+	if (!p->swap_map[offset]) {
+		printk("swap_duplicate: trying to duplicate unused page\n");
+		return;
+	}
+	p->swap_map[offset]++;
+	return;
+}
+
+void swap_free(unsigned long entry)
+{
+	struct swap_info_struct * p;
+	unsigned long offset, type;
+
+	if (!entry)
+		return;
+	type = SWP_TYPE(entry);
+	if (type == SHM_SWP_TYPE)
+		return;
+	if (type >= nr_swapfiles) {
+		printk("Trying to free nonexistent swap-page\n");
+		return;
+	}
+	p = & swap_info[type];
+	offset = SWP_OFFSET(entry);
+	if (offset >= p->max) {
+		printk("swap_free: weirdness\n");
+		return;
+	}
+	if (!(p->flags & SWP_USED)) {
+		printk("Trying to free swap from unused swap-device\n");
+		return;
+	}
+	if (offset < p->lowest_bit)
+		p->lowest_bit = offset;
+	if (offset > p->highest_bit)
+		p->highest_bit = offset;
+	if (!p->swap_map[offset])
+		printk("swap_free: swap-space map bad (entry %08lx)\n",entry);
+	else
+		if (!--p->swap_map[offset])
+			nr_swap_pages++;
+}
+
+/*
+ * The tests may look silly, but it essentially makes sure that
+ * no other process did a swap-in on us just as we were waiting.
+ *
+ * Also, don't bother to add to the swap cache if this page-in
+ * was due to a write access.
+ */
+void swap_in(struct vm_area_struct * vma, pte_t * page_table,
+	unsigned long entry, int write_access)
+{
+	unsigned long page = get_free_page(GFP_KERNEL);
+
+	if (pte_val(*page_table) != entry) {
+		free_page(page);
+		return;
+	}
+	if (!page) {
+		*page_table = BAD_PAGE;
+		swap_free(entry);
+		oom(current);
+		return;
+	}
+	read_swap_page(entry, (char *) page);
+	if (pte_val(*page_table) != entry) {
+		free_page(page);
+		return;
+	}
+	vma->vm_task->mm->rss++;
+	vma->vm_task->mm->maj_flt++;
+	if (!write_access && add_to_swap_cache(page, entry)) {
+		*page_table = mk_pte(page, vma->vm_page_prot);
+		return;
+	}
+	*page_table = pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
+  	swap_free(entry);
+  	return;
+}
+
+/*
+ * The swap-out functions return 1 of they successfully
+ * threw something out, and we got a free page. It returns
+ * zero if it couldn't do anything, and any other value
+ * indicates it decreased rss, but the page was shared.
+ *
+ * NOTE! If it sleeps, it *must* return 1 to make sure we
+ * don't continue with the swap-out. Otherwise we may be
+ * using a process that no longer actually exists (it might
+ * have died while we slept).
+ */
+static inline int try_to_swap_out(struct vm_area_struct* vma, unsigned long address, pte_t * page_table)
+{
+	pte_t pte;
+	unsigned long entry;
+	unsigned long page;
+
+	pte = *page_table;
+	if (!pte_present(pte))
+		return 0;
+	page = pte_page(pte);
+	if (page >= high_memory)
+		return 0;
+	if (mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED)
+		return 0;
+	if ((pte_dirty(pte) && delete_from_swap_cache(page)) || pte_young(pte))  {
+		*page_table = pte_mkold(pte);
+		return 0;
+	}	
+	if (pte_dirty(pte)) {
+		if (mem_map[MAP_NR(page)] != 1)
+			return 0;
+		if (vma->vm_ops && vma->vm_ops->swapout) {
+			vma->vm_task->mm->rss--;
+			vma->vm_ops->swapout(vma, address-vma->vm_start, page_table);
+		} else {
+			if (!(entry = get_swap_page()))
+				return 0;
+			vma->vm_task->mm->rss--;
+			pte_val(*page_table) = entry;
+			invalidate();
+			write_swap_page(entry, (char *) page);
+		}
+		free_page(page);
+		return 1;	/* we slept: the process may not exist any more */
+	}
+        if ((entry = find_in_swap_cache(page)))  {
+		if (mem_map[MAP_NR(page)] != 1) {
+			*page_table = pte_mkdirty(pte);
+			printk("Aiee.. duplicated cached swap-cache entry\n");
+			return 0;
+		}
+		vma->vm_task->mm->rss--;
+		pte_val(*page_table) = entry;
+		invalidate();
+		free_page(page);
+		return 1;
+	} 
+	vma->vm_task->mm->rss--;
+	pte_clear(page_table);
+	invalidate();
+	entry = mem_map[MAP_NR(page)];
+	free_page(page);
+	return entry;
+}
+
+/*
+ * A new implementation of swap_out().  We do not swap complete processes,
+ * but only a small number of blocks, before we continue with the next
+ * process.  The number of blocks actually swapped is determined on the
+ * number of page faults, that this process actually had in the last time,
+ * so we won't swap heavily used processes all the time ...
+ *
+ * Note: the priority argument is a hint on much CPU to waste with the
+ *       swap block search, not a hint, of how much blocks to swap with
+ *       each process.
+ *
+ * (C) 1993 Kai Petzke, wpp@marie.physik.tu-berlin.de
+ */
+
+/*
+ * These are the minimum and maximum number of pages to swap from one process,
+ * before proceeding to the next:
+ */
+#define SWAP_MIN	4
+#define SWAP_MAX	32
+
+/*
+ * The actual number of pages to swap is determined as:
+ * SWAP_RATIO / (number of recent major page faults)
+ */
+#define SWAP_RATIO	128
+
+static inline int swap_out_pmd(struct vm_area_struct * vma, pmd_t *dir,
+	unsigned long address, unsigned long end)
+{
+	pte_t * pte;
+	unsigned long pmd_end;
+
+	if (pmd_none(*dir))
+		return 0;
+	if (pmd_bad(*dir)) {
+		printk("swap_out_pmd: bad pmd (%08lx)\n", pmd_val(*dir));
+		pmd_clear(dir);
+		return 0;
+	}
+	
+	pte = pte_offset(dir, address);
+	
+	pmd_end = (address + PMD_SIZE) & PMD_MASK;
+	if (end > pmd_end)
+		end = pmd_end;
+
+	do {
+		int result;
+		vma->vm_task->mm->swap_address = address + PAGE_SIZE;
+		result = try_to_swap_out(vma, address, pte);
+		if (result)
+			return result;
+		address += PAGE_SIZE;
+		pte++;
+	} while (address < end);
+	return 0;
+}
+
+static inline int swap_out_pgd(struct vm_area_struct * vma, pgd_t *dir,
+	unsigned long address, unsigned long end)
+{
+	pmd_t * pmd;
+	unsigned long pgd_end;
+
+	if (pgd_none(*dir))
+		return 0;
+	if (pgd_bad(*dir)) {
+		printk("swap_out_pgd: bad pgd (%08lx)\n", pgd_val(*dir));
+		pgd_clear(dir);
+		return 0;
+	}
+
+	pmd = pmd_offset(dir, address);
+
+	pgd_end = (address + PGDIR_SIZE) & PGDIR_MASK;	
+	if (end > pgd_end)
+		end = pgd_end;
+	
+	do {
+		int result = swap_out_pmd(vma, pmd, address, end);
+		if (result)
+			return result;
+		address = (address + PMD_SIZE) & PMD_MASK;
+		pmd++;
+	} while (address < end);
+	return 0;
+}
+
+static int swap_out_vma(struct vm_area_struct * vma, pgd_t *pgdir,
+	unsigned long start)
+{
+	unsigned long end;
+
+	/* Don't swap out areas like shared memory which have their
+	    own separate swapping mechanism. */
+	if (vma->vm_flags & VM_SHM)
+		return 0;
+
+	end = vma->vm_end;
+	while (start < end) {
+		int result = swap_out_pgd(vma, pgdir, start, end);
+		if (result)
+			return result;
+		start = (start + PGDIR_SIZE) & PGDIR_MASK;
+		pgdir++;
+	}
+	return 0;
+}
+
+static int swap_out_process(struct task_struct * p)
+{
+	unsigned long address;
+	struct vm_area_struct* vma;
+
+	/*
+	 * Go through process' page directory.
+	 */
+	address = p->mm->swap_address;
+	p->mm->swap_address = 0;
+
+	/*
+	 * Find the proper vm-area
+	 */
+	vma = find_vma(p, address);
+	if (!vma)
+		return 0;
+	if (address < vma->vm_start)
+		address = vma->vm_start;
+
+	for (;;) {
+		int result = swap_out_vma(vma, pgd_offset(p, address), address);
+		if (result)
+			return result;
+		vma = vma->vm_next;
+		if (!vma)
+			break;
+		address = vma->vm_start;
+	}
+	p->mm->swap_address = 0;
+	return 0;
+}
+
+static int swap_out(unsigned int priority)
+{
+	static int swap_task;
+	int loop, counter;
+	struct task_struct *p;
+
+	counter = 2*NR_TASKS >> priority;
+	for(; counter >= 0; counter--) {
+		/*
+		 * Check that swap_task is suitable for swapping.  If not, look for
+		 * the next suitable process.
+		 */
+		loop = 0;
+		while(1) {
+			if (swap_task >= NR_TASKS) {
+				swap_task = 1;
+				if (loop)
+					/* all processes are unswappable or already swapped out */
+					return 0;
+				loop = 1;
+			}
+
+			p = task[swap_task];
+			if (p && p->mm->swappable && p->mm->rss)
+				break;
+
+			swap_task++;
+		}
+
+		/*
+		 * Determine the number of pages to swap from this process.
+		 */
+		if (!p->mm->swap_cnt) {
+			p->mm->dec_flt = (p->mm->dec_flt * 3) / 4 + p->mm->maj_flt - p->mm->old_maj_flt;
+			p->mm->old_maj_flt = p->mm->maj_flt;
+
+			if (p->mm->dec_flt >= SWAP_RATIO / SWAP_MIN) {
+				p->mm->dec_flt = SWAP_RATIO / SWAP_MIN;
+				p->mm->swap_cnt = SWAP_MIN;
+			} else if (p->mm->dec_flt <= SWAP_RATIO / SWAP_MAX)
+				p->mm->swap_cnt = SWAP_MAX;
+			else
+				p->mm->swap_cnt = SWAP_RATIO / p->mm->dec_flt;
+		}
+		if (!--p->mm->swap_cnt)
+			swap_task++;
+		switch (swap_out_process(p)) {
+			case 0:
+				if (p->mm->swap_cnt)
+					swap_task++;
+				break;
+			case 1:
+				return 1;
+			default:
+				break;
+		}
+	}
+	return 0;
+}
+
+/*
+ * we keep on shrinking one resource until it's considered "too hard",
+ * and then switch to the next one (priority being an indication on how
+ * hard we should try with the resource).
+ *
+ * This should automatically find the resource that can most easily be
+ * free'd, so hopefully we'll get reasonable behaviour even under very
+ * different circumstances.
+ */
+static int try_to_free_page(int priority)
+{
+	static int state = 0;
+	int i=6;
+
+	switch (state) {
+		do {
+		case 0:
+			if (priority != GFP_NOBUFFER && shrink_buffers(i))
+				return 1;
+			state = 1;
+		case 1:
+			if (shm_swap(i))
+				return 1;
+			state = 2;
+		default: /*rmk*/ state = 0; /* try to reduce buffers! */
+			if (swap_out(i))
+				return 1;
+			state = 0;
+		} while(--i);
+	}
+	return 0;
+}
+
+static inline void add_mem_queue(struct mem_list * head, struct mem_list * entry)
+{
+	entry->prev = head;
+	(entry->next = head->next)->prev = entry;
+	head->next = entry;
+}
+
+static inline void remove_mem_queue(struct mem_list * head, struct mem_list * entry)
+{
+	entry->next->prev = entry->prev;
+	entry->prev->next = entry->next;
+}
+
+/*
+ * Free_page() adds the page to the free lists. This is optimized for
+ * fast normal cases (no error jumps taken normally).
+ *
+ * The way to optimize jumps for gcc-2.2.2 is to:
+ *  - select the "normal" case and put it inside the if () { XXX }
+ *  - no else-statements if you can avoid them
+ *
+ * With the above two rules, you get a straight-line execution path
+ * for the normal case, giving better asm-code.
+ *
+ * free_page() may sleep since the page being freed may be a buffer
+ * page or present in the swap cache. It will not sleep, however,
+ * for a freshly allocated page (get_free_page()).
+ */
+
+/*
+ * Buddy system. Hairy. You really aren't expected to understand this
+ */
+static inline void free_pages_ok(unsigned long addr, unsigned long order)
+{
+	unsigned long index = MAP_NR(addr) >> (1 + order);
+	unsigned long mask = PAGE_MASK << order;
+
+	addr &= mask;
+	nr_free_pages += 1 << order;
+	while (order < NR_MEM_LISTS-1) {
+		if (!change_bit(index, free_area_map[order]))
+			break;
+		remove_mem_queue(free_area_list+order, (struct mem_list *) (addr ^ (1+~mask)));
+		order++;
+		index >>= 1;
+		mask <<= 1;
+		addr &= mask;
+	}
+	add_mem_queue(free_area_list+order, (struct mem_list *) addr);
+}
+
+static inline void check_free_buffers(unsigned long addr)
+{
+	struct buffer_head * bh;
+
+	bh = buffer_pages[MAP_NR(addr)];
+	if (bh) {
+		struct buffer_head *tmp = bh;
+		do {
+			if (tmp->b_list == BUF_SHARED && tmp->b_dev != 0xffff)
+				refile_buffer(tmp);
+			tmp = tmp->b_this_page;
+		} while (tmp != bh);
+	}
+}
+
+void free_pages(unsigned long addr, unsigned long order)
+{ void *bir; __asm__ ("ldr %0, [ip,#-8]\n\t": "=r" (bir));
+	if (addr < high_memory) {
+		unsigned long flag;
+		mem_map_t * map = mem_map + MAP_NR(addr);
+		if (*map) {
+			if (!(*map & MAP_PAGE_RESERVED)) {
+				save_flags(flag);
+				cli();
+				if (!--*map)  {
+					free_pages_ok(addr, order);
+					delete_from_swap_cache(addr);
+				}
+				restore_flags(flag);
+				if (*map == 1)
+					check_free_buffers(addr);
+			}
+			return;
+		}
+		printk("Trying to free free memory (%08lx): memory probably corrupted\n",addr);
+		printk("PC = %p\n", bir /*__builtin_return_address(0)*/);
+		while(1);
+		return;
+	}
+}
+
+/*
+ * Some ugly macros to speed up __get_free_pages()..
+ */
+#define RMQUEUE(order) \
+do { struct mem_list * queue = free_area_list+order; \
+     unsigned long new_order = order; \
+	do { struct mem_list *next = queue->next; \
+		if (queue != next) { \
+			(queue->next = next->next)->prev = queue; \
+			mark_used((unsigned long) next, new_order); \
+			nr_free_pages -= 1 << order; \
+			restore_flags(flags); \
+			EXPAND(next, order, new_order); \
+			return (unsigned long) next; \
+		} new_order++; queue++; \
+	} while (new_order < NR_MEM_LISTS); \
+} while (0)
+
+static inline int mark_used(unsigned long addr, unsigned long order)
+{
+	return change_bit(MAP_NR(addr) >> (1+order), free_area_map[order]);
+}
+
+#define EXPAND(addr,low,high) \
+do { unsigned long size = PAGE_SIZE << high; \
+	while (high > low) { \
+		high--; size >>= 1; cli(); \
+		add_mem_queue(free_area_list+high, addr); \
+		mark_used((unsigned long) addr, high); \
+		restore_flags(flags); \
+		addr = (struct mem_list *) (size + (unsigned long) addr); \
+	} mem_map[MAP_NR((unsigned long) addr)] = 1; \
+} while (0)
+
+unsigned long __get_free_pages(int priority, unsigned long order)
+{
+	unsigned long flags;
+	int reserved_pages;
+
+	if (intr_count && priority != GFP_ATOMIC) {
+		static int count = 0;
+		if (++count < 5) {
+			printk("gfp called nonatomically from interrupt %p\n",
+				__builtin_return_address(0));
+			priority = GFP_ATOMIC;
+		}
+	}
+	reserved_pages = 5;
+	if (priority != GFP_NFS)
+		reserved_pages = min_free_pages;
+	save_flags(flags);
+repeat:
+	cli();
+	if ((priority==GFP_ATOMIC) || nr_free_pages > reserved_pages) {
+		RMQUEUE(order);
+		restore_flags(flags);
+		return 0;
+	}
+	restore_flags(flags);
+	if (priority != GFP_BUFFER && try_to_free_page(priority))
+		goto repeat;
+	return 0;
+}
+
+/*
+ * Yes, I know this is ugly. Don't tell me.
+ */
+unsigned long __get_dma_pages(int priority, unsigned long order)
+{
+	unsigned long list = 0;
+	unsigned long result;
+	unsigned long limit = MAX_DMA_ADDRESS;
+
+	/* if (EISA_bus) limit = ~0UL; */
+	if (priority != GFP_ATOMIC)
+		priority = GFP_BUFFER;
+	for (;;) {
+		result = __get_free_pages(priority, order);
+		if (result < limit) /* covers failure as well */
+			break;
+		*(unsigned long *) result = list;
+		list = result;
+	}
+	while (list) {
+		unsigned long tmp = list;
+		list = *(unsigned long *) list;
+		free_pages(tmp, order);
+	}
+	return result;
+}
+
+/*
+ * Show free area list (used inside shift_scroll-lock stuff)
+ * We also calculate the percentage fragmentation. We do this by counting the
+ * memory on each free list with the exception of the first item on the list.
+ */
+void show_free_areas(void)
+{
+ 	unsigned long order, flags;
+ 	unsigned long total = 0;
+
+	printk("Free pages:      %6dkB\n ( ",nr_free_pages<<(PAGE_SHIFT-10));
+	save_flags(flags);
+	cli();
+ 	for (order=0 ; order < NR_MEM_LISTS; order++) {
+		struct mem_list * tmp;
+		unsigned long nr = 0;
+		for (tmp = free_area_list[order].next ; tmp != free_area_list + order ; tmp = tmp->next) {
+			nr ++;
+		}
+		total += nr * ((PAGE_SIZE>>10) << order);
+		printk("%lu*%lukB ", nr, (PAGE_SIZE>>10) << order);
+	}
+	restore_flags(flags);
+	printk("= %lukB)\n", total);
+#ifdef SWAP_CACHE_INFO
+	show_swap_cache_info();
+#endif	
+}
+
+/*
+ * Trying to stop swapping from a file is fraught with races, so
+ * we repeat quite a bit here when we have to pause. swapoff()
+ * isn't exactly timing-critical, so who cares (but this is /really/
+ * inefficient, ugh).
+ *
+ * We return 1 after having slept, which makes the process start over
+ * from the beginning for this process..
+ */
+static inline int unuse_pte(struct vm_area_struct * vma, unsigned long address,
+	pte_t *dir, unsigned int type, unsigned long page)
+{
+	pte_t pte = *dir;
+
+	if (pte_none(pte))
+		return 0;
+	if (pte_present(pte)) {
+		unsigned long page = pte_page(pte);
+		if (page >= high_memory)
+			return 0;
+		if (!in_swap_cache(page))
+			return 0;
+		if (SWP_TYPE(in_swap_cache(page)) != type)
+			return 0;
+		delete_from_swap_cache(page);
+		*dir = pte_mkdirty(pte);
+		return 0;
+	}
+	if (SWP_TYPE(pte_val(pte)) != type)
+		return 0;
+	read_swap_page(pte_val(pte), (char *) page);
+	if (pte_val(*dir) != pte_val(pte)) {
+		free_page(page);
+		return 1;
+	}
+	*dir = pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
+	++vma->vm_task->mm->rss;
+	swap_free(pte_val(pte));
+	return 1;
+}
+
+static inline int unuse_pmd(struct vm_area_struct * vma, pmd_t *dir,
+	unsigned long address, unsigned long size, unsigned long offset,
+	unsigned int type, unsigned long page)
+{
+	pte_t * pte;
+	unsigned long end;
+
+	if (pmd_none(*dir))
+		return 0;
+	if (pmd_bad(*dir)) {
+		printk("unuse_pmd: bad pmd (%08lx)\n", pmd_val(*dir));
+		pmd_clear(dir);
+		return 0;
+	}
+	pte = pte_offset(dir, address);
+	offset += address & PMD_MASK;
+	address &= ~PMD_MASK;
+	end = address + size;
+	if (end > PMD_SIZE)
+		end = PMD_SIZE;
+	do {
+		if (unuse_pte(vma, offset+address-vma->vm_start, pte, type, page))
+			return 1;
+		address += PAGE_SIZE;
+		pte++;
+	} while (address < end);
+	return 0;
+}
+
+static inline int unuse_pgd(struct vm_area_struct * vma, pgd_t *dir,
+	unsigned long address, unsigned long size,
+	unsigned int type, unsigned long page)
+{
+	pmd_t * pmd;
+	unsigned long offset, end;
+
+	if (pgd_none(*dir))
+		return 0;
+	if (pgd_bad(*dir)) {
+		printk("unuse_pgd: bad pgd (%08lx)\n", pgd_val(*dir));
+		pgd_clear(dir);
+		return 0;
+	}
+	pmd = pmd_offset(dir, address);
+	offset = address & PGDIR_MASK;
+	address &= ~PGDIR_MASK;
+	end = address + size;
+	if (end > PGDIR_SIZE)
+		end = PGDIR_SIZE;
+	do {
+		if (unuse_pmd(vma, pmd, address, end - address, offset, type, page))
+			return 1;
+		address = (address + PMD_SIZE) & PMD_MASK;
+		pmd++;
+	} while (address < end);
+	return 0;
+}
+
+static int unuse_vma(struct vm_area_struct * vma, pgd_t *pgdir,
+	unsigned long start, unsigned long end,
+	unsigned int type, unsigned long page)
+{
+	while (start < end) {
+		if (unuse_pgd(vma, pgdir, start, end - start, type, page))
+			return 1;
+		start = (start + PGDIR_SIZE) & PGDIR_MASK;
+		pgdir++;
+	}
+	return 0;
+}
+
+static int unuse_process(struct task_struct * p, unsigned int type, unsigned long page)
+{
+	struct vm_area_struct* vma;
+
+	/*
+	 * Go through process' page directory.
+	 */
+	vma = p->mm->mmap;
+	while (vma) {
+		pgd_t * pgd = pgd_offset(p, vma->vm_start);
+		if (unuse_vma(vma, pgd, vma->vm_start, vma->vm_end, type, page))
+			return 1;
+		vma = vma->vm_next;
+	}
+	return 0;
+}
+
+/*
+ * To avoid races, we repeat for each process after having
+ * swapped something in. That gets rid of a few pesky races,
+ * and "swapoff" isn't exactly timing critical.
+ */
+static int try_to_unuse(unsigned int type)
+{
+	int nr;
+	unsigned long page = get_free_page(GFP_KERNEL);
+
+	if (!page)
+		return -ENOMEM;
+	nr = 0;
+	while (nr < NR_TASKS) {
+		if (task[nr]) {
+			if (unuse_process(task[nr], type, page)) {
+				page = get_free_page(GFP_KERNEL);
+				if (!page)
+					return -ENOMEM;
+				continue;
+			}
+		}
+		nr++;
+	}
+	free_page(page);
+	return 0;
+}
+
+asmlinkage int sys_swapoff(const char * specialfile)
+{
+	struct swap_info_struct * p;
+	struct inode * inode;
+	unsigned int type;
+	struct file filp;
+	int i;
+
+	if (!suser())
+		return -EPERM;
+	i = namei(specialfile,&inode);
+	if (i)
+		return i;
+	p = swap_info;
+	for (type = 0 ; type < nr_swapfiles ; type++,p++) {
+		if ((p->flags & SWP_WRITEOK) != SWP_WRITEOK)
+			continue;
+		if (p->swap_file) {
+			if (p->swap_file == inode)
+				break;
+		} else {
+			if (!S_ISBLK(inode->i_mode))
+				continue;
+			if (p->swap_device == inode->i_rdev)
+				break;
+		}
+	}
+
+	if (type >= nr_swapfiles){
+		iput(inode);
+		return -EINVAL;
+	}
+	p->flags = SWP_USED;
+	i = try_to_unuse(type);
+	if (i) {
+		iput(inode);
+		p->flags = SWP_WRITEOK;
+		return i;
+	}
+
+	if(p->swap_device){
+		memset(&filp, 0, sizeof(filp));		
+		filp.f_inode = inode;
+		filp.f_mode = 3; /* read write */
+		/* open it again to get fops */
+		if( !blkdev_open(inode, &filp) &&
+		   filp.f_op && filp.f_op->release){
+			filp.f_op->release(inode,&filp);
+			filp.f_op->release(inode,&filp);
+		}
+	}
+	iput(inode);
+
+	nr_swap_pages -= p->pages;
+	iput(p->swap_file);
+	p->swap_file = NULL;
+	p->swap_device = 0;
+	vfree(p->swap_map);
+	p->swap_map = NULL;
+	free_page((long) p->swap_lockmap);
+	p->swap_lockmap = NULL;
+	p->flags = 0;
+	return 0;
+}
+
+/*
+ * Written 01/25/92 by Simmule Turner, heavily changed by Linus.
+ *
+ * The swapon system call
+ */
+asmlinkage int sys_swapon(const char * specialfile)
+{
+	struct swap_info_struct * p;
+	struct inode * swap_inode;
+	unsigned int type;
+	int i,j;
+	int error;
+	struct file filp;
+
+	memset(&filp, 0, sizeof(filp));
+	if (!suser())
+		return -EPERM;
+	p = swap_info;
+	for (type = 0 ; type < nr_swapfiles ; type++,p++)
+		if (!(p->flags & SWP_USED))
+			break;
+	if (type >= MAX_SWAPFILES)
+		return -EPERM;
+	if (type >= nr_swapfiles)
+		nr_swapfiles = type+1;
+	p->flags = SWP_USED;
+	p->swap_file = NULL;
+	p->swap_device = 0;
+	p->swap_map = NULL;
+	p->swap_lockmap = NULL;
+	p->lowest_bit = 0;
+	p->highest_bit = 0;
+	p->max = 1;
+	error = namei(specialfile,&swap_inode);
+	if (error)
+		goto bad_swap_2;
+	p->swap_file = swap_inode;
+	error = -EBUSY;
+	if (swap_inode->i_count != 1)
+		goto bad_swap_2;
+	error = -EINVAL;
+
+	if (S_ISBLK(swap_inode->i_mode)) {
+		p->swap_device = swap_inode->i_rdev;
+
+		filp.f_inode = swap_inode;
+		filp.f_mode = 3; /* read write */
+		error = blkdev_open(swap_inode, &filp);
+		p->swap_file = NULL;
+		iput(swap_inode);
+		if(error)
+			goto bad_swap_2;
+		error = -ENODEV;
+		if (!p->swap_device)
+			goto bad_swap;
+		error = -EBUSY;
+		for (i = 0 ; i < nr_swapfiles ; i++) {
+			if (i == type)
+				continue;
+			if (p->swap_device == swap_info[i].swap_device)
+				goto bad_swap;
+		}
+	} else if (!S_ISREG(swap_inode->i_mode))
+		goto bad_swap;
+	p->swap_lockmap = (unsigned char *) get_free_page(GFP_USER);
+	if (!p->swap_lockmap) {
+		printk("Unable to start swapping: out of memory :-)\n");
+		error = -ENOMEM;
+		goto bad_swap;
+	}
+	read_swap_page(SWP_ENTRY(type,0), (char *) p->swap_lockmap);
+	if (memcmp("SWAP-SPACE",p->swap_lockmap+4086,10)) {
+		printk("Unable to find swap-space signature\n");
+		error = -EINVAL;
+		goto bad_swap;
+	}
+	memset(p->swap_lockmap+PAGE_SIZE-10,0,10);
+	j = 0;
+	p->lowest_bit = 0;
+	p->highest_bit = 0;
+	for (i = 1 ; i < 8*PAGE_SIZE ; i++) {
+		if (test_bit(i,p->swap_lockmap)) {
+			if (!p->lowest_bit)
+				p->lowest_bit = i;
+			p->highest_bit = i;
+			p->max = i+1;
+			j++;
+		}
+	}
+	if (!j) {
+		printk("Empty swap-file\n");
+		error = -EINVAL;
+		goto bad_swap;
+	}
+	p->swap_map = (unsigned char *) vmalloc(p->max);
+	if (!p->swap_map) {
+		error = -ENOMEM;
+		goto bad_swap;
+	}
+	for (i = 1 ; i < p->max ; i++) {
+		if (test_bit(i,p->swap_lockmap))
+			p->swap_map[i] = 0;
+		else
+			p->swap_map[i] = 0x80;
+	}
+	p->swap_map[0] = 0x80;
+	memset(p->swap_lockmap,0,PAGE_SIZE);
+	p->flags = SWP_WRITEOK;
+	p->pages = j;
+	nr_swap_pages += j;
+	printk("Adding Swap: %dk swap-space\n",j<<2);
+	return 0;
+bad_swap:
+	if(filp.f_op && filp.f_op->release)
+		filp.f_op->release(filp.f_inode,&filp);
+bad_swap_2:
+	free_page((long) p->swap_lockmap);
+	vfree(p->swap_map);
+	iput(p->swap_file);
+	p->swap_device = 0;
+	p->swap_file = NULL;
+	p->swap_map = NULL;
+	p->swap_lockmap = NULL;
+	p->flags = 0;
+	return error;
+}
+
+void si_swapinfo(struct sysinfo *val)
+{
+	unsigned int i, j;
+
+	val->freeswap = val->totalswap = 0;
+	for (i = 0; i < nr_swapfiles; i++) {
+		if ((swap_info[i].flags & SWP_WRITEOK) != SWP_WRITEOK)
+			continue;
+		for (j = 0; j < swap_info[i].max; ++j)
+			switch (swap_info[i].swap_map[j]) {
+				case 128:
+					continue;
+				case 0:
+					++val->freeswap;
+				default:
+					++val->totalswap;
+			}
+	}
+	val->freeswap <<= PAGE_SHIFT;
+	val->totalswap <<= PAGE_SHIFT;
+	return;
+}
+
+/*
+ * set up the free-area data structures:
+ *   - mark all pages MAP_PAGE_RESERVED
+ *   - mark all memory queues empty
+ *   - clear the memory bitmaps
+ */
+
+
+unsigned long free_area_init(unsigned long start_mem, unsigned long end_mem)
+{
+	mem_map_t * p;
+	unsigned long mask = PAGE_MASK;
+	int i;
+
+	/*
+	 * select nr of pages we try to keep free for important stuff
+	 * with a minimum of 16 pages. This is totally arbitrary
+	 */
+	i = (end_mem - PAGE_OFFSET) >> (PAGE_SHIFT+6);
+	if (i < 16)
+		i = 16;
+	min_free_pages = i;
+	start_mem = init_swap_cache(start_mem, end_mem);
+	mem_map = (mem_map_t *) start_mem;
+	p = mem_map + MAP_NR(end_mem);
+	start_mem = (unsigned long) p;
+	while (p > mem_map)
+		*--p = MAP_PAGE_RESERVED;
+
+	for (i = 0 ; i < NR_MEM_LISTS ; i++) {
+		unsigned long bitmap_size;
+		free_area_list[i].prev = free_area_list[i].next = &free_area_list[i];
+		mask += mask;
+		end_mem = (end_mem + ~mask) & mask;
+		bitmap_size = (end_mem - PAGE_OFFSET) >> (PAGE_SHIFT + i);
+		bitmap_size = (bitmap_size + 7) >> 3;
+		bitmap_size = (bitmap_size + sizeof(unsigned long) - 1) & ~(sizeof(unsigned long)-1);
+		free_area_map[i] = (unsigned char *) start_mem;
+		memset((void *) start_mem, 0, bitmap_size);
+		start_mem += bitmap_size;
+	}
+
+	return start_mem;
+}
diff -r -u -N linux.orig/mm/vmalloc.c linux.arm/mm/vmalloc.c
--- linux.orig/mm/vmalloc.c	Fri Oct 27 23:09:12 1995
+++ linux.arm/mm/vmalloc.c	Fri Oct 27 23:10:33 1995
@@ -61,6 +61,9 @@
 		if (pte_none(page))
 			continue;
 		if (pte_present(page)) {
+#ifdef __arm__
+			mem_map[MAP_NR(pte_page(page))] = 1;
+#endif
 			free_page(pte_page(page));
 			continue;
 		}
@@ -100,6 +103,9 @@
 	dir = pgd_offset(&init_task, 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++;
 	}
@@ -119,6 +125,9 @@
 		if (!pte_none(*pte))
 			printk("alloc_area_pte: page already exists\n");
 		page = __get_free_page(GFP_KERNEL);
+#ifdef __arm__
+		mem_map[MAP_NR(page)] = MAP_PAGE_RESERVED;
+#endif
 		if (!page)
 			return -ENOMEM;
 		*pte = mk_pte(page, PAGE_KERNEL);
@@ -181,7 +190,8 @@
 	for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) {
 		if (tmp->addr == addr) {
 			*p = tmp->next;
-			free_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size);
+/* rmk - subtract page size added ??? */
+			free_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size - PAGE_SIZE);
 			kfree(tmp);
 			return;
 		}
diff -r -u -N linux.orig/mm/vmalloc.c.old linux.arm/mm/vmalloc.c.old
--- linux.orig/mm/vmalloc.c.old	Thu Jan  1 01:00:00 1970
+++ linux.arm/mm/vmalloc.c.old	Fri Oct 27 23:10:38 1995
@@ -0,0 +1,246 @@
+#define THREE_LEVEL
+/*
+ *  linux/mm/vmalloc.c
+ *
+ *  Copyright (C) 1993  Linus Torvalds
+ */
+
+#include <asm/system.h>
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/head.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+
+#include <asm/segment.h>
+#include <asm/pgtable.h>
+
+struct vm_struct {
+	unsigned long flags;
+	void * addr;
+	unsigned long size;
+	struct vm_struct * next;
+};
+
+static struct vm_struct * vmlist = NULL;
+
+static inline void set_pgdir(unsigned long address, pgd_t entry)
+{
+	struct task_struct * p;
+
+	for_each_task(p)
+		*pgd_offset(p,address) = entry;
+}
+
+static inline void free_area_pte(pmd_t * pmd, unsigned long address, unsigned long size)
+{
+	pte_t * pte;
+	unsigned long end;
+
+	if (pmd_none(*pmd))
+		return;
+	if (pmd_bad(*pmd)) {
+		printk("free_area_pte: bad pmd (%08lx)\n", pmd_val(*pmd));
+		pmd_clear(pmd);
+		return;
+	}
+	pte = pte_offset(pmd, address);
+	address &= ~PMD_MASK;
+	end = address + size;
+	if (end > PMD_SIZE)
+		end = PMD_SIZE;
+	while (address < end) {
+		pte_t page = *pte;
+		pte_clear(pte);
+		address += PAGE_SIZE;
+		pte++;
+		if (pte_none(page))
+			continue;
+		if (pte_present(page)) {
+			free_page(pte_page(page));
+			continue;
+		}
+		printk("Whee.. Swapped out page in kernel page table\n");
+	}
+}
+
+static inline void free_area_pmd(pgd_t * dir, unsigned long address, unsigned long size)
+{
+	pmd_t * pmd;
+	unsigned long end;
+
+	if (pgd_none(*dir))
+		return;
+	if (pgd_bad(*dir)) {
+		printk("free_area_pmd: bad pgd (%08lx)\n", pgd_val(*dir));
+		pgd_clear(dir);
+		return;
+	}
+	pmd = pmd_offset(dir, address);
+	address &= ~PGDIR_MASK;
+	end = address + size;
+	if (end > PGDIR_SIZE)
+		end = PGDIR_SIZE;
+	while (address < end) {
+		free_area_pte(pmd, address, end - address);
+		address = (address + PMD_SIZE) & PMD_MASK;
+		pmd++;
+	}
+}
+
+static void free_area_pages(unsigned long address, unsigned long size)
+{
+	pgd_t * dir;
+	unsigned long end = address + size;
+
+	dir = pgd_offset(&init_task, address);
+	while (address < end) {
+		free_area_pmd(dir, address, end - address);
+		address = (address + PGDIR_SIZE) & PGDIR_MASK;
+		dir++;
+	}
+	invalidate();
+}
+
+static inline int alloc_area_pte(pte_t * pte, unsigned long address, unsigned long size)
+{
+	unsigned long end;
+
+	address &= ~PMD_MASK;
+	end = address + size;
+	if (end > PMD_SIZE)
+		end = PMD_SIZE;
+	while (address < end) {
+		unsigned long page;
+		if (!pte_none(*pte))
+			printk("alloc_area_pte: page already exists\n");
+		page = __get_free_page(GFP_KERNEL);
+		if (!page)
+			return -ENOMEM;
+		*pte = mk_pte(page, PAGE_KERNEL);
+		address += PAGE_SIZE;
+		pte++;
+	}
+	return 0;
+}
+
+static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size)
+{
+	unsigned long end;
+
+	address &= ~PGDIR_MASK;
+	end = address + size;
+	if (end > PGDIR_SIZE)
+		end = PGDIR_SIZE;
+	while (address < end) {
+		pte_t * pte = pte_alloc_kernel(pmd, address);
+		if (!pte)
+			return -ENOMEM;
+		if (alloc_area_pte(pte, address, end - address))
+			return -ENOMEM;
+		address = (address + PMD_SIZE) & PMD_MASK;
+		pmd++;
+	}
+	return 0;
+}
+
+static int alloc_area_pages(unsigned long address, unsigned long size)
+{
+	pgd_t * dir;
+	unsigned long end = address + size;
+
+	dir = pgd_offset(&init_task, address);
+	while (address < end) {
+		pmd_t *pmd = pmd_alloc_kernel(dir, address);
+		if (!pmd)
+			return -ENOMEM;
+		if (alloc_area_pmd(pmd, address, end - address))
+			return -ENOMEM;
+		set_pgdir(address, *dir);
+		address = (address + PGDIR_SIZE) & PGDIR_MASK;
+		dir++;
+	}
+	invalidate();
+	return 0;
+}
+
+void vfree(void * addr)
+{
+	struct vm_struct **p, *tmp;
+
+	if (!addr)
+		return;
+	if ((PAGE_SIZE-1) & (unsigned long) addr) {
+		printk("Trying to vfree() bad address (%p)\n", addr);
+		return;
+	}
+	for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) {
+		if (tmp->addr == addr) {
+			*p = tmp->next;
+/* rmk - subtract page size added ??? */
+			free_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size - PAGE_SIZE);
+			kfree(tmp);
+			return;
+		}
+	}
+	printk("Trying to vfree() nonexistent vm area (%p)\n", addr);
+}
+
+void * vmalloc(unsigned long size)
+{
+	void * addr;
+	struct vm_struct **p, *tmp, *area;
+
+	size = PAGE_ALIGN(size);
+	if (!size || size > high_memory)
+		return NULL;
+	area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL);
+	if (!area)
+		return NULL;
+	addr = (void *) VMALLOC_START;
+	area->size = size + PAGE_SIZE;
+	area->next = NULL;
+	for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
+		if (size + (unsigned long) addr < (unsigned long) tmp->addr)
+			break;
+		addr = (void *) (tmp->size + (unsigned long) tmp->addr);
+	}
+	area->addr = addr;
+	area->next = *p;
+	*p = area;
+	if (alloc_area_pages(VMALLOC_VMADDR(addr), size)) {
+		vfree(addr);
+		return NULL;
+	}
+	return addr;
+}
+
+int vread(char *buf, char *addr, int count)
+{
+	struct vm_struct **p, *tmp;
+	char *vaddr, *buf_start = buf;
+	int n;
+
+	for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
+		vaddr = (char *) tmp->addr;
+		while (addr < vaddr) {
+			if (count == 0)
+				goto finished;
+			put_fs_byte('\0', buf++), addr++, count--;
+		}
+		n = tmp->size - PAGE_SIZE;
+		if (addr > vaddr)
+			n -= addr - vaddr;
+		while (--n >= 0) {
+			if (count == 0)
+				goto finished;
+			put_fs_byte(*addr++, buf++), count--;
+		}
+	}
+finished:
+	return buf - buf_start;
+}
diff -r -u -N linux.orig/net/Makefile linux.arm/net/Makefile
--- linux.orig/net/Makefile	Fri Oct 27 23:09:20 1995
+++ linux.arm/net/Makefile	Fri Oct 27 23:11:23 1995
@@ -27,6 +27,8 @@
 
 net.o:		$(OBJS) network.a
 		$(LD) -r -o net.o $(OBJS) network.a
+#		stripper net.o net2.o
+		sync
 
 network.a:	subdirs
 		rm -f $@
diff -r -u -N linux.orig/net/inet/dev.c linux.arm/net/inet/dev.c
--- linux.orig/net/inet/dev.c	Fri Oct 27 23:09:14 1995
+++ linux.arm/net/inet/dev.c	Fri Oct 27 23:11:25 1995
@@ -29,6 +29,8 @@
  *					you start doing multicast video 8)
  *		Alan Cox	:	Rewrote net_bh and list manager.
  *		Alan Cox	: 	Fix ETH_P_ALL echoback lengths.
+ *		Russell King	:	Added in code to trap non word aligned
+ *				:	hard_header_lengths (for alignment in ip)
  *
  *	Cleaned up and recommented by Alan Cox 2nd April 1994. I hope to have
  *	the rest as well commented in the end.
@@ -1417,7 +1419,6 @@
 void dev_init(void)
 {
 	struct device *dev, *dev2;
-
 	/*
 	 *	Add the devices.
 	 *	If the call to dev->init fails, the dev is removed
@@ -1428,7 +1429,7 @@
 	dev2 = NULL;
 	for (dev = dev_base; dev != NULL; dev=dev->next) 
 	{
-		if (dev->init && dev->init(dev)) 
+		if (dev->init && dev->init(dev))
 		{
 			/*
 			 *	It failed to come up. Unhook it.
@@ -1441,6 +1442,14 @@
 		} 
 		else
 		{
+#ifdef __arm__
+			if (dev->hard_header_len & 3)
+			{
+				printk("%s: warning: hard header len is not word aligned\n", dev->name);
+				dev->hard_header_len |= 3;
+				dev->hard_header_len += 1;
+			}
+#endif
 			dev2 = dev;
 		}
 	}
diff -r -u -N linux.orig/net/inet/eth.c linux.arm/net/inet/eth.c
--- linux.orig/net/inet/eth.c	Fri Oct 27 23:09:14 1995
+++ linux.arm/net/inet/eth.c	Fri Oct 27 23:11:26 1995
@@ -25,6 +25,8 @@
  *		Alan Cox	: Redid header building to reflect new format.
  *		Alan Cox	: ARP only when compiled with CONFIG_INET
  *		Greg Page	: 802.2 and SNAP stuff
+ *		Russell King	: Adjusted structured to allow for words to be
+ *				: aligned in the ip levels
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -85,7 +87,11 @@
 	   void *daddr, void *saddr, unsigned len,
 	   struct sk_buff *skb)
 {
+#ifndef __arm__
 	struct ethhdr *eth = (struct ethhdr *)buff;
+#else
+	struct ethhdr *eth = (struct ethhdr *)((int)buff + 2);
+#endif
 
 	/* 
 	 *	Set the protocol type. For a packet of type ETH_P_802_3 we put the length
@@ -135,7 +141,11 @@
 int eth_rebuild_header(void *buff, struct device *dev, unsigned long dst,
 			struct sk_buff *skb)
 {
+#ifndef __arm__
 	struct ethhdr *eth = (struct ethhdr *)buff;
+#else
+	struct ethhdr *eth = (struct ethhdr *)((int)buff + 2);
+#endif
 
 	/*
 	 *	Only ARP/IP is currently supported
@@ -167,7 +177,11 @@
  
 unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev)
 {
+#ifndef __arm__
 	struct ethhdr *eth = (struct ethhdr *) skb->data;
+#else
+	struct ethhdr *eth = (struct ethhdr *) ((int)skb->data + 2);
+#endif
 	unsigned char *rawp;
 	
 	if(*eth->h_dest&1)
diff -r -u -N linux.orig/net/inet/ip.c linux.arm/net/inet/ip.c
--- linux.orig/net/inet/ip.c	Fri Oct 27 23:09:15 1995
+++ linux.arm/net/inet/ip.c	Fri Oct 27 23:11:25 1995
@@ -68,6 +68,7 @@
  *		Bjorn Ekwall	:	Removed ip_csum (from slhc.c too)
  *		Bjorn Ekwall	:	Moved ip_fast_csum to ip.h (inline!)
  *		Stefan Becker   :       Send out ICMP HOST REDIRECT
+ *		Russell King	:	Altered so that it is able to compile for the ARM.
  *		Alan Cox	:	Only send ICMP_REDIRECT if src/dest are the same net.
  *  
  *
@@ -500,7 +501,7 @@
  * This routine does all the checksum computations that don't
  * require anything special (like copying or special headers).
  */
-
+#ifndef __arm__
 unsigned short ip_compute_csum(unsigned char * buff, int len)
 {
 	unsigned long sum = 0;
@@ -544,7 +545,9 @@
 	sum =~sum;
 	return(sum & 0xffff);
 }
-
+#else
+extern unsigned short ip_compute_csum(unsigned char * buff, int len);
+#endif
 /*
  *	Generate a checksum for an outgoing IP datagram.
  */
diff -r -u -N linux.orig/net/inet/ip.h linux.arm/net/inet/ip.h
--- linux.orig/net/inet/ip.h	Fri Oct 27 23:09:15 1995
+++ linux.arm/net/inet/ip.h	Fri Oct 27 23:11:26 1995
@@ -103,7 +103,7 @@
  *	Used by ip.c and slhc.c (the net driver module)
  *	(Moved to here by bj0rn@blox.se)
  */
-
+#if defined(__i386__)
 static inline unsigned short ip_fast_csum(unsigned char * buff, int wlen)
 {
 	unsigned long sum = 0;
@@ -127,4 +127,10 @@
 	}
 	return (~sum) & 0xffff;
 }
+#elif defined(__arm__)
+extern unsigned short ip_fast_csum(unsigned char *buff, int wlen);
+#else
+#error "Please supply your own processor-dependent routine"
+#endif
+
 #endif	/* _IP_H */
diff -r -u -N linux.orig/net/inet/tcp.c linux.arm/net/inet/tcp.c
--- linux.orig/net/inet/tcp.c	Fri Oct 27 23:09:16 1995
+++ linux.arm/net/inet/tcp.c	Fri Oct 27 23:11:28 1995
@@ -135,6 +135,7 @@
  *		Alan Cox	:	tcp_data() doesn't ack illegal PSH
  *					only frames. At least one pc tcp stack
  *					generates them.
+ *		Russell King	:	(8/7/95) Altered to allow this to compile on the arm
  *
  *
  * To Fix:
@@ -1015,7 +1016,7 @@
 /*
  *	This routine computes a TCP checksum. 
  */
- 
+#ifndef __arm__
 unsigned short tcp_check(struct tcphdr *th, int len,
 	  unsigned long saddr, unsigned long daddr)
 {     
@@ -1095,7 +1096,10 @@
   
   	return((~sum) & 0xffff);
 }
-
+#else
+extern unsigned short tcp_check(struct tcphdr *th, int len,
+	  unsigned long saddr, unsigned long daddr);
+#endif
 
 
 void tcp_send_check(struct tcphdr *th, unsigned long saddr, 
diff -r -u -N linux.orig/net/inet/udp.c linux.arm/net/inet/udp.c
--- linux.orig/net/inet/udp.c	Fri Oct 27 23:09:16 1995
+++ linux.arm/net/inet/udp.c	Fri Oct 27 23:11:30 1995
@@ -40,6 +40,7 @@
  *		Alan Cox	:	Smarter af_inet used properly.
  *		Alan Cox	:	Use new kernel side addressing.
  *		Alan Cox	:	Incorrect return on truncated datagram receive.
+ *		Russell King	:	(8/7/95) Altered to allow it to be compiled on the arm.
  *
  *
  *		This program is free software; you can redistribute it and/or
@@ -151,7 +152,7 @@
 #endif
 }
 
-
+#ifndef __arm__
 static unsigned short udp_check(struct udphdr *uh, int len, unsigned long saddr, unsigned long daddr)
 {
 	unsigned long sum;
@@ -223,7 +224,9 @@
 
 	return((~sum) & 0xffff);
 }
-
+#else
+extern unsigned short udp_check(struct udphdr *uh, int len, unsigned long saddr, unsigned long daddr);
+#endif
 /*
  *	Generate UDP checksums. These may be disabled, eg for fast NFS over ethernet
  *	We default them enabled.. if you turn them off you either know what you are
diff -r -u -N linux.orig/net/unix/Makefile linux.arm/net/unix/Makefile
--- linux.orig/net/unix/Makefile	Fri Oct 27 23:09:20 1995
+++ linux.arm/net/unix/Makefile	Fri Oct 27 23:11:46 1995
@@ -18,6 +18,8 @@
 
 unix.o: $(OBJS)
 	$(LD) -r -o unix.o $(OBJS)
+#	stripper unix.o unix2.o
+	sync
 
 dep:
 	$(CPP) -M *.c > .depend
diff -r -u -N linux.orig/net/unix/sock.c linux.arm/net/unix/sock.c
--- linux.orig/net/unix/sock.c	Fri Oct 27 23:09:20 1995
+++ linux.arm/net/unix/sock.c	Fri Oct 27 23:11:46 1995
@@ -289,7 +289,11 @@
 	{
 		if (upd->buf) 
 		{
+#ifndef __arm__
 			free_page((unsigned long)upd->buf);
+#else
+			kfree((void *) upd->buf);
+#endif
 			upd->buf = NULL;
 			upd->bp_head = upd->bp_tail = 0;
 		}
@@ -321,7 +325,11 @@
 		printk("UNIX: create: can't allocate buffer\n");
 		return(-ENOMEM);
 	}
-	if (!(upd->buf = (char*) get_free_page(GFP_USER))) 
+#ifndef __arm__
+	if (!(upd->buf = (char*) get_free_page(GFP_USER)))
+#else
+	if (!(upd->buf = (char*) kmalloc(BUF_SIZE, GFP_USER)))
+#endif
 	{
 		printk("UNIX: create: can't get page!\n");
 		unix_data_deref(upd);
diff -r -u -N linux.orig/net/unix/unix.h linux.arm/net/unix/unix.h
--- linux.orig/net/unix/unix.h	Fri Oct 27 23:09:20 1995
+++ linux.arm/net/unix/unix.h	Fri Oct 27 23:11:46 1995
@@ -58,7 +58,11 @@
  * note that buffer contents can wraparound, and we can write one byte less
  * than full size to discern full vs empty.
  */
+#ifndef __arm__
 #define BUF_SIZE		PAGE_SIZE
+#else
+#define BUF_SIZE		4096
+#endif
 #define UN_BUF_AVAIL(UPD)	(((UPD)->bp_head - (UPD)->bp_tail) & \
 								(BUF_SIZE-1))
 #define UN_BUF_SPACE(UPD)	((BUF_SIZE-1) - UN_BUF_AVAIL(UPD))
diff -r -u -N linux.orig/versions.mk linux.arm/versions.mk
--- linux.orig/versions.mk	Fri Oct 27 23:09:22 1995
+++ linux.arm/versions.mk	Fri Oct 27 23:11:23 1995
@@ -14,7 +14,7 @@
 	$(CC) $(CFLAGS) -E -D__GENKSYMS__ $< | genksyms -w $(TOPINCL)/modules
 	@ln -sf $(TOPINCL)/modules/$@ .
 
-$(SYMTAB_OBJS):
+$(SYMTAB_OBJS): $(TOPINCL)/modversions.h
 	$(CC) $(CFLAGS) -DEXPORT_SYMTAB -c $(@:.o=.c)
 
 $(TOPINCL)/modversions.h: $(SYMTAB_OBJS:.o=.ver)
