]> git.neil.brown.name Git - history.git/commitdiff
Import 1.1.38 1.1.38
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:09:36 +0000 (15:09 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:09:36 +0000 (15:09 -0500)
86 files changed:
Makefile
config.in
drivers/FPU-emu/Makefile
drivers/FPU-emu/README
drivers/FPU-emu/div_Xsig.S [new file with mode: 0644]
drivers/FPU-emu/errors.c
drivers/FPU-emu/fpu_emu.h
drivers/FPU-emu/fpu_proto.h
drivers/FPU-emu/fpu_trig.c
drivers/FPU-emu/mul_Xsig.S [new file with mode: 0644]
drivers/FPU-emu/poly.h [new file with mode: 0644]
drivers/FPU-emu/poly_2xm1.c
drivers/FPU-emu/poly_atan.c
drivers/FPU-emu/poly_l2.c
drivers/FPU-emu/poly_sin.c
drivers/FPU-emu/poly_tan.c
drivers/FPU-emu/polynom_Xsig.S [new file with mode: 0644]
drivers/FPU-emu/reg_ld_str.c
drivers/FPU-emu/round_Xsig.S [new file with mode: 0644]
drivers/FPU-emu/shr_Xsig.S [new file with mode: 0644]
drivers/FPU-emu/version.h
drivers/block/README.sbpcd
drivers/block/cdu31a.c
drivers/block/floppy.c
drivers/block/mcd.c
drivers/block/sbpcd.c
drivers/net/Space.c
drivers/net/depca.c
drivers/net/net_init.c
drivers/scsi/53c7,8xx.c [new file with mode: 0644]
drivers/scsi/53c7,8xx.h [new file with mode: 0644]
drivers/scsi/53c7,8xx.scr [new file with mode: 0644]
drivers/scsi/53c8xx_d.h [new file with mode: 0644]
drivers/scsi/53c8xx_u.h [new file with mode: 0644]
drivers/scsi/Makefile
drivers/scsi/NCR5380.c
drivers/scsi/NCR5380.h
drivers/scsi/constants.c
drivers/scsi/fdomain.c
drivers/scsi/fdomain.h
drivers/scsi/g_NCR5380.h
drivers/scsi/hosts.c
drivers/scsi/pas16.c
drivers/scsi/pas16.h
drivers/scsi/script_asm.pl [new file with mode: 0644]
drivers/scsi/scsi.c
drivers/scsi/scsi.h
drivers/scsi/scsi_ioctl.h
drivers/scsi/scsicam.c [new file with mode: 0644]
drivers/scsi/sd.c
drivers/scsi/seagate.c
drivers/scsi/sr.c
drivers/scsi/t128.c
fs/dcache.c
fs/exec.c
fs/ext2/namei.c
fs/locks.c
fs/proc/link.c
fs/super.c
include/linux/bios32.h [new file with mode: 0644]
include/linux/fs.h
include/linux/kernel.h
include/linux/mm.h
include/linux/mman.h
include/linux/pci.h [new file with mode: 0644]
include/linux/sbpcd.h
include/linux/sched.h
include/linux/serial.h
include/linux/sysv_fs.h
init/main.c
kernel/Makefile
kernel/bios32.c [new file with mode: 0644]
kernel/ksyms.c
kernel/sched.c
kernel/splx.c [new file with mode: 0644]
kernel/sys.c
kernel/time.c
mm/mmap.c
net/inet/af_inet.c
net/inet/arp.c
net/inet/dev.c
net/inet/ip.c
net/inet/ipx.c
net/inet/packet.c
net/inet/tcp.c
tools/version.c

index 9f707fa7be06ed79a6470112adbbcca1ae517c21..48bd87b891bc51511234197357af5c014ca9f523 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 1
-SUBLEVEL = 37
+SUBLEVEL = 38
 
 all:   Version zImage
 
@@ -153,6 +153,7 @@ tools/version.h: $(CONFIGURE) Makefile
        @echo \#define LINUX_COMPILE_BY \"`whoami`\" >> tools/version.h
        @echo \#define LINUX_COMPILE_HOST \"`hostname`\" >> tools/version.h
        @echo \#define LINUX_COMPILE_DOMAIN \"`domainname`\" >> tools/version.h
+       @echo \#define LINUX_COMPILER \"`$(HOSTCC) -v 2>&1 | tail -1`\" >> tools/version.h
 
 tools/build: tools/build.c $(CONFIGURE)
        $(HOSTCC) $(CFLAGS) -o $@ $<
index 2171d49493628e881c6ede6a1fc1948c9175e997..8106bbcaf5e0a055d9319ad67b17c437c1079002 100644 (file)
--- a/config.in
+++ b/config.in
@@ -53,6 +53,7 @@ bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 n
 bool 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC n
 bool 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN n
 bool 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 n
+bool 'NCR53c7,8xx SCSI support'  CONFIG_SCSI_NCR53C7xx n
 #bool 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 n
 bool 'PAS16 SCSI support' CONFIG_SCSI_PAS16 n
 bool 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE n
index 544c357cec6248751a1a8a3d1082d21e42d80759..46ff0c5411b8acf4503f50317590275c2f4c14f8 100644 (file)
@@ -20,12 +20,13 @@ OBJS =      fpu_entry.o div_small.o errors.o \
        fpu_arith.o fpu_aux.o fpu_etc.o fpu_trig.o \
        load_store.o get_address.o \
        poly_atan.o poly_l2.o poly_2xm1.o poly_sin.o poly_tan.o \
-       poly_div.o poly_mul64.o polynomial.o \
        reg_add_sub.o reg_compare.o reg_constant.o reg_ld_str.o \
        reg_div.o reg_mul.o reg_norm.o \
        reg_u_add.o reg_u_div.o reg_u_mul.o reg_u_sub.o \
        reg_round.o \
-       wm_shrx.o wm_sqrt.o
+       wm_shrx.o wm_sqrt.o \
+       div_Xsig.o polynom_Xsig.o round_Xsig.o \
+       shr_Xsig.o mul_Xsig.o
 
 math.a: $(OBJS)
        rm -f math.a
index 032f8d1d52ce0dd77a96f6c5ca3bada256cf16f8..aa9a3e49ad5b4b4d1270d9a8a6cb68ecf8fe7ad3 100644 (file)
 
 
 wm-FPU-emu is an FPU emulator for Linux. It is derived from wm-emu387
-which is my 80387 emulator for djgpp (gcc under msdos); wm-emu387 was
-in turn based upon emu387 which was written by DJ Delorie for djgpp.
-The interface to the Linux kernel is based upon the original Linux
-math emulator by Linus Torvalds.
+which was my 80387 emulator for early versions of djgpp (gcc under
+msdos); wm-emu387 was in turn based upon emu387 which was written by
+DJ Delorie for djgpp.  The interface to the Linux kernel is based upon
+the original Linux math emulator by Linus Torvalds.
 
 My target FPU for wm-FPU-emu is that described in the Intel486
 Programmer's Reference Manual (1992 edition). Unfortunately, numerous
@@ -37,8 +37,9 @@ possible to be sure that all of the peculiarities of the 80486 have
 been discovered, so there is always likely to be obscure differences
 in the detailed behaviour of the emulator and a real 80486.
 
-wm-FPU-emu does not implement all of the behaviour of the 80486 FPU.
-See "Limitations" later in this file for a list of some differences.
+wm-FPU-emu does not implement all of the behaviour of the 80486 FPU,
+but is very close.  See "Limitations" later in this file for a list of
+some differences.
 
 Please report bugs, etc to me at:
        billm@vaxc.cc.monash.edu.au
@@ -47,7 +48,7 @@ Please report bugs, etc to me at:
 
 
 --Bill Metzenthen
-  June 1994
+  August 1994
 
 
 ----------------------- Internals of wm-FPU-emu -----------------------
@@ -96,7 +97,7 @@ form of re-entrancy which is required by the Linux kernel.
 ----------------------- Limitations of wm-FPU-emu -----------------------
 
 There are a number of differences between the current wm-FPU-emu
-(version 1.12) and the 80486 FPU (apart from bugs). Some of the more
+(version 1.20) and the 80486 FPU (apart from bugs). Some of the more
 important differences are listed below:
 
 The Roundup flag does not have much meaning for the transcendental
@@ -233,54 +234,58 @@ have been implemented.
 ----------------------- Accuracy of wm-FPU-emu -----------------------
 
 
-Accuracy: The following table gives the accuracy of the sqrt(), trig
-and log functions. Each function was tested at about 400 points. Ideal
-results would be 64 bits. The reduced accuracy of cos() and tan() for
-arguments greater than pi/4 can be thought of as being due to the
+The accuracy of the emulator is in almost all cases equal to or better
+than that of an Intel 80486 FPU.
+
+The results of the basic arithmetic functions (+,-,*,/), and fsqrt
+match those of an 80486 FPU. They are the best possible; the error for
+these never exceeds 1/2 an lsb. The fprem and fprem1 instructions
+return exact results; they have no error.
+
+
+The following table compares the emulator accuracy for the sqrt(),
+trig and log functions against the Turbo C "emulator". For this table,
+each function was tested at about 400 points. Ideal worst-case results
+would be 64 bits. The reduced Turbo C accuracy of cos() and tan() for
+arguments greater than pi/4 can be thought of as being related to the
 precision of the argument x; e.g. an argument of pi/2-(1e-10) which is
-accurate to 64 bits can result in a relative accuracy in cos() of about
-64 + log2(cos(x)) = 31 bits. Results for the Turbo C emulator are given
-in the last column.
+accurate to 64 bits can result in a relative accuracy in cos() of
+about 64 + log2(cos(x)) = 31 bits.
 
 
 Function      Tested x range            Worst result                Turbo C
                                         (relative bits)
 
 sqrt(x)       1 .. 2                    64.1                         63.2
-atan(x)       1e-10 .. 200              62.6                         62.8
-cos(x)        0 .. pi/2-(1e-10)         63.2 (x <= pi/4)             62.4
-                                        35.2 (x = pi/2-(1e-10))      31.9
-sin(x)        1e-10 .. pi/2             63.0                         62.8
-tan(x)        1e-10 .. pi/2-(1e-10)     62.4 (x <= pi/4)             62.1
-                                        35.2 (x = pi/2-(1e-10))      31.9
-exp(x)        0 .. 1                    63.1                         62.9
-log(x)        1+1e-6 .. 2               62.4                         62.1
-
-
-As of version 1.3 of the emulator, the accuracy of the basic
-arithmetic has been improved (by a small fraction of a bit). Care has
-been taken to ensure full accuracy of the rounding of the basic
-arithmetic functions (+,-,*,/,and fsqrt), and they all now produce
-results which are exact to the 64th bit (unless there are any bugs
-left). To ensure this, it was necessary to effectively get information
-of up to about 128 bits precision. The emulator now passes the
-"paranoia" tests (compiled with gcc 2.3.3) for 'float' variables (24
-bit precision numbers) when precision control is set to 24, 53 or 64
-bits, and for 'double' variables (53 bit precision numbers) when
-precision control is set to 53 bits (a properly performing FPU cannot
-pass the 'paranoia' tests for 'double' variables when precision
-control is set to 64 bits).
-
-For version 1.5, the accuracy of fprem and fprem1 has been improved.
-These functions now produce exact results. The code for reducing the
-argument for the trig functions (fsin, fcos, fptan and fsincos) has
-been improved and now effectively uses a value for pi which is
-accurate to more than 128 bits precision. As a consquence, the
-accuracy of these functions for large arguments has been dramatically
-improved (and is now very much better than an 80486 FPU). There is
-also now no degradation of accuracy for fcos and ftan for operands
-close to pi/2. Measured results are (note that the definition of
-accuracy has changed slightly from that used for the above table):
+atan(x)       1e-10 .. 200              64.2                         62.8
+cos(x)        0 .. pi/2-(1e-10)         64.4 (x <= pi/4)             62.4
+                                        64.1 (x = pi/2-(1e-10))      31.9
+sin(x)        1e-10 .. pi/2             64.0                         62.8
+tan(x)        1e-10 .. pi/2-(1e-10)     64.0 (x <= pi/4)             62.1
+                                        64.1 (x = pi/2-(1e-10))      31.9
+exp(x)        0 .. 1                    63.1 **                      62.9
+log(x)        1+1e-6 .. 2               63.8 **                      62.1
+
+** The accuracy for exp() and log() is low because the FPU (emulator)
+does not compute them directly; two operations are required.
+
+
+The emulator passes the "paranoia" tests (compiled with gcc 2.3.3 or
+later) for 'float' variables (24 bit precision numbers) when precision
+control is set to 24, 53 or 64 bits, and for 'double' variables (53
+bit precision numbers) when precision control is set to 53 bits (a
+properly performing FPU cannot pass the 'paranoia' tests for 'double'
+variables when precision control is set to 64 bits).
+
+The code for reducing the argument for the trig functions (fsin, fcos,
+fptan and fsincos) has been improved and now effectively uses a value
+for pi which is accurate to more than 128 bits precision. As a
+consequence, the accuracy of these functions for large arguments has
+been dramatically improved (and is now very much better than an 80486
+FPU). There is also now no degradation of accuracy for fcos and fptan
+for operands close to pi/2. Measured results are (note that the
+definition of accuracy has changed slightly from that used for the
+above table):
 
 Function      Tested x range          Worst result
                                      (absolute bits)
@@ -303,6 +308,99 @@ For arguments close to critical angles (which occur at multiples of
 pi/2) the emulator is more accurate than an 80486 FPU. For very large
 arguments, the emulator is far more accurate.
 
+
+Prior to version 1.20 of the emulator, the accuracy of the results for
+the transcendental functions (in their principal range) was not as
+good as the results from an 80486 FPU. From version 1.20, the accuracy
+has been considerably improved and these functions now give measured
+worst-case results which are better than the worst-case results given
+by an 80486 FPU.
+
+The following table gives the measured results for the emulator. The
+number of randomly selected arguments in each case is about half a
+million.  The group of three columns gives the frequency of the given
+accuracy in number of times per million, thus the second of these
+columns shows that an accuracy of between 63.80 and 63.89 bits was
+found at a rate of 133 times per one million measurements for fsin.
+The results show that the fsin, fcos and fptan instructions return
+results which are in error (i.e. less accurate than the best possible
+result (which is 64 bits)) for about one per cent of all arguments
+between -pi/2 and +pi/2.  The other instructions have a lower
+frequency of results which are in error.  The last two columns give
+the worst accuracy which was found (in bits) and the approximate value
+of the argument which produced it.
+
+                                frequency (per M)
+                               -------------------   ---------------
+instr   arg range    # tests   63.7   63.8    63.9   worst   at arg
+                               bits   bits    bits    bits
+-----  ------------  -------   ----   ----   -----   -----  --------
+fsin     (0,pi/2)     547756      0    133   10673   63.89  0.451317
+fcos     (0,pi/2)     547563      0    126   10532   63.85  0.700801
+fptan    (0,pi/2)     536274     11    267   10059   63.74  0.784876
+fpatan  4 quadrants   517087      0      8    1855   63.88  0.435121 (4q)
+fyl2x     (0,20)      541861      0      0    1323   63.94  1.40923  (x)
+fyl2xp1 (-.293,.414)  520256      0      0    5678   63.93  0.408542 (x)
+f2xm1     (-1,1)      538847      4    481    6488   63.79  0.167709
+
+
+Tests performed on an 80486 FPU showed results of lower accuracy. The
+following table gives the results which were obtained with an AMD
+486DX2/66 (other tests indictate that an Intel 486DX produces
+identical results).  The tests were basically the same as those used
+to measure the emulator (the values, being random, were in general not
+the same).  The total number of tests for each instruction are given
+at the end of the table, in case each about 100k tests were performed.
+Another line of figures at the end of the table shows that most of the
+instructions return results which are in error for more than 10
+percent of the arguments tested.
+
+The numbers in the body of the table give the approx number of times a
+result of the given accuracy in bits (given in the left-most column)
+was obtained per one million arguments. For three of the instructions,
+two columns of results are given: * The second column for f2xm1 gives
+the number cases where the results of the first column were for a
+positive argument, this shows that this instruction gives better
+results for positive arguments than it does for negative.  * In the
+cases of fcos and fptan, the first column gives the results when all
+cases where arguments greater than 1.5 were removed from the results
+given in the second column. Unlike the emulator, an 80486 FPU returns
+results of relatively poor accuracy for these instructions when the
+argument approaches pi/2. The table does not show those cases when the
+accuracy of the results were less than 62 bits, which occurs quite
+often for fsin and fptan when the argument approaches pi/2. This poor
+accuracy is discussed above in relation to the Turbo C "emulator", and
+the accuracy of the value of pi.
+
+
+bits   f2xm1  f2xm1 fpatan   fcos   fcos  fyl2x fyl2xp1  fsin  fptan  fptan
+62.0       0      0      0      0    437      0      0      0      0    925
+62.1       0      0     10      0    894      0      0      0      0   1023
+62.2      14      0      0      0   1033      0      0      0      0    945
+62.3      57      0      0      0   1202      0      0      0      0   1023
+62.4     385      0      0     10   1292      0     23      0      0   1178
+62.5    1140      0      0    119   1649      0     39      0      0   1149
+62.6    2037      0      0    189   1620      0     16      0      0   1169
+62.7    5086     14      0    646   2315     10    101     35     39   1402
+62.8    8818     86      0    984   3050     59    287    131    224   2036
+62.9   11340   1355      0   2126   4153     79    605    357    321   1948
+63.0   15557   4750      0   3319   5376    246   1281    862    808   2688
+63.1   20016   8288      0   4620   6628    511   2569   1723   1510   3302
+63.2   24945  11127     10   6588   8098   1120   4470   2968   2990   4724
+63.3   25686  12382     69   8774  10682   1906   6775   4482   5474   7236
+63.4   29219  14722     79  11109  12311   3094   9414   7259   8912  10587
+63.5   30458  14936    393  13802  15014   5874  12666   9609  13762  15262
+63.6   32439  16448   1277  17945  19028  10226  15537  14657  19158  20346
+63.7   35031  16805   4067  23003  23947  18910  20116  21333  25001  26209
+63.8   33251  15820   7673  24781  25675  24617  25354  24440  29433  30329
+63.9   33293  16833  18529  28318  29233  31267  31470  27748  29676  30601
+
+Per cent with error:
+        30.9           3.2          18.5    9.8   13.1   11.6          17.4
+Total arguments tested:
+       70194  70099 101784 100641 100641 101799 128853 114893 102675 102675
+
+
 ------------------------- Contributors -------------------------------
 
 A number of people have contributed to the development of the
diff --git a/drivers/FPU-emu/div_Xsig.S b/drivers/FPU-emu/div_Xsig.S
new file mode 100644 (file)
index 0000000..67d8be9
--- /dev/null
@@ -0,0 +1,369 @@
+       .file   "div_Xsig.S"
+/*---------------------------------------------------------------------------+
+ |  div_Xsig.S                                                               |
+ |                                                                           |
+ | Division subroutine for 96 bit quantities                                 |
+ |                                                                           |
+ | Copyright (C) 1994                                                        |
+ |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ |                                                                           |
+ |                                                                           |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | Divide the 96 bit quantity pointed to by a, by that pointed to by b, and  |
+ | put the 96 bit result at the location d.                                  |
+ |                                                                           |
+ | The result may not be accurate to 96 bits. It is intended for use where   |
+ | a result better than 64 bits is required. The result should usually be    |
+ | good to at least 94 bits.                                                 |
+ | The returned result is actually divided by one half. This is done to      |
+ | prevent overflow.                                                         |
+ |                                                                           |
+ |  .aaaaaaaaaaaaaa / .bbbbbbbbbbbbb  ->  .dddddddddddd                      |
+ |                                                                           |
+ |  void div_Xsig(Xsig *a, Xsig *b, Xsig *dest)                              |
+ |                                                                           |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "fpu_asm.h"
+
+
+#define        XsigLL(x)       (x)
+#define        XsigL(x)        4(x)
+#define        XsigH(x)        8(x)
+
+
+#ifndef NON_REENTRANT_FPU
+/*
+       Local storage on the stack:
+       Accumulator:    FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
+ */
+#define FPU_accum_3    -4(%ebp)
+#define FPU_accum_2    -8(%ebp)
+#define FPU_accum_1    -12(%ebp)
+#define FPU_accum_0    -16(%ebp)
+#define FPU_result_3   -20(%ebp)
+#define FPU_result_2   -24(%ebp)
+#define FPU_result_1   -28(%ebp)
+
+#else
+.data
+/*
+       Local storage in a static area:
+       Accumulator:    FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
+ */
+       .align 2,0
+FPU_accum_3:
+       .long   0
+FPU_accum_2:
+       .long   0
+FPU_accum_1:
+       .long   0
+FPU_accum_0:
+       .long   0
+FPU_result_3:
+       .long   0
+FPU_result_2:
+       .long   0
+FPU_result_1:
+       .long   0
+#endif NON_REENTRANT_FPU
+
+
+.text
+       .align 2,144
+
+.globl _div_Xsig
+
+_div_Xsig:
+       pushl   %ebp
+       movl    %esp,%ebp
+#ifndef NON_REENTRANT_FPU
+       subl    $28,%esp
+#endif NON_REENTRANT_FPU
+
+       pushl   %esi
+       pushl   %edi
+       pushl   %ebx
+
+       movl    PARAM1,%esi     /* pointer to num */
+       movl    PARAM2,%ebx     /* pointer to denom */
+
+#ifdef PARANOID
+       testl   $0x80000000, XsigH(%ebx)        /* Divisor */
+       je      L_bugged
+#endif PARANOID
+
+
+/*---------------------------------------------------------------------------+
+ |  Divide:   Return  arg1/arg2 to arg3.                                     |
+ |                                                                           |
+ |  The maximum returned value is (ignoring exponents)                       |
+ |               .ffffffff ffffffff                                          |
+ |               ------------------  =  1.ffffffff fffffffe                  |
+ |               .80000000 00000000                                          |
+ | and the minimum is                                                        |
+ |               .80000000 00000000                                          |
+ |               ------------------  =  .80000000 00000001   (rounded)       |
+ |               .ffffffff ffffffff                                          |
+ |                                                                           |
+ +---------------------------------------------------------------------------*/
+
+       /* Save extended dividend in local register */
+
+       /* Divide by 2 to prevent overflow */
+       clc
+       movl    XsigH(%esi),%eax
+       rcrl    %eax
+       movl    %eax,FPU_accum_3
+       movl    XsigL(%esi),%eax
+       rcrl    %eax
+       movl    %eax,FPU_accum_2
+       movl    XsigLL(%esi),%eax
+       rcrl    %eax
+       movl    %eax,FPU_accum_1
+       movl    $0,%eax
+       rcrl    %eax
+       movl    %eax,FPU_accum_0
+
+       movl    FPU_accum_2,%eax        /* Get the current num */
+       movl    FPU_accum_3,%edx
+
+/*----------------------------------------------------------------------*/
+/* Initialization done.
+   Do the first 32 bits. */
+
+       /* We will divide by a number which is too large */
+       movl    XsigH(%ebx),%ecx
+       addl    $1,%ecx
+       jnc     LFirst_div_not_1
+
+       /* here we need to divide by 100000000h,
+          i.e., no division at all.. */
+       mov     %edx,%eax
+       jmp     LFirst_div_done
+
+LFirst_div_not_1:
+       divl    %ecx            /* Divide the numerator by the augmented
+                                  denom ms dw */
+
+LFirst_div_done:
+       movl    %eax,FPU_result_3       /* Put the result in the answer */
+
+       mull    XsigH(%ebx)     /* mul by the ms dw of the denom */
+
+       subl    %eax,FPU_accum_2        /* Subtract from the num local reg */
+       sbbl    %edx,FPU_accum_3
+
+       movl    FPU_result_3,%eax       /* Get the result back */
+       mull    XsigL(%ebx)     /* now mul the ls dw of the denom */
+
+       subl    %eax,FPU_accum_1        /* Subtract from the num local reg */
+       sbbl    %edx,FPU_accum_2
+       sbbl    $0,FPU_accum_3
+       je      LDo_2nd_32_bits         /* Must check for non-zero result here */
+
+#ifdef PARANOID
+       jb      L_bugged_1
+#endif PARANOID
+
+       /* need to subtract another once of the denom */
+       incl    FPU_result_3    /* Correct the answer */
+
+       movl    XsigL(%ebx),%eax
+       movl    XsigH(%ebx),%edx
+       subl    %eax,FPU_accum_1        /* Subtract from the num local reg */
+       sbbl    %edx,FPU_accum_2
+
+#ifdef PARANOID
+       sbbl    $0,FPU_accum_3
+       jne     L_bugged_1      /* Must check for non-zero result here */
+#endif PARANOID
+
+/*----------------------------------------------------------------------*/
+/* Half of the main problem is done, there is just a reduced numerator
+   to handle now.
+   Work with the second 32 bits, FPU_accum_0 not used from now on */
+LDo_2nd_32_bits:
+       movl    FPU_accum_2,%edx        /* get the reduced num */
+       movl    FPU_accum_1,%eax
+
+       /* need to check for possible subsequent overflow */
+       cmpl    XsigH(%ebx),%edx
+       jb      LDo_2nd_div
+       ja      LPrevent_2nd_overflow
+
+       cmpl    XsigL(%ebx),%eax
+       jb      LDo_2nd_div
+
+LPrevent_2nd_overflow:
+/* The numerator is greater or equal, would cause overflow */
+       /* prevent overflow */
+       subl    XsigL(%ebx),%eax
+       sbbl    XsigH(%ebx),%edx
+       movl    %edx,FPU_accum_2
+       movl    %eax,FPU_accum_1
+
+       incl    FPU_result_3    /* Reflect the subtraction in the answer */
+
+#ifdef PARANOID
+       je      L_bugged_2      /* Can't bump the result to 1.0 */
+#endif PARANOID
+
+LDo_2nd_div:
+       cmpl    $0,%ecx         /* augmented denom msw */
+       jnz     LSecond_div_not_1
+
+       /* %ecx == 0, we are dividing by 1.0 */
+       mov     %edx,%eax
+       jmp     LSecond_div_done
+
+LSecond_div_not_1:
+       divl    %ecx            /* Divide the numerator by the denom ms dw */
+
+LSecond_div_done:
+       movl    %eax,FPU_result_2       /* Put the result in the answer */
+
+       mull    XsigH(%ebx)     /* mul by the ms dw of the denom */
+
+       subl    %eax,FPU_accum_1        /* Subtract from the num local reg */
+       sbbl    %edx,FPU_accum_2
+
+#ifdef PARANOID
+       jc      L_bugged_2
+#endif PARANOID
+
+       movl    FPU_result_2,%eax       /* Get the result back */
+       mull    XsigL(%ebx)     /* now mul the ls dw of the denom */
+
+       subl    %eax,FPU_accum_0        /* Subtract from the num local reg */
+       sbbl    %edx,FPU_accum_1        /* Subtract from the num local reg */
+       sbbl    $0,FPU_accum_2
+
+#ifdef PARANOID
+       jc      L_bugged_2
+#endif PARANOID
+
+       jz      LDo_3rd_32_bits
+
+#ifdef PARANOID
+       cmpl    $1,FPU_accum_2
+       jne     L_bugged_2
+#endif PARANOID
+
+       /* need to subtract another once of the denom */
+       movl    XsigL(%ebx),%eax
+       movl    XsigH(%ebx),%edx
+       subl    %eax,FPU_accum_0        /* Subtract from the num local reg */
+       sbbl    %edx,FPU_accum_1
+       sbbl    $0,FPU_accum_2
+
+#ifdef PARANOID
+       jc      L_bugged_2
+       jne     L_bugged_2
+#endif PARANOID
+
+       addl    $1,FPU_result_2 /* Correct the answer */
+       adcl    $0,FPU_result_3
+
+#ifdef PARANOID
+       jc      L_bugged_2      /* Must check for non-zero result here */
+#endif PARANOID
+
+/*----------------------------------------------------------------------*/
+/* The division is essentially finished here, we just need to perform
+   tidying operations.
+   Deal with the 3rd 32 bits */
+LDo_3rd_32_bits:
+       /* We use an approximation for the third 32 bits.
+       To take account of the 3rd 32 bits of the divisor
+       (call them del), we subtract  del * (a/b) */
+
+       movl    FPU_result_3,%eax       /* a/b */
+       mull    XsigLL(%ebx)            /* del */
+
+       subl    %edx,FPU_accum_1
+
+       /* A borrow indicates that the result is negative */
+       jnb     LTest_over
+
+       movl    XsigH(%ebx),%edx
+       addl    %edx,FPU_accum_1
+
+       subl    $1,FPU_result_2         /* Adjust the answer */
+       sbbl    $0,FPU_result_3
+
+       /* The above addition might not have been enough, check again. */
+       movl    FPU_accum_1,%edx        /* get the reduced num */
+       cmpl    XsigH(%ebx),%edx        /* denom */
+       jb      LDo_3rd_div
+
+       movl    XsigH(%ebx),%edx
+       addl    %edx,FPU_accum_1
+
+       subl    $1,FPU_result_2         /* Adjust the answer */
+       sbbl    $0,FPU_result_3
+       jmp     LDo_3rd_div
+
+LTest_over:
+       movl    FPU_accum_1,%edx        /* get the reduced num */
+
+       /* need to check for possible subsequent overflow */
+       cmpl    XsigH(%ebx),%edx        /* denom */
+       jb      LDo_3rd_div
+
+       /* prevent overflow */
+       subl    XsigH(%ebx),%edx
+       movl    %edx,FPU_accum_1
+
+       addl    $1,FPU_result_2 /* Reflect the subtraction in the answer */
+       adcl    $0,FPU_result_3
+
+LDo_3rd_div:
+       movl    FPU_accum_0,%eax
+       movl    FPU_accum_1,%edx
+       divl    XsigH(%ebx)
+
+       movl    %eax,FPU_result_1       /* Rough estimate of third word */
+
+       movl    PARAM3,%esi             /* pointer to answer */
+
+       movl    FPU_result_1,%eax
+       movl    %eax,XsigLL(%esi)
+       movl    FPU_result_2,%eax
+       movl    %eax,XsigL(%esi)
+       movl    FPU_result_3,%eax
+       movl    %eax,XsigH(%esi)
+
+L_exit:
+       popl    %ebx
+       popl    %edi
+       popl    %esi
+
+       leave
+       ret
+
+
+#ifdef PARANOID
+/* The logic is wrong if we got here */
+L_bugged:
+       pushl   EX_INTERNAL|0x240
+       call    EXCEPTION
+       pop     %ebx
+       jmp     L_exit
+
+L_bugged_1:
+       pushl   EX_INTERNAL|0x241
+       call    EXCEPTION
+       pop     %ebx
+       jmp     L_exit
+
+L_bugged_2:
+       pushl   EX_INTERNAL|0x242
+       call    EXCEPTION
+       pop     %ebx
+       jmp     L_exit
+#endif PARANOID
index ddc589a952a23487354a9b7b82f46cc24c88e815..e34eec9422ee0abf12ecee628e40937fcecf8c15 100644 (file)
@@ -235,10 +235,8 @@ static struct {
        0x1nn  in a *.c file:
               0x101  in reg_add_sub.c
               0x102  in reg_mul.c
-              0x103  in poly_sin.c
               0x104  in poly_atan.c
               0x105  in reg_mul.c
-             0x106  in reg_ld_str.c
               0x107  in fpu_trig.c
              0x108  in reg_compare.c
              0x109  in reg_compare.c
@@ -246,7 +244,6 @@ static struct {
              0x111  in fpe_entry.c
              0x112  in fpu_trig.c
              0x113  in errors.c
-             0x114  in reg_ld_str.c
              0x115  in fpu_trig.c
              0x116  in fpu_trig.c
              0x117  in fpu_trig.c
@@ -267,6 +264,12 @@ static struct {
              0x133  in get_address.c
              0x140  in load_store.c
              0x141  in load_store.c
+              0x150  in poly_sin.c
+              0x151  in poly_sin.c
+             0x160  in reg_ld_str.c
+             0x161  in reg_ld_str.c
+             0x162  in reg_ld_str.c
+             0x163  in reg_ld_str.c
        0x2nn  in an *.S file:
               0x201  in reg_u_add.S
               0x202  in reg_u_div.S
@@ -292,6 +295,9 @@ static struct {
              0x234  in reg_round.S
              0x235  in reg_round.S
              0x236  in reg_round.S
+             0x240  in div_Xsig.S
+             0x241  in div_Xsig.S
+             0x242  in div_Xsig.S
  */
 
 void exception(int n)
index a0d1387d521dd00b950852fde54092cd192957dc..9d2c5dd130efcdc625914bc69f0f690e5b3ad536 100644 (file)
@@ -143,13 +143,6 @@ extern unsigned char const data_sizes_16[32];
 /*----- Prototypes for functions written in assembler -----*/
 /* extern void reg_move(FPU_REG *a, FPU_REG *b); */
 
-asmlinkage void mul64(unsigned long long const *a, unsigned long long const *b,
-                     unsigned long long *result);
-asmlinkage void poly_div2(unsigned long long *x);
-asmlinkage void poly_div4(unsigned long long *x);
-asmlinkage void poly_div16(unsigned long long *x);
-asmlinkage void polynomial(unsigned accum[], unsigned const x[],
-                      unsigned short const terms[][4], int const n);
 asmlinkage void normalize(FPU_REG *x);
 asmlinkage void normalize_nuo(FPU_REG *x);
 asmlinkage int reg_div(FPU_REG const *arg1, FPU_REG const *arg2,
index a5a4650fc24816b24203572dc709790969daf99f..b4392fe57de70085683089739547a52c2fe86bea 100644 (file)
@@ -78,18 +78,18 @@ extern int load_store_instr(unsigned char type, fpu_addr_modes addr_modes,
 extern int poly_2xm1(FPU_REG const *arg, FPU_REG *result);
 
 /* poly_atan.c */
-extern void poly_atan(FPU_REG *arg);
-extern void poly_add_1(FPU_REG *src);
+extern void poly_atan(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *result);
 
 /* poly_l2.c */
-extern void poly_l2(FPU_REG const *arg, FPU_REG *result);
-extern int poly_l2p1(FPU_REG const *arg, FPU_REG *result);
+extern void poly_l2(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result);
+extern int poly_l2p1(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result);
 
 /* poly_sin.c */
 extern void poly_sine(FPU_REG const *arg, FPU_REG *result);
+extern void poly_cos(FPU_REG const *arg, FPU_REG *result);
 
 /* poly_tan.c */
-extern void poly_tan(FPU_REG const *arg, FPU_REG *result, int invert);
+extern void poly_tan(FPU_REG const *arg, FPU_REG *result);
 
 /* reg_add_sub.c */
 extern int reg_add(FPU_REG const *a, FPU_REG const *b,
index a368a2e77ca34c2af81f8d855156df63eedca273..05241f70033088bd4eee87968bfa642dd5c98c8a 100644 (file)
@@ -25,8 +25,9 @@ static void rem_kernel(unsigned long long st0, unsigned long long *y,
 #define BETTER_THAN_486
 
 #define FCOS  4
+/* Not needed now with new code
 #define FPTAN 1
-
+ */
 
 /* Used only by fptan, fsin, fcos, and fsincos. */
 /* This routine produces very accurate results, similar to
@@ -64,6 +65,7 @@ static int trig_arg(FPU_REG *X, int even)
       reg_move(&tmp, X);
     }
 
+#ifdef FPTAN
   if ( even == FPTAN )
     {
       if ( ((X->exp >= EXP_BIAS) ||
@@ -73,6 +75,7 @@ static int trig_arg(FPU_REG *X, int even)
       else
        even = 0;
     }
+#endif FPTAN
 
   if ( (even && !(q & 1)) || (!even && (q & 1)) )
     {
@@ -234,43 +237,27 @@ static void f2xm1(FPU_REG *st0_ptr)
     {
     case TW_Valid:
       {
-       FPU_REG rv, tmp;
-
        if ( st0_ptr->exp >= 0 )
          {
            /* For an 80486 FPU, the result is undefined. */
          }
-       else if ( st0_ptr->exp >= -64 )
+#ifdef DENORM_OPERAND
+       else if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+         return;
+#endif DENORM_OPERAND
+       else
          {
-           if ( st0_ptr->sign == SIGN_POS )
-             {
-               /* poly_2xm1(x) requires 0 < x < 1. */
-               poly_2xm1(st0_ptr, &rv);
-               reg_mul(&rv, st0_ptr, st0_ptr, FULL_PRECISION);
-             }
-           else
-             {
-               /* poly_2xm1(x) doesn't handle negative numbers yet. */
-               /* So we compute z=poly_2xm1(-x), and the answer is
-                  then -z/(1+z) */
-               st0_ptr->sign = SIGN_POS;
-               poly_2xm1(st0_ptr, &rv);
-               reg_mul(&rv, st0_ptr, &rv, FULL_PRECISION);
-               reg_add(&rv, &CONST_1, &tmp, FULL_PRECISION);
-               reg_div(&rv, &tmp, st0_ptr, FULL_PRECISION);
-               st0_ptr->sign = SIGN_NEG;
-             }
+           /* poly_2xm1(x) requires 0 < x < 1. */
+           poly_2xm1(st0_ptr, st0_ptr);
          }
-       else
+       if ( st0_ptr->exp <= EXP_UNDER )
          {
-#ifdef DENORM_OPERAND
-           if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-             return;
-#endif DENORM_OPERAND
-           /* For very small arguments, this is accurate enough. */
-           reg_mul(&CONST_LN2, st0_ptr, st0_ptr, FULL_PRECISION);
+           /* A denormal result has been produced.
+              Precision must have been lost, this is always
+              an underflow. */
+           arith_underflow(st0_ptr);
          }
-       set_precision_flag_up();
+       set_precision_flag_up();   /* 80486 appears to always do this */
        return;
       }
     case TW_Zero:
@@ -315,15 +302,12 @@ static void fptan(FPU_REG *st0_ptr)
   switch ( st0_tag )
     {
     case TW_Valid:
-
       if ( st0_ptr->exp > EXP_BIAS - 40 )
        {
          st0_ptr->sign = SIGN_POS;
-         if ( (q = trig_arg(st0_ptr, FPTAN)) != -1 )
+         if ( (q = trig_arg(st0_ptr, 0)) != -1 )
            {
-             reg_div(st0_ptr, &CONST_PI2, st0_ptr,
-                     FULL_PRECISION);
-             poly_tan(st0_ptr, st0_ptr, q & FCOS);
+             poly_tan(st0_ptr, st0_ptr);
              st0_ptr->sign = (q & 1) ^ arg_sign;
            }
          else
@@ -332,6 +316,7 @@ static void fptan(FPU_REG *st0_ptr)
              st0_ptr->sign = arg_sign;         /* restore st(0) */
              return;
            }
+         set_precision_flag_up();  /* We do not really know if up or down */
        }
       else
        {
@@ -350,8 +335,7 @@ static void fptan(FPU_REG *st0_ptr)
              if ( arith_underflow(st0_ptr) )
                return;
            }
-         else
-           set_precision_flag_up();  /* Must be up. */
+         set_precision_flag_down();  /* Must be down. */
        }
       push();
       reg_move(&CONST_1, st_new_ptr);
@@ -550,7 +534,6 @@ static void fsin(FPU_REG *st0_ptr)
          st0_ptr->sign = SIGN_POS;
          if ( (q = trig_arg(st0_ptr, 0)) != -1 )
            {
-             reg_div(st0_ptr, &CONST_PI2, st0_ptr, FULL_PRECISION);
 
              poly_sine(st0_ptr, &rv);
 
@@ -619,10 +602,20 @@ static int f_cos(FPU_REG *arg)
       if ( arg->exp > EXP_BIAS - 40 )
        {
          arg->sign = SIGN_POS;
-         if ( (q = trig_arg(arg, FCOS)) != -1 )
+         if ( (arg->exp < EXP_BIAS)
+             || ((arg->exp == EXP_BIAS)
+                 && (significand(arg) <= 0xc90fdaa22168c234LL)) )
+           {
+             poly_cos(arg, &rv);
+             reg_move(&rv, arg);
+
+             /* We do not really know if up or down */
+             set_precision_flag_down();
+         
+             return 0;
+           }
+         else if ( (q = trig_arg(arg, FCOS)) != -1 )
            {
-             reg_div(arg, &CONST_PI2, arg, FULL_PRECISION);
-             
              poly_sine(arg, &rv);
 
              if ((q+1) & 2)
@@ -988,38 +981,57 @@ static void do_fprem(FPU_REG *st0_ptr, int round)
 static void fyl2x(FPU_REG *st0_ptr)
 {
   char st0_tag = st0_ptr->tag;
-  FPU_REG *st1_ptr = &st(1);
+  FPU_REG *st1_ptr = &st(1), exponent;
   char st1_tag = st1_ptr->tag;
+  int e;
 
   clear_C1();
   if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
     {
       if ( st0_ptr->sign == SIGN_POS )
        {
-         int saved_control, saved_status;
-
 #ifdef DENORM_OPERAND
          if ( ((st0_ptr->exp <= EXP_UNDER) ||
                (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
            return;
 #endif DENORM_OPERAND
 
-         /* We use the general purpose arithmetic,
-            so we need to save these. */
-         saved_status = partial_status;
-         saved_control = control_word;
-         control_word = FULL_PRECISION;
-
-         poly_l2(st0_ptr, st0_ptr);
-
-         /* Enough of the basic arithmetic is done now */
-         control_word = saved_control;
-         partial_status = saved_status;
-
-         /* Let the multiply set the flags */
-         reg_mul(st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION);
-
+         if ( (st0_ptr->sigh == 0x80000000) && (st0_ptr->sigl == 0) )
+           {
+             /* Special case. The result can be precise. */
+             e = st0_ptr->exp - EXP_BIAS;
+             if ( e > 0 )
+               {
+                 exponent.sigh = e;
+                 exponent.sign = SIGN_POS;
+               }
+             else
+               {
+                 exponent.sigh = -e;
+                 exponent.sign = SIGN_NEG;
+               }
+             exponent.sigl = 0;
+             exponent.exp = EXP_BIAS + 31;
+             exponent.tag = TW_Valid;
+             normalize_nuo(&exponent);
+             reg_mul(&exponent, st1_ptr, st1_ptr, FULL_PRECISION);
+           }
+         else
+           {
+             /* The usual case */
+             poly_l2(st0_ptr, st1_ptr, st1_ptr);
+             if ( st1_ptr->exp <= EXP_UNDER )
+               {
+                 /* A denormal result has been produced.
+                    Precision must have been lost, this is always
+                    an underflow. */
+                 arith_underflow(st1_ptr);
+               }
+             else
+               set_precision_flag_up();  /* 80486 appears to always do this */
+           }
          pop();
+         return;
        }
       else
        {
@@ -1181,67 +1193,27 @@ static void fpatan(FPU_REG *st0_ptr)
   char st0_tag = st0_ptr->tag;
   FPU_REG *st1_ptr = &st(1);
   char st1_tag = st1_ptr->tag;
-  char st1_sign = st1_ptr->sign, st0_sign = st0_ptr->sign;
 
   clear_C1();
   if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
     {
-      int saved_control, saved_status;
-      FPU_REG sum;
-      char inverted;
-
 #ifdef DENORM_OPERAND
       if ( ((st0_ptr->exp <= EXP_UNDER) ||
            (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
        return;
 #endif DENORM_OPERAND
 
-      /* We use the general purpose arithmetic so we need to save these. */
-      saved_status = partial_status;
-      saved_control = control_word;
-      control_word = FULL_PRECISION;
+      poly_atan(st0_ptr, st1_ptr, st1_ptr);
 
-      st1_ptr->sign = st0_ptr->sign = SIGN_POS;
-      if ( (compare(st1_ptr) & ~COMP_Denormal) == COMP_A_lt_B )
-       {
-         inverted = 1;
-         reg_div(st0_ptr, st1_ptr, &sum, FULL_PRECISION);
-       }
-      else
-       {
-         inverted = 0;
-         if ( (st0_sign == 0) &&
-             (st1_ptr->exp - st0_ptr->exp < -64) )
-           {
-             control_word = saved_control;
-             partial_status = saved_status;
-             reg_div(st1_ptr, st0_ptr, st1_ptr,
-                     control_word | PR_64_BITS);
-             st1_ptr->sign = st1_sign;
-             pop();
-             set_precision_flag_down();
-             return;
-           }
-         reg_div(st1_ptr, st0_ptr, &sum, FULL_PRECISION);
-       }
-
-      poly_atan(&sum);
-
-      if ( inverted )
-       {
-         reg_sub(&CONST_PI2, &sum, &sum, FULL_PRECISION);
-       }
-      if ( st0_sign )
+      if ( st1_ptr->exp <= EXP_UNDER )
        {
-         reg_sub(&CONST_PI, &sum, &sum, FULL_PRECISION);
+         /* A denormal result has been produced.
+            Precision must have been lost.
+            This is by definition an underflow. */
+         arith_underflow(st1_ptr);
+         pop();
+         return;
        }
-      sum.sign = st1_sign;
-
-      /* All of the basic arithmetic is done now */
-      control_word = saved_control;
-      partial_status = saved_status;
-
-      reg_move(&sum, st1_ptr);
     }
   else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) )
     {
@@ -1358,48 +1330,41 @@ static void fprem1(FPU_REG *st0_ptr)
 
 static void fyl2xp1(FPU_REG *st0_ptr)
 {
-  char st0_tag = st0_ptr->tag;
+  char st0_tag = st0_ptr->tag, sign;
   FPU_REG *st1_ptr = &st(1);
   char st1_tag = st1_ptr->tag;
 
   clear_C1();
   if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
     {
-      int saved_control, saved_status;
-
 #ifdef DENORM_OPERAND
       if ( ((st0_ptr->exp <= EXP_UNDER) ||
            (st1_ptr->exp <= EXP_UNDER)) && denormal_operand() )
        return;
 #endif DENORM_OPERAND
 
-      /* We use the general purpose arithmetic so we need to save these. */
-      saved_status = partial_status;
-      saved_control = control_word;
-      control_word = FULL_PRECISION;
-
-      if ( poly_l2p1(st0_ptr, st0_ptr) )
+      if ( poly_l2p1(st0_ptr, st1_ptr, st1_ptr) )
        {
 #ifdef PECULIAR_486   /* Stupid 80486 doesn't worry about log(negative). */
          st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
-         control_word = saved_control;
-         partial_status = saved_status;
-         set_precision_flag_down();
 #else
          if ( arith_invalid(st1_ptr) )  /* poly_l2p1() returned invalid */
            return;
 #endif PECULIAR_486
-         pop(); return;
        }
-      
-      /* Enough of the basic arithmetic is done now */
-      control_word = saved_control;
-      partial_status = saved_status;
-
-      /* Let the multiply set the flags */
-      reg_mul(st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION);
-
+      if ( st1_ptr->exp <= EXP_UNDER )
+       {
+         /* A denormal result has been produced.
+            Precision must have been lost, this is always
+            an underflow. */
+         sign = st1_ptr->sign;
+         arith_underflow(st1_ptr);
+         st1_ptr->sign = sign;
+       }
+      else
+       set_precision_flag_up();   /* 80486 appears to always do this */
       pop();
+      return;
     }
   else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) )
     {
diff --git a/drivers/FPU-emu/mul_Xsig.S b/drivers/FPU-emu/mul_Xsig.S
new file mode 100644 (file)
index 0000000..1d88d44
--- /dev/null
@@ -0,0 +1,182 @@
+/*---------------------------------------------------------------------------+
+ |  mul_Xsig.S                                                               |
+ |                                                                           |
+ | Multiply a 12 byte fixed point number by another fixed point number.      |
+ |                                                                           |
+ | Copyright (C) 1992,1994                                                   |
+ |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ |                                                                           |
+ | Call from C as:                                                           |
+ |   void mul32_Xsig(Xsig *x, unsigned b)                                    |
+ |                                                                           |
+ |   void mul64_Xsig(Xsig *x, unsigned long long *b)                         |
+ |                                                                           |
+ |   void mul_Xsig_Xsig(Xsig *x, unsigned *b)                                |
+ |                                                                           |
+ | The result is neither rounded nor normalized, and the ls bit or so may    |
+ | be wrong.                                                                 |
+ |                                                                           |
+ +---------------------------------------------------------------------------*/
+       .file   "mul_Xsig.S"
+
+
+#include "fpu_asm.h"
+
+.text
+       .align 2,144
+.globl _mul32_Xsig
+_mul32_Xsig:
+       pushl %ebp
+       movl %esp,%ebp
+       subl $16,%esp
+       pushl %esi
+
+       movl PARAM1,%esi
+       movl PARAM2,%ecx
+
+       xor %eax,%eax
+       movl %eax,-4(%ebp)
+       movl %eax,-8(%ebp)
+
+       movl (%esi),%eax        /* lsl of Xsig */
+       mull %ecx               /* msl of b */
+       movl %edx,-12(%ebp)
+
+       movl 4(%esi),%eax       /* midl of Xsig */
+       mull %ecx               /* msl of b */
+       addl %eax,-12(%ebp)
+       adcl %edx,-8(%ebp)
+       adcl $0,-4(%ebp)
+
+       movl 8(%esi),%eax       /* msl of Xsig */
+       mull %ecx               /* msl of b */
+       addl %eax,-8(%ebp)
+       adcl %edx,-4(%ebp)
+
+       movl -12(%ebp),%eax
+       movl %eax,(%esi)
+       movl -8(%ebp),%eax
+       movl %eax,4(%esi)
+       movl -4(%ebp),%eax
+       movl %eax,8(%esi)
+
+       popl %esi
+       leave
+       ret
+
+
+       .align 2,144
+.globl _mul64_Xsig
+_mul64_Xsig:
+       pushl %ebp
+       movl %esp,%ebp
+       subl $16,%esp
+       pushl %esi
+
+       movl PARAM1,%esi
+       movl PARAM2,%ecx
+
+       xor %eax,%eax
+       movl %eax,-4(%ebp)
+       movl %eax,-8(%ebp)
+
+       movl (%esi),%eax        /* lsl of Xsig */
+       mull 4(%ecx)            /* msl of b */
+       movl %edx,-12(%ebp)
+
+       movl 4(%esi),%eax       /* midl of Xsig */
+       mull (%ecx)             /* lsl of b */
+       addl %edx,-12(%ebp)
+       adcl $0,-8(%ebp)
+       adcl $0,-4(%ebp)
+
+       movl 4(%esi),%eax       /* midl of Xsig */
+       mull 4(%ecx)            /* msl of b */
+       addl %eax,-12(%ebp)
+       adcl %edx,-8(%ebp)
+       adcl $0,-4(%ebp)
+
+       movl 8(%esi),%eax       /* msl of Xsig */
+       mull (%ecx)             /* lsl of b */
+       addl %eax,-12(%ebp)
+       adcl %edx,-8(%ebp)
+       adcl $0,-4(%ebp)
+
+       movl 8(%esi),%eax       /* msl of Xsig */
+       mull 4(%ecx)            /* msl of b */
+       addl %eax,-8(%ebp)
+       adcl %edx,-4(%ebp)
+
+       movl -12(%ebp),%eax
+       movl %eax,(%esi)
+       movl -8(%ebp),%eax
+       movl %eax,4(%esi)
+       movl -4(%ebp),%eax
+       movl %eax,8(%esi)
+
+       popl %esi
+       leave
+       ret
+
+
+
+       .align 2,144
+.globl _mul_Xsig_Xsig
+_mul_Xsig_Xsig:
+       pushl %ebp
+       movl %esp,%ebp
+       subl $16,%esp
+       pushl %esi
+
+       movl PARAM1,%esi
+       movl PARAM2,%ecx
+
+       xor %eax,%eax
+       movl %eax,-4(%ebp)
+       movl %eax,-8(%ebp)
+
+       movl (%esi),%eax        /* lsl of Xsig */
+       mull 8(%ecx)            /* msl of b */
+       movl %edx,-12(%ebp)
+
+       movl 4(%esi),%eax       /* midl of Xsig */
+       mull 4(%ecx)            /* midl of b */
+       addl %edx,-12(%ebp)
+       adcl $0,-8(%ebp)
+       adcl $0,-4(%ebp)
+
+       movl 8(%esi),%eax       /* msl of Xsig */
+       mull (%ecx)             /* lsl of b */
+       addl %edx,-12(%ebp)
+       adcl $0,-8(%ebp)
+       adcl $0,-4(%ebp)
+
+       movl 4(%esi),%eax       /* midl of Xsig */
+       mull 8(%ecx)            /* msl of b */
+       addl %eax,-12(%ebp)
+       adcl %edx,-8(%ebp)
+       adcl $0,-4(%ebp)
+
+       movl 8(%esi),%eax       /* msl of Xsig */
+       mull 4(%ecx)            /* midl of b */
+       addl %eax,-12(%ebp)
+       adcl %edx,-8(%ebp)
+       adcl $0,-4(%ebp)
+
+       movl 8(%esi),%eax       /* msl of Xsig */
+       mull 8(%ecx)            /* msl of b */
+       addl %eax,-8(%ebp)
+       adcl %edx,-4(%ebp)
+
+       movl -12(%ebp),%edx
+       movl %edx,(%esi)
+       movl -8(%ebp),%edx
+       movl %edx,4(%esi)
+       movl -4(%ebp),%edx
+       movl %edx,8(%esi)
+
+       popl %esi
+       leave
+       ret
+
diff --git a/drivers/FPU-emu/poly.h b/drivers/FPU-emu/poly.h
new file mode 100644 (file)
index 0000000..397cb9e
--- /dev/null
@@ -0,0 +1,116 @@
+/*---------------------------------------------------------------------------+
+ |  poly.h                                                                   |
+ |                                                                           |
+ |  Header file for the FPU-emu poly*.c source files.                        |
+ |                                                                           |
+ | Copyright (C) 1994                                                        |
+ |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ |                                                                           |
+ | Declarations and definitions for functions operating on Xsig (12-byte     |
+ | extended-significand) quantities.                                         |
+ |                                                                           |
+ +---------------------------------------------------------------------------*/
+
+#ifndef _POLY_H
+#define _POLY_H
+
+/* This 12-byte structure is used to improve the accuracy of computation
+   of transcendental functions.
+   Intended to be used to get results better than 8-byte computation
+   allows. 9-byte would probably be sufficient.
+   */
+typedef struct {
+  unsigned long lsw;
+  unsigned long midw;
+  unsigned long msw;
+} Xsig;
+
+asmlinkage void mul64(unsigned long long const *a, unsigned long long const *b,
+                     unsigned long long *result);
+asmlinkage void polynomial_Xsig(Xsig *, const unsigned long long *x,
+                               const unsigned long long terms[], const int n);
+
+asmlinkage void mul32_Xsig(Xsig *, const unsigned long mult);
+asmlinkage void mul64_Xsig(Xsig *, const unsigned long long *mult);
+asmlinkage void mul_Xsig_Xsig(Xsig *dest, const Xsig *mult);
+
+asmlinkage void shr_Xsig(Xsig *, const int n);
+asmlinkage int round_Xsig(Xsig *);
+asmlinkage int norm_Xsig(Xsig *);
+asmlinkage void div_Xsig(Xsig *x1, const Xsig *x2, const Xsig *dest);
+
+/* Macro to extract the most significant 32 bits from a long long */
+#define LL_MSW(x)     (((unsigned long *)&x)[1])
+
+/* Macro to initialize an Xsig struct */
+#define MK_XSIG(a,b,c)     { c, b, a }
+
+/* Macro to access the 8 ms bytes of an Xsig as a long long */
+#define XSIG_LL(x)         (*(unsigned long long *)&x.midw)
+
+
+/*
+   Need to run gcc with optimizations on to get these to
+   actually be in-line.
+   */
+
+/* Multiply two fixed-point 32 bit numbers. */
+extern inline void mul_32_32(const unsigned long arg1,
+                            const unsigned long arg2,
+                            unsigned long *out)
+{
+  asm volatile ("movl %1,%%eax; mull %2; movl %%edx,%0" \
+               :"=g" (*out) \
+               :"g" (arg1), "g" (arg2) \
+               :"ax","dx");
+}
+
+
+/* Add the 12 byte Xsig x2 to Xsig dest, with no checks for overflow. */
+extern inline void add_Xsig_Xsig(Xsig *dest, const Xsig *x2)
+{
+  asm volatile ("movl %1,%%edi; movl %2,%%esi;
+                 movl (%%esi),%%eax; addl %%eax,(%%edi);
+                 movl 4(%%esi),%%eax; adcl %%eax,4(%%edi);
+                 movl 8(%%esi),%%eax; adcl %%eax,8(%%edi);"
+                 :"=g" (*dest):"g" (dest), "g" (x2)
+                 :"ax","si","di");
+}
+
+
+/* Add the 12 byte Xsig x2 to Xsig dest, adjust exp if overflow occurs. */
+/* Note: the constraints in the asm statement didn't always work properly
+   with gcc 2.5.8.  Changing from using edi to using ecx got around the
+   problem, but keep fingers crossed! */
+extern inline int add_two_Xsig(Xsig *dest, const Xsig *x2, long int *exp)
+{
+  asm volatile ("movl %2,%%ecx; movl %3,%%esi;
+                 movl (%%esi),%%eax; addl %%eax,(%%ecx);
+                 movl 4(%%esi),%%eax; adcl %%eax,4(%%ecx);
+                 movl 8(%%esi),%%eax; adcl %%eax,8(%%ecx);
+                 jnc 0f;
+                rcrl 8(%%ecx); rcrl 4(%%ecx); rcrl (%%ecx)
+                 movl %4,%%ecx; incl (%%ecx)
+                 movl $1,%%eax; jmp 1f;
+                 0: xorl %%eax,%%eax;
+                 1:"
+               :"=g" (*exp), "=g" (*dest)
+               :"g" (dest), "g" (x2), "g" (exp)
+               :"cx","si","ax");
+}
+
+
+/* Negate (subtract from 1.0) the 12 byte Xsig */
+/* This is faster in a loop on my 386 than using the "neg" instruction. */
+extern inline void negate_Xsig(Xsig *x)
+{
+  asm volatile("movl %1,%%esi; "
+               "xorl %%ecx,%%ecx; "
+               "movl %%ecx,%%eax; subl (%%esi),%%eax; movl %%eax,(%%esi); "
+               "movl %%ecx,%%eax; sbbl 4(%%esi),%%eax; movl %%eax,4(%%esi); "
+               "movl %%ecx,%%eax; sbbl 8(%%esi),%%eax; movl %%eax,8(%%esi); "
+               :"=g" (*x):"g" (x):"si","ax","cx");
+}
+
+#endif _POLY_H
index 4844aa72249cd8641fdaf7ba25c92231fc394011..f7c585d605f5c21fe45d082ec18681ad61890bfc 100644 (file)
@@ -3,7 +3,7 @@
  |                                                                           |
  | Function to compute 2^x-1 by a polynomial approximation.                  |
  |                                                                           |
- | Copyright (C) 1992,1993                                                   |
+ | Copyright (C) 1992,1993,1994                                              |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
  |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
 #include "exception.h"
 #include "reg_constant.h"
 #include "fpu_emu.h"
+#include "control_w.h"
+#include "poly.h"
 
 
-
-#define        HIPOWER 13
-static unsigned short const    lterms[HIPOWER][4] =
-       {
-       { 0x79b5, 0xd1cf, 0x17f7, 0xb172 },
-       { 0x1b56, 0x058b, 0x7bff, 0x3d7f },
-       { 0x8bb0, 0x8250, 0x846b, 0x0e35 },
-       { 0xbc65, 0xf747, 0x556d, 0x0276 },
-       { 0x17cb, 0x9e39, 0x61ff, 0x0057 },
-       { 0xe018, 0x9776, 0x1848, 0x000a },
-       { 0x66f2, 0xff30, 0xffe5, 0x0000 },
-       { 0x682f, 0xffb6, 0x162b, 0x0000 },
-       { 0xb7ca, 0x2956, 0x01b5, 0x0000 },
-       { 0xcd3e, 0x4817, 0x001e, 0x0000 },
-       { 0xb7e2, 0xecbe, 0x0001, 0x0000 },
-       { 0x0ed5, 0x1a27, 0x0000, 0x0000 },
-       { 0x101d, 0x0222, 0x0000, 0x0000 },
-       };
+#define        HIPOWER 11
+static const unsigned long long lterms[HIPOWER] =
+{
+  0x0000000000000000LL,  /* This term done separately as 12 bytes */
+  0xf5fdeffc162c7543LL,
+  0x1c6b08d704a0bfa6LL,
+  0x0276556df749cc21LL,
+  0x002bb0ffcf14f6b8LL,
+  0x0002861225ef751cLL,
+  0x00001ffcbfcd5422LL,
+  0x00000162c005d5f1LL,
+  0x0000000da96ccb1bLL,
+  0x0000000078d1b897LL,
+  0x000000000422b029LL
+};
+
+static const Xsig hiterm = MK_XSIG(0xb17217f7, 0xd1cf79ab, 0xc8a39194);
+
+/* Four slices: 0.0 : 0.25 : 0.50 : 0.75 : 1.0,
+   These numbers are 2^(1/4), 2^(1/2), and 2^(3/4)
+ */
+static const Xsig shiftterm0 = MK_XSIG(0, 0, 0);
+static const Xsig shiftterm1 = MK_XSIG(0x9837f051, 0x8db8a96f, 0x46ad2318);
+static const Xsig shiftterm2 = MK_XSIG(0xb504f333, 0xf9de6484, 0x597d89b3);
+static const Xsig shiftterm3 = MK_XSIG(0xd744fcca, 0xd69d6af4, 0x39a68bb9);
+
+static const Xsig *shiftterm[] = { &shiftterm0, &shiftterm1,
+                                    &shiftterm2, &shiftterm3 };
 
 
 /*--- poly_2xm1() -----------------------------------------------------------+
- | Requires a positive argument which is TW_Valid and < 1.                   |
+ | Requires an argument which is TW_Valid and < 1.                           |
  +---------------------------------------------------------------------------*/
 int    poly_2xm1(FPU_REG const *arg, FPU_REG *result)
 {
-  short                exponent;
-  long long     Xll;
-  FPU_REG       accum;
+  long int               exponent, shift;
+  unsigned long long     Xll;
+  Xsig                   accumulator, Denom, argSignif;
 
 
   exponent = arg->exp - EXP_BIAS;
 
 #ifdef PARANOID
-  if ( (arg->sign != SIGN_POS) /* Can't hack a number < 0.0 */
-      || (exponent >= 0)       /* or a |number| >= 1.0 */
+  if (   (exponent >= 0)       /* Don't want a |number| >= 1.0 */
       || (arg->tag != TW_Valid) )
     {
       /* Number negative, too large, or not Valid. */
@@ -58,27 +69,83 @@ int poly_2xm1(FPU_REG const *arg, FPU_REG *result)
     }
 #endif PARANOID
 
-  *(unsigned *)&Xll = arg->sigl;
-  *(((unsigned *)&Xll)+1) = arg->sigh;
-  if ( exponent < -1 )
+  argSignif.lsw = 0;
+  XSIG_LL(argSignif) = Xll = significand(arg);
+
+  if ( exponent == -1 )
+    {
+      shift = (argSignif.msw & 0x40000000) ? 3 : 2;
+      /* subtract 0.5 or 0.75 */
+      exponent -= 2;
+      XSIG_LL(argSignif) <<= 2;
+      Xll <<= 2;
+    }
+  else if ( exponent == -2 )
+    {
+      shift = 1;
+      /* subtract 0.25 */
+      exponent--;
+      XSIG_LL(argSignif) <<= 1;
+      Xll <<= 1;
+    }
+  else
+    shift = 0;
+
+  if ( exponent < -2 )
     {
       /* Shift the argument right by the required places. */
-      if ( shrx(&Xll, -1-exponent) >= 0x80000000U )
+      if ( shrx(&Xll, -2-exponent) >= 0x80000000U )
        Xll++;  /* round up */
     }
 
-  *(short *)&(accum.sign) = 0; /* Will be a valid positive nr with expon = 0 */
-  accum.exp = 0;
+  accumulator.lsw = accumulator.midw = accumulator.msw = 0;
+  polynomial_Xsig(&accumulator, &Xll, lterms, HIPOWER-1);
+  mul_Xsig_Xsig(&accumulator, &argSignif);
+  shr_Xsig(&accumulator, 3);
 
-  /* Do the basic fixed point polynomial evaluation */
-  polynomial((unsigned *)&accum.sigl, (unsigned *)&Xll, lterms, HIPOWER-1);
+  mul_Xsig_Xsig(&argSignif, &hiterm);   /* The leading term */
+  add_two_Xsig(&accumulator, &argSignif, &exponent);
 
-  /* Convert to 64 bit signed-compatible */
-  accum.exp += EXP_BIAS - 1;
+  if ( shift )
+    {
+      /* The argument is large, use the identity:
+        f(x+a) = f(a) * (f(x) + 1) - 1;
+        */
+      shr_Xsig(&accumulator, - exponent);
+      accumulator.msw |= 0x80000000;      /* add 1.0 */
+      mul_Xsig_Xsig(&accumulator, shiftterm[shift]);
+      accumulator.msw &= 0x3fffffff;      /* subtract 1.0 */
+      exponent = 1;
+    }
+
+  if ( arg->sign != SIGN_POS )
+    {
+      /* The argument is negative, use the identity:
+            f(-x) = -f(x) / (1 + f(x))
+        */
+      Denom.lsw = accumulator.lsw;
+      XSIG_LL(Denom) = XSIG_LL(accumulator);
+      if ( exponent < 0 )
+       shr_Xsig(&Denom, - exponent);
+      else if ( exponent > 0 )
+       {
+         /* exponent must be 1 here */
+         XSIG_LL(Denom) <<= 1;
+         if ( Denom.lsw & 0x80000000 )
+           XSIG_LL(Denom) |= 1;
+         (Denom.lsw) <<= 1;
+       }
+      Denom.msw |= 0x80000000;      /* add 1.0 */
+      div_Xsig(&accumulator, &Denom, &accumulator);
+    }
 
-  reg_move(&accum, result);
+  /* Convert to 64 bit signed-compatible */
+  exponent += round_Xsig(&accumulator);
 
-  normalize(result);
+  significand(result) = XSIG_LL(accumulator);
+  result->tag = TW_Valid;
+  result->exp = exponent + EXP_BIAS;
+  result->sign = arg->sign;
 
   return 0;
 
index 521eeb8a8d65e8958374f5af7bfeda92caebcfc5..6edca625f2a1bab79600286806e48747f366cdf2 100644 (file)
@@ -1,9 +1,9 @@
 /*---------------------------------------------------------------------------+
- |  p_atan.c                                                                 |
+ |  poly_atan.c                                                              |
  |                                                                           |
- | Compute the tan of a FPU_REG, using a polynomial approximation.           |
+ | Compute the arctan of a FPU_REG, using a polynomial approximation.        |
  |                                                                           |
- | Copyright (C) 1992,1993                                                   |
+ | Copyright (C) 1992,1993,1994                                              |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
  |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
 #include "exception.h"
 #include "reg_constant.h"
 #include "fpu_emu.h"
+#include "status_w.h"
 #include "control_w.h"
+#include "poly.h"
 
 
 #define        HIPOWERon       6       /* odd poly, negative terms */
-static unsigned const oddnegterms[HIPOWERon][2] =
+static const unsigned long long oddnegterms[HIPOWERon] =
 {
-  { 0x00000000, 0x00000000 }, /* for + 1.0 */
-  { 0x763b6f3d, 0x1adc4428 },
-  { 0x20f0630b, 0x0502909d },
-  { 0x4e825578, 0x0198ce38 },
-  { 0x22b7cb87, 0x008da6e3 },
-  { 0x9b30ca03, 0x00239c79 }
+  0x0000000000000000LL, /* Dummy (not for - 1.0) */
+  0x015328437f756467LL,
+  0x0005dda27b73dec6LL,
+  0x0000226bf2bfb91aLL,
+  0x000000ccc439c5f7LL,
+  0x0000000355438407LL
 } ;
 
 #define        HIPOWERop       6       /* odd poly, positive terms */
-static unsigned const  oddplterms[HIPOWERop][2] =
+static const unsigned long long oddplterms[HIPOWERop] =
 {
-  { 0xa6f67cb8, 0x94d910bd },
-  { 0xa02ffab4, 0x0a43cb45 },
-  { 0x04265e6b, 0x02bf5655 },
-  { 0x0a728914, 0x00f280f7 },
-  { 0x6d640e01, 0x004d6556 },
-  { 0xf1dd2dbf, 0x000a530a }
+/*  0xaaaaaaaaaaaaaaabLL,  transferred to fixedpterm[] */
+  0x0db55a71875c9ac2LL,
+  0x0029fce2d67880b0LL,
+  0x0000dfd3908b4596LL,
+  0x00000550fd61dab4LL,
+  0x0000001c9422b3f9LL,
+  0x000000003e3301e1LL
 };
 
-static unsigned long long const denomterm = 0xea2e6612fc4bd208LL;
+static const unsigned long long denomterm = 0xebd9b842c5c53a0eLL;
+
+static const Xsig fixedpterm = MK_XSIG(0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa);
+
+static const Xsig pi_signif = MK_XSIG(0xc90fdaa2, 0x2168c234, 0xc4c6628b);
 
 
 /*--- poly_atan() -----------------------------------------------------------+
  |                                                                           |
  +---------------------------------------------------------------------------*/
-void   poly_atan(FPU_REG *arg)
+void   poly_atan(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *result)
 {
-  char         recursions = 0;
-  short                exponent;
-  FPU_REG       odd_poly, even_poly, pos_poly, neg_poly, ratio;
-  FPU_REG       argSq;
-  unsigned long long     arg_signif, argSqSq;
+  char                 transformed, inverted,
+                        sign1 = arg1->sign, sign2 = arg2->sign;
+  long int             exponent, dummy_exp;
+  Xsig                  accumulator, Numer, Denom, accumulatore, argSignif,
+                        argSq, argSqSq;
   
 
-#ifdef PARANOID
-  if ( arg->sign != 0 )        /* Can't hack a number < 0.0 */
-    { arith_invalid(arg); return; }  /* Need a positive number */
-#endif PARANOID
-
-  exponent = arg->exp - EXP_BIAS;
-  
-  if ( arg->tag == TW_Zero )
+  arg1->sign = arg2->sign = SIGN_POS;
+  if ( (compare(arg2) & ~COMP_Denormal) == COMP_A_lt_B )
     {
-      /* Return 0.0 */
-      reg_move(&CONST_Z, arg);
-      return;
+      inverted = 1;
+      exponent = arg1->exp - arg2->exp;
+      Numer.lsw = Denom.lsw = 0;
+      XSIG_LL(Numer) = significand(arg1);
+      XSIG_LL(Denom) = significand(arg2);
     }
-  
-  if ( exponent >= -2 )
+  else
+    {
+      inverted = 0;
+      exponent = arg2->exp - arg1->exp;
+      Numer.lsw = Denom.lsw = 0;
+      XSIG_LL(Numer) = significand(arg2);
+      XSIG_LL(Denom) = significand(arg1);
+     }
+  div_Xsig(&Numer, &Denom, &argSignif);
+  exponent += norm_Xsig(&argSignif);
+
+  if ( (exponent >= -1)
+      || ((exponent == -2) && (argSignif.msw > 0xd413ccd0)) )
     {
-      /* argument is in the range  [0.25 .. 1.0] */
+      /* The argument is greater than sqrt(2)-1 (=0.414213562...) */
+      /* Convert the argument by an identity for atan */
+      transformed = 1;
+
       if ( exponent >= 0 )
        {
 #ifdef PARANOID
-         if ( (exponent == 0) && 
-             (arg->sigl == 0) && (arg->sigh == 0x80000000) )
-#endif PARANOID
+         if ( !( (exponent == 0) && 
+                (argSignif.lsw == 0) && (argSignif.midw == 0) &&
+                (argSignif.msw == 0x80000000) ) )
            {
-             reg_move(&CONST_PI4, arg);
+             EXCEPTION(EX_INTERNAL|0x104);  /* There must be a logic error */
              return;
            }
-#ifdef PARANOID
-         EXCEPTION(EX_INTERNAL|0x104); /* There must be a logic error */
-         return;
 #endif PARANOID
+         argSignif.msw = 0;   /* Make the transformed arg -> 0.0 */
        }
-
-      /* If the argument is greater than sqrt(2)-1 (=0.414213562...) */
-      /* convert the argument by an identity for atan */
-      if ( (exponent >= -1) || (arg->sigh > 0xd413ccd0) )
+      else
        {
-         FPU_REG numerator, denom;
-
-         recursions++;
+         Numer.lsw = Denom.lsw = argSignif.lsw;
+         XSIG_LL(Numer) = XSIG_LL(Denom) = XSIG_LL(argSignif);
 
-         arg_signif = significand(arg);
          if ( exponent < -1 )
-           {
-             if ( shrx(&arg_signif, -1-exponent) >= 0x80000000U )
-               arg_signif++;   /* round up */
-           }
-         significand(&numerator) = -arg_signif;
-         numerator.exp = EXP_BIAS - 1;
-         normalize(&numerator);                       /* 1 - arg */
-
-         arg_signif = significand(arg);
-         if ( shrx(&arg_signif, -exponent) >= 0x80000000U )
-           arg_signif++;       /* round up */
-         significand(&denom) = arg_signif;
-         denom.sigh |= 0x80000000;                    /* 1 + arg */
-
-         arg->exp = numerator.exp;
-         reg_u_div(&numerator, &denom, arg, FULL_PRECISION);
-
-         exponent = arg->exp - EXP_BIAS;
+           shr_Xsig(&Numer, -1-exponent);
+         negate_Xsig(&Numer);
+      
+         shr_Xsig(&Denom, -exponent);
+         Denom.msw |= 0x80000000;
+      
+         div_Xsig(&Numer, &Denom, &argSignif);
+
+         exponent = -1 + norm_Xsig(&argSignif);
        }
     }
-
-  arg_signif = significand(arg);
-
-#ifdef PARANOID
-  /* This must always be true */
-  if ( exponent >= -1 )
+  else
     {
-      EXCEPTION(EX_INTERNAL|0x120);    /* There must be a logic error */
+      transformed = 0;
     }
-#endif PARANOID
 
-  /* shift the argument right by the required places */
-  if ( shrx(&arg_signif, -1-exponent) >= 0x80000000U )
-    arg_signif++;      /* round up */
+  argSq.lsw = argSignif.lsw; argSq.midw = argSignif.midw;
+  argSq.msw = argSignif.msw;
+  mul_Xsig_Xsig(&argSq, &argSq);
   
-  /* Now have arg_signif with binary point at the left
-     .1xxxxxxxx */
-  mul64(&arg_signif, &arg_signif, &significand(&argSq));
-  mul64(&significand(&argSq), &significand(&argSq), &argSqSq);
+  argSqSq.lsw = argSq.lsw; argSqSq.midw = argSq.midw; argSqSq.msw = argSq.msw;
+  mul_Xsig_Xsig(&argSqSq, &argSqSq);
 
-  /* will be a valid positive nr with expon = 0 */
-  *(short *)&(pos_poly.sign) = 0;
-  pos_poly.exp = EXP_BIAS;
+  accumulatore.lsw = argSq.lsw;
+  XSIG_LL(accumulatore) = XSIG_LL(argSq);
 
-  /* Do the basic fixed point polynomial evaluation */
-  polynomial(&pos_poly.sigl, (unsigned *)&argSqSq,
-            (unsigned short (*)[4])oddplterms, HIPOWERop-1);
-  mul64(&significand(&argSq), &significand(&pos_poly),
-       &significand(&pos_poly));
+  shr_Xsig(&argSq, 2*(-1-exponent-1));
+  shr_Xsig(&argSqSq, 4*(-1-exponent-1));
 
-  /* will be a valid positive nr with expon = 0 */
-  *(short *)&(neg_poly.sign) = 0;
-  neg_poly.exp = EXP_BIAS;
+  /* Now have argSq etc with binary point at the left
+     .1xxxxxxxx */
 
   /* Do the basic fixed point polynomial evaluation */
-  polynomial(&neg_poly.sigl, (unsigned *)&argSqSq,
-            (unsigned short (*)[4])oddnegterms, HIPOWERon-1);
+  accumulator.msw = accumulator.midw = accumulator.lsw = 0;
+  polynomial_Xsig(&accumulator, &XSIG_LL(argSqSq),
+                  oddplterms, HIPOWERop-1);
+  mul64_Xsig(&accumulator, &XSIG_LL(argSq));
+  negate_Xsig(&accumulator);
+  polynomial_Xsig(&accumulator, &XSIG_LL(argSqSq), oddnegterms, HIPOWERon-1);
+  negate_Xsig(&accumulator);
+  add_two_Xsig(&accumulator, &fixedpterm, &dummy_exp);
 
-  /* Subtract the mantissas */
-  significand(&pos_poly) -= significand(&neg_poly);
+  mul64_Xsig(&accumulatore, &denomterm);
+  shr_Xsig(&accumulatore, 1 + 2*(-1-exponent));
+  accumulatore.msw |= 0x80000000;
 
-  reg_move(&pos_poly, &odd_poly);
-  poly_add_1(&odd_poly);
+  div_Xsig(&accumulator, &accumulatore, &accumulator);
 
-  /* will be a valid positive nr with expon = 0 */
-  *(short *)&(even_poly.sign) = 0;
+  mul_Xsig_Xsig(&accumulator, &argSignif);
+  mul_Xsig_Xsig(&accumulator, &argSq);
 
-  mul64(&significand(&argSq), &denomterm, &significand(&even_poly));
+  shr_Xsig(&accumulator, 3);
+  negate_Xsig(&accumulator);
+  add_Xsig_Xsig(&accumulator, &argSignif);
 
-  poly_add_1(&even_poly);
-
-  reg_div(&odd_poly, &even_poly, &ratio, FULL_PRECISION);
-
-  reg_u_mul(&ratio, arg, arg, FULL_PRECISION);
-
-  if ( recursions )
-    reg_sub(&CONST_PI4, arg, arg, FULL_PRECISION);
-
-}
-
-
-/* The argument to this function must be polynomial() compatible,
-   i.e. have an exponent (not checked) of EXP_BIAS-1 but need not
-   be normalized.
-   This function adds 1.0 to the (assumed positive) argument. */
-void poly_add_1(FPU_REG *src)
-{
-/* Rounding in a consistent direction produces better results
-   for the use of this function in poly_atan. Simple truncation
-   is used here instead of round-to-nearest. */
+  if ( transformed )
+    {
+      /* compute pi/4 - accumulator */
+      shr_Xsig(&accumulator, -1-exponent);
+      negate_Xsig(&accumulator);
+      add_Xsig_Xsig(&accumulator, &pi_signif);
+      exponent = -1;
+    }
 
-shrx(&src->sigl, 1);
+  if ( inverted )
+    {
+      /* compute pi/2 - accumulator */
+      shr_Xsig(&accumulator, -exponent);
+      negate_Xsig(&accumulator);
+      add_Xsig_Xsig(&accumulator, &pi_signif);
+      exponent = 0;
+    }
 
-src->sigh |= 0x80000000;
+  if ( sign1 )
+    {
+      /* compute pi - accumulator */
+      shr_Xsig(&accumulator, 1 - exponent);
+      negate_Xsig(&accumulator);
+      add_Xsig_Xsig(&accumulator, &pi_signif);
+      exponent = 1;
+    }
 
-src->exp = EXP_BIAS;
+  exponent += round_Xsig(&accumulator);
+  significand(result) = XSIG_LL(accumulator);
+  result->exp = exponent + EXP_BIAS;
+  result->tag = TW_Valid;
+  result->sign = sign2;
 
 }
index 6d36fefb22e8e3916a29d3bcce5f338725b497e1..1677f4aff3352b2d2a4b3365786d27d485dc5570 100644 (file)
@@ -3,7 +3,7 @@
  |                                                                           |
  | Compute the base 2 log of a FPU_REG, using a polynomial approximation.    |
  |                                                                           |
- | Copyright (C) 1992,1993                                                   |
+ | Copyright (C) 1992,1993,1994                                              |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
  |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
 #include "reg_constant.h"
 #include "fpu_emu.h"
 #include "control_w.h"
+#include "poly.h"
 
 
 
-#define        HIPOWER 9
-static unsigned short const    lterms[HIPOWER][4] =
-       {
-       /* Ideal computation with these coeffs gives about
-          64.6 bit rel accuracy. */
-       { 0xe177, 0xb82f, 0x7652, 0x7154 },
-       { 0xee0f, 0xe80f, 0x2770, 0x7b1c },
-       { 0x0fc0, 0xbe87, 0xb143, 0x49dd },
-       { 0x78b9, 0xdadd, 0xec54, 0x34c2 },
-       { 0x003a, 0x5de9, 0x628b, 0x2909 },
-       { 0x5588, 0xed16, 0x4abf, 0x2193 },
-       { 0xb461, 0x85f7, 0x347a, 0x1c6a },
-       { 0x0975, 0x87b3, 0xd5bf, 0x1876 },
-       { 0xe85c, 0xcec9, 0x84e7, 0x187d }
-       };
-
-
+static void log2_kernel(FPU_REG const *arg,
+                       Xsig *accum_result, long int *expon);
 
 
 /*--- poly_l2() -------------------------------------------------------------+
  |   Base 2 logarithm by a polynomial approximation.                         |
  +---------------------------------------------------------------------------*/
-void   poly_l2(FPU_REG const *arg, FPU_REG *result)
+void   poly_l2(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result)
 {
-  short                  exponent;
-  char           zero;         /* flag for an Xx == 0 */
-  unsigned short  bits, shift;
-  unsigned long long       Xsq;
-  FPU_REG        accum, denom, num, Xx;
+  long int            exponent, expon, expon_expon;
+  Xsig                 accumulator, expon_accum, yaccum;
+  char                sign;
+  FPU_REG              x;
 
 
   exponent = arg->exp - EXP_BIAS;
 
-  accum.tag = TW_Valid;        /* set the tags to Valid */
-
+  /* From arg, make a number > sqrt(2)/2 and < sqrt(2) */
   if ( arg->sigh > (unsigned)0xb504f334 )
     {
-      /* This is good enough for the computation of the polynomial
-        sum, but actually results in a loss of precision for
-        the computation of Xx. This will matter only if exponent
-        becomes zero. */
+      /* Treat as  sqrt(2)/2 < arg < 1 */
+      significand(&x) = - significand(arg);
+      x.sign = SIGN_NEG;
+      x.tag = TW_Valid;
+      x.exp = EXP_BIAS-1;
       exponent++;
-      accum.sign = 1;  /* sign to negative */
-      num.exp = EXP_BIAS;  /* needed to prevent errors in div routine */
-      reg_u_div(&CONST_1, arg, &num, FULL_PRECISION);
+      normalize(&x);
     }
   else
     {
-      accum.sign = 0;  /* set the sign to positive */
-      num.sigl = arg->sigl;            /* copy the mantissa */
-      num.sigh = arg->sigh;
+      /* Treat as  1 <= arg < sqrt(2) */
+      x.sigh = arg->sigh - 0x80000000;
+      x.sigl = arg->sigl;
+      x.sign = SIGN_POS;
+      x.tag = TW_Valid;
+      x.exp = EXP_BIAS;
+      normalize(&x);
     }
 
+  if ( x.tag == TW_Zero )
+    {
+      expon = 0;
+      accumulator.msw = accumulator.midw = accumulator.lsw = 0;
+    }
+  else
+    {
+      log2_kernel(&x, &accumulator, &expon);
+    }
 
-  /* shift num left, lose the ms bit */
-  num.sigh <<= 1;
-  if ( num.sigl & 0x80000000 ) num.sigh |= 1;
-  num.sigl <<= 1;
-
-  denom.sigl = num.sigl;
-  denom.sigh = num.sigh;
-  poly_div4(&significand(&denom));
-  denom.sigh += 0x80000000;                    /* set the msb */
-  Xx.exp = EXP_BIAS;  /* needed to prevent errors in div routine */
-  reg_u_div(&num, &denom, &Xx, FULL_PRECISION);
+  sign = exponent < 0;
+  if ( sign ) exponent = -exponent;
+  expon_accum.msw = exponent; expon_accum.midw = expon_accum.lsw = 0;
+  if ( exponent )
+    {
+      expon_expon = 31 + norm_Xsig(&expon_accum);
+      shr_Xsig(&accumulator, expon_expon - expon);
 
-  zero = !(Xx.sigh | Xx.sigl);
-  
-  mul64(&significand(&Xx), &significand(&Xx), &Xsq);
-  poly_div16(&Xsq);
+      if ( sign ^ (x.sign == SIGN_NEG) )
+       negate_Xsig(&accumulator);
+      add_Xsig_Xsig(&accumulator, &expon_accum);
+    }
+  else
+    {
+      expon_expon = expon;
+      sign = x.sign;
+    }
 
-  accum.exp = -1;              /* exponent of accum */
+  yaccum.lsw = 0; XSIG_LL(yaccum) = significand(y);
+  mul_Xsig_Xsig(&accumulator, &yaccum);
 
-  /* Do the basic fixed point polynomial evaluation */
-  polynomial((unsigned *)&accum.sigl, (unsigned *)&Xsq, lterms, HIPOWER-1);
+  expon_expon += round_Xsig(&accumulator);
 
-  if ( !exponent )
+  if ( accumulator.msw == 0 )
     {
-      /* If the exponent is zero, then we would lose precision by
-        sticking to fixed point computation here */
-      /* We need to re-compute Xx because of loss of precision. */
-      FPU_REG   lXx;
-      char     sign;
-      
-      sign = accum.sign;
-      accum.sign = 0;
-
-      /* make accum compatible and normalize */
-      accum.exp = EXP_BIAS + accum.exp;
-      normalize(&accum);
-
-      if ( zero )
-       {
-         reg_move(&CONST_Z, result);
-       }
-      else
-       {
-         /* we need to re-compute lXx to better accuracy */
-         num.tag = TW_Valid;           /* set the tags to Vaild */
-         num.sign = 0;         /* set the sign to positive */
-         num.exp = EXP_BIAS - 1;
-         if ( sign )
-           {
-             /* The argument is of the form 1-x */
-             /* Use  1-1/(1-x) = x/(1-x) */
-             significand(&num) = - significand(arg);
-             normalize(&num);
-             reg_div(&num, arg, &num, FULL_PRECISION);
-           }
-         else
-           {
-             normalize(&num);
-           }
-
-         denom.tag = TW_Valid; /* set the tags to Valid */
-         denom.sign = SIGN_POS;        /* set the sign to positive */
-         denom.exp = EXP_BIAS;
-         
-         reg_div(&num, &denom, &lXx, FULL_PRECISION);
-
-         reg_u_mul(&lXx, &accum, &accum, FULL_PRECISION);
-
-         reg_u_add(&lXx, &accum, result, FULL_PRECISION);
-         
-         normalize(result);
-       }
-
-      result->sign = sign;
-      return;
+      reg_move(&CONST_Z, y);
+    }
+  else
+    {
+      result->exp = expon_expon + y->exp + 1;
+      significand(result) = XSIG_LL(accumulator);
+      result->tag = TW_Valid; /* set the tags to Valid */
+      result->sign = sign ^ y->sign;
     }
 
-  mul64(&significand(&accum),
-       &significand(&Xx), &significand(&accum));
+  return;
+}
 
-  significand(&accum) += significand(&Xx);
 
-  if ( Xx.sigh > accum.sigh )
-    {
-      /* There was an overflow */
+/*--- poly_l2p1() -----------------------------------------------------------+
+ |   Base 2 logarithm by a polynomial approximation.                         |
+ |   log2(x+1)                                                               |
+ +---------------------------------------------------------------------------*/
+int    poly_l2p1(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result)
+{
+  char                 sign;
+  long int             exponent;
+  Xsig                 accumulator, yaccum;
 
-      poly_div2(&significand(&accum));
-      accum.sigh |= 0x80000000;
-      accum.exp++;
-    }
 
-  /* When we add the exponent to the accum result later, we will
-     require that their signs are the same. Here we ensure that
-     this is so. */
-  if ( exponent && ((exponent < 0) ^ (accum.sign)) )
+  sign = arg->sign;
+
+  if ( arg->exp < EXP_BIAS )
     {
-      /* signs are different */
+      log2_kernel(arg, &accumulator, &exponent);
 
-      accum.sign = !accum.sign;
+      yaccum.lsw = 0;
+      XSIG_LL(yaccum) = significand(y);
+      mul_Xsig_Xsig(&accumulator, &yaccum);
 
-      /* An exceptional case is when accum is zero */
-      if ( accum.sigl | accum.sigh )
-       {
-         /* find 1-accum */
-         /* Shift to get exponent == 0 */
-         if ( accum.exp < 0 )
-           {
-             poly_div2(&significand(&accum));
-             accum.exp++;
-           }
-         /* Just negate, but throw away the sign */
-         significand(&accum) = - significand(&accum);
-         if ( exponent < 0 )
-           exponent++;
-         else
-           exponent--;
-       }
-    }
+      exponent += round_Xsig(&accumulator);
 
-  shift = exponent >= 0 ? exponent : -exponent ;
-  bits = 0;
-  if ( shift )
+      result->exp = exponent + y->exp + 1;
+      significand(result) = XSIG_LL(accumulator);
+      result->tag = TW_Valid; /* set the tags to Valid */
+      result->sign = sign ^ y->sign;
+
+      return 0;
+    }
+  else
     {
-      if ( accum.exp )
+      /* The magnitude of arg is far too large. */
+      reg_move(y, result);
+      if ( sign != SIGN_POS )
        {
-         accum.exp++;
-         poly_div2(&significand(&accum));
+         /* Trying to get the log of a negative number. */
+         return 1;
        }
-      while ( shift )
+      else
        {
-         poly_div2(&significand(&accum));
-         if ( shift & 1)
-           accum.sigh |= 0x80000000;
-         shift >>= 1;
-         bits++;
+         return 0;
        }
     }
 
-  /* Convert to 64 bit signed-compatible */
-  accum.exp += bits + EXP_BIAS - 1;
+}
 
-  reg_move(&accum, result);
-  normalize(result);
 
-  return;
-}
 
 
-/*--- poly_l2p1() -----------------------------------------------------------+
+#undef HIPOWER
+#define        HIPOWER 10
+static const unsigned long long logterms[HIPOWER] =
+{
+  0x2a8eca5705fc2ef0LL,
+  0xf6384ee1d01febceLL,
+  0x093bb62877cdf642LL,
+  0x006985d8a9ec439bLL,
+  0x0005212c4f55a9c8LL,
+  0x00004326a16927f0LL,
+  0x0000038d1d80a0e7LL,
+  0x0000003141cc80c6LL,
+  0x00000002b1668c9fLL,
+  0x000000002c7a46aaLL
+};
+
+static const unsigned long leadterm = 0xb8000000;
+
+
+/*--- log2_kernel() ---------------------------------------------------------+
  |   Base 2 logarithm by a polynomial approximation.                         |
  |   log2(x+1)                                                               |
  +---------------------------------------------------------------------------*/
-int    poly_l2p1(FPU_REG const *arg, FPU_REG *result)
+static void log2_kernel(FPU_REG const *arg, Xsig *accum_result,
+                       long int *expon)
 {
-  char         sign = 0;
-  unsigned long long     Xsq;
-  FPU_REG              arg_pl1, denom, accum, local_arg, poly_arg;
-
+  char                 sign;
+  long int             exponent, adj;
+  unsigned long long   Xsq;
+  Xsig                 accumulator, Numer, Denom, argSignif, arg_signif;
 
   sign = arg->sign;
 
-  reg_add(arg, &CONST_1, &arg_pl1, FULL_PRECISION);
-
-  if ( (arg_pl1.sign) | (arg_pl1.tag) )
-    {                  /* We need a valid positive number! */
-      return 1;
+  exponent = arg->exp - EXP_BIAS;
+  Numer.lsw = Denom.lsw = 0;
+  XSIG_LL(Numer) = XSIG_LL(Denom) = significand(arg);
+  if ( sign == SIGN_POS )
+    {
+      shr_Xsig(&Denom, 2 - (1 + exponent));
+      Denom.msw |= 0x80000000;
+      div_Xsig(&Numer, &Denom, &argSignif);
     }
-
-  reg_add(&CONST_1, &arg_pl1, &denom, FULL_PRECISION);
-  reg_div(arg, &denom, &local_arg, FULL_PRECISION);
-  local_arg.sign = 0;  /* Make the sign positive */
-
-  /* Now we need to check that  |local_arg| is less than
-     3-2*sqrt(2) = 0.17157.. = .0xafb0ccc0 * 2^-2 */
-
-  if ( local_arg.exp >= EXP_BIAS - 3 )
+  else
     {
-      if ( (local_arg.exp > EXP_BIAS - 3) ||
-         (local_arg.sigh > (unsigned)0xafb0ccc0) )
+      shr_Xsig(&Denom, 1 - (1 + exponent));
+      negate_Xsig(&Denom);
+      if ( Denom.msw & 0x80000000 )
        {
-         /* The argument is large */
-         poly_l2(&arg_pl1, result); return 0;
+         div_Xsig(&Numer, &Denom, &argSignif);
+         exponent ++;
+       }
+      else
+       {
+         /* Denom must be 1.0 */
+         argSignif.lsw = Numer.lsw; argSignif.midw = Numer.midw;
+         argSignif.msw = Numer.msw;
        }
     }
 
-  /* Make a copy of local_arg */
-  reg_move(&local_arg, &poly_arg);
-
-  /* Get poly_arg bits aligned as required */
-  shrx((unsigned *)&(poly_arg.sigl), -(poly_arg.exp - EXP_BIAS + 3));
-
-  mul64(&significand(&poly_arg), &significand(&poly_arg), &Xsq);
-  poly_div16(&Xsq);
-
+#ifndef PECULIAR_486
+  /* Should check here that  |local_arg|  is within the valid range */
+  if ( exponent >= -2 )
+    {
+      if ( (exponent > -2) ||
+         (argSignif.msw > (unsigned)0xafb0ccc0) )
+       {
+         /* The argument is too large */
+       }
+    }
+#endif PECULIAR_486
+
+  arg_signif.lsw = argSignif.lsw; XSIG_LL(arg_signif) = XSIG_LL(argSignif);
+  adj = norm_Xsig(&argSignif);
+  accumulator.lsw = argSignif.lsw; XSIG_LL(accumulator) = XSIG_LL(argSignif);
+  mul_Xsig_Xsig(&accumulator, &accumulator);
+  shr_Xsig(&accumulator, 2*(-1 - (1 + exponent + adj)));
+  Xsq = XSIG_LL(accumulator);
+  if ( accumulator.lsw & 0x80000000 )
+    Xsq++;
+
+  accumulator.msw = accumulator.midw = accumulator.lsw = 0;
   /* Do the basic fixed point polynomial evaluation */
-  polynomial(&(accum.sigl), (unsigned *)&Xsq, lterms, HIPOWER-1);
-
-  accum.tag = TW_Valid;        /* set the tags to Valid */
-  accum.sign = SIGN_POS;       /* and make accum positive */
-
-  /* make accum compatible and normalize */
-  accum.exp = EXP_BIAS - 1;
-  normalize(&accum);
+  polynomial_Xsig(&accumulator, &Xsq, logterms, HIPOWER-1);
 
-  reg_u_mul(&local_arg, &accum, &accum, FULL_PRECISION);
+  mul_Xsig_Xsig(&accumulator, &argSignif);
+  shr_Xsig(&accumulator, 6 - adj);
 
-  reg_u_add(&local_arg, &accum, result, FULL_PRECISION);
+  mul32_Xsig(&arg_signif, leadterm);
+  add_two_Xsig(&accumulator, &arg_signif, &exponent);
 
-  /* Multiply the result by 2 */
-  result->exp++;
+  *expon = exponent + 1;
+  accum_result->lsw = accumulator.lsw;
+  accum_result->midw = accumulator.midw;
+  accum_result->msw = accumulator.msw;
 
-  result->sign = sign;
-  
-  return 0;
 }
index aa43448a124dbce5062878a7dfac66ea84810324..03db5b6aacc7b56ec4f3ab7c4ad8431d490daec3 100644 (file)
@@ -1,7 +1,8 @@
 /*---------------------------------------------------------------------------+
  |  poly_sin.c                                                               |
  |                                                                           |
- |  Computation of an approximation of the sin function by a polynomial      |
+ |  Computation of an approximation of the sin function and the cosine       |
+ |  function by a polynomial.                                                |
  |                                                                           |
  | Copyright (C) 1992,1993,1994                                              |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
 #include "reg_constant.h"
 #include "fpu_emu.h"
 #include "control_w.h"
+#include "poly.h"
 
 
-#define        HIPOWER 5
-static unsigned short const    lterms[HIPOWER][4] =
-       {
-       { 0x846a, 0x42d1, 0xb544, 0x921f},
-       { 0xe110, 0x75aa, 0xbc67, 0x1466},
-       { 0x503d, 0xa43f, 0x83c1, 0x000a},
-       { 0x8f9d, 0x7a19, 0x00f4, 0x0000},
-       { 0xda03, 0x06aa, 0x0000, 0x0000},
-       };
-
-static unsigned short const    negterms[HIPOWER][4] =
-       {
-       { 0x95ed, 0x2df2, 0xe731, 0xa55d},
-       { 0xd159, 0xe62b, 0xd2cc, 0x0132},
-       { 0x6342, 0xe9fb, 0x3c60, 0x0000},
-       { 0x6256, 0xdf5a, 0x0002, 0x0000},
-       { 0xf279, 0x000b, 0x0000, 0x0000},
-       };
+#define        N_COEFF_P       4
+#define        N_COEFF_N       4
+
+static const unsigned long long pos_terms_l[N_COEFF_P] =
+{
+  0xaaaaaaaaaaaaaaabLL,
+  0x00d00d00d00cf906LL,
+  0x000006b99159a8bbLL,
+  0x000000000d7392e6LL
+};
+
+static const unsigned long long neg_terms_l[N_COEFF_N] =
+{
+  0x2222222222222167LL,
+  0x0002e3bc74aab624LL,
+  0x0000000b09229062LL,
+  0x00000000000c7973LL
+};
+
+
+
+#define        N_COEFF_PH      4
+#define        N_COEFF_NH      4
+static const unsigned long long pos_terms_h[N_COEFF_PH] =
+{
+  0x0000000000000000LL,
+  0x05b05b05b05b0406LL,
+  0x000049f93edd91a9LL,
+  0x00000000c9c9ed62LL
+};
+
+static const unsigned long long neg_terms_h[N_COEFF_NH] =
+{
+  0xaaaaaaaaaaaaaa98LL,
+  0x001a01a01a019064LL,
+  0x0000008f76c68a77LL,
+  0x0000000000d58f5eLL
+};
 
 
 /*--- poly_sine() -----------------------------------------------------------+
@@ -42,109 +64,345 @@ static unsigned short const       negterms[HIPOWER][4] =
  +---------------------------------------------------------------------------*/
 void   poly_sine(FPU_REG const *arg, FPU_REG *result)
 {
-  short        exponent;
-  FPU_REG      fixed_arg, arg_sqrd, arg_to_4, accum, negaccum;
-  
-  
-  exponent = arg->exp - EXP_BIAS;
-  
+  int                 exponent, echange;
+  Xsig                accumulator, argSqrd, argTo4;
+  unsigned long       fix_up, adj;
+  unsigned long long  fixed_arg;
+
+
+#ifdef PARANOID
   if ( arg->tag == TW_Zero )
     {
       /* Return 0.0 */
       reg_move(&CONST_Z, result);
       return;
     }
-  
-#ifdef PARANOID
-  if ( arg->sign != 0 )        /* Can't hack a number < 0.0 */
+#endif PARANOID
+
+  exponent = arg->exp - EXP_BIAS;
+
+  accumulator.lsw = accumulator.midw = accumulator.msw = 0;
+
+  /* Split into two ranges, for arguments below and above 1.0 */
+  /* The boundary between upper and lower is approx 0.88309101259 */
+  if ( (exponent < -1) || ((exponent == -1) && (arg->sigh <= 0xe21240aa)) )
     {
-      EXCEPTION(EX_Invalid);
-      reg_move(&CONST_QNaN, result);
-      return;
+      /* The argument is <= 0.88309101259 */
+
+      argSqrd.msw = arg->sigh; argSqrd.midw = arg->sigl; argSqrd.lsw = 0;
+      mul64_Xsig(&argSqrd, &significand(arg));
+      shr_Xsig(&argSqrd, 2*(-1-exponent));
+      argTo4.msw = argSqrd.msw; argTo4.midw = argSqrd.midw;
+      argTo4.lsw = argSqrd.lsw;
+      mul_Xsig_Xsig(&argTo4, &argTo4);
+
+      polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_l,
+                     N_COEFF_N-1);
+      mul_Xsig_Xsig(&accumulator, &argSqrd);
+      negate_Xsig(&accumulator);
+
+      polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_l,
+                     N_COEFF_P-1);
+
+      shr_Xsig(&accumulator, 2);    /* Divide by four */
+      accumulator.msw |= 0x80000000;  /* Add 1.0 */
+
+      mul64_Xsig(&accumulator, &significand(arg));
+      mul64_Xsig(&accumulator, &significand(arg));
+      mul64_Xsig(&accumulator, &significand(arg));
+
+      /* Divide by four, FPU_REG compatible, etc */
+      exponent = 3*exponent + EXP_BIAS;
+
+      /* The minimum exponent difference is 3 */
+      shr_Xsig(&accumulator, arg->exp - exponent);
+
+      negate_Xsig(&accumulator);
+      XSIG_LL(accumulator) += significand(arg);
+
+      echange = round_Xsig(&accumulator);
+
+      result->exp = arg->exp + echange;
     }
-  
-  if ( exponent >= 0 ) /* Can't hack a number > 1.0 */
+  else
     {
-      if ( (exponent == 0) && (arg->sigl == 0) && (arg->sigh == 0x80000000) )
+      /* The argument is > 0.88309101259 */
+      /* We use sin(arg) = cos(pi/2-arg) */
+
+      fixed_arg = significand(arg);
+
+      if ( exponent == 0 )
        {
-         reg_move(&CONST_1, result);
-         return;
+         /* The argument is >= 1.0 */
+
+         /* Put the binary point at the left. */
+         fixed_arg <<= 1;
+       }
+      /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */
+      fixed_arg = 0x921fb54442d18469LL - fixed_arg;
+
+      XSIG_LL(argSqrd) = fixed_arg; argSqrd.lsw = 0;
+      mul64_Xsig(&argSqrd, &fixed_arg);
+
+      XSIG_LL(argTo4) = XSIG_LL(argSqrd); argTo4.lsw = argSqrd.lsw;
+      mul_Xsig_Xsig(&argTo4, &argTo4);
+
+      polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_h,
+                     N_COEFF_NH-1);
+      mul_Xsig_Xsig(&accumulator, &argSqrd);
+      negate_Xsig(&accumulator);
+
+      polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_h,
+                     N_COEFF_PH-1);
+      negate_Xsig(&accumulator);
+
+      mul64_Xsig(&accumulator, &fixed_arg);
+      mul64_Xsig(&accumulator, &fixed_arg);
+
+      shr_Xsig(&accumulator, 3);
+      negate_Xsig(&accumulator);
+
+      add_Xsig_Xsig(&accumulator, &argSqrd);
+
+      shr_Xsig(&accumulator, 1);
+
+      accumulator.lsw |= 1;  /* A zero accumulator here would cause problems */
+      negate_Xsig(&accumulator);
+
+      /* The basic computation is complete. Now fix the answer to
+        compensate for the error due to the approximation used for
+        pi/2
+        */
+
+      /* This has an exponent of -65 */
+      fix_up = 0x898cc517;
+      /* The fix-up needs to be improved for larger args */
+      if ( argSqrd.msw & 0xffc00000 )
+       {
+         /* Get about 32 bit precision in these: */
+         mul_32_32(0x898cc517, argSqrd.msw, &adj);
+         fix_up -= adj/6;
        }
+      mul_32_32(fix_up, LL_MSW(fixed_arg), &fix_up);
+
+      adj = accumulator.lsw;    /* temp save */
+      accumulator.lsw -= fix_up;
+      if ( accumulator.lsw > adj )
+       XSIG_LL(accumulator) --;
+
+      echange = round_Xsig(&accumulator);
+
+      result->exp = EXP_BIAS - 1 + echange;
+    }
+
+  significand(result) = XSIG_LL(accumulator);
+  result->tag = TW_Valid;
+  result->sign = arg->sign;
+
+#ifdef PARANOID
+  if ( (result->exp >= EXP_BIAS)
+      && (significand(result) > 0x8000000000000000LL) )
+    {
+      EXCEPTION(EX_INTERNAL|0x150);
+    }
+#endif PARANOID
+
+}
+
+
+
+/*--- poly_cos() ------------------------------------------------------------+
+ |                                                                           |
+ +---------------------------------------------------------------------------*/
+void   poly_cos(FPU_REG const *arg, FPU_REG *result)
+{
+  long int            exponent, exp2, echange;
+  Xsig                accumulator, argSqrd, fix_up, argTo4;
+  unsigned long       adj;
+  unsigned long long  fixed_arg;
+
+
+#ifdef PARANOID
+  if ( arg->tag == TW_Zero )
+    {
+      /* Return 1.0 */
+      reg_move(&CONST_1, result);
+      return;
+    }
+
+  if ( (arg->exp > EXP_BIAS)
+      || ((arg->exp == EXP_BIAS)
+         && (significand(arg) > 0xc90fdaa22168c234LL)) )
+    {
       EXCEPTION(EX_Invalid);
       reg_move(&CONST_QNaN, result);
       return;
     }
 #endif PARANOID
-  
-  fixed_arg.sigl = arg->sigl;
-  fixed_arg.sigh = arg->sigh;
-  if ( exponent < -1 )
+
+  exponent = arg->exp - EXP_BIAS;
+
+  accumulator.lsw = accumulator.midw = accumulator.msw = 0;
+
+  if ( (exponent < -1) || ((exponent == -1) && (arg->sigh <= 0xb00d6f54)) )
     {
-      /* shift the argument right by the required places */
-      if ( shrx(&(fixed_arg.sigl), -1-exponent) >= 0x80000000U )
-       significand(&fixed_arg)++;      /* round up */
+      /* arg is < 0.687705 */
+
+      argSqrd.msw = arg->sigh; argSqrd.midw = arg->sigl; argSqrd.lsw = 0;
+      mul64_Xsig(&argSqrd, &significand(arg));
+
+      if ( exponent < -1 )
+       {
+         /* shift the argument right by the required places */
+         shr_Xsig(&argSqrd, 2*(-1-exponent));
+       }
+
+      argTo4.msw = argSqrd.msw; argTo4.midw = argSqrd.midw;
+      argTo4.lsw = argSqrd.lsw;
+      mul_Xsig_Xsig(&argTo4, &argTo4);
+
+      polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_h,
+                     N_COEFF_NH-1);
+      mul_Xsig_Xsig(&accumulator, &argSqrd);
+      negate_Xsig(&accumulator);
+
+      polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_h,
+                     N_COEFF_PH-1);
+      negate_Xsig(&accumulator);
+
+      mul64_Xsig(&accumulator, &significand(arg));
+      mul64_Xsig(&accumulator, &significand(arg));
+      shr_Xsig(&accumulator, -2*(1+exponent));
+
+      shr_Xsig(&accumulator, 3);
+      negate_Xsig(&accumulator);
+
+      add_Xsig_Xsig(&accumulator, &argSqrd);
+
+      shr_Xsig(&accumulator, 1);
+
+      /* It doesn't matter if accumulator is all zero here, the
+        following code will work ok */
+      negate_Xsig(&accumulator);
+
+      if ( accumulator.lsw & 0x80000000 )
+       XSIG_LL(accumulator) ++;
+      if ( accumulator.msw == 0 )
+       {
+         /* The result is 1.0 */
+         reg_move(&CONST_1, result);
+       }
+      else
+       {
+         significand(result) = XSIG_LL(accumulator);
+      
+         /* will be a valid positive nr with expon = -1 */
+         *(short *)&(result->sign) = 0;
+         result->exp = EXP_BIAS - 1;
+       }
     }
-  
-  mul64(&significand(&fixed_arg), &significand(&fixed_arg),
-       &significand(&arg_sqrd));
-  mul64(&significand(&arg_sqrd), &significand(&arg_sqrd),
-       &significand(&arg_to_4));
-  
-  /* will be a valid positive nr with expon = 0 */
-  *(short *)&(accum.sign) = 0;
-  accum.exp = 0;
-
-  /* Do the basic fixed point polynomial evaluation */
-  polynomial(&(accum.sigl), &(arg_to_4.sigl), lterms, HIPOWER-1);
-  
-  /* will be a valid positive nr with expon = 0 */
-  *(short *)&(negaccum.sign) = 0;
-  negaccum.exp = 0;
-  
-  /* Do the basic fixed point polynomial evaluation */
-  polynomial(&(negaccum.sigl), &(arg_to_4.sigl), negterms, HIPOWER-1);
-  mul64(&significand(&arg_sqrd), &significand(&negaccum),
-       &significand(&negaccum));
-
-  /* Subtract the mantissas */
-  significand(&accum) -= significand(&negaccum);
-  
-  /* Convert to 64 bit signed-compatible */
-  accum.exp = EXP_BIAS - 1 + accum.exp;
-
-  reg_move(&accum, result);
-
-  normalize(result);
-
-  reg_mul(result, arg, result, FULL_PRECISION);
-  reg_u_add(result, arg, result, FULL_PRECISION);
-  
-  if ( result->exp >= EXP_BIAS )
+  else
     {
-      /* A small overflow may be possible... but an illegal result. */
-      if (    (result->exp > EXP_BIAS) /* Larger or equal 2.0 */
-         || (result->sigl > 1)   /* Larger than 1.0+msb */
-         ||    (result->sigh != 0x80000000) /* Much > 1.0 */
-         )
+      fixed_arg = significand(arg);
+
+      if ( exponent == 0 )
        {
-#ifdef DEBUGGING
-         RE_ENTRANT_CHECK_OFF;
-         printk("\nEXP=%d, MS=%08x, LS=%08x\n", result->exp,
-                result->sigh, result->sigl);
-         RE_ENTRANT_CHECK_ON;
-#endif DEBUGGING
-         EXCEPTION(EX_INTERNAL|0x103);
+         /* The argument is >= 1.0 */
+
+         /* Put the binary point at the left. */
+         fixed_arg <<= 1;
        }
-      
-#ifdef DEBUGGING
-      RE_ENTRANT_CHECK_OFF;
-      printk("\n***CORRECTING ILLEGAL RESULT*** in poly_sin() computation\n");
-      printk("EXP=%d, MS=%08x, LS=%08x\n", result->exp,
-            result->sigh, result->sigl);
-      RE_ENTRANT_CHECK_ON;
-#endif DEBUGGING
-
-      result->sigl = 0;        /* Truncate the result to 1.00 */
+      /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */
+      fixed_arg = 0x921fb54442d18469LL - fixed_arg;
+
+      exponent = -1;
+      exp2 = -1;
+
+      /* A shift is needed here only for a narrow range of arguments,
+        i.e. for fixed_arg approx 2^-32, but we pick up more... */
+      if ( !(LL_MSW(fixed_arg) & 0xffff0000) )
+       {
+         fixed_arg <<= 16;
+         exponent -= 16;
+         exp2 -= 16;
+       }
+
+      XSIG_LL(argSqrd) = fixed_arg; argSqrd.lsw = 0;
+      mul64_Xsig(&argSqrd, &fixed_arg);
+
+      if ( exponent < -1 )
+       {
+         /* shift the argument right by the required places */
+         shr_Xsig(&argSqrd, 2*(-1-exponent));
+       }
+
+      argTo4.msw = argSqrd.msw; argTo4.midw = argSqrd.midw;
+      argTo4.lsw = argSqrd.lsw;
+      mul_Xsig_Xsig(&argTo4, &argTo4);
+
+      polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_l,
+                     N_COEFF_N-1);
+      mul_Xsig_Xsig(&accumulator, &argSqrd);
+      negate_Xsig(&accumulator);
+
+      polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_l,
+                     N_COEFF_P-1);
+
+      shr_Xsig(&accumulator, 2);    /* Divide by four */
+      accumulator.msw |= 0x80000000;  /* Add 1.0 */
+
+      mul64_Xsig(&accumulator, &fixed_arg);
+      mul64_Xsig(&accumulator, &fixed_arg);
+      mul64_Xsig(&accumulator, &fixed_arg);
+
+      /* Divide by four, FPU_REG compatible, etc */
+      exponent = 3*exponent;
+
+      /* The minimum exponent difference is 3 */
+      shr_Xsig(&accumulator, exp2 - exponent);
+
+      negate_Xsig(&accumulator);
+      XSIG_LL(accumulator) += fixed_arg;
+
+      /* The basic computation is complete. Now fix the answer to
+        compensate for the error due to the approximation used for
+        pi/2
+        */
+
+      /* This has an exponent of -65 */
+      XSIG_LL(fix_up) = 0x898cc51701b839a2ll;
+      fix_up.lsw = 0;
+
+      /* The fix-up needs to be improved for larger args */
+      if ( argSqrd.msw & 0xffc00000 )
+       {
+         /* Get about 32 bit precision in these: */
+         mul_32_32(0x898cc517, argSqrd.msw, &adj);
+         fix_up.msw -= adj/2;
+         mul_32_32(0x898cc517, argTo4.msw, &adj);
+         fix_up.msw += adj/24;
+       }
+
+      exp2 += norm_Xsig(&accumulator);
+      shr_Xsig(&accumulator, 1); /* Prevent overflow */
+      exp2++;
+      shr_Xsig(&fix_up, 65 + exp2);
+
+      add_Xsig_Xsig(&accumulator, &fix_up);
+
+      echange = round_Xsig(&accumulator);
+
+      result->exp = exp2 + EXP_BIAS + echange;
+      *(short *)&(result->sign) = 0;      /* Is a valid positive nr */
+      significand(result) = XSIG_LL(accumulator);
+    }
+
+#ifdef PARANOID
+  if ( (result->exp >= EXP_BIAS)
+      && (significand(result) > 0x8000000000000000LL) )
+    {
+      EXCEPTION(EX_INTERNAL|0x151);
     }
+#endif PARANOID
 
 }
index 6b11988dcfa87246fb3835024b9826247764830e..845b19bf865ef79dab8514b9ecdf9cecde63ea44 100644 (file)
@@ -3,7 +3,7 @@
  |                                                                           |
  | Compute the tan of a FPU_REG, using a polynomial approximation.           |
  |                                                                           |
- | Copyright (C) 1992,1993                                                   |
+ | Copyright (C) 1992,1993,1994                                              |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
  |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  |                                                                           |
 #include "reg_constant.h"
 #include "fpu_emu.h"
 #include "control_w.h"
+#include "poly.h"
 
 
-#define        HIPOWERop       3       /* odd poly, positive terms */
-static unsigned short const    oddplterms[HIPOWERop][4] =
-       {
-       { 0x846a, 0x42d1, 0xb544, 0x921f},
-       { 0x6fb2, 0x0215, 0x95c0, 0x099c},
-       { 0xfce6, 0x0cc8, 0x1c9a, 0x0000}
-       };
+#define        HiPOWERop       3       /* odd poly, positive terms */
+static const unsigned long long oddplterm[HiPOWERop] =
+{
+  0x0000000000000000LL,
+  0x0051a1cf08fca228LL,
+  0x0000000071284ff7LL
+};
 
-#define        HIPOWERon       2       /* odd poly, negative terms */
-static unsigned short const    oddnegterms[HIPOWERon][4] =
-       {
-       { 0x6906, 0xe205, 0x25c8, 0x8838},
-       { 0x1dd7, 0x3fe3, 0x944e, 0x002c}
-       };
+#define        HiPOWERon       2       /* odd poly, negative terms */
+static const unsigned long long oddnegterm[HiPOWERon] =
+{
+   0x1291a9a184244e80LL,
+   0x0000583245819c21LL
+};
 
-#define        HIPOWERep       2       /* even poly, positive terms */
-static unsigned short const    evenplterms[HIPOWERep][4] =
-       {
-       { 0xdb8f, 0x3761, 0x1432, 0x2acf},
-       { 0x16eb, 0x13c1, 0x3099, 0x0003}
-       };
+#define        HiPOWERep       2       /* even poly, positive terms */
+static const unsigned long long evenplterm[HiPOWERep] =
+{
+  0x0e848884b539e888LL,
+  0x00003c7f18b887daLL
+};
 
-#define        HIPOWERen       2       /* even poly, negative terms */
-static unsigned short const    evennegterms[HIPOWERen][4] =
-       {
-       { 0x3a7c, 0xe4c5, 0x7f87, 0x2945},
-       { 0x572b, 0x664c, 0xc543, 0x018c}
-       };
+#define        HiPOWERen       2       /* even poly, negative terms */
+static const unsigned long long evennegterm[HiPOWERen] =
+{
+  0xf1f0200fd51569ccLL,
+  0x003afb46105c4432LL
+};
+
+static const unsigned long long twothirds = 0xaaaaaaaaaaaaaaabLL;
 
 
 /*--- poly_tan() ------------------------------------------------------------+
  |                                                                           |
  +---------------------------------------------------------------------------*/
-void   poly_tan(FPU_REG const *arg, FPU_REG *result, int invert)
+void   poly_tan(FPU_REG const *arg, FPU_REG *result)
 {
-  short                exponent;
-  FPU_REG       odd_poly, even_poly, pos_poly, neg_poly;
-  FPU_REG       argSq;
-  unsigned long long     arg_signif, argSqSq;
-  
+  long int             exponent;
+  int                   invert;
+  Xsig                  argSq, argSqSq, accumulatoro, accumulatore, accum,
+                        argSignif, fix_up;
+  unsigned long         adj;
 
   exponent = arg->exp - EXP_BIAS;
 
@@ -64,86 +67,147 @@ void       poly_tan(FPU_REG const *arg, FPU_REG *result, int invert)
     { arith_invalid(result); return; }  /* Need a positive number */
 #endif PARANOID
 
-  arg_signif = significand(arg);
-  if ( exponent < -1 )
+  /* Split the problem into two domains, smaller and larger than pi/4 */
+  if ( (exponent == 0) || ((exponent == -1) && (arg->sigh > 0xc90fdaa2)) )
     {
-      /* shift the argument right by the required places */
-      if ( shrx(&arg_signif, -1-exponent) >= 0x80000000U )
-       arg_signif++;   /* round up */
+      /* The argument is greater than (approx) pi/4 */
+      invert = 1;
+      accum.lsw = 0;
+      XSIG_LL(accum) = significand(arg);
+      if ( exponent == 0 )
+       {
+         /* The argument is >= 1.0 */
+         /* Put the binary point at the left. */
+         XSIG_LL(accum) <<= 1;
+       }
+      /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */
+      XSIG_LL(accum) = 0x921fb54442d18469LL - XSIG_LL(accum);
+
+      argSignif.lsw = accum.lsw;
+      XSIG_LL(argSignif) = XSIG_LL(accum);
+      exponent = -1 + norm_Xsig(&argSignif);
+    }
+  else
+    {
+      invert = 0;
+      argSignif.lsw = 0;
+      XSIG_LL(accum) = XSIG_LL(argSignif) = significand(arg);
+      if ( exponent < -1 )
+       {
+         /* shift the argument right by the required places */
+         if ( shrx(&XSIG_LL(accum), -1-exponent) >= 0x80000000U )
+           XSIG_LL(accum) ++;  /* round up */
+       }
     }
 
-  mul64(&arg_signif, &arg_signif, &significand(&argSq));
-  mul64(&significand(&argSq), &significand(&argSq), &argSqSq);
-
-  /* will be a valid positive nr with expon = 0 */
-  *(short *)&(pos_poly.sign) = 0;
-  pos_poly.exp = EXP_BIAS;
-
-  /* Do the basic fixed point polynomial evaluation */
-  polynomial(&pos_poly.sigl, (unsigned *)&argSqSq, oddplterms, HIPOWERop-1);
-
-  /* will be a valid positive nr with expon = 0 */
-  *(short *)&(neg_poly.sign) = 0;
-  neg_poly.exp = EXP_BIAS;
-
-  /* Do the basic fixed point polynomial evaluation */
-  polynomial(&neg_poly.sigl, (unsigned *)&argSqSq, oddnegterms, HIPOWERon-1);
-  mul64(&significand(&argSq), &significand(&neg_poly),
-       &significand(&neg_poly));
-
-  /* Subtract the mantissas */
-  significand(&pos_poly) -= significand(&neg_poly);
-
-  /* Convert to 64 bit signed-compatible */
-  pos_poly.exp -= 1;
+  XSIG_LL(argSq) = XSIG_LL(accum); argSq.lsw = accum.lsw;
+  mul_Xsig_Xsig(&argSq, &argSq);
+  XSIG_LL(argSqSq) = XSIG_LL(argSq); argSqSq.lsw = argSq.lsw;
+  mul_Xsig_Xsig(&argSqSq, &argSqSq);
 
-  reg_move(&pos_poly, &odd_poly);
-  normalize(&odd_poly);
-  
-  reg_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION);
-  /* Complete the odd polynomial. */
-  reg_u_add(&odd_poly, arg, &odd_poly, FULL_PRECISION);
+  /* Compute the negative terms for the numerator polynomial */
+  accumulatoro.msw = accumulatoro.midw = accumulatoro.lsw = 0;
+  polynomial_Xsig(&accumulatoro, &XSIG_LL(argSqSq), oddnegterm, HiPOWERon-1);
+  mul_Xsig_Xsig(&accumulatoro, &argSq);
+  negate_Xsig(&accumulatoro);
+  /* Add the positive terms */
+  polynomial_Xsig(&accumulatoro, &XSIG_LL(argSqSq), oddplterm, HiPOWERop-1);
 
-  /* will be a valid positive nr with expon = 0 */
-  *(short *)&(pos_poly.sign) = 0;
-  pos_poly.exp = EXP_BIAS;
   
-  /* Do the basic fixed point polynomial evaluation */
-  polynomial(&pos_poly.sigl, (unsigned *)&argSqSq, evenplterms, HIPOWERep-1);
-  mul64(&significand(&argSq),
-       &significand(&pos_poly), &significand(&pos_poly));
-  
-  /* will be a valid positive nr with expon = 0 */
-  *(short *)&(neg_poly.sign) = 0;
-  neg_poly.exp = EXP_BIAS;
-
-  /* Do the basic fixed point polynomial evaluation */
-  polynomial(&neg_poly.sigl, (unsigned *)&argSqSq, evennegterms, HIPOWERen-1);
-
-  /* Subtract the mantissas */
-  significand(&neg_poly) -= significand(&pos_poly);
-  /* and multiply by argSq */
-
-  /* Convert argSq to a valid reg number */
-  *(short *)&(argSq.sign) = 0;
-  argSq.exp = EXP_BIAS - 1;
-  normalize(&argSq);
-
-  /* Convert to 64 bit signed-compatible */
-  neg_poly.exp -= 1;
+  /* Compute the positive terms for the denominator polynomial */
+  accumulatore.msw = accumulatore.midw = accumulatore.lsw = 0;
+  polynomial_Xsig(&accumulatore, &XSIG_LL(argSqSq), evenplterm, HiPOWERep-1);
+  mul_Xsig_Xsig(&accumulatore, &argSq);
+  negate_Xsig(&accumulatore);
+  /* Add the negative terms */
+  polynomial_Xsig(&accumulatore, &XSIG_LL(argSqSq), evennegterm, HiPOWERen-1);
+  /* Multiply by arg^2 */
+  mul64_Xsig(&accumulatore, &XSIG_LL(argSignif));
+  mul64_Xsig(&accumulatore, &XSIG_LL(argSignif));
+  /* de-normalize and divide by 2 */
+  shr_Xsig(&accumulatore, -2*(1+exponent) + 1);
+  negate_Xsig(&accumulatore);      /* This does 1 - accumulator */
+
+  /* Now find the ratio. */
+  if ( accumulatore.msw == 0 )
+    {
+      /* accumulatoro must contain 1.0 here, (actually, 0) but it
+        really doesn't matter what value we use because it will
+        have neglibible effect in later calculations
+        */
+      XSIG_LL(accum) = 0x8000000000000000LL;
+      accum.lsw = 0;
+    }
+  else
+    {
+      div_Xsig(&accumulatoro, &accumulatore, &accum);
+    }
 
-  reg_move(&neg_poly, &even_poly);
-  normalize(&even_poly);
+  /* Multiply by 1/3 * arg^3 */
+  mul64_Xsig(&accum, &XSIG_LL(argSignif));
+  mul64_Xsig(&accum, &XSIG_LL(argSignif));
+  mul64_Xsig(&accum, &XSIG_LL(argSignif));
+  mul64_Xsig(&accum, &twothirds);
+  shr_Xsig(&accum, -2*(exponent+1));
 
-  reg_mul(&even_poly, &argSq, &even_poly, FULL_PRECISION);
-  reg_add(&even_poly, &argSq, &even_poly, FULL_PRECISION);
-  /* Complete the even polynomial */
-  reg_sub(&CONST_1, &even_poly, &even_poly, FULL_PRECISION);
+  /* tan(arg) = arg + accum */
+  add_two_Xsig(&accum, &argSignif, &exponent);
 
-  /* Now ready to copy the results */
   if ( invert )
-    { reg_div(&even_poly, &odd_poly, result, FULL_PRECISION); }
-  else
-    { reg_div(&odd_poly, &even_poly, result, FULL_PRECISION); }
+    {
+      /* We now have the value of tan(pi_2 - arg) where pi_2 is an
+        approximation for pi/2
+        */
+      /* The next step is to fix the answer to compensate for the
+        error due to the approximation used for pi/2
+        */
+
+      /* This is (approx) delta, the error in our approx for pi/2
+        (see above). It has an exponent of -65
+        */
+      XSIG_LL(fix_up) = 0x898cc51701b839a2LL;
+      fix_up.lsw = 0;
+
+      if ( exponent == 0 )
+       adj = 0xffffffff;   /* We want approx 1.0 here, but
+                              this is close enough. */
+      else if ( exponent > -30 )
+       {
+         adj = accum.msw >> -(exponent+1);      /* tan */
+         mul_32_32(adj, adj, &adj);           /* tan^2 */
+       }
+      else
+       adj = 0;
+      mul_32_32(0x898cc517, adj, &adj);        /* delta * tan^2 */
+
+      fix_up.msw += adj;
+      if ( !(fix_up.msw & 0x80000000) )   /* did fix_up overflow ? */
+       {
+         /* Yes, we need to add an msb */
+         shr_Xsig(&fix_up, 1);
+         fix_up.msw |= 0x80000000;
+         shr_Xsig(&fix_up, 64 + exponent);
+       }
+      else
+       shr_Xsig(&fix_up, 65 + exponent);
+
+      add_two_Xsig(&accum, &fix_up, &exponent);
+
+      /* accum now contains tan(pi/2 - arg).
+        Use tan(arg) = 1.0 / tan(pi/2 - arg)
+        */
+      accumulatoro.lsw = accumulatoro.midw = 0;
+      accumulatoro.msw = 0x80000000;
+      div_Xsig(&accumulatoro, &accum, &accum);
+      exponent = - exponent - 1;
+    }
+
+  /* Transfer the result */
+  round_Xsig(&accum);
+  *(short *)&(result->sign) = 0;
+  significand(result) = XSIG_LL(accum);
+  result->exp = EXP_BIAS + exponent;
 
 }
diff --git a/drivers/FPU-emu/polynom_Xsig.S b/drivers/FPU-emu/polynom_Xsig.S
new file mode 100644 (file)
index 0000000..585221f
--- /dev/null
@@ -0,0 +1,137 @@
+/*---------------------------------------------------------------------------+
+ |  polynomial_Xsig.S                                                        |
+ |                                                                           |
+ | Fixed point arithmetic polynomial evaluation.                             |
+ |                                                                           |
+ | Copyright (C) 1992,1993,1994                                              |
+ |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ |                                                                           |
+ | Call from C as:                                                           |
+ |   void polynomial_Xsig(Xsig *accum, unsigned long long x,                 |
+ |                        unsigned long long terms[], int n)                 |
+ |                                                                           |
+ | Computes:                                                                 |
+ | terms[0] + (terms[1] + (terms[2] + ... + (terms[n-1]*x)*x)*x)*x) ... )*x  |
+ | and adds the result to the 12 byte Xsig.                                  |
+ | The terms[] are each 8 bytes, but all computation is performed to 12 byte |
+ | precision.                                                                |
+ |                                                                           |
+ | This function must be used carefully: most overflow of intermediate       |
+ | results is controlled, but overflow of the result is not.                 |
+ |                                                                           |
+ +---------------------------------------------------------------------------*/
+       .file   "polynomial_Xsig.S"
+
+#include "fpu_asm.h"
+
+
+#define        TERM_SIZE       $8
+#define        SUM_MS          -20(%ebp)       /* sum ms long */
+#define SUM_MIDDLE     -24(%ebp)       /* sum middle long */
+#define        SUM_LS          -28(%ebp)       /* sum ls long */
+#define        ACCUM_MS        -4(%ebp)        /* accum ms long */
+#define        ACCUM_MIDDLE    -8(%ebp)        /* accum middle long */
+#define        ACCUM_LS        -12(%ebp)       /* accum ls long */
+#define OVERFLOWED      -16(%ebp)      /* addition overflow flag */
+
+.text
+       .align 2,144
+.globl _polynomial_Xsig
+_polynomial_Xsig:
+       pushl   %ebp
+       movl    %esp,%ebp
+       subl    $32,%esp
+       pushl   %esi
+       pushl   %edi
+       pushl   %ebx
+
+       movl    PARAM2,%esi             /* x */
+       movl    PARAM3,%edi             /* terms */
+
+       movl    TERM_SIZE,%eax
+       mull    PARAM4                  /* n */
+       addl    %eax,%edi
+
+       movl    4(%edi),%edx            /* terms[n] */
+       movl    %edx,SUM_MS
+       movl    (%edi),%edx             /* terms[n] */
+       movl    %edx,SUM_MIDDLE
+       xor     %eax,%eax
+       movl    %eax,SUM_LS
+       movb    %al,OVERFLOWED
+
+       subl    TERM_SIZE,%edi
+       decl    PARAM4
+       js      L_accum_done
+
+L_accum_loop:
+       xor     %eax,%eax
+       movl    %eax,ACCUM_MS
+       movl    %eax,ACCUM_MIDDLE
+
+       movl    SUM_MIDDLE,%eax
+       mull    (%esi)                  /* x ls long */
+       movl    %edx,ACCUM_LS
+
+       movl    SUM_MIDDLE,%eax
+       mull    4(%esi)                 /* x ms long */
+       addl    %eax,ACCUM_LS
+       adcl    %edx,ACCUM_MIDDLE
+       adcl    $0,ACCUM_MS
+
+       movl    SUM_MS,%eax
+       mull    (%esi)                  /* x ls long */
+       addl    %eax,ACCUM_LS
+       adcl    %edx,ACCUM_MIDDLE
+       adcl    $0,ACCUM_MS
+
+       movl    SUM_MS,%eax
+       mull    4(%esi)                 /* x ms long */
+       addl    %eax,ACCUM_MIDDLE
+       adcl    %edx,ACCUM_MS
+
+       testb   $0xff,OVERFLOWED
+       jz      L_no_overflow
+
+       movl    (%esi),%eax
+       addl    %eax,ACCUM_MIDDLE
+       movl    4(%esi),%eax
+       adcl    %eax,ACCUM_MS           /* This could overflow too */
+
+L_no_overflow:
+
+/*
+ * Now put the sum of next term and the accumulator
+ * into the sum register
+ */
+       movl    ACCUM_LS,%eax
+       addl    (%edi),%eax             /* term ls long */
+       movl    %eax,SUM_LS
+       movl    ACCUM_MIDDLE,%eax
+       adcl    (%edi),%eax             /* term ls long */
+       movl    %eax,SUM_MIDDLE
+       movl    ACCUM_MS,%eax
+       adcl    4(%edi),%eax            /* term ms long */
+       movl    %eax,SUM_MS
+       sbbb    %al,%al
+       movb    %al,OVERFLOWED          /* Used in the next iteration */
+
+       subl    TERM_SIZE,%edi
+       decl    PARAM4
+       jns     L_accum_loop
+
+L_accum_done:
+       movl    PARAM1,%edi             /* accum */
+       movl    SUM_LS,%eax
+       addl    %eax,(%edi)
+       movl    SUM_MIDDLE,%eax
+       adcl    %eax,4(%edi)
+       movl    SUM_MS,%eax
+       adcl    %eax,8(%edi)
+
+       popl    %ebx
+       popl    %edi
+       popl    %esi
+       leave
+       ret
index 9f8133b6b18bb844d991397c622e293c59de3448..405a8745630e4c2ea41c2ee3180f5794dbaee60c 100644 (file)
@@ -825,7 +825,7 @@ int reg_store_single(float *single, FPU_REG *st0_ptr)
 #ifdef PARANOID
   else
     {
-      EXCEPTION(EX_INTERNAL|0x106);
+      EXCEPTION(EX_INTERNAL|0x163);
       return 0;
     }
 #endif
@@ -1389,16 +1389,16 @@ static void write_to_extended(FPU_REG *rp, char *d)
     {
     case TW_Zero:
       if ( rp->sigh | rp->sigl | e )
-       EXCEPTION(EX_INTERNAL | 0x114);
+       EXCEPTION(EX_INTERNAL | 0x160);
       break;
     case TW_Infinity:
     case TW_NaN:
       if ( (e ^ 0x7fff) | !(rp->sigh & 0x80000000) )
-       EXCEPTION(EX_INTERNAL | 0x114);
+       EXCEPTION(EX_INTERNAL | 0x161);
       break;
     default:
       if (e > 0x7fff || e < -63)
-       EXCEPTION(EX_INTERNAL | 0x114);
+       EXCEPTION(EX_INTERNAL | 0x162);
     }
 #endif PARANOID
 
diff --git a/drivers/FPU-emu/round_Xsig.S b/drivers/FPU-emu/round_Xsig.S
new file mode 100644 (file)
index 0000000..1637558
--- /dev/null
@@ -0,0 +1,148 @@
+/*---------------------------------------------------------------------------+
+ |  round_Xsig.S                                                             |
+ |                                                                           |
+ | Copyright (C) 1992,1993,1994                                              |
+ |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ |                                                                           |
+ | Normalize and round a 12 byte quantity.                                   |
+ | Call from C as:                                                           |
+ |   int round_Xsig(Xsig *n)                                                 |
+ |                                                                           |
+ | Normalize a 12 byte quantity.                                             |
+ | Call from C as:                                                           |
+ |   int norm_Xsig(Xsig *n)                                                  |
+ |                                                                           |
+ | Each function returns the size of the shift (nr of bits).                 |
+ |                                                                           |
+ +---------------------------------------------------------------------------*/
+       .file   "round_Xsig.S"
+
+#include "fpu_asm.h"
+
+
+.text
+
+       .align 2,144
+.globl _round_Xsig
+
+_round_Xsig:
+       pushl   %ebp
+       movl    %esp,%ebp
+       pushl   %ebx            /* Reserve some space */
+       pushl   %ebx
+       pushl   %esi
+
+       movl    PARAM1,%esi
+
+       movl    8(%esi),%edx
+       movl    4(%esi),%ebx
+       movl    (%esi),%eax
+
+       movl    $0,-4(%ebp)
+
+       orl     %edx,%edx       /* ms bits */
+       js      L_round         /* Already normalized */
+       jnz     L_shift_1       /* Shift left 1 - 31 bits */
+
+       movl    %ebx,%edx
+       movl    %eax,%ebx
+       xorl    %eax,%eax
+       movl    $-32,-4(%ebp)
+
+/* We need to shift left by 1 - 31 bits */
+L_shift_1:
+       bsrl    %edx,%ecx       /* get the required shift in %ecx */
+       subl    $31,%ecx
+       negl    %ecx
+       subl    %ecx,-4(%ebp)
+       shld    %cl,%ebx,%edx
+       shld    %cl,%eax,%ebx
+       shl     %cl,%eax
+
+L_round:
+       testl   $0x80000000,%eax
+       jz      L_exit
+
+       addl    $1,%ebx
+       adcl    $0,%edx
+       jnz     L_exit
+
+       movl    $0x80000000,%edx
+       incl    -4(%ebp)
+
+L_exit:
+       movl    %edx,8(%esi)
+       movl    %ebx,4(%esi)
+       movl    %eax,(%esi)
+
+       movl    -4(%ebp),%eax
+
+       popl    %esi
+       popl    %ebx
+       leave
+       ret
+
+
+
+
+       .align 2,144
+.globl _norm_Xsig
+
+_norm_Xsig:
+       pushl   %ebp
+       movl    %esp,%ebp
+       pushl   %ebx            /* Reserve some space */
+       pushl   %ebx
+       pushl   %esi
+
+       movl    PARAM1,%esi
+
+       movl    8(%esi),%edx
+       movl    4(%esi),%ebx
+       movl    (%esi),%eax
+
+       movl    $0,-4(%ebp)
+
+       orl     %edx,%edx       /* ms bits */
+       js      L_n_exit                /* Already normalized */
+       jnz     L_n_shift_1     /* Shift left 1 - 31 bits */
+
+       movl    %ebx,%edx
+       movl    %eax,%ebx
+       xorl    %eax,%eax
+       movl    $-32,-4(%ebp)
+
+       orl     %edx,%edx       /* ms bits */
+       js      L_n_exit        /* Normalized now */
+       jnz     L_n_shift_1     /* Shift left 1 - 31 bits */
+
+       movl    %ebx,%edx
+       movl    %eax,%ebx
+       xorl    %eax,%eax
+       addl    $-32,-4(%ebp)
+       jmp     L_n_exit        /* Might not be normalized,
+                                  but shift no more. */
+
+/* We need to shift left by 1 - 31 bits */
+L_n_shift_1:
+       bsrl    %edx,%ecx       /* get the required shift in %ecx */
+       subl    $31,%ecx
+       negl    %ecx
+       subl    %ecx,-4(%ebp)
+       shld    %cl,%ebx,%edx
+       shld    %cl,%eax,%ebx
+       shl     %cl,%eax
+
+L_n_exit:
+       movl    %edx,8(%esi)
+       movl    %ebx,4(%esi)
+       movl    %eax,(%esi)
+
+       movl    -4(%ebp),%eax
+
+       popl    %esi
+       popl    %ebx
+       leave
+       ret
+
diff --git a/drivers/FPU-emu/shr_Xsig.S b/drivers/FPU-emu/shr_Xsig.S
new file mode 100644 (file)
index 0000000..d6724a2
--- /dev/null
@@ -0,0 +1,90 @@
+       .file   "shr_Xsig.S"
+/*---------------------------------------------------------------------------+
+ |  shr_Xsig.S                                                               |
+ |                                                                           |
+ | 12 byte right shift function                                              |
+ |                                                                           |
+ | Copyright (C) 1992,1994                                                   |
+ |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
+ |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ |                                                                           |
+ | Call from C as:                                                           |
+ |   void shr_Xsig(Xsig *arg, unsigned nr)                                   |
+ |                                                                           |
+ |   Extended shift right function.                                          |
+ |   Fastest for small shifts.                                               |
+ |   Shifts the 12 byte quantity pointed to by the first arg (arg)           |
+ |   right by the number of bits specified by the second arg (nr).           |
+ |                                                                           |
+ +---------------------------------------------------------------------------*/
+
+#include "fpu_asm.h"
+
+.text
+       .align 2,144
+
+       .globl  _shr_Xsig
+_shr_Xsig:
+       push    %ebp
+       movl    %esp,%ebp
+       pushl   %esi
+       movl    PARAM2,%ecx
+       movl    PARAM1,%esi
+       cmpl    $32,%ecx        /* shrd only works for 0..31 bits */
+       jnc     L_more_than_31
+
+/* less than 32 bits */
+       pushl   %ebx
+       movl    (%esi),%eax     /* lsl */
+       movl    4(%esi),%ebx    /* midl */
+       movl    8(%esi),%edx    /* msl */
+       shrd    %cl,%ebx,%eax
+       shrd    %cl,%edx,%ebx
+       shr     %cl,%edx
+       movl    %eax,(%esi)
+       movl    %ebx,4(%esi)
+       movl    %edx,8(%esi)
+       popl    %ebx
+       popl    %esi
+       leave
+       ret
+
+L_more_than_31:
+       cmpl    $64,%ecx
+       jnc     L_more_than_63
+
+       subb    $32,%cl
+       movl    4(%esi),%eax    /* midl */
+       movl    8(%esi),%edx    /* msl */
+       shrd    %cl,%edx,%eax
+       shr     %cl,%edx
+       movl    %eax,(%esi)
+       movl    %edx,4(%esi)
+       movl    $0,8(%esi)
+       popl    %esi
+       leave
+       ret
+
+L_more_than_63:
+       cmpl    $96,%ecx
+       jnc     L_more_than_95
+
+       subb    $64,%cl
+       movl    8(%esi),%eax    /* msl */
+       shr     %cl,%eax
+       xorl    %edx,%edx
+       movl    %eax,(%esi)
+       movl    %edx,4(%esi)
+       movl    %edx,8(%esi)
+       popl    %esi
+       leave
+       ret
+
+L_more_than_95:
+       xorl    %eax,%eax
+       movl    %eax,(%esi)
+       movl    %eax,4(%esi)
+       movl    %eax,8(%esi)
+       popl    %esi
+       leave
+       ret
index 1dc3b1a00e949085c0e030a06251abd7b91bac5a..4c75a4792d2c1b59f1cf2cde10099f8342c08c56 100644 (file)
@@ -9,4 +9,4 @@
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
-#define FPU_VERSION "wm-FPU-emu version 1.12"
+#define FPU_VERSION "wm-FPU-emu version 1.20"
index 6787a2c50622cc06c904bb041ce175c5db534973..fa51a2eec3893c1896fcb6c4f196b287325e880c 100644 (file)
@@ -1,11 +1,18 @@
-This README belongs to release 2.3 of the SoundBlaster Pro (Matsushita,
+This README belongs to release 2.5 of the SoundBlaster Pro (Matsushita,
 Kotobuki, Panasonic, CreativeLabs) CD-ROM driver for Linux.
 
-The driver is able to drive the whole family of IDE-style
-Matsushita/Kotobuki/Panasonic drives (the "double speed" versions like CR-562
-and CR-563, too), and it will work with the soundcard interfaces (SB Pro,
-SB 16, Galaxy, SoundFX, ...) and/or with the "no-sound" cards (Panasonic
-CI-101P, LaserMate, WDH-7001C, Aztech, ...).
+The driver is able to drive the whole family of "traditional" IDE-style (that
+has nothing to do with the new "Enhanced IDE" drive standard) Matsushita,
+Kotobuki, Panasonic drives, sometimes labelled as "CreativeLabs". The
+well-known drives are CR-521, CR-522, CR-523, CR-562, CR-563.
+
+This driver is NOT for Mitsumi or Sony or Aztech or Philips or XXX drives.
+The matter that some other brand's drives work with newer sound card
+interfaces does NOT make the drives compatible. :-)
+
+It will work with the soundcard interfaces (SB Pro, SB 16, Galaxy, SoundFX,
+...) and/or with the "no-sound" cards (Panasonic CI-101P, LaserMate,
+WDH-7001C, older Aztech cards, ...).
 It should work too now with the "configurable" interface "Sequoia S-1000",
 which is found on the Spea Media FX sound card.
 The interface type has to get configured in /usr/include/linux/sbpcd.h, 
@@ -15,11 +22,12 @@ The driver respects different drive firmware releases - my old drive is a 2.11,
 but it should work with "old" drives <2.01 ... >3.00 and with "new" drives
 (which count the releases around 0.75 or 1.00).
 
-Up to 4 drives are supported. CR-52x ("old") and CR-56x ("new") drives can be
-mixed, but the CR-521 ones are hard-wired to drive ID 0. The drives have to
-use different drive IDs, and each drive has to get a unique minor number
-(0...3), corresponding to it's drive ID. The drive IDs may be selected freely
-from 0 to 3 - they must not be in consecutive order.
+Up to 4 drives per interface card, and up to 4 interface cards are supported.
+CR-52x ("old") and CR-56x ("new") drives can be mixed, but the CR-521 ones are
+hard-wired to drive ID 0. The drives have to use different drive IDs, and each
+drive has to get a unique minor number (0...3), corresponding to it's drive ID.
+The drive IDs may be selected freely from 0 to 3 - they do not have to be in
+consecutive order.
 
 As Don Carroll, don@ds9.us.dell.com or FIDO 1:382/14, told me, it is possible
 to change old drives to any ID, too. He writes in this sense:
@@ -39,8 +47,9 @@ to change old drives to any ID, too. He writes in this sense:
    ID 3 behaves like ID 0."
 
 To use more than 4 drives (now that the single-speed CR-521's are as cheap as
-50$), you have to "duplicate" the driver. Just copy sbpcd.c into sbpcd2.c and
-change SBPCD_ISSUE accordingly.
+50$), you need a second interface card and you have to "duplicate" the driver.
+Just copy sbpcd.c into sbpcd2.c and so forth and change SBPCD_ISSUE
+accordingly.
 
 The driver supports reading of data from the CD and playing of audio tracks.
 The audio part should run with WorkMan, xcdplayer, with the "non-X11" products
@@ -168,7 +177,9 @@ execution of a binary from the CD; you have to copy it to a device with the
 standard BLOCK_SIZE (1024) before. So, do not use this if your system is
 directly "running from the CDROM" (like some of YGGDRASIL's installation
 variants). There are CDs on the market (like the german "unifix" Linux
-distribution) which MUST get handled with a block_size of 1024.
+distribution) which MUST get handled with a block_size of 1024. Generally,
+one can say all the CDs which hold files of the name YMTRANS.TBL are defective;
+do not use block=2048 with those.
 
 
 Auto-probing at boot time:
@@ -209,9 +220,9 @@ Most of the "SoundBlaster compatible" cards behave like the no-sound
 interfaces! 
 
 With "original" SB Pro cards, an initial setting of CD_volume through the
-sound cards MIXER register gets done. That happens at the end of "sbpcd_init".
-If you are using a "compatible" sound card of type "LaserMate", you can change
-that code to get it done with your card, too...
+sound cards MIXER register gets done.
+If you are using a "compatible" sound card of types "LaserMate" or "SPEA",
+you can set SOUND_BASE (in sbpcd.h) to get it done with your card, too...
 
 
 Using audio CDs:
@@ -367,9 +378,9 @@ Known problems:
 
 Currently, the detection of disk change or removal is actively disabled.
 
-All attempts to read the UPC/EAN code result in a stream of zeroes. All my
-drives are telling there is no UPC/EAN code on disk or there is, but it is an
-all-zero number.
+Most attempts to read the UPC/EAN code result in a stream of zeroes. All my
+drives are mostly telling there is no UPC/EAN code on disk or there is, but it
+is an all-zero number. I guess now almost no CD holds such a number.
 
 Bug reports, comments, wishes, donations (technical information is a donation,
 too :-) etc. to
index 724d976075d948fde65ffdc2f45d26106290ddc1..3c75973af51fe9b72a9558a466934a8b6e1fa0e2 100644 (file)
@@ -1773,7 +1773,7 @@ scd_open(struct inode *inode,
 
 
    if (filp->f_mode & 2)
-      return -EACCES;
+      return -EROFS;
 
    if (!sony_spun_up)
    {
index 382d0bd164ee12b6748905bec84b9e60d2fb596b..fcf1f3562c0132354817e9d5c2047ae6787503b0 100644 (file)
@@ -1417,7 +1417,7 @@ static int floppy_open(struct inode * inode, struct file * filp)
                if (filp->f_mode & 2) {
                        if (1 & (read_only >> drive)) {
                                floppy_release(inode, filp);
-                               return -EACCES;
+                               return -EROFS;
                        }
                }
        }
index 7b876202f993fd1f6b605941020528d380fb6d13..d544eeab7ea9ce434400d4bc40e991e67e4d307b 100644 (file)
@@ -1031,7 +1031,7 @@ mcd_open(struct inode *ip, struct file *fp)
                return -ENXIO;                  /* no hardware */
        
        if (fp->f_mode & 2)                     /* write access? */
-               return -EACCES;
+               return -EROFS;
 
        if (!mcd_open_count && mcd_state == MCD_S_IDLE) {
 
index 83abc2b66d779bf48a0bca9ae42425e7a7270ef2..30a57befe15fe78664dad8669ebdf6c8d3e6b888 100644 (file)
@@ -5,7 +5,7 @@
  *            and for "no-sound" interfaces like Lasermate and the
  *            Panasonic CI-101P.
  *
- *  NOTE:     This is release 2.4.
+ *  NOTE:     This is release 2.5.
  *            It works with my SbPro & drive CR-521 V2.11 from 2/92
  *            and with the new CR-562-B V0.75 on a "naked" Panasonic
  *            CI-101P interface. And vice versa. 
  *
  *  2.4  Use different names for device registering.
  *       
+ *  2.5  Added "#ifdef EJECT" code (default: enabled) to automatically eject
+ *       the tray during last call to "sbpcd_release".
+ *       Added "#ifdef JUKEBOX" code (default: disabled) to automatically eject
+ *       the tray during call to "sbpcd_open" if no disk is in.
+ *       Turn on the CD volume of "compatible" sound cards, too; just define
+ *       SOUND_BASE (in sbpcd.h) accordingly (default: disabled).
  *
  *  TODO
  *
 
 #include "blk.h"
 
-#define VERSION "2.4 Eberhard Moenkeberg <emoenke@gwdg.de>"
+#define VERSION "2.5 Eberhard Moenkeberg <emoenke@gwdg.de>"
 
 #define SBPCD_DEBUG
 
 /*
  * still testing around...
  */
+#define JUKEBOX 0 /* tray control: eject if no disk is in */
+#define EJECT 1 /* tray control: eject tray after last use */
 #define LONG_TIMING 0 /* test against timeouts with "gold" CDs on CR-521 */
 #define MANY_SESSION 0 /* this will conflict with "true" multi-session! */
 #undef  FUTURE
@@ -3226,7 +3234,7 @@ static int sbpcd_open(struct inode *ip, struct file *fp)
   if (ndrives==0) return (-ENXIO);             /* no hardware */
 
   if (fp->f_mode & 2)
-         return -EACCES;
+         return -EROFS;
 
   i = MINOR(ip->i_rdev);
   if ( (i<0) || (i>=NR_SBPCD) )
@@ -3254,9 +3262,14 @@ static int sbpcd_open(struct inode *ip, struct file *fp)
   if (!st_door_closed||!st_caddy_in)
     {
       printk("SBPCD: sbpcd_open: no disk in drive\n");
+#if JUKEBOX
+      do
+       i=yy_LockDoor(0);
+      while (i!=0);
+      if (new_drive) yy_SpinDown(); /* eject tray */
+#endif
       return (-ENXIO);
     }
-
 /*
  * try to keep an "open" counter here and lock the door if 0->1.
  */
@@ -3308,6 +3321,9 @@ static void sbpcd_release(struct inode * ip, struct file * file)
          do
            i=yy_LockDoor(0);
          while (i!=0);
+#ifdef EJECT
+         if (new_drive) yy_SpinDown();
+#endif
        }
     }
 }
@@ -3559,12 +3575,22 @@ unsigned long SBPCD_INIT(u_long mem_start, u_long mem_end)
       if (i>=0) DriveStruct[d].CD_changed=1;
     }
 
-  if (sbpro_type==1)
+/*
+ * Turn on the CD audio channels.
+ * For "compatible" soundcards (with "SBPRO 0" or "SBPRO 2"), the addresses
+ * are obtained from SOUND_BASE (see sbpcd.h).
+ */
+  if ((sbpro_type==1) || (SOUND_BASE))
     {
-      OUT(MIXER_addr,MIXER_CD_Volume);
-      OUT(MIXER_data,0xCC); /* one nibble per channel */
+      if (sbpro_type!=1)
+       {
+         MIXER_addr=SOUND_BASE+0x04; /* sound card's address register */
+         MIXER_data=SOUND_BASE+0x05; /* sound card's data register */
+       }
+      OUT(MIXER_addr,MIXER_CD_Volume); /* select SB Pro mixer register */
+      OUT(MIXER_data,0xCC); /* one nibble per channel, max. value: 0xFF */
     }
-  
+
   if (register_blkdev(MAJOR_NR, major_name, &sbpcd_fops) != 0)
     {
       printk("SBPCD: Can't get MAJOR %d for Matsushita CDROM\n", MAJOR_NR);
index dd38c70603bba3bbbab52a5c070934b9a4915911..4a48e51673315b59b7bd91bfe4566c5644efdaec 100644 (file)
@@ -58,7 +58,7 @@ extern int ni52_probe(struct device *);
 extern int ni65_probe(struct device *);
 extern int SK_init(struct device *);
 
-/* Detachable devices ("pocket adaptors" and special PCMCIA drivers). */
+/* Detachable devices ("pocket adaptors") */
 extern int atp_init(struct device *);
 extern int de600_probe(struct device *);
 extern int de620_probe(struct device *);
@@ -144,38 +144,6 @@ ethif_probe(struct device *dev)
     return 0;
 }
 
-#ifdef CONFIG_PCMCIA_NET
-extern int dl_open(struct device *dev);
-extern int tc589_open(struct device *dev);
-extern int ibmccae_open(struct device *dev);
-static int pc_eth_open(struct device *dev);
-
-static int pc_eth_probe(struct device *dev)
-  {
-  dev->open = &pc_eth_open;
-  dev->set_config = &ether_config;
-  dev->tbusy = 1;
-  return 0;
-  }
-
-static int pc_eth_open(struct device *dev)
-  {
-  if (1
-#ifdef CONFIG_DE650
-      && dl_open(dev)
-#endif
-#ifdef CONFIG_3C589
-      && tc589_open(dev)
-#endif
-#ifdef CONFIG_IBMCCAE
-      && ibmccae_open(dev)
-#endif
-      && 1)
-    return -ENODEV;
-  else
-    return 0;
-  }
-#endif /* CONFIG_PCMCIA_NET */
 
 
 /* Run-time ATtachable (Pocket) devices have a different (not "eth#") name. */
@@ -186,17 +154,6 @@ static struct device atp_dev = {
 #   define NEXT_DEV    (&atp_dev)
 #endif
 
-#ifdef CONFIG_PCMCIA_NET
-static struct device pc_eth1_dev = {
-    "pc_eth1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, pc_eth_probe,
-    };
-static struct device pc_eth0_dev = {
-    "pc_eth0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &pc_eth1_dev, pc_eth_probe,
-    };
-#   undef NEXT_DEV
-#   define NEXT_DEV    (&pc_eth0_dev)
-#endif /* CONFIG_PCMCIA_NET */
-
 /* The first device defaults to I/O base '0', which means autoprobe. */
 #ifndef ETH0_ADDR
 # define ETH0_ADDR 0
index 986f82a9e87212a22cabd2bc6e1c160cf7b1495c..f19c69cf3b5866be94a13f28f0f99de0dedf7ebc 100644 (file)
     used and the common memory area is in low memory on the network card (my
     current system has 20MB and I've not had problems yet).
 
+    The ability to load this driver as a loadable module has been added. To
+    utilise this ability, you have to do <8 things:
+
+    1) copy depca.c from the  /linux/drivers/net directory to your favourite
+    temporary directory.
+    2) edit the  source code near  line 1530 to reflect  the I/O address and
+    IRQ you're using.
+    3) compile  depca.c, but include -DMODULE in  the command line to ensure
+    that the correct bits are compiled (see end of source code).
+    4) if you are wanting to add a new  card, goto 5. Otherwise, recompile a
+    kernel with the depca configuration turned off and reboot.
+    5) insmod depca.o
+    6) run the net startup bits for your eth?? interface manually 
+    (usually /etc/rc.inet[12] at boot time). 
+    7) enjoy!
+
+    Note that autoprobing is not allowed in loadable modules - the system is
+    already up and running and you're messing with interrupts. Also, there
+    is no way to check on the number of depcas installed at the moment.
+
+    To unload a module, turn off the associated interface 
+    'ifconfig eth?? down' then 'rmmod depca'.
+
     TO DO:
     ------
 
     Version   Date        Description
   
       0.1   25-jan-94     Initial writing.
-      0.2   27-jan-94     Added LANCE TX buffer chaining.
+      0.2   27-jan-94     Added LANCE TX hardware buffer chaining.
       0.3    1-feb-94     Added multiple DEPCA support.
       0.31   4-feb-94     Added DE202 recognition.
       0.32  19-feb-94     Tidy up. Improve multi-DEPCA support.
       0.35   8-mar-94     Added DE201 recognition. Tidied up.
       0.351 30-apr-94     Added EISA support. Added DE422 recognition.
       0.36  16-may-94     DE422 fix released.
+      0.37  22-jul-94     Added MODULE support
 
     =========================================================================
 */
 
-static char *version = "depca.c:v0.36 5/16/94 davies@wanton.lkg.dec.com\n";
+static char *version = "depca.c:v0.37 7/22/94 davies@wanton.lkg.dec.com\n";
 
 #include <stdarg.h>
 #include <linux/config.h>
@@ -159,12 +183,18 @@ static char *version = "depca.c:v0.36 5/16/94 davies@wanton.lkg.dec.com\n";
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
+
+#ifdef MODULE
+#include <linux/module.h>
+#include "/linux/tools/version.h"
+#endif /* MODULE */
+
 #include "depca.h"
 
 #ifdef DEPCA_DEBUG
-int depca_debug = DEPCA_DEBUG;
+static int depca_debug = DEPCA_DEBUG;
 #else
-int depca_debug = 1;
+static int depca_debug = 1;
 #endif
 
 #ifndef PROBE_LENGTH
@@ -302,12 +332,20 @@ static int DevicePresent(short ioaddr);
 #ifdef HAVE_MULTICAST
 static void SetMulticastFilter(int num_addrs, char *addrs, char *multicast_table);
 #endif
+
+#ifndef MODULE
 static struct device *isa_probe(struct device *dev);
 static struct device *eisa_probe(struct device *dev);
 static struct device *alloc_device(struct device *dev, int ioaddr);
 
 static int num_depcas = 0, num_eth = 0;;
 
+#else
+int  init_module(void);
+void cleanup_module(void);
+
+#endif /* MODULE */
+
 /*
 ** Miscellaneous defines...
 */
@@ -320,9 +358,11 @@ static int num_depcas = 0, num_eth = 0;;
 
 int depca_probe(struct device *dev)
 {
-    int base_addr = dev->base_addr;
+    short base_addr = dev->base_addr;
     int status = -ENODEV;
+#ifndef MODULE
     struct device *eth0;
+#endif
 
     if (base_addr > 0x1ff) {         /* Check a single specified location. */
       if (DevicePresent(base_addr) == 0) { /* Is DEPCA really here? */
@@ -331,10 +371,16 @@ int depca_probe(struct device *dev)
     } else if (base_addr > 0) {              /* Don't probe at all. */
       status = -ENXIO;
     } else {                          /* First probe for the DEPCA test */
-                                      /* pattern in ROM */
+
+#ifdef MODULE                         /* pattern in ROM */
+      printk("Autoprobing is not supported when loading a module based driver.\n");
+      status = -EIO;
+#else
       eth0=isa_probe(dev);
       eth0=eisa_probe(eth0);
       if (dev->priv) status=0;
+#endif /* MODULE */
+
     }
 
     if (status) dev->base_addr = base_addr;
@@ -559,10 +605,11 @@ depca_probe1(struct device *dev, short ioaddr)
 
        /* The DMA channel may be passed in on this parameter. */
        dev->dma = 0;
-       
+
        /* To auto-IRQ we enable the initialization-done and DMA err,
         interrupts. For now we will always get a DMA error. */
        if (dev->irq < 2) {
+#ifndef MODULE
          autoirq_setup(0);
 
          /* Trigger an initialization just for the interrupt. */
@@ -575,6 +622,7 @@ depca_probe1(struct device *dev, short ioaddr)
            printk(" and failed to detect IRQ line.\n");
            status = -EAGAIN;
          }
+#endif /* MODULE */
        } else {
          printk(" and assigned IRQ%d.\n", dev->irq);
        }
@@ -690,6 +738,10 @@ depca_open(struct device *dev)
       printk("nicsr: 0x%02x\n",inb(DEPCA_NICSR));
     }
 
+#ifdef MODULE
+  MOD_INC_USE_COUNT;
+#endif       
+
     return 0;                            /* Always succeed */
 }
 
@@ -1091,6 +1143,10 @@ depca_close(struct device *dev)
 
     irq2dev_map[dev->irq] = 0;
 
+#ifdef MODULE
+    MOD_DEC_USE_COUNT;
+#endif    
+
     return 0;
 }
 
@@ -1229,6 +1285,7 @@ static void SetMulticastFilter(int num_addrs, char *addrs, char *multicast_table
 
 #endif  /* HAVE_MULTICAST */
 
+#ifndef MODULE
 /*
 ** ISA bus I/O device probe
 */
@@ -1242,11 +1299,6 @@ struct device *dev;
                              *port && (num_depcas < MAX_NUM_DEPCAS); port++) {
     int ioaddr = *port;
 
-#ifdef HAVE_PORTRESERVE
-    if (check_region(ioaddr, DEPCA_TOTAL_SIZE))
-           continue;
-#endif
-
     if (DevicePresent(ioaddr) == 0) {
       if (num_depcas > 0) {        /* only gets here in autoprobe */
        dev = alloc_device(dev, ioaddr);
@@ -1274,10 +1326,6 @@ struct device *dev;
   ioaddr+=0x1000;                         /* get the first slot address */
   for (status = -ENODEV, i=1; i<MAX_EISA_SLOTS; i++, ioaddr+=0x1000) {
 
-#ifdef HAVE_PORTRESERVE
-    if (check_region(ioaddr, DEPCA_TOTAL_SIZE))
-           continue;
-#endif
     if (DevicePresent(ioaddr) == 0) {
       if (num_depcas > 0) {        /* only gets here in autoprobe */
        dev = alloc_device(dev, ioaddr);
@@ -1340,6 +1388,7 @@ int ioaddr;
 
   return dev;
 }
+#endif    /* MODULE */
 
 /*
 ** Look for a particular board name in the on-board Remote Diagnostics
@@ -1473,12 +1522,38 @@ static char asc2hex(char value)
   return value;                   /* return hex char or error */
 }
 
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+static struct device thisDepca = {
+  "        ", /* device name inserted by /linux/drivers/net/net_init.c */
+  0, 0, 0, 0,
+  0x200, 7,   /* I/O address, IRQ <--- EDIT THIS LINE FOR YOUR CONFIGURATION */
+  0, 0, 0, NULL, depca_probe };
+       
+int
+init_module(void)
+{
+  if (register_netdev(&thisDepca) != 0)
+    return -EIO;
+  return 0;
+}
+
+void
+cleanup_module(void)
+{
+  if (MOD_IN_USE) {
+    printk("%s: device busy, remove delayed\n",thisDepca.name);
+  } else {
+    unregister_netdev(&thisDepca);
+  }
+}
+#endif /* MODULE */
+
 \f
 /*
  * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c depca.c"
+ *  kernel-compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c depca.c"
+ *
+ *  module-compile-command: "gcc -D__KERNEL__ -DMODULE -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c depca.c"
  * End:
  */
-
-
-
index 44887b812cd904b976e702fef9b53289c90debe2..c2237f4f4b99aeb5cb3f81b8b22df97d87518e09 100644 (file)
@@ -194,28 +194,29 @@ int register_netdev(struct device *dev)
 {
        struct device *d = dev_base;
        unsigned long flags;
-       int i;
+       int i=MAX_ETH_CARDS;
 
        save_flags(flags);
        cli();
 
        if (dev && dev->init) {
-               if (dev->init(dev) != 0) {
-                       restore_flags(flags);
-                       return -EIO;
-               }
-
                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("device '%s' loaded\n", dev->name);
+                                       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)
diff --git a/drivers/scsi/53c7,8xx.c b/drivers/scsi/53c7,8xx.c
new file mode 100644 (file)
index 0000000..dece0fa
--- /dev/null
@@ -0,0 +1,3778 @@
+/* 
+ * Set these options for all host adapters.
+ *     - Memory mapped IO does not work.
+ *     - Test 1 does a bus mastering test, which will help
+ *       weed out brain damaged main boards.
+ */
+
+#define PERM_OPTIONS (OPTION_IO_MAPPED|OPTION_DEBUG_TEST1)
+
+/*
+ * Define SCSI_MALLOC to use scsi_malloc instead of kmalloc.  Other than
+ * preventing deadlock, I'm not sure why we'd want to do this.
+ */
+
+#define SCSI_MALLOC
+
+/*
+ * Sponsored by 
+ *     iX Multiuser Multitasking Magazine
+ *     Hannover, Germany
+ *     hm@ix.de
+ *
+ * Copyright 1993, 1994 Drew Eckhardt
+ *      Visionary Computing 
+ *      (Unix and Linux consulting and custom programming)
+ *      drew@Colorado.EDU
+ *     +1 (303) 786-7975
+ *
+ * TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation.
+ * 
+ * PRE-ALPHA
+ *
+ * For more information, please consult 
+ *
+ *
+ * NCR 53C700/53C700-66
+ * SCSI I/O Processor
+ * Data Manual
+ *
+ * NCR53C710 
+ * SCSI I/O Processor
+ * Programmer's Guide
+ *
+ * NCR 53C810
+ * PCI-SCSI I/O Processor
+ * Data Manual
+ *
+ * NCR 53C810/53C820
+ * PCI-SCSI I/O Processor Design In Guide
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * +1 (719) 578-3400
+ *
+ * Toll free literature number
+ * +1 (800) 334-5454
+ *
+ * PCI BIOS Specification Revision
+ * PCI Local Bus Specification
+ * PCI System Design Guide
+ *
+ * PCI Special Interest Group
+ * M/S HF3-15A
+ * 5200 N.E. Elam Young Parkway
+ * Hillsboro, Oregon 97124-6497
+ * +1 (503) 696-2000 
+ * +1 (800) 433-5177
+ */
+
+/*
+ * Design issues : 
+ * The cumulative latency needed to propogate a read/write request 
+ * through the filesystem, buffer cache, driver stacks, SCSI host, and 
+ * SCSI device is ultimately the limiting factor in throughput once we 
+ * have a sufficiently fast host adapter.
+ *  
+ * So, to maximize performance we want to keep the ratio of latency to data 
+ * transfer time to a minimum by
+ * 1.  Minimizing the total number of commands sent (typical command latency
+ *     including drive and busmatering host overhead is as high as 4.5ms)
+ *     to transfer a given amount of data.  
+ *
+ *      This is accomplished by placing no arbitrary limit on the number
+ *     of scatter/gather buffers supported, since we can transfer 1K
+ *     per scatter/gather buffer without Eric's cluster patches, 
+ *     4K with.  
+ *
+ * 2.  Minimizing the number of fatal interrupts serviced, since
+ *     fatal interrupts halt the SCSI I/O processor.  Basically,
+ *     this means offloading the practical maximum amount of processing 
+ *     to the SCSI chip.
+ * 
+ *     On the NCR53c810/820,  this is accomplished by using 
+ *             interrupt-on-the-fly signals with the DSA address as a 
+ *             parameter when commands complete, and only handling fatal 
+ *             errors and SDTR / WDTR  messages in the host code.
+ *
+ *     On the NCR53c710/720, interrupts are generated as on the NCR53c8x0,
+ *             only the lack of a interrupt-on-the-fly facility complicates
+ *             things.  
+ *             
+ *     On the NCR53c700 and NCR53c700-66, operations that were done via 
+ *             indirect, table mode on the more advanced chips have
+ *             been replaced by calls through a jump table which 
+ *             acts as a surrogate for the DSA.  Unfortunately, this 
+ *             means that we must service an interrupt for each 
+ *             disconnect/reconnect.
+ * 
+ * 3.  Eliminating latency by piplining operations at the different levels.
+ *     
+ *     This driver allows a configurable number of commands to be enqueued
+ *     for each target/lun combination (experimentally, I have discovered
+ *     that two seems to work best) and will ultimately allow for 
+ *     SCSI-II tagged queueing.
+ *     
+ *
+ * Architecture : 
+ * This driver is built arround two queues of commands waiting to 
+ * be executed - the Linux issue queue, and the shared Linux/NCR  
+ * queue which are manipulated by the NCR53c7xx_queue_command and 
+ * NCR53c7x0_intr routines.
+ *
+ * When the higher level routines pass a SCSI request down to 
+ * NCR53c7xx_queue_command, it looks to see if that target/lun 
+ * is currently busy. If not, the command is inserted into the 
+ * shared Linux/NCR queue, otherwise it is inserted into the Linux 
+ * queue.
+ *
+ * As commands are completed, the interrupt routine is triggered,
+ * looks for commands in the linked list of completed commands with
+ * valid status, removes these commands from the list, calls 
+ * the done routine, and flags their target/luns as not busy.
+ *
+ * Due to limitations in the intelligence of the NCR chips, certain
+ * concessions are made.  In many cases, it is easier to dynamically 
+ * generate/fixup code rather than calculate on the NCR at run time.  
+ * So, code is generated or fixed up for
+ *
+ * - Handling data transfers, using a variable number of MOVE instructions
+ *     interspersed with CALL MSG_IN, WHEN MSGIN instructions.
+ *
+ *     The DATAIN and DATAOUT routines are separate, so that an incorrect
+ *     direction can be trapped, and space isn't wasted. 
+ *
+ *     It may turn out that we're better off using some sort 
+ *     of table indirect instruction in a loop with a variable
+ *     sized table on the NCR53c710 and newer chips.
+ *
+ * - Checking for reselection (NCR53c710 and better)
+ *
+ * - Handling the details of SCSI context switches (NCR53c710 and better),
+ *     such as reprogramming appropriate synchronous parameters, 
+ *     removing the dsa structure from the NCR's queue of outstanding
+ *     commands, etc.
+ *
+ */
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include "../block/blk.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "53c7,8xx.h"
+#include "constants.h"
+#include "sd.h"
+
+static void abnormal_finished (struct NCR53c7x0_cmd *cmd, int result);
+static int NCR53c8xx_run_tests (struct Scsi_Host *host);
+static int NCR53c8xx_script_len;
+static void NCR53c7x0_intr (int irq);
+static void intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd 
+    *cmd);
+static void intr_dma (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd);
+static void print_dsa (struct Scsi_Host *host, unsigned long *dsa);
+static int print_insn (struct Scsi_Host *host, unsigned long *insn,
+    char *prefix, int kernel);
+
+static void NCR53c8xx_dsa_fixup (struct NCR53c7x0_cmd *cmd);
+static void NCR53c8x0_init_fixup (struct Scsi_Host *host);
+static int NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct 
+    NCR53c7x0_cmd *cmd);
+static void NCR53c8x0_soft_reset (struct Scsi_Host *host);
+
+static struct Scsi_Host *first_host = NULL;    /* Head of list of NCR boards */
+static Scsi_Host_Template *the_template = NULL;        
+
+/* Alocate storage space for constant messages, etc. */
+
+static long NCR53c7xx_zero = 0;                        
+static long NCR53c7xx_sink;
+static char NCR53c7xx_msg_reject = MESSAGE_REJECT;
+static char NCR53c7xx_msg_abort = ABORT;
+static char NCR53c7xx_msg_nop = NOP;
+
+/* Buffer for commands run before *malloc() works */
+/* 
+ * XXX - if need be, replace this with normal wait.
+ */
+static int scan_scsis_buf_busy = 0;
+static char scan_scsis_buf[512];
+
+/*
+ * TODO : 
+ *
+ * 1.  Implement single step / trace code?
+ * 
+ * 2.  The initial code has been tested on the NCR53c810.  I don't 
+ *     have access to NCR53c700, 700-66 (Forex boards), NCR53c710
+ *     (NCR Pentium systems), NCR53c720, or NCR53c820 boards to finish
+ *     development on those platforms.
+ *
+ *     NCR53c820/720 - need to add wide transfer support, including WDTR 
+ *                     negotiation, programming of wide transfer capabilities
+ *             on reselection and table indirect selection.
+ *
+ *     NCR53c720/710 - need to add fatal interrupt or GEN code for 
+ *             command completion signaling.   Need to take care of 
+ *             ADD WITH CARRY instructions since carry is unimplemented.
+ *             Also need to modify all SDID, SCID, etc. registers,
+ *             and table indirect select code since these use bit
+ *             fielded (ie 1<<target) instead of binary encoded
+ *             target ids.  Also, SCNTL3 is _not_ automatically
+ *             programmed on selection, so we need to add more code.
+ * 
+ *     NCR53c700/700-66 - need to add code to refix addresses on 
+ *             every nexus change, elimate all table indirect code.
+ *
+ * 3.  The NCR53c7x0 series is very popular on other platforms that 
+ *     could be running Linux - ie, some high performance AMIGA SCSI 
+ *     boards use it.  
+ *     
+ *     So, I should include #ifdef'd code so that it is 
+ *     compatable with these systems.
+ *     
+ *     Specifically, the little Endian assumptions I made in my 
+ *     bit fields need to change, and if the NCR doesn't see memory
+ *     the right way, we need to provide options to reverse words
+ *     when the scripts are relocated.
+ *
+ * 4.  Implement code to include page table entries for the 
+ *     area occupied by memory mapped boards so we don't have 
+ *     to use the potentially slower I/O accesses.
+ */
+
+/* 
+ * XXX - note that my assembler was modified so that internally,
+ * the names used can take a prefix, so that there is no conflict
+ * between multiple copies of the same script assembled with 
+ * different defines.
+ *
+ *
+ * Allow for simultaneous existance of mutliple SCSI scripts so we 
+ * can have a single driver binary for all of the family.
+ *
+ * - one for NCR53c700 and NCR53c700-66 chips  (not yet supported)
+ * - one for NCR53c710 and NCR53c720 chips     (not yet supported)
+ * - one for NCR53c810 and NCR53c820 chips     (only the NCR53c810 is
+ *     currently supported)
+ *
+ * For the very similar chips, we should probably hack the fixup code
+ * and interrupt code so that it works everywhere, but I suspect the 
+ * NCR53c700 is going
+ * to need it's own fixup routine.
+ */
+
+/*
+ * Use to translate between device IDs of various types.
+ */
+
+struct pci_chip {
+    short pci_device_id;
+    int chip;
+    int max_revision;
+    int min_revision;
+};
+
+static struct pci_chip pci_chip_ids[2] = { 
+    {PCI_DEVICE_ID_NCR_53C810, 810, 1, 1}, 
+    {PCI_DEVICE_ID_NCR_53C820, 820, -1, -1},
+    {PCI_DEVICE_ID_NCR_53C825, 825, -1, -1},
+};
+
+#define NPCI_CHIP_IDS (sizeof (pci_chip_ids) / sizeof(pci_chip_ids[0]))
+
+
+/* Forced detection and autoprobe code for various hardware */
+
+static struct override {
+    int chip;  /* 700, 70066, 710, 720, 810, 820 */
+    int board; /* Any special board level gunk */
+    unsigned pci:1;
+    union {
+       struct {
+           int base;   /* Memory address - indicates memory mapped regs */
+           int io_port;/* I/O port address - indicates I/O mapped regs */
+           int irq;    /* IRQ line */          
+           int dma;    /* DMA channel          - often none */
+       } normal;
+       struct {
+           int bus;
+           int device;
+           int function;
+       } pci;
+    } data;
+    int options;
+} overrides [4] = {{0,},};
+static int commandline_current = 0;
+static int no_overrides = 0;
+
+#if 0
+#define OVERRIDE_LIMIT (sizeof(overrides) / sizeof(struct override))
+#else
+#define OVERRIDE_LIMIT commandline_current
+#endif
+
+/*
+ * Function : static internal_setup(int board, int chip, char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ * 
+ * Inputs : board - currently, unsupported.  chip - 700, 70066, 710, 720
+ *     810, 815, 820, 825, allthough currently only the NCR53c810 is 
+ *     supported.
+ * 
+ */
+
+static void internal_setup(int board, int chip, char *str, int *ints) {
+    unsigned char pci;         /* Specifies a PCI override, with bus, device,
+                                  function */
+
+    pci = (str && !strcmp (str, "pci")) ? 1 : 0;
+    
+/*
+ * Override syntaxes are as follows : 
+ * ncr53c700,ncr53c700-66,ncr53c710,ncr53c720=mem,io,irq,dma
+ * ncr53c810,ncr53c820,ncr53c825=mem,io,irq or pci,bus,device,function
+ */
+
+    if (commandline_current < OVERRIDE_LIMIT) {
+       overrides[commandline_current].pci = pci ? 1 : 0;
+       if (!pci) {
+           overrides[commandline_current].data.normal.base = ints[1];
+           overrides[commandline_current].data.normal.io_port = ints[2];
+           overrides[commandline_current].data.normal.irq = ints[3];
+           overrides[commandline_current].data.normal.dma = (ints[0] >= 4) ?
+               ints[4] : DMA_NONE;
+           overrides[commandline_current].options = (ints[0] >= 5) ?
+               ints[5] : 0;
+       } else {
+           overrides[commandline_current].data.pci.bus = ints[1];
+           overrides[commandline_current].data.pci.device = ints[2];
+           overrides[commandline_current].data.pci.device = ints[3];
+           overrides[commandline_current].options = (ints[0] >= 4) ?
+               ints[4] : 0;
+       }
+       overrides[commandline_current].board = board;
+       overrides[commandline_current].chip = chip;
+       ++commandline_current;
+       ++no_overrides;
+    } else {
+       printk ("53c7,7x0.c:internal_setup() : too many overrides\n");
+    }
+}
+
+/*
+ * XXX - we might want to implement a single override function
+ *       with a chip type field, revamp the command line configuration,
+ *      etc.
+ */
+
+#define setup_wrapper(x)                               \
+void ncr53c##x##_setup (char *str, int *ints) {                \
+    internal_setup (BOARD_GENERIC, x, str, ints);      \
+}
+
+setup_wrapper(700)
+setup_wrapper(70066)
+setup_wrapper(710)
+setup_wrapper(720)
+setup_wrapper(810)
+setup_wrapper(815)
+setup_wrapper(820)
+setup_wrapper(825)
+
+static struct sigaction NCR53c7x0_sigaction =  { NCR53c7x0_intr, 0, 
+    SA_INTERRUPT , NULL };
+
+/* 
+ * Function : static int NCR53c7x0_init (struct Scsi_Host *host)
+ *
+ * Purpose :  initialize the internal structures for a given SCSI host
+ *
+ * Inputs : host - pointer to this host adapter's structure/ 
+ *
+ * Preconditions : when this function is called, the chip_type 
+ *     field of the hostdata structure MUST have been set.
+ */
+
+static int NCR53c7x0_init (struct Scsi_Host *host) {
+    NCR53c7x0_local_declare();
+    /* unsigned char tmp; */
+    int i, j, ccf;
+    struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+       host->hostdata;
+    struct Scsi_Host *search;
+    NCR53c7x0_local_setup(host);
+
+    switch (hostdata->chip) {
+    case 810:
+    case 815:
+       hostdata->dstat_sir_intr = NCR53c8x0_dstat_sir_intr;
+       hostdata->init_save_regs = NULL;
+       hostdata->dsa_fixup = NCR53c8xx_dsa_fixup;
+       hostdata->init_fixup = NCR53c8x0_init_fixup;
+       hostdata->soft_reset = NCR53c8x0_soft_reset;
+       hostdata->run_tests = NCR53c8xx_run_tests;
+/* Is the SCSI clock ever anything else on these chips? */
+       hostdata->scsi_clock = 40000000;
+       break;
+    default:
+       printk ("scsi%d : chip type of %d is not supported yet, detaching.\n",
+           host->host_no, hostdata->chip);
+       scsi_unregister (host);
+       return -1;
+    }
+
+    /*
+     * Set up an interrupt handler if we aren't allready sharing an IRQ
+     * with another board.
+     */
+
+    for (search = first_host; search && (search->hostt == the_template) &&
+       (search->irq != host->irq); search=search->next);
+
+    if (!search) {
+       if (irqaction (host->irq, &NCR53c7x0_sigaction)) {
+           printk("scsi%d : IRQ%d not free, detaching\n", 
+               host->host_no, host->irq);
+           scsi_unregister (host);
+           return -1;
+       } 
+    } else {
+       printk("scsi%d : using interrupt handler previously installed for scsi%d\n",
+           host->host_no, search->host_no);
+    }
+
+    printk ("scsi%d : using %s mapped access\n", host->host_no, 
+       (hostdata->options & OPTION_MEMORY_MAPPED) ? "memory" : 
+        "io");
+
+    hostdata->dmode = (hostdata->chip == 700 || hostdata->chip == 70066) ? 
+       DMODE_REG_00 : DMODE_REG_10;
+    hostdata->istat = ((hostdata->chip / 100) == 8) ? 
+       ISTAT_REG_800 : ISTAT_REG_700;
+
+/* 
+ * XXX - the NCR53c700 uses bitfielded registers for SCID, SDID, etc,
+ *     as does the 710 with one bit per SCSI ID.  Conversly, the NCR
+ *     uses a normal, 3 bit binary representation of these values.
+ *
+ * Get the rest of the NCR documentation, and FIND OUT where the change
+ * was.
+ */
+#if 0
+    tmp = hostdata->this_id_mask = NCR53c7x0_read8(SCID_REG);
+    for (host->this_id = 0; tmp != 1; tmp >>=1, ++host->this_id);
+#else
+    host->this_id = NCR53c7x0_read8(SCID_REG) & 7;
+    hostdata->this_id_mask = 1 << host->this_id;
+#endif
+
+    printk("scsi%d : using initiator ID %d\n", host->host_no,
+       host->this_id);
+
+    /*
+     * Save important registers to allow a soft reset.
+     */
+
+    if ((hostdata->chip / 100) == 8) {
+    /* 
+     * CTEST4 controls burst mode disable.
+     */
+       hostdata->saved_ctest4 = NCR53c7x0_read8(CTEST4_REG_800) & 
+           CTEST4_800_SAVE;
+    } else {
+    /*
+     * CTEST7 controls cache snooping, burst mode, and support for 
+     * external differential drivers.
+     */
+       hostdata->saved_ctest7 = NCR53c7x0_read8(CTEST7_REG) & CTEST7_SAVE;
+    }
+
+    /*
+     * On NCR53c700 series chips, DCNTL controls the SCSI clock dvisior,
+     * on 800 series chips, it allows for a totem-pole IRQ driver.
+     */
+    hostdata->saved_dcntl = NCR53c7x0_read8(DCNTL_REG);
+    
+    if ((hostdata->chip / 100) == 8)
+       printk ("scsi%d : using %s interrupts.\n", host->host_no,
+           (hostdata->saved_dcntl & DCNTL_800_IRQM) ? "level active" : 
+           "edge triggered");
+
+    /*
+     * DMODE controls DMA burst length, and on 700 series chips,
+     * 286 mode and bus width  
+     */
+    hostdata->saved_dmode = NCR53c7x0_read8(hostdata->dmode);
+
+    /* 
+     * Now that burst length and enabled/disabled status is known, 
+     * clue the user in on it.
+     */
+   
+    if ((hostdata->chip / 100) == 8) {
+       if (hostdata->saved_ctest4 & CTEST4_800_BDIS) {
+           printk ("scsi%d : burst mode disabled\n", host->host_no);
+       } else {
+           switch (hostdata->saved_dmode & DMODE_BL_MASK) {
+           case DMODE_BL_2: i = 2; break;
+           case DMODE_BL_4: i = 4; break;
+           case DMODE_BL_8: i = 8; break;
+           case DMODE_BL_16: i = 16; break;
+           }
+           printk ("scsi%d ; burst length %d\n", host->host_no, i);
+       }
+    }
+
+    /*
+     * On NCR53c810 and NCR53c820 chips, SCNTL3 contails the synchronous
+     * and normal clock conversion factors.
+     */
+    if (hostdata->chip / 100 == 8)  {
+       hostdata->saved_scntl3 = NCR53c7x0_read8(SCNTL3_REG_800);
+       ccf = hostdata->saved_scntl3 & SCNTL3_800_CCF_MASK;
+    } else
+       ccf = 0;
+
+    /*
+     * If we don't have a SCSI clock programmed, pick one on the upper
+     * bound of that allowed by NCR so that our transfers err on the 
+     * slow side, since transfer period must be >= the agreed 
+     * appon period.
+     */
+
+    if (!hostdata->scsi_clock) 
+       switch(ccf) {
+       case 1: hostdata->scsi_clock = 25000000; break; /* Divide by 1.0 */
+       case 2: hostdata->scsi_clock = 37500000; break; /* Divide by 1.5 */
+       case 3: hostdata->scsi_clock = 50000000; break; /* Divide by 2.0 */
+       case 0:                                         /* Divide by 3.0 */
+       case 4: hostdata->scsi_clock = 66000000; break; 
+       default: 
+           printk ("scsi%d : clock conversion factor %d unknown.\n"
+                   "         synchronous transfers disabled\n",
+                   host->host_no, ccf);
+           hostdata->options &= ~OPTION_SYNCHRONOUS;
+           hostdata->scsi_clock = 0; 
+       }
+
+    printk ("scsi%d : using %dMHz SCSI clock\n", host->host_no, 
+       hostdata->scsi_clock / 1000000);
+    /*
+     * Initialize per-target structures, including busy flags and 
+     * synchronous transfer parameters.
+     */
+
+    for (i = 0; i < 8; ++i) {
+       for (j = 0; j < 8; ++j)
+           hostdata->busy[i][j] = 0;
+       /* 
+        * NCR53c700 and NCR53c700-66 chips lack the DSA and use a 
+        * different architecture.  For chips using the DSA architecutre,
+        * initialize the per-target synchronous parameters. 
+        */
+       if (hostdata->chip != 700 && hostdata->chip != 70066) {
+           hostdata->sync[i].select_indirect |= (i << 16); 
+           /* XXX - program SCSI script for immediate return */ 
+           hostdata->sync[i].script[0] = (DCMD_TYPE_TCI|DCMD_TCI_OP_RETURN) << 24 | 
+               DBC_TCI_TRUE;
+           switch (hostdata->chip) {
+           /* Clock divisor */
+           case 825:
+           case 820:
+               /* Fall through to 810 */
+           case 815:
+           case 810:
+               hostdata->sync[i].select_indirect |= (hostdata->saved_scntl3) << 24;
+               break;
+           default:
+           }
+       }
+    }
+
+    hostdata->issue_queue = hostdata->running_list = 
+       hostdata->finished_queue = NULL;
+    hostdata->issue_dsa_head = 
+       hostdata->issue_dsa_tail = NULL;
+
+    if (hostdata->init_save_regs)
+       hostdata->init_save_regs (host);
+    if (hostdata->init_fixup)
+       hostdata->init_fixup (host);
+
+    if (!the_template) {
+       the_template = host->hostt;
+       first_host = host;
+    }
+
+    hostdata->idle = 1;
+
+    /* 
+     * Linux SCSI drivers have always been plagued with initialization 
+     * problems - some didn't work with the BIOS disabled since they expected
+     * initialization from it, some didn't work when the networking code
+     * was enabled and registers got scrambled, etc.
+     *
+     * To avoid problems like this, in the future, we will do a soft 
+     * reset on the SCSI chip, taking it back to a sane state.
+     */
+
+    hostdata->soft_reset (host);
+
+    hostdata->debug_count_limit = -1;
+    hostdata->intrs = -1;
+    hostdata->expecting_iid = 0;
+    hostdata->expecting_sto = 0;
+
+    if ((hostdata->run_tests && hostdata->run_tests(host) == -1) ||
+        (hostdata->options & OPTION_DEBUG_TESTS_ONLY)) {
+       /* XXX Should disable interrupts, etc. here */
+       scsi_unregister (host);
+       return -1;
+    } else 
+       return 0;
+}
+
+/* 
+ * Function : static int normal_init(Scsi_Host_Template *tpnt, int board, 
+ *     int chip, int base, int io_port, int irq, int dma, int pcivalid,
+ *     unsigned char pci_bus, unsigned char pci_device_fn,
+ *     int options);
+ *
+ * Purpose : initializes a NCR53c7,8x0 based on base addresses,
+ *     IRQ, and DMA channel.   
+ *     
+ *     Useful where a new NCR chip is backwards compatable with
+ *     a supported chip, but the DEVICE ID has changed so it 
+ *     doesn't show up when the autoprobe does a pcibios_find_device.
+ *
+ * Inputs : tpnt - Template for this SCSI adapter, board - board level
+ *     product, chip - 810, 820, or 825, bus - PCI bus, device_fn -
+ *     device and function encoding as used by PCI BIOS calls.
+ * 
+ * Returns : 0 on success, -1 on failure.
+ *
+ */
+
+static int normal_init (Scsi_Host_Template *tpnt, int board, int chip, 
+    int base, int io_port, int irq, int dma, int pci_valid, 
+    unsigned char pci_bus, unsigned char pci_device_fn, int options) {
+    struct Scsi_Host *instance;
+    struct NCR53c7x0_hostdata *hostdata;
+    char chip_str[80];
+    int script_len = 0, size = 0;
+    int ok = 0;
+
+    
+    options |= PERM_OPTIONS;
+
+    switch (chip) {
+    case 825:
+    case 820:
+    case 815:
+    case 810:
+       script_len = NCR53c8xx_script_len;
+       options |= OPTION_INTFLY;
+       sprintf (chip_str, "NCR53c%d", chip);
+       break;
+    default:
+       printk("scsi-ncr53c7,8xx : unsupported SCSI chip %d\n", chip);
+       return -1;
+    }
+
+    printk("scsi-ncr53c7,8xx : %s at memory 0x%x, io 0x%x, irq %d",
+       chip_str, base, io_port, irq);
+    if (dma == DMA_NONE)
+       printk("\n");
+    else 
+       printk(", dma %d\n", dma);
+
+    if ((chip / 100 == 8) && !pci_valid) 
+       printk ("scsi-ncr53c7,8xx : for better reliability and performance, please use the\n" 
+               "        PCI override instead.\n"
+               "        Syntax : ncr53c8{10,20,25}=pci,<bus>,<device>,<function>\n"
+               "                 <bus> and <device> are usually 0.\n");
+
+    if (options & OPTION_DEBUG_PROBE_ONLY) {
+       printk ("scsi-ncr53c7,8xx : probe only enabled, aborting initialization\n");
+       return -1;
+    }
+
+    size = sizeof(struct NCR53c7x0_hostdata) + script_len;
+
+    instance = scsi_register (tpnt, size);
+    hostdata = (struct NCR53c7x0_hostdata *) 
+       instance->hostdata;
+    hostdata->size = size;
+    hostdata->script_count = script_len / sizeof(long);
+    hostdata = (struct NCR53c7x0_hostdata *) instance->hostdata;
+    hostdata->board = board;
+    hostdata->chip = chip;
+    if ((hostdata->pci_valid = pci_valid)) {
+       hostdata->pci_bus = pci_bus;
+       hostdata->pci_device_fn = pci_device_fn;
+    }
+
+    /*
+     * Being memory mapped is more desireable, since 
+     *
+     * - Memory accesses may be faster.
+     *
+     * - The destination and source addresse spaces are the same for 
+     *  all instructions, meaning we don't have to twiddle dmode or 
+     *  any other registers.
+     *
+     * So, we try for memory mapped, and if we don't get it,
+     * we go for port mapped, and that failing we tell the user
+     * it can't work.
+     */
+
+    if (base) {
+       instance->base = (unsigned char *) base;
+       /* Check for forced I/O mapping */
+       if (!(options & OPTION_IO_MAPPED)) {
+           options |= OPTION_MEMORY_MAPPED;
+           ok = 1;
+       }
+    } else {
+       options &= ~OPTION_MEMORY_MAPPED;
+    }
+
+    if (io_port) {
+       instance->io_port = io_port;
+       options |= OPTION_IO_MAPPED;
+       ok = 1;
+    } else {
+       options &= ~OPTION_IO_MAPPED;
+    }
+
+    if (!ok) {
+       printk ("scsi%d : not initializing, no I/O or memory mapping known \n",
+           instance->host_no);
+       scsi_unregister (instance);
+       return -1;
+    }
+    instance->irq = irq;
+    instance->dma_channel = dma;
+
+    hostdata->options = options;
+    
+    return NCR53c7x0_init(instance);
+}
+
+
+/* 
+ * Function : static int pci_init(Scsi_Host_Template *tpnt, int board, 
+ *     int chip, int bus, int device_fn, int options)
+ *
+ * Purpose : initializes a NCR53c800 family based on the PCI
+ *     bus, device, and function location of it.  Allows 
+ *     reprogramming of latency timer and determining addresses
+ *     and weather bus mastering, etc. are OK.
+ *     
+ *     Useful where a new NCR chip is backwards compatable with
+ *     a supported chip, but the DEVICE ID has changed so it 
+ *     doesn't show up when the autoprobe does a pcibios_find_device.
+ *
+ * Inputs : tpnt - Template for this SCSI adapter, board - board level
+ *     product, chip - 810, 820, or 825, bus - PCI bus, device_fn -
+ *     device and function encoding as used by PCI BIOS calls.
+ * 
+ * Returns : 0 on success, -1 on failure.
+ *
+ */
+
+static int pci_init (Scsi_Host_Template *tpnt, int board, int chip, 
+    unsigned char bus, unsigned char device_fn, int options) {
+    unsigned short vendor_id, device_id, command;
+    unsigned long base, io_port; 
+    unsigned char irq, revision;
+    int error, expected_chip, expected_id, max_revision, min_revision;
+    int i;
+
+    printk("scsi-ncr53c7,8xx : at PCI bus %d, device %d,  function %d\n",
+       bus, (int) (device_fn & 0xf8) >> 3, 
+       (int) device_fn & 7);
+
+    if (!pcibios_present) {
+       printk("scsi-ncr53c7,8xx : not initializing due to lack of PCI BIOS,\n"
+              "        try using memory, port, irq override instead.\n");
+       return -1;
+    }
+
+    if ((error = pcibios_read_config_word (bus, device_fn, PCI_VENDOR_ID, 
+       &vendor_id)) ||
+       (error = pcibios_read_config_word (bus, device_fn, PCI_DEVICE_ID, 
+           &device_id)) ||
+       (error = pcibios_read_config_word (bus, device_fn, PCI_COMMAND, 
+           &command)) ||
+       (error = pcibios_read_config_dword (bus, device_fn, 
+           PCI_BASE_ADDRESS_0, &io_port)) || 
+       (error = pcibios_read_config_dword (bus, device_fn, 
+           PCI_BASE_ADDRESS_1, &base)) ||
+       (error = pcibios_read_config_byte (bus, device_fn, PCI_CLASS_REVISION,
+           &revision)) ||
+       (error = pcibios_read_config_byte (bus, device_fn, PCI_INTERRUPT_LINE,
+           &irq))) {
+       printk ("scsi-ncr53c7,8xx : error %s not initializing due to error reading configuration space\n"
+               "        perhaps you specied an incorrect PCI bus, device, or function.\n"
+               , pcibios_strerror(error));
+       return -1;
+    }
+
+    /* If any one ever clones the NCR chips, this will have to change */
+
+    if (vendor_id != PCI_VENDOR_ID_NCR) {
+       printk ("scsi-ncr53c7,8xx : not initializing, 0x%04x is not NCR vendor ID\n",
+           (int) vendor_id);
+       return -1;
+    }
+
+
+    /* 
+     * Bit 0 is the address space indicator and must be one for I/O
+     * space mappings, bit 1 is reserved, discard them after checking
+     * that they have the correct value of 1.
+     */
+
+    if (command & PCI_COMMAND_IO) { 
+       if ((io_port & 3) != 1) {
+           printk ("scsi-ncr53c7,8xx : disabling I/O mapping since base address 0 (0x%lx)\n"
+                   "        bits 0..1 indicate a non-IO mapping\n", io_port);
+           io_port = 0;
+       } else
+           io_port &= PCI_BASE_ADDRESS_IO_MASK;
+    }
+
+    if (command & PCI_COMMAND_MEMORY) {
+       if ((base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
+           printk("scsi-ncr53c7,8xx : disabling memory mapping since base address 1\n"
+                  "        contains a non-memory mapping\n");
+           base = 0;
+       } else 
+           base &= PCI_BASE_ADDRESS_MEM_MASK;
+    }
+       
+    if (!io_port && !base) {
+       printk ("scsi-ncr53c7,8xx : not initializing, both I/O and memory mappings disabled\n");
+       return -1;
+    }
+       
+    if (!(command & PCI_COMMAND_MASTER)) {
+       printk ("scsi-ncr53c7,8xx : not initializing, BUS MASTERING was disabled\n");
+       return -1;
+    }
+
+    for (i = 0; i < NPCI_CHIP_IDS; ++i) {
+       if (device_id == pci_chip_ids[i].pci_device_id) {
+           max_revision = pci_chip_ids[i].max_revision;
+           min_revision = pci_chip_ids[i].min_revision;
+           expected_chip = pci_chip_ids[i].chip;
+       }
+       if (chip == pci_chip_ids[i].chip)
+           expected_id = pci_chip_ids[i].pci_device_id;
+    }
+
+    if (chip && device_id != expected_id) 
+       printk ("scsi-ncr53c7,8xx : warning : device id of 0x%04x doesn't\n"
+                "                   match expected 0x%04x\n",
+           (unsigned int) device_id, (unsigned int) expected_id );
+    
+    if (max_revision != -1 && revision > max_revision) 
+       printk ("scsi-ncr53c7,8xx : warning : revision of %d is greater than %d.\n",
+           (int) revision, max_revision);
+    else if (min_revision != -1 && revision < min_revision)
+       printk ("scsi-ncr53c7,8xx : warning : revision of %d is less than %d.\n",
+           (int) revision, min_revision);
+
+    return normal_init (tpnt, board, chip, (int) base, (int) io_port, 
+       (int) irq, DMA_NONE, 1, bus, device_fn, options);
+}
+
+
+/* 
+ * Function : int NCR53c7xx_detect(Scsi_Host_Template *tpnt)
+ *
+ * Purpose : detects and initializes NCR53c7,8x0 SCSI chips
+ *     that were autoprobed, overriden on the LILO command line, 
+ *     or specified at compile time.
+ *
+ * Inputs : tpnt - template for this SCSI adapter
+ * 
+ * Returns : number of host adapters detected
+ *
+ */
+
+int NCR53c7xx_detect(Scsi_Host_Template *tpnt) {
+    short current_chip;
+    int i;
+    int current_override;
+    int count;                 /* Number of boards detected */
+    unsigned char pci_bus, pci_device_fn;
+    static short pci_index=0;  /* Device index to PCI BIOS calls */
+    
+
+    for (current_override = count = 0; current_override < OVERRIDE_LIMIT; 
+        ++current_override) {
+        if (overrides[current_override].pci ? 
+           !pci_init (tpnt, overrides[current_override].board,
+               overrides[current_override].chip,
+               (unsigned char) overrides[current_override].data.pci.bus,
+               (((overrides[current_override].data.pci.device
+               << 3) & 0xf8)|(overrides[current_override].data.pci.function & 
+               7)), overrides[current_override].options):
+           !normal_init (tpnt, overrides[current_override].board, 
+               overrides[current_override].chip, 
+               overrides[current_override].data.normal.base, 
+               overrides[current_override].data.normal.io_port,
+               overrides[current_override].data.normal.irq,
+               overrides[current_override].data.normal.dma,
+               0 /* PCI data invalid */, 0 /* PCI bus place holder */,  
+               0 /* PCI device_function place holder */,
+               overrides[current_override].options)) {
+           ++count;
+       } 
+    }
+
+    if (pcibios_present()) {
+       for (i = 0; i < NPCI_CHIP_IDS; ++i) 
+           for (pci_index = 0;
+               !pcibios_find_device (PCI_VENDOR_ID_NCR, 
+                   pci_chip_ids[i].pci_device_id, pci_index, &pci_bus, 
+                   &pci_device_fn) && 
+               !pci_init (tpnt, BOARD_GENERIC, pci_chip_ids[i].chip, 
+                   pci_bus, pci_device_fn, /* no options */ 0); 
+               ++count, ++pci_index);
+    }
+    return count;
+}
+
+/* NCR53c810 and NCR53c820 script handling code */
+
+#include "53c8xx_d.h"
+static int NCR53c8xx_script_len = sizeof (SCRIPT);
+
+/* 
+ * Function : static void NCR53c8x0_init_fixup (struct Scsi_Host *host)
+ *
+ * Purpose :  copy and fixup the SCSI SCRIPTS(tm) code for this device.
+ *
+ * Inputs : host - pointer to this host adapter's structure
+ *
+ */
+
+static void NCR53c8x0_init_fixup (struct Scsi_Host *host) {
+    NCR53c7x0_local_declare();
+    struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+       host->hostdata;
+    unsigned char tmp;
+    int i, ncr_to_memory, memory_to_ncr, ncr_to_ncr;
+    unsigned long base;
+    NCR53c7x0_local_setup(host);
+
+
+
+    /* XXX - NOTE : this code MUST be made endian aware */
+    /*  Copy code into buffer that was allocated at detection time.  */
+    memcpy ((void *) hostdata->script, (void *) SCRIPT, 
+       sizeof(SCRIPT));
+    /* Fixup labels */
+    for (i = 0; i < PATCHES; ++i) 
+       hostdata->script[LABELPATCHES[i]] += 
+           (unsigned long) hostdata->script;
+
+    /* 
+     * Fixup absolutes set at boot-time.
+     * 
+     * All Absolute variables suffixed with "dsa_" and "int_"
+     * are constants, and need no fixup provided the assembler has done 
+     * it for us (I don't know what the "real" NCR assembler does in 
+     * this case, my assembler does the right magic).
+     */
+
+    /*
+     * Just for the hell of it, preserve the settings of 
+     * Burst Length and Enable Read Line bits from the DMODE 
+     * register.  Make sure SCRIPTS start automagically.
+     */
+
+    tmp = NCR53c7x0_read8(DMODE_REG_10);
+    tmp &= (DMODE_800_ERL | DMODE_BL_MASK);
+
+    if (!(hostdata->options & OPTION_MEMORY_MAPPED)) {
+       base = (long) host->io_port;
+       memory_to_ncr = tmp|DMODE_800_DIOM;
+       ncr_to_memory = tmp|DMODE_800_SIOM;
+       ncr_to_ncr = tmp|DMODE_800_DIOM|DMODE_800_SIOM;
+    } else {
+       base = (long) host->base;
+       ncr_to_ncr = memory_to_ncr = ncr_to_memory = tmp;
+    }
+
+    printk ("scsi%d : m_to_n = 0x%x, n_to_m = 0x%x, n_to_n = 0x%x\n",
+           (int) host->host_no, (int) memory_to_ncr, (int) 
+           ncr_to_memory, ncr_to_ncr);
+
+    patch_abs_32 (hostdata->script, 0, addr_scratch, base + SCRATCHA_REG_800);
+    patch_abs_32 (hostdata->script, 0, addr_sfbr, base + SFBR_REG);
+    patch_abs_32 (hostdata->script, 0, addr_temp, base + TEMP_REG);
+
+    /*
+     * I needed some variables in the script to be accessable to 
+     * both the NCR chip and the host processor. For these variables,
+     * I made the arbitrary decession to store them directly in the 
+     * hostdata structure rather than in the RELATIVE area of the 
+     * SCRIPTS.
+     */
+    
+
+    patch_abs_rwri_data (hostdata->script, 0, dmode_memory_to_memory, tmp);
+    patch_abs_rwri_data (hostdata->script, 0, dmode_memory_to_ncr, memory_to_ncr);
+    patch_abs_rwri_data (hostdata->script, 0, dmode_ncr_to_memory, ncr_to_memory);
+    patch_abs_rwri_data (hostdata->script, 0, dmode_ncr_to_ncr, ncr_to_ncr);
+
+    patch_abs_32 (hostdata->script, 0, issue_dsa_head, (long) &(hostdata->issue_dsa_head));
+    patch_abs_32 (hostdata->script, 0, msg_buf, (long) &(hostdata->msg_buf));
+    patch_abs_32 (hostdata->script, 0, reconnect_dsa_head, (long) &(hostdata->reconnect_dsa_head));
+    patch_abs_32 (hostdata->script, 0, reselected_identify, (long) &(hostdata->reselected_identify));
+    patch_abs_32 (hostdata->script, 0, reselected_tag, (long) &(hostdata->reselected_tag));
+
+    patch_abs_32 (hostdata->script, 0, test_dest, (long) &(hostdata->test_dest));
+    patch_abs_32 (hostdata->script, 0, test_src, (long) &(hostdata->test_source));
+
+
+    /*
+     * Make sure the NCR and Linux code agree on the location of 
+     * certain fields.
+     */
+
+/* 
+ * XXX - for cleanness, E_* fields should be type unsigned long *
+ * and should reflect the _relocated_ addresses.  Change this.
+ */
+    hostdata->E_accept_message = Ent_accept_message;
+    hostdata->E_command_complete = Ent_command_complete;               
+    hostdata->E_debug_break = Ent_debug_break; 
+    hostdata->E_dsa_code_template = Ent_dsa_code_template;
+    hostdata->E_dsa_code_template_end = Ent_dsa_code_template_end;
+    hostdata->E_initiator_abort = Ent_initiator_abort;
+    hostdata->E_msg_in = Ent_msg_in;
+    hostdata->E_other_transfer = Ent_other_transfer;
+    hostdata->E_reject_message = Ent_reject_message;
+    hostdata->E_respond_message = Ent_respond_message;
+    hostdata->E_schedule = Ent_schedule;                       
+    hostdata->E_select = Ent_select;
+    hostdata->E_select_msgout = Ent_select_msgout;
+    hostdata->E_target_abort = Ent_target_abort;
+#ifdef Ent_test_0
+    hostdata->E_test_0 = Ent_test_0;
+#endif
+    hostdata->E_test_1 = Ent_test_1;
+    hostdata->E_test_2 = Ent_test_2;
+#ifdef Ent_test_3
+    hostdata->E_test_3 = Ent_test_3;
+#endif
+
+    hostdata->dsa_cmdout = A_dsa_cmdout;
+    hostdata->dsa_cmnd = A_dsa_cmnd;
+    hostdata->dsa_datain = A_dsa_datain;
+    hostdata->dsa_dataout = A_dsa_dataout;
+    hostdata->dsa_end = A_dsa_end;                     
+    hostdata->dsa_msgin = A_dsa_msgin;
+    hostdata->dsa_msgout = A_dsa_msgout;
+    hostdata->dsa_msgout_other = A_dsa_msgout_other;
+    hostdata->dsa_next = A_dsa_next;
+    hostdata->dsa_select = A_dsa_select;
+    hostdata->dsa_start = Ent_dsa_code_template - Ent_dsa_zero;
+    hostdata->dsa_status = A_dsa_status;
+
+    /* sanity check */
+    if (A_dsa_fields_start != Ent_dsa_code_template_end - 
+       Ent_dsa_zero) 
+       printk("scsi%d : NCR dsa_fields start is %d not %d\n",
+           host->host_no, A_dsa_fields_start, Ent_dsa_code_template_end - 
+           Ent_dsa_zero);
+
+    printk("scsi%d : NCR code relocated to 0x%lx\n", host->host_no,
+       (unsigned long) hostdata->script);
+}
+
+/*
+ * Function : static int NCR53c8xx_run_tests (struct Scsi_Host *host)
+ *
+ * Purpose : run various verification tests on the NCR chip, 
+ *     including interrupt generation, and propper bus mastering
+ *     operation.
+ * 
+ * Inputs : host - a properly initialized Scsi_Host structure
+ *
+ * Preconditions : the NCR chip must be in a halted state.
+ *
+ * Returns : 0 if all tests were successful, -1 on error.
+ * 
+ */
+
+static int NCR53c8xx_run_tests (struct Scsi_Host *host) {
+    NCR53c7x0_local_declare();
+    struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+       host->hostdata;
+    unsigned long timeout, start;
+    int old_level, failed, i;
+    NCR53c7x0_local_setup(host);
+
+    printk("scsi%d : testing\n", host->host_no);
+
+    /* The NCR chip _must_ be idle to run the test scripts */
+
+    old_level = splx(0);
+    if (!hostdata->idle) {
+       printk ("scsi%d : chip not idle, aborting tests\n", host->host_no);
+       splx(old_level);
+       return -1;
+    }
+
+    /* 
+     * Check for functional interrupts, this could work as an
+     * autoprobe routine.
+     */
+
+    if (hostdata->issue_dsa_head) {
+       printk ("scsi%d : hostdata->issue_dsa_head corrupt before test 1\n",
+           host->host_no);
+       hostdata->issue_dsa_head = NULL;
+    }
+       
+    if (hostdata->options & OPTION_DEBUG_TEST1) {
+       hostdata->idle = 0;
+       hostdata->test_running = 1;
+       hostdata->test_completed = -1;
+       hostdata->test_dest = 0;
+       hostdata->test_source = 0xdeadbeef;
+       start = ((unsigned long) hostdata->script) + hostdata->E_test_1;
+       hostdata->state = STATE_RUNNING;
+       printk ("scsi%d : test 1", host->host_no);
+       NCR53c7x0_write32 (DSP_REG, start);
+       printk (" started\n");
+       splx(7);
+
+       timeout = jiffies + 50; /* arbitrary */
+       while ((hostdata->test_completed == -1) && jiffies < timeout);
+
+       failed = 1;
+       if (hostdata->test_completed == -1)
+           printk ("scsi%d : driver test 1 timed out%s\n",host->host_no ,
+               (hostdata->test_dest == 0xdeadbeef) ? 
+                   " due to lost interrupt.\n"
+                   "         Please verify that the correct IRQ is being used for your board,\n"
+                   "         and that the motherboard IRQ jumpering matches the PCI setup on\n"
+                   "         PCI systems.\n"
+                   "         If you are using a NCR53c810 board in a PCI system, you should\n" 
+                   "         also verify that the board is jumpered to use PCI INTA, since\n"
+                   "         most PCI motherboards lack support for INTB, INTC, and INTD.\n"
+                   : "");
+       else if (hostdata->test_completed != 1) 
+           printk ("scsi%d : test 1 bad interrupt value (%ld)\n", host->host_no,
+               hostdata->test_completed);
+       else 
+           failed = (hostdata->test_dest != 0xdeadbeef);
+
+       if (hostdata->test_dest != 0xdeadbeef) {
+           printk ("scsi%d : driver test 1 read 0x%x instead of 0xdeadbeef indicating a\n"
+                    "         probable cache invalidation problem.  Please configure caching\n"
+                   "         as write-through or disabled\n",
+               host->host_no, hostdata->test_dest);
+       }
+
+       if (failed) {
+           printk ("scsi%d : DSP = 0x%lx (script at 0x%lx, start at 0x%lx)\n",
+               host->host_no, (unsigned long) NCR53c7x0_read32(DSP_REG),
+               (unsigned long) hostdata->script, start);
+           printk ("scsi%d : DSPS = 0x%lx\n", host->host_no,
+               (unsigned long) NCR53c7x0_read32(DSPS_REG));
+           splx(old_level);
+           return -1;
+       }
+       hostdata->test_running = 0;
+    }
+
+    if (hostdata->issue_dsa_head) {
+       printk ("scsi%d : hostdata->issue_dsa_head corrupt after test 1\n",
+           host->host_no);
+       hostdata->issue_dsa_head = NULL;
+    }
+
+    if (hostdata->options & OPTION_DEBUG_TEST2) {
+       unsigned long dsa[48];
+       unsigned char identify = IDENTIFY(0, 0);
+       unsigned char cmd[6];
+       unsigned char data[36];
+       unsigned char status = 0xff;
+       unsigned char msg = 0xff;
+
+       cmd[0] = INQUIRY;
+       cmd[1] = cmd[2] = cmd[3] = cmd[5] = 0;
+       cmd[4] = sizeof(data); 
+
+       dsa[2] = 1;
+       dsa[3] = (unsigned long) &identify;
+       dsa[4] = 6;
+       dsa[5] = (unsigned long) &cmd;
+       dsa[6] = sizeof(data);
+       dsa[7] = (unsigned long) &data;
+       dsa[8] = 1;
+       dsa[9] = (unsigned long) &status;
+       dsa[10] = 1;
+       dsa[11] = (unsigned long) &msg;
+
+       for (i = 0; i < 3; ++i) {
+           splx(0);
+           if (!hostdata->idle) {
+               printk ("scsi%d : chip not idle, aborting tests\n", host->host_no);
+               splx(old_level);
+               return -1;
+           }
+
+           /*       SCNTL3         SDID        */
+           dsa[0] = (0x33 << 24) | (i << 16)  ;
+           hostdata->idle = 0;
+           hostdata->test_running = 2;
+           hostdata->test_completed = -1;
+           start = ((unsigned long) hostdata->script) + hostdata->E_test_2;
+           hostdata->state = STATE_RUNNING;
+           NCR53c7x0_write32 (DSA_REG, (unsigned long) dsa);
+           NCR53c7x0_write32 (DSP_REG, start);
+           splx(7);
+
+           timeout = jiffies + 500;    /* arbitrary */
+           while ((hostdata->test_completed == -1) && jiffies < timeout);
+           NCR53c7x0_write32 (DSA_REG, 0);
+
+           if (hostdata->test_completed == 2) {
+               data[35] = 0;
+               printk ("scsi%d : test 2 INQUIRY to target %d, lun 0 : %s\n",
+                   host->host_no, i, data + 8);
+               printk ("scsi%d : status ", host->host_no);
+               print_status (status);
+               printk ("\nscsi%d : message ", host->host_no);
+               print_msg (&msg);
+               printk ("\n");
+           } else if (hostdata->test_completed == 3) {
+               printk("scsi%d : test 2 no connection with target %d\n",
+                   host->host_no, i);
+               if (!hostdata->idle) {
+                   printk("scsi%d : not idle\n", host->host_no);
+                   splx(old_level);
+                   return -1;
+               }
+           } else if (hostdata->test_completed == -1) {
+               printk ("scsi%d : test 2 timed out\n", host->host_no);
+               splx(old_level);
+               return -1;
+           } 
+           hostdata->test_running = 0;
+           if (hostdata->issue_dsa_head) {
+               printk ("scsi%d : hostdata->issue_dsa_head corrupt after test 2 id %d\n",
+                   host->host_no, i);
+               hostdata->issue_dsa_head = NULL;
+       }
+       }
+    }
+    printk ("scsi%d : tests complete.\n", host->host_no);
+
+    splx(old_level);
+    return 0;
+}
+
+/*
+ * Function : static void NCR53c8xx_dsa_fixup (struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : copy the NCR53c8xx dsa structure into cmd's dsa buffer,
+ *     performing all necessary relocation.
+ *
+ * Inputs : cmd, a NCR53c7x0_cmd structure with a dsa area large
+ *     enough to hold the NCR53c8xx dsa.
+ */
+
+static void NCR53c8xx_dsa_fixup (struct NCR53c7x0_cmd *cmd) {
+    Scsi_Cmnd *c = cmd->cmd;
+    struct Scsi_Host *host = c->host;
+    struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+       host->hostdata;
+    int i;
+
+    memcpy (cmd->dsa, hostdata->script + (hostdata->E_dsa_code_template / 4),
+       hostdata->E_dsa_code_template_end - hostdata->E_dsa_code_template);
+
+    patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(long), 
+       dsa_temp_jump_resume, ((unsigned long) cmd->dsa) + 
+       Ent_dsa_jump_resume - Ent_dsa_zero);
+    patch_abs_rwri_data (cmd->dsa, Ent_dsa_code_template / sizeof(long),
+       dsa_temp_lun, c->lun);
+    patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(long),
+       dsa_temp_dsa_next, ((unsigned long) cmd->dsa) + A_dsa_next);
+    patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(long), 
+       dsa_temp_sync, hostdata->sync[c->target].select_indirect);
+    patch_abs_rwri_data (cmd->dsa, Ent_dsa_code_template / sizeof(long),
+       dsa_temp_target, c->target);
+}
+
+/*
+ * Function : static void abnormal_finished (struct NCR53c7x0_cmd *cmd, int
+ *     result)
+ *
+ * Purpose : mark SCSI command as finished, OR'ing the host portion 
+ *     of the result word into the result field of the corresponding
+ *     Scsi_Cmnd structure, and removing it from the internal queues.
+ *
+ * Inputs : cmd - command, result - entire result field
+ *
+ * Preconditions : the         NCR chip should be in a halted state when 
+ *     abnormal_finished is run, since it modifies structures which
+ *     the NCR expects to have exclusive access to.
+ */
+
+static void abnormal_finished (struct NCR53c7x0_cmd *cmd, int result) {
+    Scsi_Cmnd *c = cmd->cmd;
+    struct Scsi_Host *host = c->host;
+    struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+       host->hostdata;
+    int old_level;
+    char **prev, *search;
+    int i;
+
+    old_level = splx(0);
+    for (i = 0; i < 2; ++i) {
+       for (search = (char *) (i ? hostdata->issue_dsa_head : 
+               hostdata->reconnect_dsa_head), prev = (char **) (i ? 
+               &(hostdata->issue_dsa_head) : &(hostdata->reconnect_dsa_head));
+            search && (search + hostdata->dsa_start) != (char *) cmd->dsa;
+            prev = (char **) (search + hostdata->dsa_next), 
+               search = *prev);
+
+       if (search)
+           *prev = *(char **) (search + hostdata->dsa_next);
+    }
+
+    if (cmd->prev)
+       cmd->prev->next = cmd->next;
+
+    if (cmd->next)
+       cmd->next->prev = cmd->prev;
+
+    if (hostdata->running_list == cmd)
+       hostdata->running_list = cmd->next;
+
+    if (!scan_scsis_buf_busy) {
+#ifdef SCSI_MALLOC
+       scsi_free ((void *) cmd->real, cmd->size);
+#else
+       kfree_s (cmd->real, cmd->size);
+#endif
+    } else {
+       scan_scsis_buf_busy = 0;
+    }
+
+    c->host_scribble = NULL;
+    c->result = result;
+    c->scsi_done(c);
+
+    splx(old_level);
+}
+
+/* 
+ * Function : static void intr_break (struct Scsi_Host *host,
+ *     struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose :  Handler for breakpoint interrutps from a SCSI script
+ *
+ * Inputs : host - pointer to this host adapter's structure,
+ *     cmd - pointer to the command (if any) dsa was pointing 
+ *     to.
+ *
+ */
+
+static void intr_break (struct Scsi_Host *host, struct 
+    NCR53c7x0_cmd *cmd) {
+    NCR53c7x0_local_declare();
+    struct NCR53c7x0_break *bp;
+#if 0
+    Scsi_Cmnd *c = cmd ? cmd->cmd : NULL;
+#endif
+    unsigned long *dsp;
+    struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+       host->hostdata;         
+    int old_level;
+    NCR53c7x0_local_setup(host);
+
+    /*
+     * Find the break point corresponding to this address, and 
+     * dump the appropriate debugging information to standard 
+     * output.  
+     */
+
+    old_level = splx(0);
+    dsp = (unsigned long *) NCR53c7x0_read32(DSP_REG);
+    for (bp = hostdata->breakpoints; bp && bp->address != dsp; 
+       bp = bp->next);
+    if (!bp) 
+       panic("scsi%d : break point interrupt from %p with no breakpoint!",
+           host->host_no, dsp);
+
+    /*
+     * Configure the NCR chip for manual start mode, so that we can 
+     * point the DSP register at the instruction that follows the 
+     * INT int_debug_break instruction.
+     */
+
+    NCR53c7x0_write8 (hostdata->dmode, 
+       NCR53c7x0_read8(hostdata->dmode)|DMODE_MAN);
+
+    /*
+     * And update the DSP register, using the size of the old 
+     * instruction in bytes.
+     */
+
+    splx(old_level);
+}
+
+/*
+ * Function : static int asynchronous (struct Scsi_Host *host, int target)
+ *
+ * Purpose : reprogram between the selected SCSI Host adapter and target 
+ *      (assumed to be currently connected) for asynchronous transfers.
+ *
+ * Inputs : host - SCSI host structure, target - numeric target ID.
+ *
+ * Preconditions : the NCR chip should be in one of the halted states
+ */
+    
+static int asynchronous (struct Scsi_Host *host, int target) {
+    NCR53c7x0_local_declare();
+    struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+       host->hostdata;
+    NCR53c7x0_local_setup(host);
+
+    if ((hostdata->chip / 100) == 8) {
+       hostdata->sync[target].select_indirect = (hostdata->saved_scntl3 << 24)
+           | (target << 16);
+/* Fill in script here */
+    } else if ((hostdata->chip != 700) && (hostdata->chip != 70066)) {
+       hostdata->sync[target].select_indirect = (1 << (target & 7)) << 16;
+    }
+
+/* 
+ * Halted implies connected, when resetting we shouldn't change the 
+ * current parameters but must reset all targets to asynchronous.
+ */
+
+    if (hostdata->state == STATE_HALTED) {
+       if ((hostdata->chip / 100) == 8) {
+           NCR53c7x0_write8 (SCNTL3_REG_800, hostdata->saved_scntl3);
+       }
+    /* Offset = 0, transfer period = divide SCLK by 4 */
+       NCR53c7x0_write8 (SXFER_REG, 0);
+    }
+    return 0;
+}
+
+/* 
+ * XXX - do we want to go out of our way (ie, add extra code to selection
+ *     in the NCR53c710/NCR53c720 script) to reprogram the synchronous
+ *     conversion bits, or can we be content in just setting the 
+ *     sxfer bits?
+ */
+
+/* Table for NCR53c8xx synchronous values */
+static const struct {
+    int div;
+    unsigned char scf;
+    unsigned char tp;
+} syncs[] = {
+/*     div     scf     tp      div     scf     tp      div     scf     tp */
+    {  40,     1,      0}, {   50,     1,      1}, {   60,     1,      2}, 
+    {  70,     1,      3}, {   75,     2,      1}, {   80,     1,      4},
+    {  90,     1,      5}, {   100,    1,      6}, {   105,    2,      3},
+    {  110,    1,      7}, {   120,    2,      4}, {   135,    2,      5},
+    {  140,    3,      3}, {   150,    2,      6}, {   160,    3,      4},
+    {  165,    2,      7}, {   180,    3,      5}, {   200,    3,      6},
+    {  210,    4,      3}, {   220,    3,      7}, {   240,    4,      4},
+    {  270,    4,      5}, {   300,    4,      6}, {   330,    4,      7}
+};
+
+/*
+ * Function : static void synchronous (struct Scsi_Host *host, int target, 
+ *     char *msg)
+ *
+ * Purpose : reprogram transfers between the selected SCSI initiator and 
+ *     target for synchronous SCSI transfers such that the synchronous 
+ *     offset is less than that requested and period at least as long 
+ *     as that requestion.  Also modify *msg such that it contains 
+ *     an appropriate response. 
+ *
+ * Inputs : host - NCR53c7,8xx SCSI host, target - number SCSI target id,
+ *     msg - synchronous tranfer request.
+ */
+
+
+static void synchronous (struct Scsi_Host *host, int target, char *msg) {
+    struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+       host->hostdata;
+    int desire, divisor, i, limit;
+    unsigned long *script;
+    unsigned char scntl3, sxfer;
+   
+/* Scale divisor by 10 to accomodate fractions */ 
+    desire = 1000000000L / (msg[3] * 4);
+    divisor = desire / (hostdata->scsi_clock / 10);
+
+    if (msg[4] > 8)
+       msg[4] = 8;
+
+    printk("scsi%d : optimal synchronous divisor of %d.%01d\n", host->host_no,
+       divisor / 10, divisor % 10);
+
+    limit = (sizeof(syncs) / sizeof(syncs[0])) - 1;
+    for (i = 0; (i < limit) && (divisor < syncs[i + 1].div); ++i);
+
+    printk("scsi%d : selected synchronous divisor of %d.%01d\n", host->host_no,
+       syncs[i].div / 10, syncs[i].div % 10);
+
+    msg[3] = (1000000000 / divisor / 10 / 4);
+
+    scntl3 = (hostdata->chip / 100 == 8) ? ((hostdata->saved_scntl3 & 
+       ~SCNTL3_800_SCF_MASK) | (syncs[i].scf << SCNTL3_800_SCF_SHIFT)) : 0;
+    sxfer = (msg[4] << SXFER_MO_SHIFT) | ((syncs[i].tp) << SXFER_TP_SHIFT);
+
+    if ((hostdata->chip != 700) && (hostdata->chip != 70066)) {
+       hostdata->sync[target].select_indirect = (scntl3 << 24) | (target << 16) | 
+               (sxfer << 8);
+
+       script = hostdata->sync[target].script;
+
+       /* XXX - add NCR53c7x0 code to reprogram SCF bits if we want to */
+       if ((hostdata->chip / 100) == 8) {
+           script[0] = ((DCMD_TYPE_RWRI | DCMD_RWRI_OPC_MODIFY |
+               DCMD_RWRI_OP_MOVE) << 24) |
+               (SCNTL3_REG_800 << 16) | (scntl3 << 8);
+           script[1] = 0;
+           script += 2;
+       }
+
+       script[0] = ((DCMD_TYPE_RWRI | DCMD_RWRI_OPC_MODIFY |
+           DCMD_RWRI_OP_MOVE) << 24) |
+               (SXFER_REG << 16) | (sxfer << 8);
+       script[1] = 0;
+       script += 2;
+
+       script[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_RETURN) << 24) | DBC_TCI_TRUE;
+       script[1] = 0;
+       script += 2;
+    }
+}
+
+/* 
+ * Function : static int NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host,
+ *     struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose :  Handler for INT generated instructions for the 
+ *     NCR53c810/820 SCSI SCRIPT
+ *
+ * Inputs : host - pointer to this host adapter's structure,
+ *     cmd - pointer to the command (if any) dsa was pointing 
+ *     to.
+ *
+ */
+
+static int NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct 
+    NCR53c7x0_cmd *cmd) {
+    NCR53c7x0_local_declare();
+    Scsi_Cmnd *c = cmd ? cmd->cmd : NULL;
+    struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+       host->hostdata;         
+    unsigned long dsps,*dsp;   /* Argument of the INT instruction */
+    NCR53c7x0_local_setup(host);
+    dsps = NCR53c7x0_read32(DSPS_REG);
+    dsp = (unsigned long *) NCR53c7x0_read32(DSP_REG);
+
+    if (hostdata->options & OPTION_DEBUG_INTR) 
+       printk ("scsi%d : DSPS = 0x%lx\n", host->host_no, dsps);
+
+    switch (dsps) {
+    case A_int_msg_1:
+       printk ("scsi%d : message", host->host_no);
+       if (cmd) 
+           printk (" from target %d lun %d", c->target, c->lun);
+       print_msg (hostdata->msg_buf);
+       printk("\n");
+       switch (hostdata->msg_buf[0]) {
+       /* 
+        * Unless we've initiated synchronous negotiation, I don't
+        * think that this should happen.
+        */
+       case MESSAGE_REJECT:
+           hostdata->dsp = hostdata->script + hostdata->E_accept_message /
+               sizeof(long);
+           hostdata->dsp_changed = 1;
+           break;
+       case INITIATE_RECOVERY:
+           printk ("scsi%d : extended contingent allegience not supported yet, rejecting\n",
+               host->host_no);
+           hostdata->dsp = hostdata->script + hostdata->E_reject_message /
+               sizeof(long);
+           hostdata->dsp_changed = 1;
+       }
+       return SPECIFIC_INT_NOTHING;
+    case A_int_msg_sdtr:
+       if (cmd) {
+           printk ("scsi%d : target %d %s synchronous transfer period %dns, offset%d\n",
+               host->host_no, c->target, (cmd->flags & CMD_FLAG_SDTR) ? "accepting" :
+               "requesting", hostdata->msg_buf[3] * 4, hostdata->msg_buf[4]);
+       /* 
+        * Initiator initiated, won't happen unless synchronous 
+        *      transfers are enabled.  If we get a SDTR message in
+        *      response to our SDTR, we should program our parameters
+        *      such that 
+        *              offset <= requested offset
+        *              period >= requested period                      
+        */
+           if (cmd->flags & CMD_FLAG_SDTR) {
+               cmd->flags &= ~CMD_FLAG_SDTR; 
+               synchronous (host, c->target, hostdata->msg_buf);
+               hostdata->dsp = hostdata->script + hostdata->E_accept_message /
+                   sizeof(long);
+               hostdata->dsp_changed = 1;
+               return SPECIFIC_INT_NOTHING;
+           } else {
+               if (hostdata->options & OPTION_SYNCHRONOUS)  {
+                   cmd->flags |= CMD_FLAG_DID_SDTR;
+                   synchronous (host, c->target, hostdata->msg_buf);
+               } else {
+                   hostdata->msg_buf[4] = 0;           /* 0 offset = async */
+               }
+
+               patch_dsa_32 (cmd->dsa, dsa_msgout_other, 0, 5);
+               patch_dsa_32 (cmd->dsa, dsa_msgout_other, 1, 
+                   hostdata->msg_buf);
+               hostdata->dsp = hostdata->script + 
+               hostdata->E_respond_message / sizeof(long);
+               hostdata->dsp_changed = 1;
+           }
+
+           if (hostdata->msg_buf[4]) {
+               int Hz = 1000000000 / (hostdata->msg_buf[3] * 4);
+               printk ("scsi%d : setting target %d to %d.%02dMhz %s SCSI%s\n"
+                       "         period = %dns, max offset = %d\n",
+                       host->host_no, c->target, Hz / 1000000, Hz % 1000000,
+                       ((hostdata->msg_buf[3] < 200) ? "FAST " : 
+                       "synchronous") ,
+                       ((hostdata->msg_buf[3] < 200) ? "-II" : ""),
+                       (int) hostdata->msg_buf[3] * 4, (int) 
+                       hostdata->msg_buf[4]);
+           } else {
+               printk ("scsi%d : setting target %d to asynchronous SCSI\n",
+                   host->host_no, c->target);
+           }
+           return SPECIFIC_INT_NOTHING;
+       }
+       /* Fall through to abort */
+    case A_int_msg_wdtr:
+       hostdata->dsp = hostdata->script + hostdata->E_reject_message /
+           sizeof(long);
+       hostdata->dsp_changed = 1;
+       return SPECIFIC_INT_NOTHING;
+    case A_int_err_unexpected_phase:
+       if (hostdata->options & OPTION_DEBUG_INTR) 
+           printk ("scsi%d : unexpected phase\n", host->host_no);
+       return SPECIFIC_INT_ABORT;
+    case A_int_err_selected:
+       printk ("scsi%d : selected by target %d\n", host->host_no,
+           (int) NCR53c7x0_read8(SSID_REG_800) &7);
+       hostdata->dsp = hostdata->script + hostdata->E_target_abort / 
+           sizeof(long);
+       hostdata->dsp_changed = 1;
+       return SPECIFIC_INT_NOTHING;
+    case A_int_err_unexpected_reselect:
+       printk ("scsi%d : unexpected reselect by target %d\n", host->host_no,
+           (int) NCR53c7x0_read8(SSID_REG_800));
+       hostdata->dsp = hostdata->script + hostdata->E_initiator_abort /
+           sizeof(long);
+       hostdata->dsp_changed = 1;
+       return SPECIFIC_INT_NOTHING;
+/*
+ * Since contingent allegience conditions are cleared by the next 
+ * command issued to a target, we must issue a REQUEST SENSE 
+ * command after receiving a CHECK CONDITION status, before
+ * another command is issued.
+ * 
+ * Since this NCR53c7x0_cmd will be freed after use, we don't 
+ * care if we step on the various fields, so modify a few things.
+ */
+    case A_int_err_check_condition: 
+#if 0
+       if (hostdata->options & OPTION_DEBUG_INTR) 
+#endif
+           printk ("scsi%d : CHECK CONDITION\n", host->host_no);
+       if (!c) {
+           printk("scsi%d : CHECK CONDITION with no SCSI command\n",
+               host->host_no);
+           return SPECIFIC_INT_PANIC;
+       }
+
+/*
+ * When a contingent allegience condition is created, the target 
+ * reverts to asynchronous transfers.
+ */
+
+       asynchronous (host, c->target);
+
+       /* 
+        * Use normal one-byte selection message, with no attempts to 
+        * restablish synchronous or wide messages since this may
+        * be the crux of our problem.
+        *
+        * XXX - once SCSI-II tagged queing is implemented, we'll
+        *      have to set this up so that the rest of the DSA
+        *      aggrees with this being an untagged queue'd command.
+        */
+
+       patch_dsa_32 (cmd->dsa, dsa_msgout, 0, 1);
+
+       /* 
+        * Modify the table indirect for COMMAND OUT phase, since 
+        * Request Sense is a six byte command.
+        */
+
+       patch_dsa_32 (cmd->dsa, dsa_cmdout, 0, 6);
+
+       c->cmnd[0] = REQUEST_SENSE;
+       c->cmnd[1] &= 0xe0;     /* Zero all but LUN */
+       c->cmnd[2] = 0;
+       c->cmnd[3] = 0;
+       c->cmnd[4] = sizeof(c->sense_buffer);
+       c->cmnd[5] = 0; 
+
+       /*
+        * Disable dataout phase, and program datain to transfer to the 
+        * sense buffer, and add a jump to other_transfer after the 
+        * command so overflow/underrun conditions are detected.
+        */
+
+       patch_dsa_32 (cmd->dsa, dsa_dataout, 0, hostdata->E_other_transfer);
+       patch_dsa_32 (cmd->dsa, dsa_datain, 0, cmd->data_transfer_start);
+       cmd->data_transfer_start[0] = (((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I | 
+           DCMD_BMI_IO)) << 24) | sizeof(c->sense_buffer);
+       cmd->data_transfer_start[1] = (unsigned long) c->sense_buffer;
+
+       cmd->data_transfer_start[2] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) 
+           << 24) | DBC_TCI_TRUE;
+       cmd->data_transfer_start[3] = hostdata->E_other_transfer;
+
+       /*
+        * Currently, this command is flagged as completed, ie 
+        * it has valid status and message data.  Reflag it as
+        * incomplete.  Q - need to do something so that original
+        * status, etc are uesed.
+        */
+
+        cmd->cmd->result = 0xffff;             
+
+       /* 
+        * Restart command as a REQUEST SENSE.
+        */
+       hostdata->dsp = hostdata->script + hostdata->E_select /
+           sizeof(long);
+       hostdata->dsp_changed = 1;
+       return SPECIFIC_INT_NOTHING;
+    case A_int_debug_break:
+       return SPECIFIC_INT_BREAK;
+    case A_int_norm_aborted:
+       hostdata->dsp = hostdata->script + hostdata->E_schedule / 
+               sizeof(long);
+       hostdata->dsp_changed = 1;
+       if (cmd)
+           abnormal_finished (cmd, DID_ERROR << 16);
+       return SPECIFIC_INT_NOTHING;
+    case A_int_test_1:
+    case A_int_test_2:
+       hostdata->idle = 1;
+       hostdata->test_completed = (dsps - A_int_test_1) / 0x00010000 + 1;
+       if (hostdata->options & OPTION_DEBUG_INTR)
+           printk("scsi%d : test%ld complete\n", host->host_no,
+               hostdata->test_completed);
+       return SPECIFIC_INT_NOTHING;
+#ifdef A_int_debug_scheduled
+    case A_int_debug_scheduled:
+       if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+           printk("scsi%d : new I/O 0x%lx scheduled\n", host->host_no,
+               NCR53c7x0_read32(DSA_REG));
+       }
+       return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_idle
+    case A_int_debug_idle:
+       if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+           printk("scsi%d : idle\n", host->host_no);
+       }
+       return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_cmd
+    case A_int_debug_cmd:
+       if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+           printk("scsi%d : command sent\n");
+       }
+    return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_dsa_loaded
+    case A_int_debug_dsa_loaded:
+       if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+           printk("scsi%d : DSA loaded with 0x%lx\n", host->host_no,
+               NCR53c7x0_read32(DSA_REG));
+       }
+       return SPECIFIC_INT_RESTART; 
+#endif
+#ifdef A_int_debug_reselected
+    case A_int_debug_reselected:
+       if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+           printk("scsi%d : reselected by target %d lun %d\n",
+               host->host_no, (int) NCR53c7x0_read8(SSID_REG_800), 
+               (int) hostdata->reselected_identify & 7);
+       }
+    return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_head
+    case A_int_debug_head:
+       if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+           printk("scsi%d : issue_dsa_head now 0x%lx\n",
+               host->host_no, (unsigned long) hostdata->issue_dsa_head);
+       }
+    return SPECIFIC_INT_RESTART;
+#endif
+    default:
+       if ((dsps & 0xff000000) == 0x03000000) {
+            printk ("scsi%d : misc debug interrupt 0x%lx\n",
+               host->host_no, dsps);
+           return SPECIFIC_INT_RESTART;
+       }
+
+       printk ("scsi%d : unknown user interrupt 0x%x\n", 
+           host->host_no, (unsigned) dsps);
+       return SPECIFIC_INT_PANIC;
+    }
+}
+
+/* 
+ * XXX - the stock NCR assembler won't output the scriptu.h file,
+ * which undefine's all #define'd CPP symbols from the script.h
+ * file, which will create problems if you use multiple scripts
+ * with the same  symbol names.
+ *
+ * If you insist on using NCR's assembler, you could generate
+ * scriptu.h from script.h using something like 
+ *
+ * grep #define script.h | \
+ * sed 's/#define[     ][      ]*\([_a-zA-Z][_a-zA-Z0-9]*\).*$/#undefine \1/' \
+ * > scriptu.h
+ */
+
+#include "53c8xx_u.h"
+
+/* XXX - add alternate script handling code here */
+
+
+#ifdef NCR_DEBUG
+/*
+ * Debugging without a debugger is no fun. So, I've provided 
+ * a debugging interface in the NCR53c7x0 driver.  To avoid
+ * kernel cruft, there's just enough here to act as an interface
+ * to a user level debugger (aka, GDB).
+ *
+ *
+ * The following restrictions apply to debugger commands : 
+ * 1.  The command must be terminated by a newline.
+ * 2.  Command length must be less than 80 bytes including the 
+ *     newline.
+ * 3.  The entire command must be written with one system call.
+ */
+
+static const char debugger_help = 
+"bc <addr>                     - clear breakpoint\n"
+"bl                            - list breakpoints\n"
+"bs <addr>                     - set breakpoint\n"
+"g                             - start\n"                              
+"h                             - halt\n"
+"?                             - this message\n"
+"i                             - info\n"
+"mp <addr> <size>              - print memory\n"
+"ms <addr> <size> <value>      - store memory\n"
+"rp <num> <size>               - print register\n"
+"rs <num> <size> <value>       - store register\n"
+"s                             - single step\n"        
+"tb                            - begin trace \n"
+"te                            - end trace\n";
+
+/*
+ * Whenever we change a break point, we should probably 
+ * set the NCR up so that it is in a single step mode.
+ */
+
+static int debugger_fn_bc (struct Scsi_Host *host, struct debugger_token *token,
+    unsigned long args[]) {
+    struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+       instance->hostdata;
+    struct NCR53c7x0_break *bp, **prev;
+    int old_level;
+    old_level = splx(0);
+    for (bp = (struct NCR53c7x0_break *) instance->breakpoints,
+           prev = (struct NCR53c7x0_break **) &instance->breakpoints;
+           bp; prev = (struct NCR53c7x0_break **) &(bp->next),
+           bp = (struct NCR53c7x0_break *) bp->next);
+
+    if (!bp) {
+       splx(old_level);
+       return -EIO;
+    }
+
+    /* 
+     * XXX - we need to insure that the processor is halted 
+     * here in order to prevent a race condition.
+     */
+    
+    memcpy ((void *) bp->addr, (void *) bp->old, sizeof(bp->old));
+    if (prev)
+       *prev = bp->next;
+
+    splx(old_level);
+    return 0;
+}
+
+
+static int debugger_fn_bl (struct Scsi_Host *host, struct debugger_token *token,
+    unsigned long args[]) {
+    struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+       host->hostdata;
+    struct NCR53c7x0_break *bp;
+    char buf[80];
+    size_t len;
+    int old_level;
+    /* 
+     * XXX - we need to insure that the processor is halted 
+     * here in order to prevent a race condition.  So, if the 
+     * processor isn't halted, print an error message and continue.
+     */
+
+    sprintf (buf, "scsi%d : bp : warning : processor not halted\b",
+       host->host_no);
+    debugger_kernel_write (host, buf, strlen(buf));
+
+    old_level=splx(0);
+    for (bp = (struct NCR53c7x0_break *) host->breakpoints;
+           bp; bp = (struct NCR53c7x0_break *) bp->next); {
+           sprintf (buf, "scsi%d : bp : success : at %08x, replaces %08x %08x",
+               bp->addr, bp->old[0], bp->old[1]);
+           len = strlen(buf);
+           if ((bp->old[0] & (DCMD_TYPE_MASK << 24)) ==
+               (DCMD_TYPE_MMI << 24)) {
+               sprintf(buf + len, "%08x\n", * (long *) bp->addr);
+           } else {
+               sprintf(buf + len, "\n");
+           }
+           len = strlen(buf);
+           debugger_kernel_write (host, buf, len);
+    }
+    splx(old_level);
+    return 0;
+}
+
+static int debugger_fn_bs (struct Scsi_Host *host, struct debugger_token *token,
+    unsigned long args[]) {
+    struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+       host->hostdata;
+    struct NCR53c7x0_break *bp;
+    char buf[80];
+    size_t len;
+    int old_level;
+    old_level=splx(0);
+
+    if (hostdata->state != STATE_HALTED) {
+       sprintf (buf, "scsi%d : bs : failure : NCR not halted\n", host->host_no);
+       debugger_kernel_write (host, buf, strlen(buf));
+       splx(old_level);
+       return -1;
+    }
+
+    if (!(bp = kmalloc (sizeof (struct NCR53c7x0_break)))) {
+       printk ("scsi%d : kmalloc(%d) of breakpoint structure failed, try again\n",
+           host->host_no, sizeof(struct NCR53c7x0_break));
+       splx(old_level);
+       return -1;
+    }
+
+    bp->address = (unsigned long *) args[0];
+    memcpy ((void *) bp->old_instruction, (void *) bp->address, 8);
+    bp->old_size = (((bp->old_instruction[0] >> 24) & DCMD_TYPE_MASK) ==
+       DCMD_TYPE_MMI ? 3 : 2;
+    bp->next = hostdata->breakpoints;
+    hostdata->breakpoints = bp->next;
+    memcpy ((void *) bp->address, (void *) hostdata->E_debug_break, 8);
+    
+    splx(old_level);
+    return 0;
+}
+
+#define TOKEN(name,nargs) {#name, nargs, debugger_fn_##name}
+static const struct debugger_token {
+    char *name;
+    int numargs;
+    int (*fn)(struct debugger_token *token, unsigned long args[]);
+} debugger_tokens[] = {
+    TOKEN(bc,1), TOKEN(bl,0), TOKEN(bs,1), TOKEN(g,0), TOKEN(halt,0),
+    {DT_help, "?", 0} , TOKEN(h,0), TOKEN(i,0), TOKEN(mp,2), 
+    TOKEN(ms,3), TOKEN(rp,2), TOKEN(rs,2), TOKEN(s,0), TOKEN(tb,0), TOKEN(te,0)
+};
+
+#define NDT sizeof(debugger_tokens / sizeof(struct debugger_token))
+
+static struct Scsi_Host * inode_to_host (struct inode *inode) {$
+    int dev;
+    struct Scsi_Host *tmp;
+    for (dev = MINOR(inode->rdev), host = first_host;
+       (host->hostt == the_template); --dev, host = host->next)
+       if (!dev) return host;
+    return NULL;
+}
+
+
+static debugger_user_write (struct inode *inode,struct file *filp,
+    char *buf,int count) {
+    struct Scsi_Host *host;                    /* This SCSI host */
+    struct NCR53c7x0_hostadata *hostdata;      
+    char input_buf[80],                        /* Kernel space copy of buf */
+       *ptr;                                   /* Pointer to argument list */
+    unsigned long args[3];                     /* Arguments */
+    int i, j, error, len;
+
+    if (!(host = inode_to_host(inode)))
+       return -ENXIO;
+
+    hostdata = (struct NCR53c7x0_hostdata *) host->hostdata;
+
+    if (error = verify_area(VERIFY_READ,buf,count))
+       return error;
+
+    if (count > 80) 
+       return -EIO;
+
+    memcpy_from_fs(input_buf, buf, count);
+
+    if (input_buf[count - 1] != '\n')
+       return -EIO;
+
+    input_buf[count - 1]=0;
+
+    for (i = 0; i < NDT; ++i) {
+       len = strlen (debugger_tokens[i].name);
+       if (!strncmp(input_buf, debugger_tokens[i].name, len)) 
+           break;
+    };
+
+    if (i == NDT) 
+       return -EIO;
+
+    for (ptr = input_buf + len, j = 0; j < debugger_tokens[i].nargs && *ptr;) {
+       if (*ptr == ' ' || *ptr == '\t') {
+           ++ptr; 
+       } else if (isdigit(*ptr)) {
+           args[j++] = simple_strtoul (ptr, &ptr, 0);
+       } else {
+           return -EIO;
+       } 
+    }
+
+    if (j != debugger_tokens[i].nargs)
+       return -EIO;
+
+    return count;
+} 
+
+static debugger_user_read (struct inode *inode,struct file *filp,
+    char *buf,int count) {
+    struct Scsi_Host *instance;
+    
+}
+
+static debugger_kernel_write (struct Scsi_Host *host, char *buf, size_t
+    buflen) {
+    struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+       host->hostdata;
+    int copy, left, old_level;
+    
+    old_level = splx(0);
+    while (buflen) {
+       left = (hostdata->debug_buf + hostdata->debug_size - 1) -
+           hostdata->debug_write;
+       copy = (buflen <= left) ? buflen : left;
+       memcpy (hostdata->debug_write, buf, copy);
+       buf += copy;
+       buflen -= copy;
+       hostdata->debug_count += copy;
+       if ((hostdata->debug_write += copy) == 
+           (hostdata->debug_buf + hostdata->debug_size))
+           hosdata->debug_write = hostdata->debug_buf;
+    }
+    (void) splx(old_level);
+}
+
+#endif /* def NCRDEBUG */
+
+/* 
+ * Function : static void NCR538xx_soft_reset (struct Scsi_Host *host)
+ *
+ * Purpose :  perform a soft reset of the NCR53c8xx chip
+ *
+ * Inputs : host - pointer to this host adapter's structure
+ *
+ * Preconditions : NCR53c7x0_init must have been called for this 
+ *      host.
+ * 
+ */
+
+static void NCR53c8x0_soft_reset (struct Scsi_Host *host) {
+    NCR53c7x0_local_declare();
+    struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+       host->hostdata;
+    NCR53c7x0_local_setup(host);
+
+
+    /*
+     * Do a soft reset of the chip so that everything is 
+     * reinitialized to the power-on state.
+     *
+     * Basically follow the procedure outlined in the NCR53c700
+     * data manual under Chapter Six, How to Use, Steps Necessary to
+     * Start SCRIPTS, with the exception of actually starting the 
+     * script and setting up the synchronous transfer gunk.
+     */
+
+    NCR53c7x0_write8(ISTAT_REG_800, ISTAT_10_SRST);
+    NCR53c7x0_write8(ISTAT_REG_800, 0);
+    NCR53c7x0_write8(hostdata->dmode, hostdata->saved_dmode & ~DMODE_MAN);
+
+
+    /* 
+     * Respond to selection and reselection by targets and 
+     * use our _initiator_ SCSI ID for arbitration. 
+     *
+     * XXX - Note : we must reprogram this when reselecting as 
+     * a target.
+     */
+
+    NCR53c7x0_write8(SCID_REG, (host->this_id & 7)|SCID_800_RRE|SCID_800_SRE);
+    NCR53c7x0_write8(RESPID_REG_800, hostdata->this_id_mask);
+
+    /*
+     * Use a maximum (1.6) second handshake to handshake timeout,
+     * and SCSI recommended .5s selection timeout.
+     */
+
+    NCR53c7x0_write8(STIME0_REG_800, 
+       ((14 << STIME0_800_SEL_SHIFT) & STIME0_800_SEL_MASK) 
+/* Disable HTH interrupt */
+#if 0
+       | ((15 << STIME0_800_HTH_SHIFT) & STIME0_800_HTH_MASK)
+#endif
+    );
+
+
+
+    /*
+     * Enable all interrupts, except parity which we only want when
+     * the user requests it.
+     */
+
+    NCR53c7x0_write8(DIEN_REG, DIEN_800_MDPE | DIEN_800_BF |
+               DIEN_ABRT | DIEN_SSI | DIEN_SIR | DIEN_800_IID);
+
+    
+    NCR53c7x0_write8(SIEN0_REG_800, ((hostdata->options & OPTION_PARITY) ?
+           SIEN_PAR : 0) | SIEN_RST | SIEN_UDC | SIEN_SGE | SIEN_800_SEL |
+           SIEN_800_RESEL | SIEN_MA);
+    NCR53c7x0_write8(SIEN1_REG_800, SIEN1_800_STO | SIEN1_800_HTH);
+
+    /* 
+     * Use saved clock frequency divisor and scripts loaded in 16 bit
+     * mode flags from the saved dcntl.
+     */
+
+    NCR53c7x0_write8(DCNTL_REG, hostdata->saved_dcntl);
+    NCR53c7x0_write8(CTEST4_REG_800, hostdata->saved_ctest4);
+
+    /* Enable active negation */
+    NCR53c7x0_write8(STEST3_REG_800, STEST3_800_TE);
+
+    
+}
+
+/*
+ * Function static struct NCR53c7x0_cmd *create_cmd (Scsi_Cmnd *cmd) 
+ *
+ * Purpose : Using scsi_malloc() if the system is initialized,
+ *     scan_scsis_buf if not, allocate space to store the variable
+ *     length NCR53c7x0_cmd structure.  Initialize it based on 
+ *     the Scsi_Cmnd structure passed in, including dsa and 
+ *     Linux field initialization, and dsa code relocation.
+ *
+ * Inputs : cmd - SCSI command
+ *
+ * Returns : NCR53c7x0_cmd structure corresponding to cmd,
+ *     NULL on failure.
+ */
+
+static struct NCR53c7x0_cmd *create_cmd (Scsi_Cmnd *cmd) {
+    NCR53c7x0_local_declare();
+    struct Scsi_Host *host = cmd->host;
+    struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+        host->hostdata;        
+    int size;                  /* Size of *tmp */
+    struct NCR53c7x0_cmd *tmp;         /* NCR53c7x0_cmd structure for this command */
+    int datain,                /* Number of instructions per phase */
+       dataout;
+    int data_transfer_instructions, /* Count of dynamic instructions */
+       i,                      /* Counter */
+       alignment;              /* Alignment adjustment (0 - 4) */
+    unsigned long *cmd_datain, /* Address of datain/dataout code */
+       *cmd_dataout;           /* Incremented as we assemble */
+    void *real;                        /* Real address */
+    NCR53c7x0_local_setup(cmd->host);
+
+
+    /*
+     * Decide weather we need to generate commands for DATA IN,
+     * DATA OUT, neither, or both based on the SCSI command 
+     */
+
+    switch (cmd->cmnd[0]) {
+    /* These commands do DATA IN */
+    case INQUIRY:
+    case MODE_SENSE:
+    case READ_6:
+    case READ_10:
+    case READ_CAPACITY:
+    case REQUEST_SENSE:
+       datain = 2 * (cmd->use_sg ? cmd->use_sg : 1) + 3;
+       dataout = 0;
+       break;
+    /* These commands do DATA OUT */
+    case MODE_SELECT: 
+    case WRITE_6:
+    case WRITE_10:
+#if 0
+       printk("scsi%d : command is ", host->host_no);
+       print_command(cmd->cmnd);
+#endif
+#if 0
+       printk ("scsi%d : %d scatter/gather segments\n", host->host_no,
+           cmd->use_sg);
+#endif
+       datain = 0;
+       dataout = 2 * (cmd->use_sg ? cmd->use_sg : 1) + 3;
+#if 0
+       hostdata->options |= OPTION_DEBUG_INTR;
+#endif
+       break;
+    /* 
+     * These commands do no data transfer, we should force an
+     * interrupt if a data phase is attempted on them.
+     */
+    case START_STOP:
+    case TEST_UNIT_READY:
+       datain = dataout = 0;
+       break;
+    /*
+     * We don't know about these commands, so generate code to handle
+     * both DATA IN and DATA OUT phases.
+     */
+    default:
+       datain = dataout = 2 * (cmd->use_sg ? cmd->use_sg : 1) + 3;
+    }
+
+    /*
+     * Allocate memory for the NCR53c7x0_cmd structure.  
+     */
+
+    /* 
+     * For each data phase implemented, we need a JUMP instruction
+     * to return control to other_transfer.  We also need a MOVE
+     * and a CALL instruction for each scatter/gather segment.
+     */
+
+    data_transfer_instructions = datain + dataout;
+
+    /*
+     * When we perform a request sense, we overwrite various things,
+     * including the data transfer code.  Make sure we have enough
+     * space to do that.
+     */
+
+    if (data_transfer_instructions < 2)
+       data_transfer_instructions = 2;
+
+    /* 
+     * We need enough space to store the base NCR53c7x0 structure,
+     * DSA, and data transfer instructions at 2 long words each,
+     * as well as padding out to the next 512 bytes for scsi_malloc.
+     *
+     * We also need to guarantee alignment of _4_ bytes. 
+     */
+
+#ifdef SCSI_MALLOC
+    size = ((sizeof (struct NCR53c7x0_cmd) + (hostdata->dsa_end - 
+       hostdata->dsa_start) + 2 * sizeof(long) * 
+       data_transfer_instructions + 4 + 511) / 512) * 512;             
+#else
+    size = sizeof (struct NCR53c7x0_cmd) + (hostdata->dsa_end - 
+       hostdata->dsa_start) + 2 * sizeof(long) * 
+       data_transfer_instructions + 4;
+#endif
+
+
+#if 0
+    if (size > 512) {
+       printk("scsi%d : size = %d\n", host->host_no, size);
+    }
+#endif
+
+#ifdef SCSI_MALLOC
+    real = in_scan_scsis ? NULL : scsi_malloc (size);
+#else
+    real = kmalloc (size, GFP_ATOMIC);
+#endif
+
+    if (!real) {
+       if (!scan_scsis_buf_busy && size <= sizeof(scan_scsis_buf)) {
+           scan_scsis_buf_busy = 1;
+           real = scan_scsis_buf;
+       } else {
+           panic ("scsi%d : scan_scsis_buf too small (need %d bytes)\n",
+               host->host_no, size);
+       }
+    }
+
+    alignment = 4 - (((unsigned) real) & 3);
+
+    tmp = (struct NCR53c7x0_cmd *) (((char *) real) + alignment);
+
+    tmp->real = real;
+
+
+    if (((unsigned long) tmp->dsa) & 0x3) 
+       panic ("scsi%d : pid %d dsa structure not dword aligned!\n",
+           host->host_no, cmd->pid);
+
+    /*
+     * Initialize Linux specific fields.
+     */
+
+    tmp->size = size;                  
+    tmp->cmd = cmd;
+    tmp->next = NULL;
+    tmp->prev = NULL;
+
+    /* 
+     * Calculate addresses of dynamnic code to fill in DSA
+     */
+
+    tmp->data_transfer_start = tmp->dsa + (hostdata->dsa_end - 
+       hostdata->dsa_start) / sizeof(long);
+    tmp->data_transfer_end = tmp->data_transfer_start + 
+       2 * data_transfer_instructions;
+
+    cmd_datain = datain ? tmp->data_transfer_start : NULL;
+    cmd_dataout = dataout ? (datain ? cmd_datain + 2 * datain : tmp->
+       data_transfer_start) : NULL;
+
+    /*
+     * Fill in the NCR53c7x0_cmd structure as follows
+     * dsa, with fixed up DSA code
+     * datain code
+     * dataout code
+     */
+
+    /* Copy template code into dsa and perform all necessary fixups */
+    if (hostdata->dsa_fixup)
+       hostdata->dsa_fixup(tmp);
+
+    patch_dsa_32(tmp->dsa, dsa_next, 0, NULL);
+    patch_dsa_32(tmp->dsa, dsa_cmnd, 0, cmd);
+    patch_dsa_32(tmp->dsa, dsa_select, 0, hostdata->sync[cmd->target].
+       select_indirect);
+    /*
+     * XXX - we need to figure this size based on weather
+     * or not we'll be using any additional messages.
+     */
+    patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1);
+#if 0
+    tmp->select[0] = IDENTIFY (1, cmd->lun);
+#else
+    tmp->select[0] = IDENTIFY (0, cmd->lun);
+#endif
+    patch_dsa_32(tmp->dsa, dsa_msgout, 1, tmp->select);
+    patch_dsa_32(tmp->dsa, dsa_cmdout, 0, COMMAND_SIZE(cmd->cmnd[0]));
+    patch_dsa_32(tmp->dsa, dsa_cmdout, 1, cmd->cmnd);
+    patch_dsa_32(tmp->dsa, dsa_dataout, 0, cmd_dataout ? 
+       cmd_dataout : hostdata->script + hostdata->E_other_transfer / 
+           sizeof (long));
+    patch_dsa_32(tmp->dsa, dsa_datain, 0, cmd_datain ? 
+       cmd_datain : hostdata->script + hostdata->E_other_transfer / 
+           sizeof (long));
+    /* 
+     * XXX - need to make endian aware, should use separate variables
+     * for both status and message bytes.
+     */
+    patch_dsa_32(tmp->dsa, dsa_msgin, 0, 1);
+    patch_dsa_32(tmp->dsa, dsa_msgin, 1, (((unsigned long) &cmd->result) + 1));
+    patch_dsa_32(tmp->dsa, dsa_status, 0, 1);
+    patch_dsa_32(tmp->dsa, dsa_status, 1, &cmd->result);
+    patch_dsa_32(tmp->dsa, dsa_msgout_other, 0, 1);
+    patch_dsa_32(tmp->dsa, dsa_msgout_other, 1, 
+       &NCR53c7xx_msg_nop);
+
+    
+    /*
+     * Generate code for zero or more of the DATA IN, DATA OUT phases 
+     * in the format 
+     *
+     * MOVE first buffer length, first buffer address, WHEN phase
+     * CALL msgin, WHEN MSG_IN 
+     * ...
+     * MOVE last buffer length, last buffer address, WHEN phase
+     * JUMP other_transfer
+     */
+
+/* See if we're getting to data transfer */
+#if 0
+    if (datain) {
+       cmd_datain[0] = 0x98080000;
+       cmd_datain[1] = 0x03ffd00d;
+       cmd_datain += 2;
+    }
+#endif
+
+/* 
+ * XXX - I'm undecided weather all of this nonsense is faster
+ * in the long run, or weather I should just go and implement a loop
+ * on the NCR chip using table indirect mode?
+ *
+ * In any case, this is how it _must_ be done for 53c700/700-66 chips,
+ * so this stays even when we come up with something better.
+ *
+ * When we're limited to 1 simultaenous command, no overlapping processing,
+ * we're seeing 630K/sec, with 7% CPU usage on a slow Syquest 45M
+ * drive.
+ *
+ * Not bad, not good. We'll see.
+ */
+
+    for (i = 0; cmd->use_sg ? (i < cmd->use_sg) : !i; cmd_datain += 4, 
+       cmd_dataout += 4, ++i) {
+       unsigned long buf = (unsigned long) (cmd->use_sg ? 
+           ((struct scatterlist *)cmd->buffer)[i].address :
+           cmd->request_buffer);
+       unsigned long count = (unsigned long) (cmd->use_sg ?
+           ((struct scatterlist *)cmd->buffer)[i].length :
+           cmd->request_bufflen);
+
+       if (datain) {
+           cmd_datain[0] = ((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I | DCMD_BMI_IO) 
+               << 24) | count;
+           cmd_datain[1] = buf;
+           cmd_datain[2] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL | 
+               DCMD_TCI_CD | DCMD_TCI_IO | DCMD_TCI_MSG) << 24) | 
+               DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE | DBC_TCI_TRUE;
+           cmd_datain[3] = hostdata->script + hostdata->E_msg_in / 
+               sizeof(long);
+#if 0
+           print_insn (host, cmd_datain, "dynamic ", 1);
+           print_insn (host, cmd_datain + 2, "dynamic ", 1);
+#endif
+       }
+       if (dataout) {
+           cmd_dataout[0] = ((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I) << 24) 
+               | count;
+           cmd_dataout[1] = buf;
+           cmd_dataout[2] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL | 
+               DCMD_TCI_CD | DCMD_TCI_IO | DCMD_TCI_MSG) << 24) | 
+               DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE | DBC_TCI_TRUE;
+           cmd_dataout[3] = hostdata->script + hostdata->E_msg_in /
+               sizeof(long);
+#if 0
+           print_insn (host, cmd_dataout, "dynamic ", 1);
+           print_insn (host, cmd_dataout + 2, "dynamic ", 1);
+#endif
+       }
+    }
+
+    /*
+     * Install JUMP instructions after the data transfer routines to return
+     * control to the do_other_transfer routines.
+     */
+  
+    
+    if (datain) {
+       cmd_datain[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) |
+           DBC_TCI_TRUE;
+       cmd_datain[1] = hostdata->script + hostdata->E_other_transfer
+           / sizeof(long);
+#if 0
+       print_insn (host, cmd_datain, "dynamic jump ", 1);
+#endif
+       cmd_datain += 2; 
+    }
+#if 0
+    if (datain) {
+       cmd_datain[0] = 0x98080000;
+       cmd_datain[1] = 0x03ffdeed;
+       cmd_datain += 2;
+    }
+#endif
+
+
+    if (dataout) {
+       cmd_dataout[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) |
+           DBC_TCI_TRUE;
+       cmd_dataout[1] = hostdata->script + hostdata->E_other_transfer
+           / sizeof(long);
+#if 0
+       print_insn (host, cmd_dataout, "dynamic jump ", 1);
+#endif
+       cmd_dataout += 2;
+    }
+
+
+    return tmp;
+}
+    
+/* 
+ * Function : int NCR53c7xx_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.
+ *
+ */
+
+int NCR53c7xx_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)) {
+    NCR53c7x0_local_declare();
+    struct NCR53c7x0_cmd *tmp;
+    struct Scsi_Host *host = cmd->host;
+    struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+       host->hostdata;
+    int old_level;
+    unsigned char target_was_busy;
+    NCR53c7x0_local_setup(host);
+    
+    if (((hostdata->options & (OPTION_DEBUG_INIT_ONLY|OPTION_DEBUG_PROBE_ONLY)) ||
+       ((hostdata->options & OPTION_DEBUG_TARGET_LIMIT) &&
+       !(hostdata->debug_lun_limit[cmd->target] & (1 << cmd->lun)))) ||
+       cmd->target > 7) {
+       printk("scsi%d : disabled target %d lun %d\n", host->host_no,
+           cmd->target, cmd->lun);
+       cmd->result = (DID_BAD_TARGET << 16);
+       done(cmd);
+       return 0;
+    }
+
+    if (hostdata->options & OPTION_DEBUG_NCOMMANDS_LIMIT) {
+       if (hostdata->debug_count_limit == 0) {
+           printk("scsi%d : maximum commands exceeded\n", host->host_no);
+           cmd->result = (DID_BAD_TARGET << 16);
+           done(cmd);
+           return 0;
+       } else if (hostdata->debug_count_limit != -1) 
+           --hostdata->debug_count_limit;
+    }
+   
+    if (hostdata->options & OPTION_DEBUG_READ_ONLY) {
+       switch (cmd->cmnd[0]) {
+       case WRITE_6:
+       case WRITE_10:
+           printk("scsi%d : WRITE attempted with NO_WRITE debugging flag set\n",
+               host->host_no);
+           cmd->result = (DID_BAD_TARGET << 16);
+           done(cmd);
+           return 0;
+       }
+    }
+
+    cmd->scsi_done = done;
+    cmd->result = 0xffff;              /* The NCR will overwite message
+                                          and status with valid data */
+    
+    cmd->host_scribble = (unsigned char *) tmp = create_cmd (cmd);
+
+    /*
+     * On NCR53c710 and better chips, we have two issue queues : 
+     * The queue maintained by the Linux driver, and the queue 
+     * maintained by the NCR chip.
+     * 
+     * The Linux queue includes commands which have been generated,
+     * but may be unable to execute because the device is busy, 
+     * where as the NCR queue contains commands to issue as soon
+     * as BUS FREE is detected.
+     *
+     * NCR53c700 and NCR53c700-66 chips use only the Linux driver
+     * queue. 
+     * 
+     * So, insert into the Linux queue if the device is busy or 
+     * we are running on an old chip, otherwise insert directly into
+     * the NCR queue.
+     */
+    
+    /*
+     * REQUEST sense commands need to be executed before all other 
+     * commands since any command will clear the contingent allegience 
+     * condition that exists and the sense data is only guranteed to be 
+     * valid while the condition exists.
+     */
+
+    old_level = splx(0);
+
+    /* 
+     * Consider a target busy if there are _any_ commands running
+     * on it.  
+     * XXX - Once we do SCSI-II tagged queuing, we want to use 
+     *     a different definition of busy.
+     */
+       
+    target_was_busy = hostdata->busy[cmd->target][cmd->lun]
+#ifdef LUN_BUSY
+       ++
+#endif
+; 
+
+    if (!(hostdata->options & OPTION_700)  &&
+       !target_was_busy) {
+       unsigned char *dsa = ((unsigned char *) tmp->dsa)
+               - hostdata->dsa_start;  
+               /* dsa start is negative, so subtraction is used */
+#if 0  
+       printk("scsi%d : new dsa is 0x%x\n", host->host_no, (unsigned) dsa);
+#endif
+
+       if (hostdata->running_list)
+           hostdata->running_list->prev = tmp;
+
+       tmp->next = hostdata->running_list;
+
+       if (!hostdata->running_list)
+           hostdata->running_list = tmp;
+       
+
+       if (hostdata->idle) {
+           hostdata->idle = 0;
+           hostdata->state = STATE_RUNNING;
+           NCR53c7x0_write32 (DSP_REG,  ((unsigned long) hostdata->script) +
+               hostdata->E_schedule);
+       }
+
+/* XXX - make function */
+       for (;;) {
+           /* 
+            * If the NCR doesn't have any commands waiting in its
+            * issue queue, then we simply create a new issue queue,
+            * and signal the NCR that we have more commands.
+            */
+               
+           if (!hostdata->issue_dsa_head) {
+#if 0
+               printk ("scsi%d : no issue queue\n", host->host_no);
+#endif
+               hostdata->issue_dsa_tail = hostdata->issue_dsa_head = dsa;
+               NCR53c7x0_write8(hostdata->istat, 
+                   NCR53c7x0_read8(hostdata->istat) | ISTAT_10_SIGP);
+               break;
+           /*
+            * Otherwise, we blindly perform an atomic write 
+            * to the next pointer of the last command we 
+            * placed in that queue.
+            *
+            * Looks like it doesn't work, but I think it does - 
+            */
+           } else {
+               printk ("scsi%d : existing issue queue\n", host->host_no);
+           /* XXX - Replace with XCHG or equivalent */
+               hostdata->issue_dsa_tail = *((unsigned char **) 
+                   (hostdata->issue_dsa_tail + hostdata->dsa_next)) = dsa;
+           /*
+            * After which, one of two things will happen : 
+            * The NCR will have scheduled a command, either this
+            * one, or the next one.  In this case, we successfully
+            * added our command to the queue.
+            *
+            * The NCR will have written the hostdata->issue_dsa_head
+            * pointer with the NULL pointer terminating the list,
+            * in which case we were too late.  If this happens,
+            * we restart
+            */
+               if (hostdata->issue_dsa_head)
+                   break;
+           }
+       }
+/* XXX - end */
+    } else {
+#if 1
+       printk ("scsi%d : using issue_queue instead of issue_dsa_head!\n",
+           host->host_no);
+#endif
+       for (tmp = (struct NCR53c7x0_cmd *) hostdata->issue_queue; 
+           tmp->next; tmp = (struct NCR53c7x0_cmd *) tmp->next);
+       tmp->next = tmp;
+    }
+    splx(old_level);
+    return 0;
+}
+
+
+int fix_pointers (unsigned long dsa) {
+    return 0;
+}
+
+/*
+ * Function : static void intr_scsi (struct Scsi_Host *host, 
+ *     struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : handle all SCSI interrupts, indicated by the setting 
+ *     of the SIP bit in the ISTAT register.
+ *
+ * Inputs : host, cmd - host and NCR command causing the interrupt, cmd
+ *     may be NULL.
+ */
+
+static void intr_scsi (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) {
+    NCR53c7x0_local_declare();
+    struct NCR53c7x0_hostdata *hostdata = 
+       (struct NCR53c7x0_hostdata *) host->hostdata;
+    unsigned char sstat0_sist0, sist1,                 /* Registers */
+           fatal;                              /* Did a fatal interrupt 
+                                                  occur ? */
+    NCR53c7x0_local_setup(host);
+
+    fatal = 0;
+  
+    if ((hostdata->chip / 100) == 8) {
+       sstat0_sist0 = NCR53c7x0_read8(SIST0_REG_800);
+       udelay(1);
+       sist1 = NCR53c7x0_read8(SIST1_REG_800);
+    } else {
+       sstat0_sist0 = NCR53c7x0_read8(SSTAT0_REG);
+       sist1 = 0;
+    }
+
+    if (hostdata->options & OPTION_DEBUG_INTR) 
+       printk ("scsi%d : SIST0 0x%0x, SIST1 0x%0x\n", host->host_no,
+           sstat0_sist0, sist1);
+
+    /* 250ms selection timeout */
+    if ((((hostdata->chip / 100) == 8) && (sist1 & SIST1_800_STO)) || 
+        (((hostdata->chip / 100) != 8) && sstat0_sist0 && SSTAT0_700_STO)) {
+       fatal = 1;
+       if (hostdata->options & OPTION_DEBUG_INTR) {
+           printk ("scsi%d : Selection Timeout\n", host->host_no);
+           if (cmd) {
+               printk("scsi%d : target %d, lun %d, command ",
+                   host->host_no, cmd->cmd->target, cmd->cmd->lun);
+               print_command (cmd->cmd->cmnd);
+               printk("scsi%d : dsp = 0x%x\n", host->host_no,
+                   (unsigned) NCR53c7x0_read32(DSP_REG));
+           } else {
+               printk("scsi%d : no command\n", host->host_no);
+           }
+       }
+/*
+ * XXX - question : how do we want to handle the Illegal Instruction
+ *     interrupt, which may occur before or after the Selection Timeout
+ *     interrupt?
+ */
+
+       if (1) {
+           hostdata->idle = 1;
+           hostdata->expecting_sto = 0;
+
+           if (hostdata->test_running) {
+               hostdata->test_running = 0;
+               hostdata->test_completed = 3;
+           } else if (cmd) {
+               abnormal_finished(cmd, DID_BAD_TARGET << 16);
+           }
+#if 0      
+           hostdata->intrs = 0;
+#endif
+       }
+    } 
+    
+    if (sstat0_sist0 & SSTAT0_UDC) {
+       fatal = 1;
+       printk("scsi%d : target %d lun %d unexpected disconnect\n",
+           host->host_no, cmd->cmd->target, cmd->cmd->lun);
+       if (cmd) {
+           abnormal_finished(cmd, DID_ERROR << 16);
+       }
+       hostdata->dsp = hostdata->script + hostdata->E_schedule / 
+           sizeof(long);
+       hostdata->dsp_changed = 1;
+    /* SCSI PARITY error */
+    } 
+
+    if (sstat0_sist0 & SSTAT0_PAR) {
+       fatal = 1;
+       if (cmd && cmd->cmd) {
+           printk("scsi%d : target %d lun %d parity error.\n",
+               host->host_no, cmd->cmd->target, cmd->cmd->lun);
+           abnormal_finished (cmd, DID_PARITY << 16); 
+       } else
+           printk("scsi%d : parity error\n", host->host_no);
+       /* Should send message out, parity error */
+
+       /* XXX - Reduce synchronous transfer rate! */
+       hostdata->dsp = hostdata->script + hostdata->E_initiator_abort /
+           sizeof(long);
+       hostdata->dsp_changed = 1; 
+    /* SCSI GROSS error */
+    } 
+
+    if (sstat0_sist0 & SSTAT0_SGE) {
+       fatal = 1;
+       printk("scsi%d : gross error\n", host->host_no);
+       /* XXX Reduce synchronous transfer rate! */
+       hostdata->dsp = hostdata->script + hostdata->E_initiator_abort /
+           sizeof(long);
+       hostdata->dsp_changed = 1;
+    /* Phase mismatch */
+    } 
+
+    if (sstat0_sist0 & SSTAT0_MA) {
+       fatal = 1;
+       if (hostdata->options & OPTION_DEBUG_INTR)
+           printk ("scsi%d : SSTAT0_MA\n", host->host_no);
+       intr_phase_mismatch (host, cmd);
+    }
+
+#if 1
+/*
+ * If a fatal SCSI interrupt occurs, we must insure that the DMA and
+ * SCSI FIFOs were flushed.
+ */
+
+    if (fatal) {
+       if (!hostdata->dstat_valid) {
+           hostdata->dstat = NCR53c7x0_read8(DSTAT_REG);
+           hostdata->dstat_valid = 1;
+       }
+
+/* XXX - code check for 700/800 chips */
+       if (!(hostdata->dstat & DSTAT_DFE)) {
+           printk ("scsi%d : DMA FIFO not empty\n", host->host_no);
+#if 0
+           if (NCR53c7x0_read8 (CTEST2_REG_800) & CTEST2_800_DDIR) {
+               NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_FLF);
+               while (!((hostdata->dstat = NCR53c7x0_read8(DSTAT_REG)) &
+                   DSTAT_DFE));
+           } else 
+#endif
+           {
+               NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_CLF);
+               while (NCR53c7x0_read8 (CTEST3_REG_800) & CTEST3_800_CLF);
+           }
+       }
+
+       NCR53c7x0_write8 (STEST3_REG_800, STEST3_800_CSF);
+       while (NCR53c7x0_read8 (STEST3_REG_800) & STEST3_800_CSF);
+    }
+#endif
+}
+
+/*
+ * Function : static void NCR53c7x0_intr (int irq)
+ *
+ * Purpose : handle NCR53c7x0 interrupts for all NCR devices sharing
+ *     the same IRQ line.  
+ * 
+ * Inputs : Since we're using the SA_INTERRUPT interrupt handler
+ *     semantics, irq indicates the interrupt which invoked 
+ *     this handler.  
+ */
+
+static void NCR53c7x0_intr (int irq) {
+    NCR53c7x0_local_declare();
+    struct Scsi_Host *host;                    /* Host we are looking at */
+    unsigned char istat;                       /* Values of interrupt regs */
+    struct NCR53c7x0_hostdata *hostdata;       /* host->hostdata */
+    struct NCR53c7x0_cmd *cmd,                 /* command which halted */
+       **cmd_prev_ptr;
+    unsigned long *dsa;                                /* DSA */
+    int done = 1;                              /* Indicates when handler 
+                                                  should terminate */
+    int interrupted = 0;                       /* This HA generated 
+                                                  an interrupt */
+    int old_level;                             
+
+#ifdef NCR_DEBUG
+    char buf[80];                              /* Debugging sprintf buffer */
+    size_t buflen;                             /* Length of same */
+#endif
+
+#if 0
+    printk("interrupt %d received\n", irq);
+#endif
+
+    do {
+       done = 1;
+       for (host = first_host; host; host = hostdata->next ?
+            hostdata->next : NULL) {
+           NCR53c7x0_local_setup(host);
+
+           hostdata = (struct NCR53c7x0_hostdata *) host->hostdata;
+           hostdata->dsp_changed = 0;
+           interrupted = 0;
+
+
+           do {
+               hostdata->dstat_valid = 0;
+               interrupted = 0;
+               /*
+                * Only read istat once, since reading it again will unstack
+                * interrupts.
+                */
+               istat = NCR53c7x0_read8(hostdata->istat);
+
+               /*
+                * INTFLY interrupts are used by the NCR53c720, NCR53c810,
+                * and NCR53c820 to signify completion of a command.  Since 
+                * the SCSI processor continues running, we can't just look
+                * at the contents of the DSA register and continue running.
+                */
+/* XXX - this is getting big, and should move to intr_intfly() */
+               if ((hostdata->options & OPTION_INTFLY) && 
+                   ((hostdata->chip / 100) == 8 && (istat & ISTAT_800_INTF))) {
+                   char search_found = 0;      /* Got at least one ? */
+                   done = 0;
+                   interrupted = 1;
+
+                   /* 
+                    * Clear the INTF bit by writing a one.  This reset operation 
+                    * is self-clearing.
+                    */
+                   NCR53c7x0_write8(hostdata->istat, istat|ISTAT_800_INTF);
+
+                   if (hostdata->options & OPTION_DEBUG_INTR)
+                       printk ("scsi%d : INTFLY\n", host->host_no); 
+
+                   /*
+                    * Traverse our list of running commands, and look
+                    * for those with valid (non-0xff ff) status and message
+                    * bytes encoded in the result which signify command
+                    * completion.
+                    */
+
+
+                   old_level = splx(0);
+restart:
+                   for (cmd_prev_ptr = (struct NCR53c7x0_cmd **) 
+                       &(hostdata->running_list), cmd = (struct NCR53c7x0_cmd *)
+                       hostdata->running_list; cmd ; cmd_prev_ptr = 
+                       &(cmd->next), cmd = (struct NCR53c7x0_cmd *) cmd->next) {
+                       Scsi_Cmnd *tmp;
+
+                       if (!cmd) {
+                           printk("scsi%d : very wierd.\n", host->host_no);
+                           break;
+                       }
+
+                       if (!(tmp = cmd->cmd)) {
+                           printk("scsi%d : wierd.  NCR53c7x0_cmd has no Scsi_Cmnd\n",
+                               host->host_no);
+                               continue;
+                       }
+#if 0
+                       printk ("scsi%d : looking at result of 0x%x\n",
+                           host->host_no, cmd->cmd->result);
+#endif
+               
+                       if (((tmp->result & 0xff) == 0xff) ||
+                           ((tmp->result & 0xff00) == 0xff00))
+                           continue;
+
+                       search_found = 1;
+
+                       /* Important - remove from list _before_ done is called */
+                       /* XXX - SLL.  Seems like DLL is unecessary */
+                       if (cmd->prev)
+                           cmd->prev->next = cmd->next;
+                       if (cmd_prev_ptr)
+                           *cmd_prev_ptr = cmd->next;
+
+#ifdef LUN_BUSY
+                       /* Check for next command for target, add to issue queue */
+                       if (--hostdata->busy[tmp->target][tmp->lun]) {
+                       }
+#endif
+
+
+                       if (!scan_scsis_buf_busy) {
+#ifdef SCSI_MALLOC
+                           scsi_free ((void *) cmd->real, cmd->size);
+#else
+                           kfree_s ((void *) cmd->real, cmd->size);
+#endif
+                       } else {
+                           scan_scsis_buf_busy = 0;
+                       }
+
+
+                       tmp->host_scribble = NULL;
+
+                       if (hostdata->options & OPTION_DEBUG_INTR) {
+                           printk ("scsi%d : command complete : pid %lu, id %d,lun %d result 0x%x ", 
+                               host->host_no, tmp->pid, tmp->target, tmp->lun, tmp->result);
+                           print_command (tmp->cmnd);
+                       }
+
+                       
+#if 0
+                       hostdata->options &= ~OPTION_DEBUG_INTR;
+#endif
+                       tmp->scsi_done(tmp);
+                       goto restart;
+
+                   }
+                   splx(old_level);
+
+                   if (!search_found)  {
+                       printk ("scsi%d : WARNING : INTFLY with no completed commands.\n",
+                           host->host_no);
+                   }
+               }
+
+               if (istat & (ISTAT_SIP|ISTAT_DIP)) {
+                   done = 0;
+                   interrupted = 1;
+                   hostdata->state = STATE_HALTED;
+                   /*
+                    * NCR53c700 and NCR53c700-66 change the current SCSI
+                    * process, hostdata->current, in the Linux driver so
+                    * cmd = hostdata->current.
+                    *
+                    * With other chips, we must look through the commands
+                    * executing and find the command structure which 
+                    * corresponds to the DSA register.
+                    */
+
+                   if (hostdata->options & OPTION_700) {
+                       cmd = (struct NCR53c7x0_cmd *) hostdata->current;
+                   } else {
+                       dsa = (unsigned long *) NCR53c7x0_read32(DSA_REG);
+                       for (cmd = (struct NCR53c7x0_cmd *) 
+                           hostdata->running_list; cmd &&
+                           (dsa + (hostdata->dsa_start / sizeof(long))) != 
+                               cmd->dsa;
+                           cmd = (struct NCR53c7x0_cmd *)(cmd->next));
+                   }
+                   if (hostdata->options & OPTION_DEBUG_INTR) {
+                       if (cmd) {
+                           printk("scsi%d : interrupt for pid %lu, id %d, lun %d ", 
+                               host->host_no, cmd->cmd->pid, (int) cmd->cmd->target,
+                               (int) cmd->cmd->lun);
+                           print_command (cmd->cmd->cmnd);
+                       } else {
+                           printk("scsi%d : no active command\n", host->host_no);
+                       }
+                   }
+
+                   if (istat & ISTAT_SIP) {
+                       if (hostdata->options & OPTION_DEBUG_INTR) 
+                           printk ("scsi%d : ISTAT_SIP\n", host->host_no);
+                       intr_scsi (host, cmd);
+                   }
+               
+                   if (istat & ISTAT_DIP) {
+                       if (hostdata->options & OPTION_DEBUG_INTR) 
+                           printk ("scsi%d : ISTAT_DIP\n", host->host_no);
+                       intr_dma (host, cmd);
+                   }
+
+                   if (!hostdata->dstat_valid) {
+                       hostdata->dstat = NCR53c7x0_read8(DSTAT_REG);
+                       hostdata->dstat_valid = 1;
+                   }
+
+#if 1
+           /* XXX - code check for 700/800 chips */
+                   if (!(hostdata->dstat & DSTAT_DFE)) {
+                       printk ("scsi%d : DMA FIFO not empty\n", host->host_no);
+           #if 0
+                       if (NCR53c7x0_read8 (CTEST2_REG_800) & CTEST2_800_DDIR) {
+                           NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_FLF);
+                           while (!((hostdata->dstat = NCR53c7x0_read8(DSTAT_REG)) &
+                               DSTAT_DFE));
+                       } else 
+           #endif
+                       {
+                           NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_CLF);
+                           while (NCR53c7x0_read8 (CTEST3_REG_800) & CTEST3_800_CLF);
+                       }
+                   }
+#endif
+               }
+           } while (interrupted);
+
+
+
+           if (hostdata->intrs != -1)
+               hostdata->intrs++;
+#if 0
+           if (hostdata->intrs > 4) {
+               printk("scsi%d : too many interrupts, halting", host->host_no);
+               hostdata->idle = 1;
+               hostdata->options |= OPTION_DEBUG_INIT_ONLY;
+               panic("dying...\n");
+           }
+#endif
+
+           if (!hostdata->idle && hostdata->state == STATE_HALTED) {
+               if (!hostdata->dsp_changed) {
+                   hostdata->dsp = (unsigned long *) NCR53c7x0_read32(DSP_REG);
+               }
+                       
+#if 0
+               printk("scsi%d : new dsp is 0x%lx\n", host->host_no, 
+                   (long) hostdata->dsp);
+#endif
+               
+               hostdata->state = STATE_RUNNING;
+               NCR53c7x0_write32 (DSP_REG, (unsigned long) hostdata->dsp);
+           }
+       }
+    } while (!done);
+}
+
+
+/* 
+ * Function : static int abort_connected (struct Scsi_Host *host)
+ *
+ * Purpose : Assuming that the NCR SCSI processor is currently 
+ *     halted, break the currently established nexus.  Clean
+ *     up of the NCR53c7x0_cmd and Scsi_Cmnd structures should
+ *     be done on receipt of the abort interrupt.
+ *
+ * Inputs : host - SCSI host
+ *
+ */
+
+static int abort_connected (struct Scsi_Host *host) {
+    struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+       host->hostdata;
+
+    hostdata->dsp = hostdata->script + hostdata->E_initiator_abort /
+       sizeof(long);
+    hostdata->dsp_changed = 1;
+    printk ("scsi%d : DANGER : abort_connected() called \n",
+       host->host_no);
+/* XXX - need to flag the command as aborted after the abort_connected
+        code runs 
+ */
+    return 0;
+}
+
+
+/* 
+ * Function : static void intr_phase_mismatch (struct Scsi_Host *host, 
+ *     struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : Handle phase mismatch interrupts
+ *
+ * Inputs : host, cmd - host and NCR command causing the interrupt, cmd
+ *     may be NULL.
+ *
+ * Side effects : The abort_connected() routine is called or the NCR chip 
+ *     is restarted, jumping to the command_complete entry point, or 
+ *     patching the address and transfer count of the current instruction 
+ *     and calling the msg_in entry point as appropriate.
+ *
+ */
+
+static void intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd
+    *cmd) {
+    NCR53c7x0_local_declare();
+    unsigned long dbc_dcmd, *dsp, *dsp_next;
+    unsigned char dcmd, sbcl;
+    struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+       host->hostdata;
+    char *phase;
+    NCR53c7x0_local_setup(host);
+
+    if (!cmd) {
+       printk ("scsi%d : phase mismatch interrupt occurred with no current command.\n",
+           host->host_no);
+       abort_connected(host);
+       return;
+    }
+
+    /*
+     * Corrective action is based on where in the SCSI SCRIPT(tm) the error 
+     * occurred, as well as which SCSI phase we are currently in.
+     */
+
+    dsp_next = (unsigned long *) NCR53c7x0_read32(DSP_REG);
+
+    /*
+     * Like other processors, the NCR adjusts the DSP pointer before
+     * instruction decode.  Set the DSP address back to what it should
+     * be for this instruction based on its size (2 or 3 longs).
+     */
+
+    dbc_dcmd = NCR53c7x0_read32(DBC_REG);
+    dcmd = (dbc_dcmd & 0xff000000) >> 24;
+    dsp = dsp_next - NCR53c7x0_insn_size(dcmd);
+    
+    /*
+     * Read new SCSI phase from the SBCL lines.
+     *
+     * Note that since all of our code uses a WHEN conditional instead of an 
+     * IF conditional, we don't need to wait for a valid REQ.
+     */
+    sbcl = NCR53c7x0_read8(SBCL_REG);
+    switch (sbcl) {
+    case SBCL_PHASE_DATAIN:
+       phase = "DATAIN";
+       break;
+    case SBCL_PHASE_DATAOUT:
+       phase = "DATAOUT";
+       break;
+    case SBCL_PHASE_MSGIN:
+       phase = "MSGIN";
+       break;
+    case SBCL_PHASE_MSGOUT:
+       phase = "MSGOUT";
+       break;
+    case SBCL_PHASE_CMDOUT:
+       phase = "CMDOUT";
+       break;
+    case SBCL_PHASE_STATIN:
+       phase = "STATUSIN";
+       break;
+    default:
+       phase = "unknown";
+       break;
+    }
+
+
+    /*
+     * The way the SCSI SCRIPTS(tm) are architected, recoverable phase
+     * mismatches should only occur in the data transfer routines, or
+     * when a command is being aborted.  
+     */
+    if (dsp >= cmd->data_transfer_start & dsp < cmd->data_transfer_end) {
+
+       /*
+        * There are three instructions used in our data transfer routines with
+        * a phase conditional on them
+        *
+        * 1.  MOVE count, address, WHEN DATA_IN
+        * 2.  MOVE count, address, WHEN DATA_OUT
+        * 3.  CALL msg_in, WHEN MSG_IN.
+        */
+       switch (sbcl & SBCL_PHASE_MASK) {
+       /*
+        * 1.  STATUS phase : pass control to command_complete as if 
+        *     a JUMP instruction was executed.  No patches are made.
+        */
+       case SBCL_PHASE_STATIN:
+           if (hostdata->options & OPTION_DEBUG_INTR) 
+               printk ("scsi%d : new phase = STATIN\n", host->host_no);
+           hostdata->dsp = hostdata->script + hostdata->E_command_complete /
+               sizeof(long);
+           hostdata->dsp_changed = 1;
+           return;
+       /*
+        * 2.  MSGIN phase : pass control to msg_in as if a CALL
+        *     instruction was executed.  Patch current instruction.
+        */
+/* 
+ * XXX - This is buggy.
+ */
+       case SBCL_PHASE_MSGIN:
+           if (hostdata->options & OPTION_DEBUG_INTR) 
+               printk ("scsi%d  : new phase = MSGIN\n", host->host_no);
+           if ((dcmd & (DCMD_TYPE_MASK|DCMD_BMI_OP_MASK|DCMD_BMI_INDIRECT|
+                   DCMD_BMI_MSG|DCMD_BMI_CD)) == (DCMD_TYPE_BMI|
+                   DCMD_BMI_OP_MOVE_I)) {
+               dsp[0] = dbc_dcmd;
+               dsp[1] = NCR53c7x0_read32(DNAD_REG);
+               NCR53c7x0_write32(TEMP_REG, (unsigned long) dsp);
+               hostdata->dsp = hostdata->script + hostdata->E_msg_in /
+                   sizeof(long);
+               hostdata->dsp_changed = 1;
+           } else {
+               printk("scsi%d : unexpected MSGIN in dynamic NCR code, dcmd=0x%x.\n",
+                   host->host_no, dcmd);
+               print_insn (host, dsp, "", 1);
+               print_insn (host, dsp_next, "", 1);
+               abort_connected (host);
+           }
+           return;
+       /*
+        * MSGOUT phase - shouldn't happen, because we haven't 
+        *              asserted ATN.
+        * CMDOUT phase - shouldn't happen, since we've allready
+        *              sent a valid command.
+        * DATAIN/DATAOUT - other one shouldn't happen, since 
+        *              SCSI commands can ONLY have one or the other.
+        *
+        * So, we abort the command if one of these things happens.
+        */
+       default:
+           printk ("scsi%d : unexpected phase %s in data routine\n",
+               host->host_no, phase);
+           abort_connected(host);
+       } 
+    /*
+     * Any other phase mismatches abort the currently executing command.
+     */
+    } else {
+       printk ("scsi%d : unexpected phase %s at dsp = 0x%x\n",
+           host->host_no, phase, (unsigned) dsp);
+       print_insn (host, dsp, "", 1);
+       print_insn (host, dsp_next, "", 1);
+       abort_connected(host);
+    }
+}
+
+/*
+ * Function : static void intr_dma (struct Scsi_Host *host, 
+ *     struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : handle all DMA interrupts, indicated by the setting 
+ *     of the DIP bit in the ISTAT register.
+ *
+ * Inputs : host, cmd - host and NCR command causing the interrupt, cmd
+ *     may be NULL.
+ */
+
+static void intr_dma (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) {
+    NCR53c7x0_local_declare();
+    struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+       host->hostdata;
+    unsigned char dstat,       /* DSTAT */     
+       dbc_dcmd;               /* DCMD (high eight bits) + DBC */
+    unsigned long *dsp,
+       *next_dsp,              /* Current dsp */
+       *dsa;
+       
+
+    int ipl,                   /* Old ipl from splx(0) */
+       tmp;
+    NCR53c7x0_local_setup(host);
+
+    if (!hostdata->dstat_valid) {
+       hostdata->dstat = NCR53c7x0_read8(DSTAT_REG);
+       hostdata->dstat_valid = 1;
+    }
+    
+    dstat = hostdata->dstat;
+    
+    if (hostdata->options & OPTION_DEBUG_INTR)
+       printk("scsi%d : DSTAT=0x%x\n", host->host_no, (int) dstat);
+
+    dbc_dcmd = NCR53c7x0_read32 (DBC_REG);
+    next_dsp = (unsigned long *) NCR53c7x0_read32(DSP_REG);
+    dsp = next_dsp - NCR53c7x0_insn_size ((dbc_dcmd >> 24) & 0xff);
+/* XXX - check chip type */
+    dsa = (unsigned long *) NCR53c7x0_read32(DSA_REG);
+
+    /*
+     * DSTAT_ABRT is the aborted interrupt.  This is set whenver the 
+     * SCSI chip is aborted.  
+     * 
+     * With NCR53c700 and NCR53c700-66 style chips, we should only 
+     * get this when the chip is currently running the accept 
+     * reselect/select code and we have set the abort bit in the 
+     * ISTAT register.
+     *
+     */
+    
+    if (dstat & DSTAT_ABRT) {
+#if 0
+       /* XXX - add code here to deal with normal abort */
+       if ((hostdata->options & OPTION_700) && (hostdata->state ==
+           STATE_ABORTING) {
+       } else 
+#endif
+       {
+           printk("scsi%d : unexpected abort interrupt at\n" 
+                  "         ", host->host_no);
+           print_insn (host, dsp, "s ", 1);
+           panic(" ");
+       }
+    }
+
+    /*
+     * DSTAT_SSI is the single step interrupt.  Should be generated 
+     * whenever we have single stepped or are tracing.
+     */
+
+    if (dstat & DSTAT_SSI) {
+       if (hostdata->options & OPTION_DEBUG_TRACE) {
+       } else if (hostdata->options & OPTION_DEBUG_SINGLE) {
+           print_insn (host, dsp, "s ", 0);
+           ipl = splx(0);
+/* XXX - should we do this, or can we get away with writing dsp? */
+
+           NCR53c7x0_write8 (DCNTL_REG, (NCR53c7x0_read8(DCNTL_REG) & 
+               ~DCNTL_SSM) | DCNTL_STD);
+           splx(ipl);
+       } else {
+           printk("scsi%d : unexpected single step interrupt at\n"
+                  "         ", host->host_no);
+           print_insn (host, dsp, "", 1);
+           panic("         mail drew@colorad.edu\n");
+       }
+    }
+
+    /*
+     * DSTAT_IID / DSTAT_OPC (same bit, same meaning, only the name 
+     * is different) is generated whenever an illegal instruction is 
+     * encountered.  
+     * 
+     * XXX - we may want to emulate INTFLY here, so we can use 
+     *    the same SCSI SCRIPT (tm) for NCR53c710 through NCR53c810  
+     *   chips once we remove the ADD WITH CARRY instructions.
+     */
+
+    if (dstat & DSTAT_OPC) {
+    /* 
+     * Ascertain if this IID interrupts occured before or after a STO 
+     * interrupt.  Since the interrupt handling code now leaves 
+     * DSP unmodified until _after_ all stacked interrupts have been
+     * processed, reading the DSP returns the original DSP register.
+     * This means that if dsp lies between the select code, and 
+     * message out following the selection code (where the IID interrupt
+     * would have to have occurred by due to the implicit wait for REQ),
+     * we have an IID interrupt resulting from a STO condition and 
+     * can ignore it.
+     */
+
+       if (((dsp >= (hostdata->script + hostdata->E_select / sizeof(long))) &&
+           (dsp <= (hostdata->script + hostdata->E_select_msgout / 
+           sizeof(long) + 8))) || (hostdata->test_running == 2)) {
+           if (hostdata->options & OPTION_DEBUG_INTR) 
+               printk ("scsi%d : ignoring DSTAT_IID for SSTAT_STO\n",
+                   host->host_no);
+           if (hostdata->expecting_iid) {
+               hostdata->expecting_iid = 0;
+               hostdata->idle = 1;
+               if (hostdata->test_running == 2) {
+                   hostdata->test_running = 0;
+                   hostdata->test_completed = 3;
+               } else if (cmd) 
+                       abnormal_finished (cmd, DID_BAD_TARGET << 16);
+           } else {
+               hostdata->expecting_sto = 1;
+           }
+       } else {
+           printk("scsi%d : illegal instruction ", host->host_no);
+           print_insn (host, dsp, "", 1);
+           printk("scsi%d : DSP=0x%lx, DCMD|DBC=0x%lx, DSA=0x%lx\n"
+              "         DSPS=0x%lx, TEMP=0x%lx, DMODE=0x%x,\n" 
+              "         DNAD=0x%lx\n",
+            host->host_no, (unsigned long) dsp, dbc_dcmd,
+            (unsigned long) dsa, NCR53c7x0_read32(DSPS_REG),
+            NCR53c7x0_read32(TEMP_REG), (int) NCR53c7x0_read8(hostdata->dmode),
+            NCR53c7x0_read32(DNAD_REG));
+           panic("         mail drew@Colorado.EDU\n");
+       }
+    }
+
+    /* 
+     * DSTAT_BF are bus fault errors, generated when the chip has 
+     * attempted to access an illegal address.
+     */
+    
+    if (dstat & DSTAT_800_BF) {
+       printk("scsi%d : BUS FAULT, DSP=0x%lx, DCMD|DBC=0x%lx, DSA=0x%lx\n"
+              "         DSPS=0x%lx, TEMP=0x%lx, DMODE=0x%x\n", 
+            host->host_no, (unsigned long) dsp, NCR53c7x0_read32(DBC_REG),
+            (unsigned long) dsa, NCR53c7x0_read32(DSPS_REG),
+            NCR53c7x0_read32(TEMP_REG), (int) NCR53c7x0_read8(hostdata->dmode));
+       print_dsa (host, dsa);
+       printk("scsi%d : DSP->\n", host->host_no);
+       print_insn(host, dsp, "", 1);
+       print_insn(host, next_dsp, "", 1);
+#if 0
+       panic("          mail drew@Colorado.EDU\n");
+#else
+       hostdata->idle = 1;
+       hostdata->options |= OPTION_DEBUG_INIT_ONLY;
+#endif
+    }
+       
+
+    /* 
+     * DSTAT_SIR interrupts are generated by the execution of 
+     * the INT instruction.  Since the exact values available 
+     * are determined entirely by the SCSI script running, 
+     * and are local to a particular script, a unique handler
+     * is called for each script.
+     */
+
+    if (dstat & DSTAT_SIR) {
+       if (hostdata->options & OPTION_DEBUG_INTR)
+           printk ("scsi%d : DSTAT_SIR\n", host->host_no);
+       switch ((tmp = hostdata->dstat_sir_intr (host, cmd))) {
+       case SPECIFIC_INT_NOTHING:
+       case SPECIFIC_INT_RESTART:
+           break;
+       case SPECIFIC_INT_ABORT:
+           abort_connected(host);
+           break;
+       case SPECIFIC_INT_PANIC:
+           printk("scsi%d : failure at ", host->host_no);
+           print_insn (host, dsp, "", 1);
+           panic("          dstat_sir_intr() returned SPECIFIC_INT_PANIC\n");
+           break;
+       case SPECIFIC_INT_BREAK:
+           intr_break (host, cmd);
+           break;
+       default:
+           printk("scsi%d : failure at ", host->host_no);
+           print_insn (host, dsp, "", 1);
+           panic("          dstat_sir_intr() returned unknown value %d\n", 
+               tmp);
+       }
+    } 
+
+/* All DMA interrupts are fatal.  Flush SCSI queue */
+    NCR53c7x0_write8 (STEST3_REG_800, STEST3_800_CSF);
+    while (NCR53c7x0_read8 (STEST3_REG_800) & STEST3_800_CSF);
+}
+
+/*
+ * Function : static int print_insn (struct Scsi_Host *host, 
+ *     unsigned long *insn, int kernel)
+ *
+ * Purpose : print numeric representation of the instruction pointed
+ *     to by insn to the debugging or kernel message buffer
+ *     as appropriate.  
+ *
+ *     If desired, a user level program can interpret this 
+ *     information.
+ *
+ * Inputs : host, insn - host, pointer to instruction, prefix - 
+ *     string to prepend, kernel - use printk instead of debugging buffer.
+ *
+ * Returns : size, in longs, of instruction printed.
+ */
+
+static int print_insn (struct Scsi_Host *host, unsigned long *insn,
+    char *prefix, int kernel) {
+    char buf[80],              /* Temporary buffer and pointer */
+       *tmp;                   
+    unsigned char dcmd;                /* dcmd register for *insn */
+    int size;
+
+    dcmd = (insn[0] >> 24) & 0xff;
+    sprintf(buf, "%s%08lx : 0x%08lx 0x%08lx", (prefix ? prefix : ""), 
+       (unsigned long) insn, insn[0], insn[1]);
+    tmp = buf + strlen(buf);
+    if ((dcmd & DCMD_TYPE_MASK) == DCMD_TYPE_MMI)  {
+       sprintf (tmp, " 0x%08lx\n", insn[2]);
+       size = 3;
+    } else {
+       sprintf (tmp, "\n");
+       size = 2;
+    }
+
+    if (kernel) 
+       printk ("%s", buf);
+#ifdef NCR_DEBUG
+    else {
+       size_t len = strlen(buf);
+       debugger_kernel_write(host, buf, len);
+    }
+#endif
+    return size;
+}
+
+/*
+ * Function : int NCR53c7xx_abort (Scsi_Cmnd *cmd)
+ * 
+ * Purpose : Abort an erratant SCSI command, doing all necessary
+ *     cleanup of the issue_queue, running_list, shared Linux/NCR
+ *     dsa issue and reconnect queues.
+ *
+ * Inputs : cmd - command to abort, code - entire result field
+ *
+ * Returns : 0 on success, -1 on failure.
+ */
+
+int NCR53c7xx_abort (Scsi_Cmnd *cmd) {
+    struct Scsi_Host *host = cmd->host;
+    struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *) 
+       host->hostdata;
+    int old_level;
+    struct NCR53c7x0_cmd *curr, **prev;
+    old_level = splx(0);
+
+/*
+ * The command could be hiding in the issue_queue.  This would be very
+ * nice, as commands can't be moved from the high level driver's issue queue 
+ * into the shared queue until an interrupt routine is serviced, and this
+ * moving is atomic.  
+ *
+ * If this is the case, we don't have to worry about anything - we simply
+ * pull the command out of the old queue, and call it aborted.
+ */
+
+    for (curr = hostdata->issue_queue, prev = &(hostdata->issue_queue);
+       curr && curr->cmd != cmd; prev = &(curr->next), curr = curr->next);
+
+    if (curr) {
+       *prev = curr->next;
+/* XXX - get rid of DLL ? */
+       if (curr->prev)
+           curr->prev->next = curr->next;
+
+       if (!scan_scsis_buf_busy) {
+#ifdef SCSI_MALLOC
+           scsi_free ((void *) curr->real, curr->size);
+#else
+           kfree_s ((void *) curr->real, curr->size);
+#endif
+       } else {
+           scan_scsis_buf_busy = 0;
+       }
+
+       cmd->result = 0;
+       cmd->scsi_done(cmd);
+       splx(old_level);
+       return SCSI_ABORT_SUCCESS;
+    }
+
+/* 
+ * That failing, the command could be in our list of allready executing 
+ * commands.  If this is the case, drastic measures are called for.  
+ */ 
+
+    for (curr = hostdata->running_list, prev = &(hostdata->running_list);
+       curr && curr->cmd != cmd; prev = &(curr->next), curr = curr->next);
+
+    if (curr) {
+       splx(old_level);
+       printk ("scsi%d : DANGER : command in running list, can not abort.\n",
+           cmd->host->host_no);
+       return SCSI_ABORT_SNOOZE;
+    }
+
+
+/* 
+ * And if we couldn't find it in any of our queues, it must have been 
+ * a dropped interrupt.
+ */
+
+    curr = (struct NCR53c7x0_cmd *) cmd->host_scribble;
+
+    if (!scan_scsis_buf_busy) {
+#ifdef SCSI_MALLOC
+       scsi_free ((void *) curr->real, curr->size);
+#else
+       kfree_s ((void *) curr->real, curr->size);
+#endif
+    } else {
+       scan_scsis_buf_busy = 0;
+    }
+
+    if (((cmd->result & 0xff00) == 0xff00) ||
+       ((cmd->result & 0xff) == 0xff)) {
+       printk ("scsi%d : did this command ever run?\n", host->host_no);
+    } else {
+       printk ("scsi%d : probably lost INTFLY, normal completion\n", 
+           host->host_no);
+    }
+    cmd->scsi_done(cmd);
+    splx(old_level);
+    return SCSI_ABORT_SNOOZE;
+}
+
+/*
+ * Function : int NCR53c7xx_reset (Scsi_Cmnd *cmd) 
+ * 
+ * Purpose : perform a hard reset of the SCSI bus and NCR
+ *     chip.
+ *
+ * Inputs : cmd - command which caused the SCSI RESET
+ *
+ * Returns : 0 on success.
+ */
+int NCR53c7xx_reset (Scsi_Cmnd *cmd) {
+    NCR53c7x0_local_declare();
+    struct Scsi_Host *host = cmd ? cmd->host : NULL;
+    struct NCR53c7x0_hostdata *hostdata = host ? 
+       (struct NCR53c7x0_hostdata *) host->hostdata : NULL;
+    if (host) NCR53c7x0_local_setup(host);
+    
+
+    printk ("scsi%d : DANGER : NCR53c7xx_reset is NOP\n",
+       cmd->host->host_no);
+    return SCSI_RESET_SNOOZE;
+}
+
+const char *NCR53c7xx_info (void) {
+    return("More info here\n");
+}
+
+/*
+ * The NCR SDMS bios follows Annex A of the SCSI-CAM draft, and 
+ * therefore shares the scsicam_bios_param function.
+ */
+
+static void print_dsa (struct Scsi_Host *host, unsigned long *dsa) {
+    struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+       host->hostdata;
+    Scsi_Cmnd * cmd;
+    struct NCR53c7x0_cmd * c;
+    int i, len;
+    char *ptr;
+
+    printk("scsi%d : dsa at 0x%x\n"
+           "        + %d : dsa_msgout length = %d, data = 0x%x\n" ,
+           host->host_no, (unsigned) dsa, hostdata->dsa_msgout,
+           dsa[hostdata->dsa_msgout / sizeof(long)],
+           dsa[hostdata->dsa_msgout / sizeof(long) + 1]);
+
+    for (i = dsa[hostdata->dsa_msgout / sizeof(long)],
+       ptr = (char *) dsa[hostdata->dsa_msgout / sizeof(long) + 1]; i > 0;
+       ptr += len, i -= len) {
+       printk("               ");
+       len = print_msg (ptr);
+       printk("\n");
+    }
+}
+
diff --git a/drivers/scsi/53c7,8xx.h b/drivers/scsi/53c7,8xx.h
new file mode 100644 (file)
index 0000000..273da7e
--- /dev/null
@@ -0,0 +1,1330 @@
+/*
+ * NCR 53c{7,8}0x0 driver, header file
+ *
+ * Sponsored by
+ *      iX Multiuser Multitasking Magazine
+ *     Hannover, Germany
+ *     hm@ix.de        
+ *
+ * Copyright 1993, Drew Eckhardt
+ *      Visionary Computing 
+ *      (Unix and Linux consulting and custom programming)
+ *      drew@Colorado.EDU
+ *     +1 (303) 786-7975
+ *
+ * TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation.
+ * 
+ * PRE-ALPHA
+ *
+ * For more information, please consult 
+ *
+ * NCR 53C700/53C700-66
+ * SCSI I/O Processor
+ * Data Manual
+ *
+ * NCR 53C810
+ * PCI-SCSI I/O Processor 
+ * Data Manual
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * +1 (719) 578-3400
+ *
+ * Toll free literature number
+ * +1 (800) 334-5454
+ *
+ */
+
+#ifndef NCR53c7x0_H
+#define NCR53c7x0_H
+
+
+/* 
+ * Prevent name space pollution in hosts.c, and only provide the 
+ * define we need to get the NCR53c7x0 driver into the host template
+ * array.
+ */
+
+#ifdef HOSTS_C 
+#include <linux/scsicam.h>
+extern int NCR53c7xx_abort(Scsi_Cmnd *);
+extern int NCR53c7xx_detect(Scsi_Host_Template *tpnt);
+extern const char *NCR53c7xx_info(void);
+extern int NCR53c7xx_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+extern int NCR53c7xx_reset(Scsi_Cmnd *);
+
+#define NCR53c7xx {NULL, "NCR53c{7,8}xx (rel 3)", NCR53c7xx_detect,    \
+       NULL, NCR53c7xx_info,                                           \
+       NULL, NCR53c7xx_queue_command, NCR53c7xx_abort, NCR53c7xx_reset,\
+        NULL, scsicam_bios_param,                                      \
+       /* can queue */ 1, /* id */ 7, 255 /* old SG_ALL */,            \
+       /* cmd per lun */ 1 , 0, 0, DISABLE_CLUSTERING}
+#else
+/* Register addresses, ordered numerically */
+
+
+/* SCSI control 0 rw, default = 0xc0 */ 
+#define SCNTL0_REG             0x00    
+#define SCNTL0_ARB1            0x80    /* 0 0 = simple arbitration */
+#define SCNTL0_ARB2            0x40    /* 1 1 = full arbitration */
+#define SCNTL0_STRT            0x20    /* Start Sequence */
+#define SCNTL0_WATN            0x10    /* Select with ATN */
+#define SCNTL0_EPC             0x08    /* Enable parity checking */
+/* Bit 2 is reserved on 800 series chips */
+#define SCNTL0_EPG_700         0x04    /* Enable parity generation */
+#define SCNTL0_AAP             0x02    /*  ATN/ on parity error */
+#define SCNTL0_TRG             0x01    /* Target mode */
+
+/* SCSI control 1 rw, default = 0x00 */
+
+#define SCNTL1_REG             0x01    
+#define SCNTL1_EXC             0x80    /* Extra Clock Cycle of Data setup */
+#define SCNTL1_ADB             0x40    /*  contents of SODL on bus */
+#define SCNTL1_ESR_700         0x20    /* Enable SIOP response to selection 
+                                          and reselection */
+#define SCNTL1_DHP_800         0x20    /* Disable halt on parity error or ATN
+                                          target mode only */
+#define SCNTL1_CON             0x10    /* Connected */
+#define SCNTL1_RST             0x08    /*  SCSI RST/ */
+#define SCNTL1_AESP            0x04    /* Force bad parity */
+#define SCNTL1_SND_700         0x02    /* Start SCSI send */
+#define SCNTL1_IARB_800                0x02    /* Immediate Arbitration, start
+                                          arbitration immediately after
+                                          busfree is detected */
+#define SCNTL1_RCV_700         0x01    /* Start SCSI receive */
+#define SCNTL1_SST_800         0x01    /* Start SCSI transfer */
+
+/* SCSI control 2 rw, */
+
+#define SCNTL2_REG_800         0x02    
+#define SCNTL2_800_SDU         0x80    /* SCSI disconnect unexpected */
+
+/* SCSI control 3 rw */
+
+#define SCNTL3_REG_800                 0x03    
+#define SCNTL3_800_SCF_SHIFT   4
+#define SCNTL3_800_SCF_MASK    0x70
+#define SCNTL3_800_SCF2                0x40    /* Synchronous divisor */
+#define SCNTL3_800_SCF1                0x20    /* 0x00 = SCLK/3 */
+#define SCNTL3_800_SCF0                0x10    /* 0x10 = SCLK/1 */
+                                       /* 0x20 = SCLK/1.5 
+                                          0x30 = SCLK/2 
+                                          0x40 = SCLK/3 */
+           
+#define SCNTL3_800_CCF_SHIFT   0
+#define SCNTL3_800_CCF_MASK    0x07
+#define SCNTL3_800_CCF2                0x04    /* 0x00 50.01 to 66 */
+#define SCNTL3_800_CCF1                0x02    /* 0x01 16.67 to 25 */
+#define SCNTL3_800_CCF0                0x01    /* 0x02 25.01 - 37.5 
+                                          0x03 37.51 - 50 
+                                          0x04 50.01 - 66 */
+
+/*  
+ * SCSI destination ID rw - the appropriate bit is set for the selected
+ * target ID.  This is written by the SCSI SCRIPTS processor.
+ * default = 0x00
+ */
+#define SDID_REG_700           0x02    
+#define SDID_REG_800           0x06
+
+#define GP_REG_800             0x07    /* General purpose IO */
+#define GP_800_IO1             0x02
+#define GP_800_IO2             0x01
+
+
+/* SCSI interrupt enable rw, default = 0x00 */
+#define SIEN_REG_700           0x03    
+#define SIEN0_REG_800          0x40
+#define SIEN_MA                        0x80    /* Phase mismatch (ini) or ATN (tgt) */
+#define SIEN_FC                        0x40    /* Functin complete */
+#define SIEN_700_STO           0x20    /* Selection or reselection timeout */
+#define SIEN_800_SEL           0x20    /* Selected */
+#define SIEN_700_SEL           0x10    /* Selected or reselected */
+#define SIEN_800_RESEL         0x10    /* Reselected */
+#define SIEN_SGE               0x08    /* SCSI gross error */
+#define SIEN_UDC               0x04    /* Unexpected disconnect */
+#define SIEN_RST               0x02    /* SCSI RST/ received */
+#define SIEN_PAR               0x01    /* Parity error */
+
+/* 
+ * SCSI chip ID rw
+ * NCR53c700 : 
+ *     When arbitrating, the highest bit is used, when reselection or selection
+ *     occurs, the chip responds to all IDs for which a bit is set.
+ *     default = 0x00 
+ * NCR53c810 : 
+ *     Uses bit mapping
+ */
+#define SCID_REG               0x04    
+/* Bit 7 is reserved on 800 series chips */
+#define SCID_800_RRE           0x40    /* Enable response to reselection */
+#define SCID_800_SRE           0x20    /* Enable response to selection */
+/* Bits four and three are reserved on 800 series chips */
+#define SCID_800_ENC_MASK      0x07    /* Encoded SCSI ID */
+
+/* SCSI transfer rw, default = 0x00 */
+#define SXFER_REG              0x05
+#define SXFER_DHP              0x80    /* Disable halt on parity */
+
+#define SXFER_TP2              0x40    /* Transfer period msb */
+#define SXFER_TP1              0x20
+#define SXFER_TP0              0x10    /* lsb */
+#define SXFER_TP_MASK          0x70
+#define SXFER_TP_SHIFT         4
+#define SXFER_TP_4             0x00    /* Divisors */
+#define SXFER_TP_5             0x10
+#define SXFER_TP_6             0x20
+#define SXFER_TP_7             0x30
+#define SXFER_TP_8             0x40
+#define SXFER_TP_9             0x50
+#define SXFER_TP_10            0x60
+#define SXFER_TP_11            0x70
+
+#define SXFER_MO3              0x08    /* Max offset msb */
+#define SXFER_MO2              0x04
+#define SXFER_MO1              0x02
+#define SXFER_MO0              0x01    /* lsb */
+#define SXFER_MO_MASK          0x0f
+#define SXFER_MO_SHIFT         0
+
+/* 
+ * SCSI output data latch rw
+ * The contents of this register are driven onto the SCSI bus when 
+ * the Assert Data Bus bit of the SCNTL1 register is set and 
+ * the CD, IO, and MSG bits of the SOCL register match the SCSI phase
+ */
+#define SODL_REG_700           0x06    
+#define SODL_REG_800           0x54
+
+
+/* 
+ * SCSI output control latch rw, default = 0 
+ * Note that when the chip is being manually programmed as an initiator,
+ * the MSG, CD, and IO bits must be set correctly for the phase the target
+ * is driving the bus in.  Otherwise no data transfer will occur due to 
+ * phase mismatch.
+ */
+
+#define SBCL_REG               0x0b
+#define SBCL_REQ               0x80    /*  REQ */
+#define SBCL_ACK               0x40    /*  ACK */
+#define SBCL_BSY               0x20    /*  BSY */
+#define SBCL_SEL               0x10    /*  SEL */
+#define SBCL_ATN               0x08    /*  ATN */
+#define SBCL_MSG               0x04    /*  MSG */
+#define SBCL_CD                        0x02    /*  C/D */
+#define SBCL_IO                        0x01    /*  I/O */
+#define SBCL_PHASE_CMDOUT      SBCL_CD
+#define SBCL_PHASE_DATAIN      SBCL_IO
+#define SBCL_PHASE_DATAOUT     0
+#define SBCL_PHASE_MSGIN       (SBCL_CD|SBCL_IO|SBCL_MSG)
+#define SBCL_PHASE_MSGOUT      (SBCL_CD|SBCL_MSG)
+#define SBCL_PHASE_STATIN      (SBCL_CD|SBCL_IO)
+#define SBCL_PHASE_MASK                (SBCL_CD|SBCL_IO|SBCL_MSG)
+
+/* 
+ * SCSI first byte recieved latch ro 
+ * This register contains the first byte received during a block MOVE 
+ * SCSI SCRIPTS instruction, including
+ * 
+ * Initiator mode      Target mode
+ * Message in          Command
+ * Status              Message out
+ * Data in             Data out
+ *
+ * It also contains the selecting or reselecting device's ID and our 
+ * ID.
+ *
+ * Note that this is the register the various IF conditionals can 
+ * operate on.
+ */
+#define SFBR_REG               0x08    
+
+/* 
+ * SCSI input data latch ro
+ * In initiator mode, data is latched into this register on the rising
+ * edge of REQ/. In target mode, data is latched on the rising edge of 
+ * ACK/
+ */
+#define SIDL_REG_700           0x09
+#define SIDL_REG_800           0x50
+
+/* 
+ * SCSI bus data lines ro 
+ * This register reflects the instantenous status of the SCSI data 
+ * lines.  Note that SCNTL0 must be set to disable parity checking, 
+ * otherwise reading this register will latch new parity.
+ */
+#define SBDL_REG_700           0x0a
+#define SBDL_REG_800           0x58
+
+#define SSID_REG_800           0x0a
+#define SSID_800_VAL           0x80    /* Exactly two bits asserted at sel */
+#define SSID_800_ENCID_MASK    0x07    /* Device which performed operation */
+
+
+/* 
+ * SCSI bus control lines rw, 
+ * instantaneous readout of control lines 
+ */
+#define SOCL_REG               0x0b    
+#define SOCL_REQ               0x80    /*  REQ ro */
+#define SOCL_ACK               0x40    /*  ACK ro */
+#define SOCL_BSY               0x20    /*  BSY ro */
+#define SOCL_SEL               0x10    /*  SEL ro */
+#define SOCL_ATN               0x08    /*  ATN ro */
+#define SOCL_MSG               0x04    /*  MSG ro */
+#define SOCL_CD                        0x02    /*  C/D ro */
+#define SOCL_IO                        0x01    /*  I/O ro */
+/* 
+ * Syncronous SCSI Clock Control bits 
+ * 0 - set by DCNTL 
+ * 1 - SCLK / 1.0
+ * 2 - SCLK / 1.5
+ * 3 - SCLK / 2.0 
+ */
+#define SBCL_SSCF1             0x02    /* wo, -66 only */
+#define SBCL_SSCF0             0x01    /* wo, -66 only */
+#define SBCL_SSCF_MASK         0x03
+
+/* 
+ * XXX note : when reading the DSTAT and STAT registers to clear interrupts,
+ * insure that 10 clocks elapse between the two  
+ */
+/* DMA status ro */
+#define DSTAT_REG              0x0c    
+#define DSTAT_DFE              0x80    /* DMA FIFO empty */
+#define DSTAT_800_MDPE         0x40    /* Master Data Parity Error */
+#define DSTAT_800_BF           0x20    /* Bus Fault */
+#define DSTAT_ABRT             0x10    /* Aborted - set on error */
+#define DSTAT_SSI              0x08    /* SCRIPTS single step interrupt */
+#define DSTAT_SIR              0x04    /* SCRIPTS interrupt received - 
+                                          set when INT instruction is 
+                                          executed */
+#define DSTAT_WTD              0x02    /* Watchdog timeout detected */
+#define DSTAT_OPC              0x01    /* Illegal instruction */
+#define DSTAT_800_IID          0x01    /* Same thing, different name */
+
+
+#define SSTAT0_REG             0x0d    /* SCSI status 0 ro */
+#define SIST0_REG_800          0x42    
+#define SSTAT0_MA              0x80    /* ini : phase mismatch,
+                                        * tgt : ATN/ asserted 
+                                        */
+#define SSTAT0_CMP             0x40    /* function complete */
+#define SSTAT0_700_STO         0x20    /* Selection or reselection timeout */
+#define SIST0_800_SEL          0x20    /* Selected */
+#define SSTAT0_700_SEL         0x10    /* Selected or reselected */
+#define SIST0_800_RSL          0x10    /* Reselected */
+#define SSTAT0_SGE             0x08    /* SCSI gross error */
+#define SSTAT0_UDC             0x04    /* Unexpected disconnect */
+#define SSTAT0_RST             0x02    /* SCSI RST/ received */
+#define SSTAT0_PAR             0x01    /* Parity error */
+
+#define SSTAT1_REG             0x0e    /* SCSI status 1 ro */
+#define SSTAT1_ILF             0x80    /* SIDL full */
+#define SSTAT1_ORF             0x40    /* SODR full */
+#define SSTAT1_OLF             0x20    /* SODL full */
+#define SSTAT1_AIP             0x10    /* Arbitration in progress */
+#define SSTAT1_LOA             0x08    /* Lost arbitration */
+#define SSTAT1_WOA             0x04    /* Won arbitration */
+#define SSTAT1_RST             0x02    /* Instant readout of RST/ */
+#define SSTAT1_SDP             0x01    /* Instant readout of SDP/ */
+
+#define SSTAT2_REG             0x0f    /* SCSI status 2 ro */
+#define SSTAT2_FF3             0x80    /* number of bytes in syncronous */
+#define SSTAT2_FF2             0x40    /* data FIFO */
+#define SSTAT2_FF1             0x20    
+#define SSTAT2_FF0             0x10
+#define SSTAT2_FF_MASK         0xf0
+
+/* 
+ * Latched signals, latched on the leading edge of REQ/ for initiators,
+ * ACK/ for targets.
+ */
+#define SSTAT2_SDP             0x08    /* SDP */
+#define SSTAT2_MSG             0x04    /* MSG */
+#define SSTAT2_CD              0x02    /* C/D */
+#define SSTAT2_IO              0x01    /* I/O */
+
+
+/* NCR53c700-66 only */
+#define SCRATCHA_REG_00                0x10    /* through  0x13 Scratch A rw */
+/* NCR53c710 and higher */
+#define DSA_REG                        0x10    /* DATA structure address */
+
+#define CTEST0_REG_700         0x14    /* Chip test 0 ro */
+#define CTEST0_REG_800         0x18    /* Chip test 0 rw, general purpose */
+/* 0x80 - 0x04 are reserved */
+#define CTEST0_700_RTRG                0x02    /* Real target mode */
+#define CTEST0_700_DDIR                0x01    /* Data direction, 1 = 
+                                        * SCSI bus to host, 0  =
+                                        * host to SCSI.
+                                        */
+
+#define CTEST1_REG_700         0x15    /* Chip test 1 ro */
+#define CTEST1_REG_800         0x19    /* Chip test 1 ro */
+#define CTEST1_FMT3            0x80    /* Identify which byte lanes are empty */
+#define CTEST1_FMT2            0x40    /* in the DMA FIFO */
+#define CTEST1_FMT1            0x20
+#define CTEST1_FMT0            0x10
+
+#define CTEST1_FFL3            0x08    /* Identify which bytes lanes are full */
+#define CTEST1_FFL2            0x04    /* in the DMA FIFO */
+#define CTEST1_FFL1            0x02
+#define CTEST1_FFL0            0x01
+
+#define CTEST2_REG_700         0x16    /* Chip test 2 ro */
+#define CTEST2_REG_800         0x1a    /* Chip test 2 ro */
+
+#define CTEST2_800_DDIR                0x80    /* 1 = SCSI->host */
+#define CTEST2_800_SIGP                0x40    /* A copy of SIGP in ISTAT.
+                                          Reading this register clears */
+#define CTEST2_800_CIO         0x20    /* Configured as IO */.
+#define CTEST2_800_CM          0x10    /* Configured as memory */
+
+/* 0x80 - 0x40 are reserved on 700 series chips */
+#define CTEST2_700_SOFF                0x20    /* SCSI Offset Compare,
+                                        * As an initator, this bit is 
+                                        * one when the synchronous offset
+                                        * is zero, as a target this bit 
+                                        * is one when the synchronous 
+                                        * offset is at the maximum
+                                        * defined in SXFER
+                                        */
+#define CTEST2_700_SFP         0x10    /* SCSI FIFO parity bit,
+                                        * reading CTEST3 unloads a byte
+                                        * from the FIFO and sets this
+                                        */
+#define CTEST2_700_DFP         0x08    /* DMA FIFO parity bit,
+                                        * reading CTEST6 unloads a byte
+                                        * from the FIFO and sets this
+                                        */
+#define CTEST2_TEOP            0x04    /* SCSI true end of process,
+                                        * indicates a totally finished
+                                        * transfer
+                                        */
+#define CTEST2_DREQ            0x02    /* Data request signal */
+/* 0x01 is reserved on 700 series chips */
+#define CTEST2_800_DACK                0x01    
+
+/* 
+ * Chip test 3 ro 
+ * Unloads the bottom byte of the eight deep SCSI synchronous FIFO,
+ * check SSTAT2 FIFO full bits to determine size.  Note that a GROSS
+ * error results if a read is attempted on this register.  Also note 
+ * that 16 and 32 bit reads of this register will cause corruption.
+ */
+#define CTEST3_REG_700         0x17    
+/*  Chip test 3 rw */
+#define CTEST3_REG_800         0x1b
+#define CTEST3_800_V3          0x80    /* Chip revision */
+#define CTEST3_800_V2          0x40
+#define CTEST3_800_V1          0x20
+#define CTEST3_800_V0          0x10
+#define CTEST3_800_FLF         0x08    /* Flush DMA FIFO */
+#define CTEST3_800_CLF         0x04    /* Clear DMA FIFO */
+#define CTEST3_800_FM          0x02    /* Fetch mode pin */
+/* bit 0 is reserved on 800 series chips */
+
+#define CTEST4_REG_700         0x18    /* Chip test 4 rw */
+#define CTEST4_REG_800         0x21    /* Chip test 4 rw */
+/* 0x80 is reserved on 700 series chips */
+#define CTEST4_800_BDIS                0x80    /* Burst mode disable */
+#define CTEST4_ZMOD            0x40    /* High impedance mode */
+#define CTEST4_SZM             0x20    /* SCSI bus high impedance */
+#define CTEST4_700_SLBE                0x10    /* SCSI loopback enabled */
+#define CTEST4_800_SRTM                0x10    /* Shadow Register Test Mode */
+#define CTEST4_700_SFWR                0x08    /* SCSI FIFO write enable, 
+                                        * redirects writes from SODL
+                                        * to the SCSI FIFO.
+                                        */
+#define CTEST4_800_MPEE                0x08    /* Enable parity checking
+                                          during master cycles on PCI
+                                          bus */
+
+/* 
+ * These bits send the contents of the CTEST6 register to the appropriate
+ * byte lane of the 32 bit DMA FIFO.  Normal operation is zero, otherwise 
+ * the high bit means the low two bits select the byte lane.
+ */
+#define CTEST4_FBL2            0x04    
+#define CTEST4_FBL1            0x02
+#define CTEST4_FBL0            0x01    
+#define CTEST4_FBL_MASK                0x07
+#define CTEST4_FBL_0           0x04    /* Select DMA FIFO byte lane 0 */
+#define CTEST4_FBL_1           0x05    /* Select DMA FIFO byte lane 1 */
+#define CTEST4_FBL_2           0x06    /* Select DMA FIFO byte lane 2 */
+#define CTEST4_FBL_3           0x07    /* Select DMA FIFO byte lane 3 */
+#define CTEST4_800_SAVE                (CTEST4_800_BDIS)
+
+
+#define CTEST5_REG_700         0x19    /* Chip test 5 rw */
+#define CTEST5_REG_800         0x22    /* Chip test 5 rw */
+/* 
+ * Clock Address Incrementor.  When set, it increments the 
+ * DNAD register to the next bus size boundary.  It automatically 
+ * resets itself when the operation is complete.
+ */
+#define CTEST5_ADCK            0x80
+/*
+ * Clock Byte Counter.  When set, it decrements the DBC register to
+ * the next bus size boundary.
+ */
+#define CTEST5_BBCK            0x40
+/*
+ * Reset SCSI Offset.  Setting this bit to 1 cleares the current offset
+ * pointer in the SCSI synchronous offset counter (SSTAT).  This bit
+ * is set to 1 if a SCSI Gross Error Condition occurs.  The offset should
+ * be cleared when a synchronous transfer fails.  When written, it is 
+ * automatically cleared after the SCSI syncrnous offset counter is 
+ * reset.
+ */
+/* Bit 5 is reserved on 800 series chips */
+#define CTEST5_700_ROFF                0x20
+/* 
+ * Master Control for Set or Reset pulses. When 1, causes the low 
+ * four bits of register to set when set, 0 causes the low bits to
+ * clear when set.
+ */
+#define CTEST5_MASR            0x10    
+#define CTEST5_DDIR            0x08    /* DMA direction */
+/*
+ * Bits 2-0 are reserved on 800 series chips
+ */
+#define CTEST5_700_EOP         0x04    /* End of process */
+#define CTEST5_700_DREQ                0x02    /* Data request */
+#define CTEST5_700_DACK                0x01    /* Data acknowledge */
+
+/* 
+ * Chip test 6 rw - writing to this register writes to the byte 
+ * lane in the DMA FIFO as determined by the FBL bits in the CTEST4
+ * register.
+ */
+#define CTEST6_REG_700         0x1a
+#define CTEST6_REG_800         0x23
+
+#define CTEST7_REG             0x1b    /* Chip test 7 rw */
+/* 0x80 - 0x40 are reserved on NCR53c700 and NCR53c700-66 chips */
+#define CTEST7_10_CDIS         0x80    /* Cache burst disable */
+#define CTEST7_10_SC1          0x40    /* Snoop control bits */
+#define CTEST7_10_SC0          0x20    
+#define CTEST7_10_SC_MASK      0x60
+/* 0x20 is reserved on the NCR53c700 */
+#define CTEST7_0060_FM         0x20    /* Fetch mode */
+#define CTEST7_STD             0x10    /* Selection timeout disable */
+#define CTEST7_DFP             0x08    /* DMA FIFO parity bit for CTEST6 */
+#define CTEST7_EVP             0x04    /* 1 = host bus even parity, 0 = odd */
+#define CTEST7_10_TT1          0x02    /* Transfer type */
+#define CTEST7_00_DC           0x02    /* Set to drive DC low during instruction 
+                                           fetch */
+#define CTEST7_DIFF            0x01    /* Differential mode */
+
+#define CTEST7_SAVE ( CTEST7_EVP | CTEST7_DIFF )
+
+
+#define TEMP_REG               0x1c    /* through 0x1f Temporary stack rw */
+
+#define DFIFO_REG              0x20    /* DMA FIFO rw */
+/* 
+ * 0x80 is reserved on the NCR53c710, the CLF and FLF bits have been
+ * moved into the CTEST8 register.
+ */
+#define DFIFO_00_FLF           0x80    /* Flush DMA FIFO to memory */
+#define DFIFO_00_CLF           0x40    /* Clear DMA and SCSI FIFOs */
+#define DFIFO_BO6              0x40
+#define DFIFO_BO5              0x20
+#define DFIFO_BO4              0x10
+#define DFIFO_BO3              0x08
+#define DFIFO_BO2              0x04 
+#define DFIFO_BO1              0x02
+#define DFIFO_BO0              0x01
+#define DFIFO_10_BO_MASK       0x7f    /* 7 bit counter */
+#define DFIFO_00_BO_MASK       0x3f    /* 6 bit counter */
+
+/* 
+ * Interrupt status rw 
+ * Note that this is the only register which can be read while SCSI
+ * SCRIPTS are being executed.
+ */
+#define ISTAT_REG_700          0x21
+#define ISTAT_REG_800          0x14
+#define ISTAT_ABRT             0x80    /* Software abort, write 
+                                        *1 to abort, wait for interrupt. */
+/* 0x40 and 0x20 are reserved on NCR53c700 and NCR53c700-66 chips */
+#define ISTAT_10_SRST          0x40    /* software reset */
+#define ISTAT_10_SIGP          0x20    /* signal script */
+/* 0x10 is reserved on NCR53c700 series chips */
+#define ISTAT_800_SEM          0x10    /* semaphore */
+#define ISTAT_CON              0x08    /* 1 when connected */
+#define ISTAT_800_INTF         0x04    /* Interrupt on the fly */
+#define ISTAT_700_PRE          0x04    /* Pointer register empty.
+                                        * Set to 1 when DSPS and DSP
+                                        * registers are empty in pipeline
+                                        * mode, allways set otherwise.
+                                        */
+#define ISTAT_SIP              0x02    /* SCSI interrupt pending from
+                                        * SCSI portion of SIOP see
+                                        * SSTAT0
+                                        */
+#define ISTAT_DIP              0x01    /* DMA interrupt pending 
+                                        * see DSTAT
+                                        */
+
+/* NCR53c700-66 and NCR53c710 only */
+#define CTEST8_REG             0x22    /* Chip test 8 rw */
+#define CTEST8_0066_EAS                0x80    /* Enable alternate SCSI clock,
+                                        * ie read from SCLK/ rather than CLK/
+                                        */
+#define CTEST8_0066_EFM                0x40    /* Enable fetch and master outputs */
+#define CTEST8_0066_GRP                0x20    /* Generate Receive Parity for 
+                                        * pass through.  This insures that 
+                                        * bad parity won't reach the host 
+                                        * bus.
+                                        */
+#define CTEST8_0066_TE         0x10    /* TolerANT enable.  Enable 
+                                        * active negation, should only
+                                        * be used for slow SCSI 
+                                        * non-differential.
+                                        */
+#define CTEST8_0066_HSC                0x08    /* Halt SCSI clock */
+#define CTEST8_0066_SRA                0x04    /* Shorten REQ/ACK filtering,
+                                        * must be set for fast SCSI-II
+                                        * speeds.
+                                        */
+#define CTEST8_0066_DAS                0x02    /* Disable automatic target/initiator
+                                        * switching.
+                                        */
+#define CTEST8_0066_LDE                0x01    /* Last disconnect enable.
+                                        * The status of pending 
+                                        * disconnect is maintained by
+                                        * the core, eliminating
+                                        * the possibility of missing a 
+                                        * selection or reselection
+                                        * while waiting to fetch a 
+                                        * WAIT DISCONNECT opcode.
+                                        */
+
+#define CTEST8_10_V3           0x80    /* Chip revision */
+#define CTEST8_10_V2           0x40
+#define CTEST8_10_V1           0x20    
+#define CTEST8_10_V0           0x10
+#define CTEST8_10_V_MASK       0xf0    
+#define CTEST8_10_FLF          0x08    /* Flush FIFOs */
+#define CTEST8_10_CLF          0x04    /* Clear FIFOs */
+#define CTEST8_10_FM           0x02    /* Fetch pin mode */
+#define CTEST8_10_SM           0x01    /* Snoop pin mode */
+
+
+/* 
+ * The CTEST9 register may be used to diffentiate between a
+ * NCR53c700 and a NCR53c710.  
+ *
+ * Write 0xff to this register.
+ * Read it.
+ * If the contents are 0xff, it is a NCR53c700
+ * If the contents are 0x00, it is a NCR53c700-66 first revision
+ * If the contents are zome other value, it is some other NCR53c700-66
+ */
+#define CTEST9_REG_00          0x23    /* Chip test 9 ro */
+#define LCRC_REG_10            0x23    
+
+/*
+ * 0x24 through 0x27 are the DMA byte counter register.  Instructions
+ * write their high 8 bits into the DCMD register, the low 24 bits into
+ * the DBC register.
+ *
+ * Function is dependant on the command type being executed.
+ */
+
+#define DBC_REG                        0x24
+/* 
+ * For Block Move Instructions, DBC is a 24 bit quantity representing 
+ *     the number of bytes to transfer.
+ * For Transfer Control Intructions, DBC is bit fielded as follows : 
+ */
+/* Bits 20 - 23 should be clear */
+#define DBC_TCI_TRUE           (1 << 19)       /* Jump when true */
+#define DBC_TCI_COMPARE_DATA   (1 << 18)       /* Compare data */
+#define DBC_TCI_COMPARE_PHASE  (1 << 17)       /* Compare phase with DCMD field */
+#define DBC_TCI_WAIT_FOR_VALID (1 << 16)       /* Wait for REQ */
+/* Bits 8 - 15 are reserved on some implementations ? */
+#define DBC_TCI_MASK_MASK      0xff00          /* Mask for data compare */
+#define DBC_TCI_MASK_SHIFT     8
+#define DBC_TCI_DATA_MASK      0xff            /* Data to be compared */ 
+#define DBC_TCI_DATA_SHIFT     0
+
+#define DBC_RWRI_IMMEDIATE_MASK        0xff00          /* Immediate data */
+#define DBC_RWRI_IMMEDIATE_SHIFT 8             /* Amount to shift */
+#define DBC_RWRI_ADDRESS_MASK  0x3f0000        /* Register address */
+#define DBC_RWRI_ADDRESS_SHIFT         16
+
+
+/*
+ * DMA command r/w
+ */
+#define DCMD_REG               0x27    
+#define DCMD_TYPE_MASK         0xc0    /* Masks off type */
+#define DCMD_TYPE_BMI          0x00    /* Indicates a Block Move instruction */
+#define DCMD_BMI_IO            0x01    /* I/O, CD, and MSG bits selecting   */
+#define DCMD_BMI_CD            0x02    /* the phase for the block MOVE      */
+#define DCMD_BMI_MSG           0x04    /* instruction                       */
+
+#define DCMD_BMI_OP_MASK       0x18    /* mask for opcode */
+#define DCMD_BMI_OP_MOVE_T     0x00    /* MOVE */
+#define DCMD_BMI_OP_MOVE_I     0x08    /* MOVE Initiator */
+
+#define DCMD_BMI_INDIRECT      0x20    /*  Indirect addressing */
+
+#define DCMD_TYPE_TCI          0x80    /* Indicates a Transfer Control 
+                                          instruction */
+#define DCMD_TCI_IO            0x01    /* I/O, CD, and MSG bits selecting   */
+#define DCMD_TCI_CD            0x02    /* the phase for the block MOVE      */
+#define DCMD_TCI_MSG           0x04    /* instruction                       */
+#define DCMD_TCI_OP_MASK       0x38    /* mask for opcode */
+#define DCMD_TCI_OP_JUMP       0x00    /* JUMP */
+#define DCMD_TCI_OP_CALL       0x08    /* CALL */
+#define DCMD_TCI_OP_RETURN     0x10    /* RETURN */
+#define DCMD_TCI_OP_INT                0x18    /* INT */
+
+#define DCMD_TYPE_RWRI         0x40    /* Indicates I/O or register Read/Write
+                                          instruction */
+#define DCMD_RWRI_OPC_MASK     0x38    /* Opcode mask */
+#define DCMD_RWRI_OPC_WRITE    0x28    /* Write SFBR to register */
+#define DCMD_RWRI_OPC_READ     0x30    /* Read register to SFBR */
+#define DCMD_RWRI_OPC_MODIFY   0x38    /* Modify inplace */
+
+#define DCMD_RWRI_OP_MASK      0x07
+#define DCMD_RWRI_OP_MOVE      0x00
+#define DCMD_RWRI_OP_SHL       0x01
+#define DCMD_RWRI_OP_OR                0x02
+#define DCMD_RWRI_OP_XOR       0x03
+#define DCMD_RWRI_OP_AND       0x04
+#define DCMD_RWRI_OP_SHR       0x05
+#define DCMD_RWRI_OP_ADD       0x06
+#define DCMD_RWRI_OP_ADDC      0x07
+
+#define DCMD_TYPE_MMI          0xc0    /* Indicates a Memory Move instruction 
+                                          (three longs) */
+
+
+#define DNAD_REG               0x28    /* through 0x2b DMA next address for 
+                                          data */
+#define DSP_REG                        0x2c    /* through 0x2f DMA SCRIPTS pointer rw */
+#define DSPS_REG               0x30    /* through 0x33 DMA SCRIPTS pointer 
+                                          save rw */
+#define DMODE_REG_00           0x34    /* DMA mode rw */
+#define DMODE_00_BL1   0x80    /* Burst length bits */
+#define DMODE_00_BL0   0x40
+#define DMODE_BL_MASK  0xc0
+/* Burst lengths (800) */
+#define DMODE_BL_2     0x00    /* 2 transfer */
+#define DMODE_BL_4     0x40    /* 4 transfers */
+#define DMODE_BL_8     0x80    /* 8 transfers */
+#define DMODE_BL_16    0xc0    /* 16 transfers */
+
+#define DMODE_700_BW16 0x20    /* Host buswidth = 16 */
+#define DMODE_700_286  0x10    /* 286 mode */
+#define DMODE_700_IOM  0x08    /* Transfer to IO port */
+#define DMODE_700_FAM  0x04    /* Fixed address mode */
+#define DMODE_700_PIPE 0x02    /* Pipeline mode disables 
+                                        * automatic fetch / exec 
+                                        */
+#define DMODE_MAN      0x01            /* Manual start mode, 
+                                        * requires a 1 to be written
+                                        * to the start DMA bit in the DCNTL
+                                        * register to run scripts 
+                                        */
+
+#define DMODE_700_SAVE ( DMODE_00_BL_MASK | DMODE_00_BW16 | DMODE_00_286 )
+
+/* NCR53c800 series only */
+#define SCRATCHA_REG_800       0x34    /* through 0x37 Scratch A rw */
+/* NCR53c710 only */
+#define SCRATCB_REG_10         0x34    /* through 0x37 scratch B rw */
+
+#define DMODE_REG_10           0x38    /* DMA mode rw, NCR53c710 and newer */
+#define DMODE_800_SIOM         0x20    /* Source IO = 1 */
+#define DMODE_800_DIOM         0x10    /* Destination IO = 1 */
+#define DMODE_800_ERL          0x08    /* Enable Read Line */
+
+/* 35-38 are reserved on 700 and 700-66 series chips */
+#define DIEN_REG               0x39    /* DMA interrupt enable rw */
+/* 0x80, 0x40, and 0x20 are reserved on 700-series chips */
+#define DIEN_800_MDPE          0x40    /* Master data parity error */
+#define DIEN_800_BF            0x20    /* BUS fault */
+#define DIEN_ABRT              0x10    /* Enable aborted interrupt */
+#define DIEN_SSI               0x08    /* Enable single step interrupt */
+#define DIEN_SIR               0x04    /* Enable SCRIPTS INT command 
+                                        * interrupt
+                                        */
+/* 0x02 is reserved on 800 series chips */
+#define DIEN_700_WTD           0x02    /* Enable watchdog timeout interrupt */
+#define DIEN_700_OPC           0x01    /* Enable illegal instruction 
+                                        * interrupt 
+                                        */
+#define DIEN_800_IID           0x01    /*  Same meaning, different name */ 
+
+/*
+ * DMA watchdog timer rw
+ * set in 16 CLK input periods.
+ */
+#define DWT_REG                        0x3a
+
+/* DMA control rw */
+#define DCNTL_REG              0x3b
+#define DCNTL_700_CF1          0x80    /* Clock divisor bits */
+#define DCNTL_700_CF0          0x40
+#define DCNTL_700_CF_MASK      0xc0
+/* Clock divisors                         Divisor SCLK range (MHZ) */
+#define DCNTL_700_CF_2         0x00    /* 2.0     37.51-50.00 */
+#define DCNTL_700_CF_1_5       0x40    /* 1.5     25.01-37.50 */
+#define DCNTL_700_CF_1         0x80    /* 1.0     16.67-25.00 */
+#define DCNTL_700_CF_3         0xc0    /* 3.0     50.01-66.67 (53c700-66) */
+
+#define DCNTL_700_S16          0x20    /* Load scripts 16 bits at a time */
+#define DCNTL_SSM              0x10    /* Single step mode */
+#define DCNTL_700_LLM          0x08    /* Low level mode, can only be set 
+                                        * after selection */
+#define DCNTL_800_IRQM         0x08    /* Totem pole IRQ pin */
+#define DCNTL_STD              0x04    /* Start DMA / SCRIPTS */
+/* 0x02 is reserved */
+#define DCNTL_00_RST           0x01    /* Software reset, resets everything
+                                        * but 286 mode bit  in DMODE. On the
+                                        * NCR53c710, this bit moved to CTEST8
+                                        */
+#define DCNTL_10_COM           0x01    /* 700 software compatability mode */
+
+#define DCNTL_700_SAVE ( DCNTL_CF_MASK | DCNTL_S16)
+
+
+/* NCR53c700-66 only */
+#define SCRATCHB_REG_00                0x3c    /* through 0x3f scratch b rw */
+#define SCRATCHB_REG_800       0x5c    /* through 0x5f scratch b rw */
+/* NCR53c710 only */
+#define ADDER_REG_10           0x3c    /* Adder, NCR53c710 only */
+
+#define SIEN1_REG_800          0x41
+#define SIEN1_800_STO          0x04    /* selection/reselection timeout */
+#define SIEN1_800_GEN          0x02    /* general purpose timer */
+#define SIEN1_800_HTH          0x01    /* handshake to handshake */
+
+#define SIST1_REG_800          0x43
+#define SIST1_800_STO          0x04    /* selection/reselection timeout */
+#define SIST1_800_GEN          0x02    /* general purpose timer */
+#define SIST1_800_HTH          0x01    /* handshake to handshake */
+
+#define SLPAR_REG_800          0x44    /* Parity */
+
+#define MACNTL_REG_800         0x46    /* Memory access control */
+#define MACNTL_800_TYP3                0x80
+#define MACNTL_800_TYP2                0x40
+#define MACNTL_800_TYP1                0x20
+#define MACNTL_800_TYP0                0x10
+#define MACNTL_800_DWR         0x08
+#define MACNTL_800_DRD         0x04
+#define MACNTL_800_PSCPT       0x02
+#define MACNTL_800_SCPTS       0x01
+
+#define GPCNTL_REG_800         0x47    /* General Purpose Pin Control */
+
+/* Timeouts are expressed such that 0=off, 1=100us, doubling after that */
+#define STIME0_REG_800         0x48    /* SCSI Timer Register 0 */
+#define STIME0_800_HTH_MASK    0xf0    /* Handshake to Handshake timeout */
+#define STIME0_800_HTH_SHIFT   4
+#define STIME0_800_SEL_MASK    0x0f    /* Selection timeout */
+#define STIME0_800_SEL_SHIFT   0
+
+#define STIME1_REG_800         0x49
+#define STIME1_800_GEN_MASK    0x0f    /* General purpose timer */
+
+#define RESPID_REG_800         0x4a    /* Response ID, bit fielded */
+
+#define STEST0_REG_800         0x4c    
+#define STEST0_800_SLT         0x08    /* Selection response logic test */
+#define STEST0_800_ART         0x04    /* Arbitration priority encoder test */
+#define STEST0_800_SOZ         0x02    /* Synchronous offset zero */
+#define STEST0_800_SOM         0x01    /* Synchronous offset maximum */
+
+#define STEST1_REG_800         0x4d
+#define STEST1_800_SCLK                0x80    /* Disable SCSI clock */
+
+#define STEST2_REG_800         0x4e    
+#define STEST2_800_SCE         0x80    /* Enable SOCL/SODL */
+#define STEST2_800_ROF         0x40    /* Reset SCSI sync offset */
+#define STEST2_800_SLB         0x10    /* Enable SCSI looback mode */
+#define STEST2_800_SZM         0x08    /* SCSI high impedance mode */
+#define STEST2_800_EXT         0x02    /* Extend REQ/ACK filter 30 to 60ns */
+#define STEST2_800_LOW         0x01    /* SCSI low level mode */
+
+#define STEST3_REG_800         0x4f     
+#define STEST3_800_TE          0x80    /* Enable active negation */
+#define STEST3_800_STR         0x40    /* SCSI FIFO test read */
+#define STEST3_800_HSC         0x20    /* Halt SCSI clock */
+#define STEST3_800_DSI         0x10    /* Disable single initiator response */
+#define STEST3_800_TTM         0x04    /* Time test mode */
+#define STEST3_800_CSF         0x02    /* Clear SCSI FIFO */
+#define STEST3_800_STW         0x01    /* SCSI FIFO test write */
+
+
+
+
+
+#define OPTION_PARITY          0x1     /* Enable parity checking */
+#define OPTION_TAGGED_QUEUE    0x2     /* Enable SCSI-II tagged queuing */
+#define OPTION_700             0x8     /* Allways run NCR53c700 scripts */
+#define OPTION_INTFLY          0x10    /* Use INTFLY interrupts */
+#define OPTION_DEBUG_INTR      0x20    /* Debug interrupts */
+#define OPTION_DEBUG_INIT_ONLY 0x40    /* Run initialization code and 
+                                          simple test code, return
+                                          DID_NO_CONNECT if any SCSI
+                                          commands are attempted. */
+#define OPTION_DEBUG_READ_ONLY 0x80    /* Return DID_ERROR if any 
+                                          SCSI write is attempted */
+#define OPTION_DEBUG_TRACE     0x100   /* Animated trace mode, print 
+                                          each address and instruction 
+                                          executed to debug buffer. */
+#define OPTION_DEBUG_SINGLE    0x200   /* stop after executing one 
+                                          instruction */
+#define OPTION_SYNCHRONOUS     0x400   /* Enable sync SCSI.  */
+#define OPTION_MEMORY_MAPPED   0x800   /* NCR registers have valid 
+                                          memory mapping */
+#define OPTION_IO_MAPPED       0x1000  /* NCR registers have valid
+                                          I/O mapping */
+#define OPTION_DEBUG_PROBE_ONLY        0x2000  /* Probe only, don't even init */
+#define OPTION_DEBUG_TESTS_ONLY        0x4000  /* Probe, init, run selected tests */
+
+#define OPTION_DEBUG_TEST0     0x08000 /* Run test 0 */
+#define OPTION_DEBUG_TEST1     0x10000 /* Run test 1 */
+#define OPTION_DEBUG_TEST2     0x20000 /* Run test 2 */
+
+#define OPTION_DEBUG_DUMP      0x40000 /* Dump commands */
+#define OPTION_DEBUG_TARGET_LIMIT 0x80000 /* Only talk to target+luns specified */
+#define OPTION_DEBUG_NCOMMANDS_LIMIT 0x100000 /* Limit the number of commands */
+#define OPTION_DEBUG_SCRIPT 0x200000 /* Print when checkpoints are passed */
+#define OPTION_DEBUG_FIXUP 0x400000 /* print fixup values */
+#define OPTION_DEBUG_DSA 0x800000
+#define OPTION_DEBUG_CORRUPTION        0x1000000       /* Detect script corruption */
+
+#if !defined(PERM_OPTIONS)
+#define PERM_OPTIONS 0
+#endif
+                               
+struct NCR53c7x0_synchronous {
+    unsigned long select_indirect;     /* Value used for indirect selection */
+    unsigned long script[6];           /* Size ?? Script used when target is 
+                                               reselected */
+    unsigned renegotiate:1;            /* Force renegotiation on next   
+                                          select */
+};
+
+#define CMD_FLAG_SDTR          1       /* Initiating synchronous 
+                                          transfer negotiation */
+#define CMD_FLAG_WDTR          2       /* Initiating wide tranfer
+                                          negotiation */
+#define CMD_FLAG_DID_SDTR      4       /* did SDTR */
+
+struct NCR53c7x0_table_indirect {
+    unsigned long count;
+    void *address;
+};
+
+struct NCR53c7x0_cmd {
+    void *real;                                /* Real, unaligned address */
+    Scsi_Cmnd *cmd;                    /* Associated Scsi_Cmnd 
+                                          structure, Scsi_Cmnd points
+                                          at NCR53c7x0_cmd using 
+                                          host_scribble structure */
+
+    int size;                          /* scsi_malloc'd size of this 
+                                          structure */
+
+    int flags;
+
+    unsigned char select[11];          /* Select message, includes
+                                          IDENTIFY
+                                          (optional) QUEUE TAG
+                                          (optional) SDTR or WDTR
+                                        */
+
+
+    struct NCR53c7x0_cmd *next, *prev; /* Linux maintained lists */
+
+
+    unsigned long *data_transfer_start;        /* Start of data transfer routines */
+    unsigned long *data_transfer_end;  /* Address after end of data transfer o
+                                          routines */
+
+    unsigned long residual[8];         /* Residual data transfer
+                                          shadow of data_transfer code.
+
+                                          Has instruction with modified
+                                          DBC field followed by jump to 
+                                          CALL routine following command.
+                                        */
+            
+    unsigned long dsa[0];              /* Variable length (depending
+                                          on host type, number of scatter /
+                                          gather buffers, etc).  */
+};
+
+struct NCR53c7x0_break {
+    unsigned long *address, old_instruction[2];
+    struct NCR53c7x0_break *next;
+    unsigned char old_size;            /* Size of old instruction */
+};
+
+/* Indicates that the NCR is not executing code */
+#define STATE_HALTED   0               
+/* 
+ * Indicates that the NCR is executing the wait for select / reselect 
+ * script.  Only used when running NCR53c700 compatable scripts, only 
+ * state during which an ABORT is _not_ considered an error condition.
+ */
+#define STATE_WAITING  1               
+/* Indicates that the NCR is executing other code. */
+#define STATE_RUNNING  2               
+/* 
+ * Indicates that the NCR was being aborted.  Only used when running 
+ * NCR53c700 compatable scripts.  
+ */
+#define STATE_ABORTING 3
+    
+
+/* 
+ * Where knowledge of SCSI SCRIPT(tm) specified values are needed 
+ * in an interrupt handler, an interrupt handler exists for each 
+ * different SCSI script so we don't have name space problems.
+ * 
+ * Return values of these handlers are as follows : 
+ */
+#define SPECIFIC_INT_NOTHING   0       /* don't even restart */
+#define SPECIFIC_INT_RESTART   1       /* restart at the next instruction */
+#define SPECIFIC_INT_ABORT     2       /* recoverable error, abort cmd */
+#define SPECIFIC_INT_PANIC     3       /* unrecoverable error, panic */
+#define SPECIFIC_INT_DONE      4       /* normal command completion */
+#define SPECIFIC_INT_BREAK     5       /* break point encountered */
+
+struct NCR53c7x0_hostdata {
+    int size;                          /* Size of entire Scsi_Host
+                                          structure */
+    struct Scsi_Host *next;            /* next of this type */
+    int board;                         /* set to board type, useful if 
+                                          we have host specific things,
+                                          ie, a general purpose I/O 
+                                          bit is being used to enable
+                                          termination, etc. */
+
+    int chip;                          /* set to chip type */
+       /*
+        * NCR53c700 = 700
+        * NCR53c700-66 = 70066
+        * NCR53c710 = 710
+        * NCR53c720 = 720 
+         * NCR53c810 = 810
+        */
+
+    /*
+     * PCI bus, device, function, only for NCR53c8x0 chips.
+     * pci_valid indicates that the PCI configuration information
+     * is valid, and we can twiddle MAX_LAT, etc. as recommended
+     * for maximum performance in the NCR documentation.
+     */
+    unsigned char pci_bus, pci_device_fn;
+    unsigned pci_valid:1;
+
+    unsigned long *dsp;                        /* dsp to restart with after
+                                          all stacked interrupts are
+                                          handled. */
+
+    unsigned dsp_changed:1;            /* Has dsp changed within this
+                                          set of stacked interrupts ? */
+
+    unsigned char dstat;               /* Most recent value of dstat */
+    unsigned dstat_valid:1;
+
+    unsigned expecting_iid:1;          /* Expect IID interrupt */
+    unsigned expecting_sto:1;          /* Expect STO interrupt */
+    
+    /* 
+     * The code stays cleaner if we use variables with function
+     * pointers and offsets that are unique for the different
+     * scripts rather than having a slew of switch(hostdata->chip) 
+     * statements.
+     * 
+     * It also means that the #defines from the SCSI SCRIPTS(tm)
+     * don't have to be visible outside of the script-specific
+     * instructions, preventing name space pollution.
+     */
+
+    void (* init_fixup)(struct Scsi_Host *host);
+    void (* init_save_regs)(struct Scsi_Host *host);
+    void (* dsa_fixup)(struct NCR53c7x0_cmd *cmd);
+    void (* soft_reset)(struct Scsi_Host *host);
+    int (* run_tests)(struct Scsi_Host *host);
+
+    /*
+     * Called when DSTAT_SIR is set, indicating an interrupt generated
+     * by the INT instruction, where values are unique for each SCSI
+     * script.  Should return one of the SPEC_* values.
+     */
+
+    int (* dstat_sir_intr)(struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd);
+
+
+    /*
+     * Location of DSA fields for the SCSI SCRIPT corresponding to this 
+     * chip.  
+     */
+
+    long dsa_start;                    
+    long dsa_end;                      
+    long dsa_next;
+    long dsa_prev;
+    long dsa_cmnd;
+    long dsa_select;
+    long dsa_msgout;
+    long dsa_cmdout;
+    long dsa_dataout;
+    long dsa_datain;
+    long dsa_msgin;
+    long dsa_msgout_other;
+    long dsa_write_sync;
+    long dsa_write_resume;
+    long dsa_jump_resume;
+    long dsa_check_reselect;
+    long dsa_status;
+
+    /* 
+     * Important entry points that generic fixup code needs
+     * to know about, fixed up.
+     */
+
+    long E_accept_message;
+    long E_dsa_code_template;
+    long E_dsa_code_template_end;
+    long E_command_complete;           
+    long E_msg_in;
+    long E_initiator_abort;
+    long E_other_transfer;
+    long E_target_abort;
+    long E_schedule;                   
+    long E_debug_break;        
+    long E_reject_message;
+    long E_respond_message;
+    long E_select;
+    long E_select_msgout;
+    long E_test_0;
+    long E_test_1;
+    long E_test_2;
+    long E_test_3;
+    long E_dsa_zero;
+    long E_dsa_jump_resume;
+
+    int options;                       /* Bitfielded set of options enabled */
+    long test_completed;               /* Test completed */
+    int test_running;                  /* Test currently running */
+    int test_source;
+    volatile int test_dest;
+
+    volatile int state;                        /* state of driver, only used for 
+                                          OPTION_700 */
+
+    unsigned char  dmode;              /* 
+                                        * set to the address of the DMODE 
+                                        * register for this chip.
+                                        */
+    unsigned char istat;               /* 
+                                        * set to the address of the ISTAT 
+                                        * register for this chip.
+                                        */
+  
+    int scsi_clock;                    /* 
+                                        * SCSI clock in HZ. 0 may be used 
+                                        * for unknown, although this will
+                                        * disable synchronous negotiation.
+                                        */
+
+    volatile int intrs;                        /* Number of interrupts */
+    unsigned char saved_dmode; 
+    unsigned char saved_ctest4;
+    unsigned char saved_ctest7;
+    unsigned char saved_dcntl;
+    unsigned char saved_scntl3;
+
+    unsigned char this_id_mask;
+
+    /* Debugger information */
+    struct NCR53c7x0_break *breakpoints, /* Linked list of all break points */
+       *breakpoint_current;            /* Current breakpoint being stepped 
+                                          through, NULL if we are running 
+                                          normally. */
+    int debug_size;                    /* Size of debug buffer */
+    volatile int debug_count;          /* Current data count */
+    volatile char *debug_buf;          /* Output ring buffer */
+    volatile char *debug_write;                /* Current write pointer */
+    volatile char *debug_read;         /* Current read pointer */
+
+    /* XXX - primitive debugging junk, remove when working ? */
+    int debug_print_limit;             /* Number of commands to print
+                                          out exhaustive debugging
+                                          information for if 
+                                          OPTION_DEBUG_DUMP is set */ 
+
+    unsigned char debug_lun_limit[8];  /* If OPTION_DEBUG_TARGET_LIMIT
+                                          set, puke if commands are sent
+                                          to other target/lun combinations */
+
+    int debug_count_limit;             /* Number of commands to execute
+                                          before puking to limit debugging 
+                                          output */
+                                   
+
+    volatile unsigned idle:1;                  /* set to 1 if idle */
+
+    /* 
+     * Table of synchronous transfer parameters set on a per-target
+     * basis.
+     * 
+     * XXX - do we need to increase this to 16 for the WIDE-SCSI
+     * flavors of the board?
+     */
+    
+    volatile struct NCR53c7x0_synchronous sync[8];
+
+    volatile struct NCR53c7x0_cmd *issue_queue;
+                                               /* waiting to be issued by
+                                                  Linux driver */
+    volatile struct NCR53c7x0_cmd *running_list;       
+                                               /* commands running, maintained
+                                                  by Linux driver */
+    volatile struct NCR53c7x0_cmd *current;    /* currently connected 
+                                                  nexus, ONLY valid for
+                                                  NCR53c700/NCR53c700-66
+                                                */
+    volatile unsigned char busy[8][8];         /* number of commands 
+                                                  executing on each target
+                                                */
+    /* 
+     * Eventually, I'll switch to a corroutine for calling 
+     * cmd->done(cmd), etc. so that we can overlap interrupt
+     * processing with this code for maximum performance.
+     */
+    
+    volatile struct NCR53c7x0_cmd *finished_queue;     
+                                               
+
+    /* Shared variables between SCRIPT and host driver */
+    volatile unsigned char *issue_dsa_head;    
+                                               /* commands waiting to be 
+                                                  issued, insertions are 
+                                                  done by Linux driver,
+                                                  deletions are done by
+                                                  NCR */
+    volatile unsigned char *issue_dsa_tail;
+    volatile unsigned char msg_buf[16];                /* buffer for messages
+                                                  other than the command
+                                                  complete message */
+    volatile struct NCR53c7x0_cmd *reconnect_dsa_head; 
+                                               /* disconnected commands,
+                                                  maintained by NCR */
+    /* Data identifying nexus we are trying to match during reselection */
+    volatile unsigned char reselected_identify; /* IDENTIFY message */
+    volatile unsigned char reselected_tag;     /* second byte of queue tag 
+                                                  message or 0 */
+    int script_count;                          /* Size of script in longs */
+    unsigned long script[0];                   /* Relocated SCSI script */
+
+};
+
+#define IRQ_NONE       255
+#define DMA_NONE       255
+#define IRQ_AUTO       254
+#define DMA_AUTO       254
+
+#define BOARD_GENERIC  0
+
+#define NCR53c7x0_insn_size(insn)                                      \
+    (((insn) & DCMD_TYPE_MASK) == DCMD_TYPE_MMI ? 3 : 2)
+    
+
+#define NCR53c7x0_local_declare()                                      \
+    volatile unsigned char *NCR53c7x0_address_memory;                  \
+    unsigned short NCR53c7x0_address_io;                               \
+    int NCR53c7x0_memory_mapped
+
+#define NCR53c7x0_local_setup(host)                                    \
+    NCR53c7x0_address_memory = (void *) (host)->base;                  \
+    NCR53c7x0_address_io = (unsigned short) (host)->io_port;           \
+    NCR53c7x0_memory_mapped = ((struct NCR53c7x0_hostdata *)           \
+       host->hostdata)-> options & OPTION_MEMORY_MAPPED 
+
+#define NCR53c7x0_read8(address)                                       \
+    (NCR53c7x0_memory_mapped ?                                                 \
+       *( (NCR53c7x0_address_memory) + (address))  :                   \
+       inb(NCR53c7x0_address_io + (address)))
+
+#define NCR53c7x0_read16(address)                                      \
+    (NCR53c7x0_memory_mapped ?                                                 \
+       *((unsigned short *) (NCR53c7x0_address_memory) + (address))  : \
+       inw(NCR53c7x0_address_io + (address)))
+
+#define NCR53c7x0_read32(address)                                      \
+    (NCR53c7x0_memory_mapped ?                                                 \
+       *((unsigned long *) (NCR53c7x0_address_memory) + (address))  :  \
+       inl(NCR53c7x0_address_io + (address)))
+
+#define NCR53c7x0_write8(address,value)                                \
+    (NCR53c7x0_memory_mapped ?                                                 \
+       *((unsigned char *) (NCR53c7x0_address_memory) + (address)) =   \
+         (value) :                                                     \
+       outb((value), NCR53c7x0_address_io + (address)))
+
+#define NCR53c7x0_write16(address,value)                               \
+    (NCR53c7x0_memory_mapped ?                                                 \
+       *((unsigned short *) (NCR53c7x0_address_memory) + (address)) =  \
+         (value) :                                                     \
+       outw((value), NCR53c7x0_address_io + (address)))
+
+#define NCR53c7x0_write32(address,value)                               \
+    (NCR53c7x0_memory_mapped ?                                                 \
+       *((unsigned long *) (NCR53c7x0_address_memory) + (address)) =   \
+         (value) :                                                     \
+       outl((value), NCR53c7x0_address_io + (address)))
+
+#define patch_abs_32(script, offset, symbol, value)                    \
+       for (i = 0; i < (sizeof (A_##symbol##_used) / sizeof            \
+           (unsigned long)); ++i) {                                    \
+           (script)[A_##symbol##_used[i] - (offset)] += (value);       \
+           if (hostdata->options & OPTION_DEBUG_FIXUP)                 \
+             printk("scsi%d : %s reference %d at 0x%lx in %s is now 0x%lx\n",\
+               host->host_no, #symbol, i, A_##symbol##_used[i] -       \
+               (offset), #script, (script)[A_##symbol##_used[i] -      \
+               (offset)]);                                             \
+       }
+
+#define patch_abs_rwri_data(script, offset, symbol, value)             \
+       for (i = 0; i < (sizeof (A_##symbol##_used) / sizeof            \
+           (unsigned long)); ++i)                                      \
+           (script)[A_##symbol##_used[i] - (offset)] =                 \
+               ((script)[A_##symbol##_used[i] - (offset)] &            \
+               ~DBC_RWRI_IMMEDIATE_MASK) |                             \
+               (((value) << DBC_RWRI_IMMEDIATE_SHIFT) &                \
+                DBC_RWRI_IMMEDIATE_MASK)
+
+#define patch_dsa_32(dsa, symbol, word, value)                         \
+       {                                                               \
+       (dsa)[(hostdata->##symbol - hostdata->dsa_start) / sizeof(long) \
+               + (word)] = (unsigned long) (value);                    \
+       if (hostdata->options & OPTION_DEBUG_DSA)                       \
+           printk("scsi : dsa %s symbol %s(%ld) word %d now 0x%lx\n",  \
+               #dsa, #symbol, (long) hostdata->##symbol,               \
+               (int) (word), (long) (value));                                  \
+       }
+    
+
+
+#endif /* NCR53c7x0_C */
+#endif /* NCR53c7x0_H */
diff --git a/drivers/scsi/53c7,8xx.scr b/drivers/scsi/53c7,8xx.scr
new file mode 100644 (file)
index 0000000..eb6aa41
--- /dev/null
@@ -0,0 +1,1022 @@
+; NCR 53c810 driver, main script
+; Sponsored by 
+;      iX Multiuser Multitasking Magazine
+;      hm@ix.de
+;
+; Copyright 1993, Drew Eckhardt
+;      Visionary Computing 
+;      (Unix and Linux consulting and custom programming)
+;      drew@Colorado.EDU
+;      +1 (303) 786-7975
+;
+; TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation.
+;
+; PRE-ALPHA
+;
+; For more information, please consult 
+;
+; NCR 53C810
+; PCI-SCSI I/O Processor
+; Data Manual
+;
+; NCR 53C710 
+; SCSI I/O Processor
+; Programmers Guide
+;
+; NCR Microelectronics
+; 1635 Aeroplaza Drive
+; Colorado Springs, CO 80916
+; 1+ (719) 578-3400
+;
+; Toll free literature number
+; +1 (800) 334-5454
+;
+; IMPORTANT : This code is self modifying due to the limitations of 
+;      the NCR53c7,8xx series chips.  Persons debugging this code with
+;      the remote debugger should take this into account, and NOT set
+;      breakpoints in modified instructions.
+;
+;
+; Design:
+; The NCR53c7x0 family of SCSI chips are busmasters with an onboard 
+; microcontroller using a simple instruction set.   
+;
+; So, to minimize the effects of interrupt latency, and to maximize 
+; throughput, this driver offloads the practical maximum amount 
+; of processing to the SCSI chip while still maintaining a common
+; structure.
+;
+; Where tradeoffs were needed between efficiency on the older
+; chips and the newer NCR53c800 series, the NCR53c800 series 
+; was chosen.
+;
+; While the NCR53c700 and NCR53c700-66 lacked the facilities to fully
+; automate SCSI transfers without host processor intervention, this 
+; isnt the case with the NCR53c710 and newer chips which allow 
+;
+; - reads and writes to the internal registers from within the SCSI
+;      scripts, allowing the SCSI SCRIPTS(tm) code to save processor
+;      state so that multiple threads of execution are possible, and also
+;      provide an ALU for loop control, etc.
+; 
+; - table indirect addressing for some instructions. This allows 
+;      pointers to be located relative to the DSA ((Data Structure
+;      Address) register.
+;
+; These features make it possible to implement a mailbox style interface,
+; where the same piece of code is run to handle I/O for multiple threads
+; at once minimizing our need to relocate code.  Since the NCR53c700/
+; NCR53c800 series have a unique combination of features, making a 
+; a standard ingoing/outgoing mailbox system, costly, Ive modified it.
+;
+; - Commands are stored in a linked list, rather than placed in 
+;      arbitrary mailboxes.  This simiplifies the amount of processing
+;      that must be done by the NCR53c810.
+;
+; - Mailboxes are a mixture of code and data.  This lets us greatly
+;      simplify the NCR53c810 code and do things that would otherwise
+;      not be possible.
+
+;
+; Note : the DSA structures must be aligned on 32 bit boundaries,
+; since the source and destination of MOVE MEMORY instructions 
+; must share the same alignment and this is the alignment of the
+; NCR registers.
+;
+
+ABSOLUTE dsa_temp_jump_resume = 0      ; Patch to dsa_jump_resume
+                                       ;       in current dsa
+ABSOLUTE dsa_temp_lun = 0              ; Patch to lun for current dsa
+ABSOLUTE dsa_temp_dsa_next = 0         ; Patch to dsa next for current dsa
+ABSOLUTE dsa_temp_sync = 0             ; Patch to address of per-target
+                                       ;       sync routine
+ABSOLUTE dsa_temp_target = 0           ; Patch to id for current dsa
+
+
+#if (CHIP != 700) && (CHIP != 70066)
+ENTRY dsa_code_template
+dsa_code_template:
+
+; Define DSA structure used for mailboxes
+
+; wrong_dsa loads the DSA register with the value of the dsa_next
+; field.
+;
+wrong_dsa:
+;              Patch the MOVE MEMORY INSTRUCTION such that 
+;              the destination address is that of the OLD next
+;              pointer.
+       MOVE MEMORY 4, dsa_temp_dsa_next, reselected_ok + 8
+
+       MOVE dmode_memory_to_ncr TO DMODE       
+       MOVE MEMORY 4, dsa_temp_dsa_next, addr_scratch
+       MOVE dmode_memory_to_memory TO DMODE
+       CALL scratch_to_dsa
+       JUMP reselected_check_next
+
+ABSOLUTE dsa_check_reselect = 0
+; dsa_check_reselect determines weather or not the current target and
+; lun match the current DSA
+ENTRY dsa_code_check_reselect
+dsa_code_check_reselect:
+       MOVE SSID TO SFBR               ; SSID contains 3 bit target ID
+       JUMP REL (wrong_dsa), IF NOT dsa_temp_target, AND MASK 7
+       MOVE dmode_memory_to_ncr TO DMODE
+       MOVE MEMORY 1, reselected_identify, addr_sfbr
+       JUMP REL (wrong_dsa), IF NOT dsa_temp_lun, AND MASK 7
+       MOVE dmode_memory_to_memory TO DMODE
+;              Patch the MOVE MEMORY INSTRUCTION such that
+;              the source address is that of this dsas
+;              next pointer.
+       MOVE MEMORY 4, dsa_temp_dsa_next, reselected_ok + 4
+       CALL reselected_ok
+       CALL dsa_temp_sync      
+ENTRY dsa_jump_resume
+dsa_jump_resume:
+       JUMP 0                          ; Jump to resume address
+ENTRY dsa_zero
+dsa_zero:
+       MOVE dmode_ncr_to_memory TO DMODE                       ; 8
+       MOVE MEMORY 4, addr_temp, dsa_temp_jump_resume          ; 16    
+       MOVE dmode_memory_to_memory TO DMODE                    ; 28
+       JUMP dsa_schedule                                       ; 36
+ENTRY dsa_code_template_end
+dsa_code_template_end:
+
+; Perform sanity check for dsa_fields_start == dsa_code_template_end - 
+; dsa_zero, puke.
+
+ABSOLUTE dsa_fields_start =  36        ; Sanity marker
+                               ;       pad 12
+ABSOLUTE dsa_next = 48         ; len 4 Next DSA
+                               ; del 4 Previous DSA address
+ABSOLUTE dsa_cmnd = 56         ; len 4 Scsi_Cmnd * for this thread.
+ABSOLUTE dsa_select = 60       ; len 4 Device ID, Period, Offset for 
+                               ;       table indirect select
+ABSOLUTE dsa_msgout = 64       ; len 8 table indirect move parameter for 
+                               ;       select message
+ABSOLUTE dsa_cmdout = 72       ; len 8 table indirect move parameter for 
+                               ;       command
+ABSOLUTE dsa_dataout = 80      ; len 4 code pointer for dataout
+ABSOLUTE dsa_datain = 84       ; len 4 code pointer for datain
+ABSOLUTE dsa_msgin = 88                ; len 8 table indirect move for msgin
+ABSOLUTE dsa_status = 96       ; len 8 table indirect move for status byte
+ABSOLUTE dsa_msgout_other = 104        ; len 8 table indirect for normal message out
+                               ; (Synchronous transfer negotiation, etc).
+ABSOLUTE dsa_end = 112
+
+; Linked lists of DSA structures
+ABSOLUTE issue_dsa_head = 0    ; Linked list of DSAs to issue
+ABSOLUTE reconnect_dsa_head = 0        ; Link list of DSAs which can reconnect
+
+; These select the source and destination of a MOVE MEMORY instruction
+ABSOLUTE dmode_memory_to_memory = 0x0
+ABSOLUTE dmode_memory_to_ncr = 0x0
+ABSOLUTE dmode_ncr_to_memory = 0x0
+ABSOLUTE dmode_ncr_to_ncr = 0x0
+
+ABSOLUTE addr_scratch = 0x0
+ABSOLUTE addr_sfbr = 0x0
+ABSOLUTE addr_temp = 0x0
+#endif /* CHIP != 700 && CHIP != 70066 */
+
+; Interrupts - 
+; MSB indicates type
+; 0    handle error condition
+; 1    handle message 
+; 2    handle normal condition
+; 3    debugging interrupt
+; 4    testing interrupt 
+; Next byte indicates specific error
+
+; XXX not yet implemented, Im not sure if I want to - 
+; Next byte indicates the routine the error occurred in
+; The LSB indicates the specific place the error occurred
+ABSOLUTE int_err_unexpected_phase = 0x00000000 ; Unexpected phase encountered
+ABSOLUTE int_err_selected = 0x00010000         ; SELECTED (nee RESELECTED)
+ABSOLUTE int_err_unexpected_reselect = 0x00020000 
+ABSOLUTE int_err_check_condition = 0x00030000  
+ABSOLUTE int_err_no_phase = 0x00040000
+ABSOLUTE int_msg_wdtr = 0x01000000             ; WDTR message received
+ABSOLUTE int_msg_sdtr = 0x01010000             ; SDTR received
+ABSOLUTE int_msg_1 = 0x01020000                        ; single byte special message
+                                               ; received
+
+ABSOLUTE int_norm_select_complete = 0x02000000 ; Select complete, reprogram
+                                               ; registers.
+ABSOLUTE int_norm_reselect_complete = 0x02010000       ; Nexus established
+ABSOLUTE int_norm_command_complete = 0x02020000 ; Command complete
+ABSOLUTE int_norm_disconnected = 0x02030000    ; Disconnected 
+ABSOLUTE int_norm_aborted =0x02040000          ; Aborted *dsa
+ABSOLUTE int_norm_reset = 0x02050000           ; Generated BUS reset.
+ABSOLUTE int_debug_break = 0x03000000          ; Break point
+ABSOLUTE int_debug_scheduled = 0x03010000      ; new I/O scheduled 
+ABSOLUTE int_debug_idle = 0x03020000           ; scheduler is idle
+ABSOLUTE int_debug_dsa_loaded = 0x03030000     ; dsa reloaded
+ABSOLUTE int_debug_reselected = 0x03040000     ; NCR reselected
+ABSOLUTE int_debug_head = 0x03050000           ; issue head overwritten
+
+ABSOLUTE int_test_1 = 0x04000000               ; Test 1 complete
+ABSOLUTE int_test_2 = 0x04010000               ; Test 2 complete
+ABSOLUTE int_test_3 = 0x04020000               ; Test 3 complete
+                                               
+EXTERNAL NCR53c7xx_msg_abort           ; Pointer to abort message
+EXTERNAL NCR53c7xx_msg_reject          ; Pointer to reject message
+EXTERNAL NCR53c7xx_zero                        ; long with zero in it, use for source
+EXTERNAL NCR53c7xx_sink                        ; long to dump worthless data in
+
+; Pointer to final bytes of multi-byte messages
+ABSOLUTE msg_buf = 0
+
+; Pointer to holding area for reselection information
+ABSOLUTE reselected_identify = 0
+ABSOLUTE reselected_tag = 0
+
+; Request sense command pointer, its a 6 byte command, should
+; be constant for all commands since we allways want 16 bytes of 
+; sense and we dont need to change any fields as we did under 
+; SCSI-I when we actually cared about the LUN field.
+;EXTERNAL NCR53c7xx_sense              ; Request sense command
+
+#if (CHIP != 700) && (CHIP != 70066)
+; dsa_schedule  
+; PURPOSE : after a DISCONNECT message has been recieved, and pointers
+;      saved, insert the current DSA structure at the head of the 
+;      disconnected queue and fall through to the scheduler.
+;
+; CALLS : OK
+;
+; INPUTS : dsa - current DSA structure, reconnect_dsa_head - list
+;      of disconnected commands
+;
+; MODIFIES : SCRATCH, reconnect_dsa_head
+; 
+; EXITS : allways passes control to schedule
+
+ENTRY dsa_schedule
+dsa_schedule:
+
+;
+; Calculate the address of the next pointer within the DSA 
+; structure of the command that is currently disconnecting
+;
+    CALL dsa_to_scratch
+; XXX - we need to deal with the NCR53c710, which lacks an add with
+;      carry instruction, by moving arround the DSA alignment to avoid
+;      carry in situations like this.
+    MOVE SCRATCH0 + dsa_next TO SCRATCH0
+    MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
+    MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
+    MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
+
+; Point the next field of this DSA structure at the current disconnected 
+; list
+    MOVE dmode_ncr_to_memory TO DMODE
+    MOVE MEMORY 4, addr_scratch, dsa_schedule_insert + 8
+    MOVE dmode_memory_to_memory TO DMODE
+dsa_schedule_insert:
+    MOVE MEMORY 4, reconnect_dsa_head, 0 
+
+; And update the head pointer.
+    CALL dsa_to_scratch
+    MOVE dmode_ncr_to_memory TO DMODE  
+    MOVE MEMORY 4, addr_scratch, reconnect_dsa_head
+    MOVE dmode_memory_to_memory TO DMODE
+    WAIT DISCONNECT
+
+; schedule
+; PURPOSE : schedule a new I/O once the bus is free by putting the 
+;      address of the next DSA structure in the DSA register.
+;
+; INPUTS : issue_dsa_head - list of new commands
+;
+; CALLS : OK
+;
+; MODIFIES : SCRATCH, DSA 
+;
+; EXITS : if the issue_dsa_head pointer is non-NULL, control
+;      is passed to select.  Otherwise, control is passed to 
+;      wait_reselect.
+
+
+ENTRY schedule
+schedule:
+    ; Point DSA at the current head of the issue queue.
+    MOVE dmode_memory_to_ncr TO DMODE
+    MOVE MEMORY 4, issue_dsa_head, addr_scratch
+    MOVE dmode_memory_to_memory TO DMODE
+
+    CALL scratch_to_dsa
+
+#if 0
+    INT int_debug_dsa_loaded
+#endif
+    ; Check for a null pointer.
+    MOVE DSA0 TO SFBR
+    JUMP select, IF NOT 0
+    MOVE DSA1 TO SFBR
+    JUMP select, IF NOT 0
+    MOVE DSA2 TO SFBR
+    JUMP select, IF NOT 0
+    MOVE DSA3 TO SFBR
+    JUMP wait_reselect, IF 0
+
+    
+#else /* (CHIP != 700) && (CHIP != 70066) */
+#endif /* (CHIP != 700) && (CHIP != 70066) */
+
+;
+; select
+;
+; PURPOSE : establish a nexus for the SCSI command referenced by DSA.
+;      On success, the current DSA structure is removed from the issue 
+;      queue.  Usually, this is entered as a fall-through from schedule,
+;      although the contingent allegience handling code will write
+;      the select entry address to the DSP to restart a command as a 
+;      REQUEST SENSE.  A message is sent (usually IDENTIFY, although
+;      additional SDTR or WDTR messages may be sent).  COMMAND OUT
+;      is handled.
+;
+; INPUTS : DSA - SCSI command, issue_dsa_head
+;
+; CALLS : OK
+;
+; MODIFIES : SCRATCH, issue_dsa_head
+;
+; EXITS : on reselection or selection, go to select_failed
+;      otherwise, fall through to data_transfer.  If a MSG_IN
+;      phase occurs before 
+;
+
+ENTRY select
+select:
+
+#if 0
+    INT int_debug_scheduled
+#endif
+    CLEAR TARGET
+
+; XXX
+;
+; In effect, SELECTION operations are backgrounded, with execution
+; continuing until code which waits for REQ or a fatal interrupt is 
+; encountered.
+;
+; So, for more performance, we could overlap the code which removes 
+; the command from the NCRs issue queue with the selection, but 
+; at this point I dont want to deal with the error recovery.
+;
+
+#if (CHIP != 700) && (CHIP != 70066)
+    SELECT ATN FROM dsa_select, select_failed
+    JUMP select_msgout, WHEN MSG_OUT
+ENTRY select_msgout
+select_msgout:
+    MOVE FROM dsa_msgout, WHEN MSG_OUT
+#else
+ENTRY select_msgout
+    SELECT ATN 0, select_failed
+select_msgout:
+    MOVE 0, 0, WHEN MSGOUT
+#endif
+
+#if (CHIP != 700) && (CHIP != 70066)
+
+    ; Calculate address of dsa_next field
+
+    CALL dsa_to_scratch
+
+    MOVE SCRATCH0 + dsa_next TO SCRATCH0
+    MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
+    MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
+    MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
+
+    ; Patch memory to memory move
+    move dmode_ncr_to_memory TO DMODE
+    MOVE MEMORY 4, addr_scratch, issue_remove + 4
+
+
+    ; And rewrite the issue_dsa_head pointer.
+    MOVE dmode_memory_to_memory TO DMODE
+issue_remove:
+;      The actual UPDATE of the issue_dsa_head variable is 
+;      atomic, with all of the setup code being irrelevant to
+;      weather the updated value being the old or new contents of 
+;      dsa_next field.
+;
+;      To insure synchronization, the host system merely needs to 
+;      do a XCHG instruction with interrupts disabled on the 
+;      issue_dsa_head memory address.
+;
+;      The net effect will be that the XCHG instruction will return
+;      either a non-NULL value, indicating that the NCR chip will not
+;      go into the idle loop when this command DISCONNECTS, or a NULL
+;      value indicating that the NCR wrote first and that the Linux
+;      code must rewrite the issue_dsa_head pointer and set SIG_P.
+;      
+
+
+    MOVE MEMORY 4, 0, issue_dsa_head
+#endif /* (CHIP != 700) && (CHIP != 70066) */
+#if 0
+INT int_debug_head
+#endif
+
+; After a successful selection, we should get either a CMD phase or 
+; some transfer request negotiation message.
+
+    JUMP cmdout, WHEN CMD
+    INT int_err_unexpected_phase, WHEN NOT MSG_IN 
+
+select_msg_in:
+    CALL msg_in, WHEN MSG_IN
+    JUMP select_msg_in, WHEN MSG_IN
+
+cmdout:
+    INT int_err_unexpected_phase, WHEN NOT CMD
+#if (CHIP == 700)
+    INT int_norm_selected
+#endif
+ENTRY cmdout_cmdout
+cmdout_cmdout:
+#if (CHIP != 700) && (CHIP != 70066)
+    MOVE FROM dsa_cmdout, WHEN CMD
+#else
+    MOVE 0, 0, WHEN CMD
+#endif /* (CHIP != 700) && (CHIP != 70066) */
+
+;
+; data_transfer  
+; other_transfer
+;
+; PURPOSE : handle the main data transfer for a SCSI command in 
+;      two parts.  In the first part, data_transfer, DATA_IN
+;      and DATA_OUT phases are allowed, with the user provided
+;      code (usually dynamically generated based on the scatter/gather
+;      list associated with a SCSI command) called to handle these 
+;      phases.
+;
+;      On completion, the user code passes control to other_transfer
+;      which causes DATA_IN and DATA_OUT to result in unexpected_phase
+;      interrupts so that data overruns may be trapped.
+;
+; INPUTS : DSA - SCSI command
+;
+; CALLS : OK
+;
+; MODIFIES : SCRATCH
+;
+; EXITS : if STATUS IN is detected, signifying command completion,
+;      the NCR jumpst to command_complete.  If MSG IN occurs, a 
+;      CALL is made to msg_in.  Otherwise, other_transfer runs in 
+;      an infinite loop.
+;      
+
+data_transfer:
+    INT int_err_unexpected_phase, WHEN CMD
+    CALL msg_in, WHEN MSG_IN
+    INT int_err_unexpected_phase, WHEN MSG_OUT
+    JUMP do_dataout, WHEN DATA_OUT
+    JUMP do_datain, WHEN DATA_IN
+    JUMP command_complete, WHEN STATUS
+    JUMP data_transfer
+
+;
+; On NCR53c700 and NCR53c700-66 chips, do_dataout/do_datain are fixed up 
+; whenever the nexus changes so it can point to the correct routine for 
+; that command.
+;
+
+#if (CHIP != 700) && (CHIP != 70066)
+; Nasty jump to dsa->dataout
+do_dataout:
+    CALL dsa_to_scratch
+    MOVE SCRATCH0 + dsa_dataout TO SCRATCH0    
+    MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY 
+    MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY 
+    MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY 
+    MOVE dmode_ncr_to_memory TO DMODE
+    MOVE MEMORY 4, addr_scratch, dataout_to_jump + 4
+    MOVE dmode_memory_to_memory TO DMODE
+dataout_to_jump:
+    MOVE MEMORY 4, 0, dataout_jump + 4 
+dataout_jump:
+    JUMP 0
+
+; Nasty jump to dsa->dsain
+do_datain:
+    CALL dsa_to_scratch
+    MOVE SCRATCH0 + dsa_datain TO SCRATCH0     
+    MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY 
+    MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY 
+    MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY 
+    MOVE dmode_ncr_to_memory TO DMODE
+    MOVE MEMORY 4, addr_scratch, datain_to_jump + 4
+    MOVE dmode_memory_to_memory TO DMODE               
+datain_to_jump:
+    MOVE MEMORY 4, 0, datain_jump + 4
+datain_jump:
+    JUMP 0
+#endif /* (CHIP != 700) && (CHIP != 70066) */
+
+;
+; other_transfer is exported because it is referenced by dynamically 
+; generated code.
+;
+ENTRY other_transfer
+other_transfer:
+#if 0
+    INT 0x03ffdead
+#endif
+    INT int_err_unexpected_phase, WHEN CMD
+    CALL msg_in, WHEN MSG_IN 
+    INT int_err_unexpected_phase, WHEN MSG_OUT
+    INT int_err_unexpected_phase, WHEN DATA_OUT
+    INT int_err_unexpected_phase, WHEN DATA_IN
+    JUMP command_complete, WHEN STATUS
+    JUMP other_transfer
+
+;
+; msg_in
+; munge_msg
+;
+; PURPOSE : process messages from a target.  msg_in is called when the 
+;      caller hasnt read the first byte of the message.  munge_message
+;      is called when the caller has read the first byte of the message,
+;      and left it in SFBR.
+;
+;      Various int_* interrupts are generated when the host system
+;      needs to intervene, as is the case with SDTR, WDTR, and
+;      INITIATE RECOVERY messages.
+;
+;      When the host system handles one of these interrupts,
+;      it can respond by rentering at reject_message, 
+;      which rejects the message and returns control to
+;      the caller of msg_in or munge_msg, accept_message
+;      which clears ACK and returns control, or reply_message
+;      which sends the message pointed to by the DSA 
+;      msgout_other table indirect field.
+;
+;      DISCONNECT messages are handled by moving the command
+;      to the reconnect_dsa_queue.
+;
+;      SAVE DATA POINTER and RESTORE DATA POINTERS are currently 
+;      treated as NOPS. 
+;
+; INPUTS : DSA - SCSI COMMAND, SFBR - first byte of message (munge_msg
+;      only)
+;
+; CALLS : NO.  The TEMP register isnt backed up to allow nested calls.
+;
+; MODIFIES : SCRATCH, DSA on DISCONNECT
+;
+; EXITS : On receipt of SAVE DATA POINTER, RESTORE POINTERS,
+;      and normal return from message handlers running under
+;      Linux, control is returned to the caller.  Receipt
+;      of DISCONNECT messages pass control to dsa_schedule.
+;
+ENTRY msg_in
+msg_in:
+    MOVE 1, msg_buf, WHEN MSG_IN
+
+munge_msg:
+    JUMP munge_extended, IF 0x01               ; EXTENDED MESSAGE
+    JUMP munge_2, IF 0x20, AND MASK 0xdf       ; two byte message
+;
+; Ive seen a handful of broken SCSI devices which fail to issue
+; a SAVE POINTERS message before disconnecting in the middle of 
+; a transfer, assuming that the DATA POINTER will be implicitly 
+; restored.  So, we treat the SAVE DATA POINTER message as a NOP.
+;
+; Ive also seen SCSI devices which dont issue a RESTORE DATA
+; POINTER message and assume that thats implicit.
+;
+    JUMP accept_message, IF 0x02               ; SAVE DATA POINTER
+    JUMP accept_message, IF 0x03               ; RESTORE POINTERS 
+    JUMP munge_disconnect, IF 0x04             ; DISCONNECT
+    INT int_msg_1, IF 0x07                     ; MESSAGE REJECT
+    INT int_msg_1, IF 0x0f                     ; INITIATE RECOVERY
+    JUMP reject_message
+
+munge_2:
+    JUMP reject_message
+
+munge_save_data_pointer:
+    CLEAR ACK
+    RETURN
+
+munge_disconnect:
+    MOVE SCNTL2 & 0x7f TO SCNTL2
+    CLEAR ACK
+
+#if (CHIP != 700) && (CHIP != 70066)
+; Pass control to the DSA routine.  Note that we can not call
+; dsa_to_scratch here because that would clobber temp, which 
+; we must preserve.
+    MOVE DSA0 TO SFBR
+    MOVE SFBR TO SCRATCH0
+    MOVE DSA1 TO SFBR
+    MOVE SFBR TO SCRATCH1
+    MOVE DSA2 TO SFBR
+    MOVE SFBR TO SCRATCH2
+    MOVE DSA3 TO SFBR
+    MOVE SFBR TO SCRATCH3
+
+    MOVE dmode_ncr_to_memory TO DMODE
+    MOVE MEMORY 4, addr_scratch, jump_to_dsa + 4
+    MOVE dmode_memory_to_memory TO DMODE
+jump_to_dsa:
+    JUMP 0
+#else
+    WAIT DISCONNECT
+    INT int_norm_disconnected
+#endif
+
+munge_extended:
+    CLEAR ACK
+    INT int_err_unexpected_phase, WHEN NOT MSG_IN
+    MOVE 1, msg_buf + 1, WHEN MSG_IN
+    JUMP munge_extended_2, IF 0x02
+    JUMP munge_extended_3, IF 0x03 
+    JUMP reject_message
+
+munge_extended_2:
+    CLEAR ACK
+    MOVE 1, msg_buf + 2, WHEN MSG_IN
+    JUMP reject_message, IF NOT 0x02   ; Must be WDTR
+    CLEAR ACK
+    MOVE 1, msg_buf + 3, WHEN MSG_IN
+    INT int_msg_wdtr
+
+munge_extended_3:
+    CLEAR ACK
+    MOVE 1, msg_buf + 2, WHEN MSG_IN
+    JUMP reject_message, IF NOT 0x01   ; Must be SDTR
+    CLEAR ACK
+    MOVE 2, msg_buf + 3, WHEN MSG_IN
+    INT int_msg_sdtr
+
+ENTRY reject_message
+reject_message:
+    SET ATN
+    CLEAR ACK
+    MOVE 1, NCR53c7xx_msg_reject, WHEN MSG_OUT
+    RETURN
+
+ENTRY accept_message
+accept_message:
+    CLEAR ACK
+    RETURN
+
+ENTRY respond_message
+msg_respond:
+    SET ATN
+    CLEAR ACK
+    MOVE FROM dsa_msgout_other, WHEN MSG_OUT
+    RETURN
+
+;
+; command_complete
+;
+; PURPOSE : handle command termination when STATUS IN is detected by reading
+;      a status byte followed by a command termination message. 
+;
+;      Normal termination results in an INTFLY instruction, and 
+;      the host system can pick out which command terminated by 
+;      examining the MESSAGE and STATUS buffers of all currently 
+;      executing commands;
+;
+;      Abnormal (CHECK_CONDITION) termination results in an
+;      int_err_check_condition interrupt so that a REQUEST SENSE
+;      command can be issued out-of-order so that no other command
+;      clears the contingent allegience condition.
+;      
+;
+; INPUTS : DSA - command       
+;
+; CALLS : OK
+;
+; EXITS : On successful termination, control is passed to schedule.
+;      On abnormal termination, the user will usually modify the 
+;      DSA fields and corresponding buffers and return control
+;      to select.
+;
+
+ENTRY command_complete
+command_complete:
+    MOVE FROM dsa_status, WHEN STATUS
+#if (CHIP != 700) && (CHIP != 70066)
+    MOVE SFBR TO SCRATCH0              ; Save status
+#endif /* (CHIP != 700) && (CHIP != 70066) */
+ENTRY command_complete_msgin
+command_complete_msgin:
+    MOVE FROM dsa_msgin, WHEN MSG_IN
+; Indicate that we should be expecting a disconnect
+    MOVE SCNTL2 & 0x7f TO SCNTL2
+    CLEAR ACK
+#if (CHIP != 700) && (CHIP != 70066)
+    MOVE SCRATCH0 TO SFBR                      
+
+;
+; The SCSI specification states that when a UNIT ATTENTION condition
+; is pending, as indicated by a CHECK CONDITION status message,
+; the target shall revert to asynchronous transfers.  Since
+; synchronous transfers parameters are maintained on a per INITIATOR/TARGET 
+; basis, and returning control to our scheduler could work on a command
+; running on another lun on that target using the old parameters, we must
+; interrupt the host processor to get them changed, or change them ourselves.
+;
+; Once SCSI-II tagged queueing is implemented, things will be even more
+; hairy, since contingent allegience conditions exist on a per-target/lun
+; basis, and issuing a new command with a different tag would clear it.
+; In these cases, we must interrupt the host processor to get a request 
+; added to the HEAD of the queue with the request sense command, or we
+; must automatically issue the request sense command.
+
+#if 0
+    JUMP command_failed, IF 0x02
+#endif
+    INTFLY
+#endif /* (CHIP != 700) && (CHIP != 70066) */
+    WAIT DISCONNECT
+#if (CHIP != 700) && (CHIP != 70066)
+    JUMP schedule
+command_failed:
+    WAIT DISCONNECT
+    INT int_err_check_condition
+#else
+    INT int_norm_command_complete
+#endif
+
+;
+; wait_reselect
+;
+; PURPOSE : This is essentially the idle routine, where control lands
+;      when there are no new processes to schedule.  wait_reselect
+;      waits for reselection, selection, and new commands.
+;
+;      When a successful reselection occurs, with the aid 
+;      of fixedup code in each DSA, wait_reselect walks the 
+;      reconnect_dsa_queue, asking each dsa if the target ID
+;      and LUN match its.
+;
+;      If a match is found, a call is made back to reselected_ok,
+;      which through the miracles of self modifying code, extracts
+;      the found DSA from the reconnect_dsa_queue and then 
+;      returns control to the DSAs thread of execution.
+;
+; INPUTS : NONE
+;
+; CALLS : OK
+;
+; MODIFIES : DSA,
+;
+; EXITS : On successful reselection, control is returned to the 
+;      DSA which called reselected_ok.  If the WAIT RESELECT
+;      was interrupted by a new commands arival signalled by 
+;      SIG_P, control is passed to schedule.  If the NCR is 
+;      selected, the host system is interrupted with an 
+;      int_err_selected which is usually responded to by
+;      setting DSP to the target_abort address.
+    
+wait_reselect:
+#if 0
+    int int_debug_idle
+#endif
+    WAIT RESELECT wait_reselect_failed
+
+reselected:
+    ; Read all data needed to restablish the nexus - 
+    MOVE 1, reselected_identify, WHEN MSG_IN
+#if (CHIP != 700) && (CHIP != 70066)
+    ; Well add a jump to here after some how determining that 
+    ; tagged queueing isnt in use on this device.
+reselected_notag:    
+    MOVE MEMORY 1, NCR53c7xx_zero, reselected_tag
+
+#ifdef DEBUG
+    int int_debug_reselected
+#endif
+
+    ; Point DSA at the current head of the disconnected queue.
+    MOVE dmode_memory_to_ncr  TO DMODE
+    MOVE MEMORY 4, reconnect_dsa_head, addr_scratch
+    MOVE dmode_memory_to_memory TO DMODE
+    CALL scratch_to_dsa
+
+    ; Fix the update-next pointer so that the reconnect_dsa_head
+    ; pointer is the one that will be updated if this DSA is a hit 
+    ; and we remove it from the queue.
+
+    MOVE MEMORY 4, reconnect_dsa_head, reselected_ok + 8
+
+ENTRY reselected_check_next
+reselected_check_next:
+    ; Check for a NULL pointer.
+    MOVE DSA0 TO SFBR
+    JUMP reselected_not_end, IF NOT 0
+    MOVE DSA1 TO SFBR
+    JUMP reselected_not_end, IF NOT 0
+    MOVE DSA2 TO SFBR
+    JUMP reselected_not_end, IF NOT 0
+    MOVE DSA3 TO SFBR
+    JUMP reselected_not_end, IF NOT 0
+    INT int_err_unexpected_reselect
+
+reselected_not_end:
+    MOVE DSA0 TO SFBR
+    ;
+    ; XXX the ALU is only eight bits wide, and the assembler
+    ; wont do the dirt work for us.  As long as dsa_check_reselect
+    ; is negative, we need to sign extend with 1 bits to the full
+    ; 32 bit width oof the address.
+    ;
+    ; A potential work arround would be to have a known alignment 
+    ; of the DSA structure such that the base address plus 
+    ; dsa_check_reselect doesnt require carryin from bytes 
+    ; higher than the LSB.
+    ;
+
+    MOVE SFBR + dsa_check_reselect TO SCRATCH0
+    MOVE DSA1 TO SFBR
+    MOVE SFBR + 0xff TO SCRATCH1 WITH CARRY
+    MOVE DSA2 TO SFBR
+    MOVE SFBR + 0xff TO SCRATCH2 WITH CARRY
+    MOVE DSA3 TO SFBR
+    MOVE SFBR + 0xff TO SCRATCH3 WITH CARRY
+
+    MOVE dmode_ncr_to_memory TO DMODE
+    MOVE MEMORY 4, addr_scratch, reselected_check + 4
+    MOVE dmode_memory_to_memory TO DMODE
+reselected_check:
+    JUMP 0
+
+
+;
+;
+reselected_ok:
+    MOVE MEMORY 4, 0, 0                                ; Patched : first word
+                                               ;       is this successful 
+                                               ;       dsa_next
+                                               ; Second word is 
+                                               ;       unsuccessful dsa_next
+    CLEAR ACK                                  ; Accept last message
+    RETURN                                     ; Return control to where
+#else
+    INT int_norm_reselected
+#endif /* (CHIP != 700) && (CHIP != 70066) */
+
+selected:
+    INT int_err_selected;
+
+;
+; A select or reselect failure can be caused by one of two conditions : 
+; 1.  SIG_P was set.  This will be the case if the user has written
+;      a new value to a previously NULL head of the issue queue.
+;
+; 2.  The NCR53c810 was selected or reselected by another device.
+; 
+
+wait_reselect_failed:
+; Reading CTEST2 clears the SIG_P bit in the ISTAT register.
+    MOVE CTEST2 & 0x40 TO SFBR 
+    JUMP selected, IF NOT 0x40
+    JUMP schedule
+
+select_failed:
+    MOVE ISTAT & 0x20 TO SFBR
+    JUMP reselected, IF NOT 0x20
+    MOVE ISTAT & 0xdf TO ISTAT
+    JUMP schedule
+
+;
+; test_1
+; test_2
+;
+; PURPOSE : run some verification tests on the NCR.  test_1
+;      copies test_src to test_dest and interrupts the host
+;      processor, testing for cache coherency and interrupt
+;      problems in the processes.
+;
+;      test_2 runs a command with offsets relative to the 
+;      DSA on entry, and is useful for miscellaneous experimentation.
+;
+
+; Verify that interrupts are working correctly and that we dont 
+; have a cache invalidation problem.
+
+ABSOLUTE test_src = 0, test_dest = 0
+ENTRY test_1
+test_1:
+    MOVE MEMORY 4, test_src, test_dest
+    INT int_test_1
+
+;
+; Run arbitrary commands, with test code establishing a DSA
+;
+ENTRY test_2
+test_2:
+    CLEAR TARGET
+    SELECT ATN FROM 0, test_2_fail
+    JUMP test_2_msgout, WHEN MSG_OUT
+ENTRY test_2_msgout
+test_2_msgout:
+    MOVE FROM 8, WHEN MSG_OUT
+    MOVE FROM 16, WHEN CMD 
+    MOVE FROM 24, WHEN DATA_IN
+    MOVE FROM 32, WHEN STATUS
+    MOVE FROM 40, WHEN MSG_IN
+    MOVE SCNTL2 & 0x7f TO SCNTL2
+    CLEAR ACK
+    WAIT DISCONNECT
+test_2_fail:
+    INT int_test_2
+
+ENTRY debug_break
+debug_break:
+    INT int_debug_break
+
+;
+; initiator_abort
+; target_abort
+;
+; PURPOSE : Abort the currently established nexus from with initiator
+;      or target mode.
+;
+;  
+
+ENTRY target_abort
+target_abort:
+    SET TARGET
+    DISCONNECT
+    CLEAR TARGET
+    JUMP schedule
+    
+ENTRY initiator_abort
+initiator_abort:
+    SET ATN
+; In order to abort the currently established nexus, we 
+; need to source/sink up to one byte of data in any SCSI phase, 
+; since the phase cannot change until REQ transitions 
+; false->true
+    JUMP no_eat_cmd, WHEN NOT CMD
+    MOVE 1, NCR53c7xx_zero, WHEN CMD
+no_eat_cmd:
+    JUMP no_eat_msg, WHEN NOT MSG_IN
+    MOVE 1, NCR53c7xx_sink, WHEN MSG_IN
+no_eat_msg:
+    JUMP no_eat_data, WHEN NOT DATA_IN
+    MOVE 1, NCR53c7xx_sink, WHEN DATA_IN
+no_eat_data:
+    JUMP no_eat_status, WHEN NOT STATUS
+    MOVE 1, NCR53c7xx_sink, WHEN STATUS
+no_eat_status:
+    JUMP no_source_data, WHEN NOT DATA_OUT
+    MOVE 1, NCR53c7xx_zero, WHEN DATA_OUT
+no_source_data:
+;
+; If DSP points here, and a phase mismatch is encountered, we need to 
+; do a bus reset.
+;
+    MOVE 1, NCR53c7xx_msg_abort, WHEN MSG_OUT
+    INT int_norm_aborted
+
+;
+; dsa_to_scratch
+; scratch_to_dsa
+;
+; PURPOSE :
+;      The NCR chips cannot do a move memory instruction with the DSA register 
+;      as the source or destination.  So, we provide a couple of subroutines
+;      that let us switch between the DSA register and scratch register.
+;
+;      Memory moves to/from the DSPS  register also dont work, but we 
+;      dont use them.
+;
+;
+
+dsa_to_scratch:
+    MOVE DSA0 TO SFBR
+    MOVE SFBR TO SCRATCH0
+    MOVE DSA1 TO SFBR
+    MOVE SFBR TO SCRATCH1
+    MOVE DSA2 TO SFBR
+    MOVE SFBR TO SCRATCH2
+    MOVE DSA3 TO SFBR
+    MOVE SFBR TO SCRATCH3
+    RETURN
+
+scratch_to_dsa:
+    MOVE SCRATCH0 TO SFBR
+    MOVE SFBR TO DSA0
+    MOVE SCRATCH1 TO SFBR
+    MOVE SFBR TO DSA1
+    MOVE SCRATCH2 TO SFBR
+    MOVE SFBR TO DSA2
+    MOVE SCRATCH3 TO SFBR
+    MOVE SFBR TO DSA3
+    RETURN
+    
diff --git a/drivers/scsi/53c8xx_d.h b/drivers/scsi/53c8xx_d.h
new file mode 100644 (file)
index 0000000..7ff3e1a
--- /dev/null
@@ -0,0 +1,2195 @@
+unsigned long SCRIPT[] = {
+/*
+; NCR 53c810 driver, main script
+; Sponsored by 
+;      iX Multiuser Multitasking Magazine
+;      hm@ix.de
+;
+; Copyright 1993, Drew Eckhardt
+;      Visionary Computing 
+;      (Unix and Linux consulting and custom programming)
+;      drew@Colorado.EDU
+;      +1 (303) 786-7975
+;
+; TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation.
+;
+; PRE-ALPHA
+;
+; For more information, please consult 
+;
+; NCR 53C810
+; PCI-SCSI I/O Processor
+; Data Manual
+;
+; NCR 53C710 
+; SCSI I/O Processor
+; Programmers Guide
+;
+; NCR Microelectronics
+; 1635 Aeroplaza Drive
+; Colorado Springs, CO 80916
+; 1+ (719) 578-3400
+;
+; Toll free literature number
+; +1 (800) 334-5454
+;
+; IMPORTANT : This code is self modifying due to the limitations of 
+;      the NCR53c7,8xx series chips.  Persons debugging this code with
+;      the remote debugger should take this into account, and NOT set
+;      breakpoints in modified instructions.
+;
+;
+; Design:
+; The NCR53c7x0 family of SCSI chips are busmasters with an onboard 
+; microcontroller using a simple instruction set.   
+;
+; So, to minimize the effects of interrupt latency, and to maximize 
+; throughput, this driver offloads the practical maximum amount 
+; of processing to the SCSI chip while still maintaining a common
+; structure.
+;
+; Where tradeoffs were needed between efficiency on the older
+; chips and the newer NCR53c800 series, the NCR53c800 series 
+; was chosen.
+;
+; While the NCR53c700 and NCR53c700-66 lacked the facilities to fully
+; automate SCSI transfers without host processor intervention, this 
+; isnt the case with the NCR53c710 and newer chips which allow 
+;
+; - reads and writes to the internal registers from within the SCSI
+;      scripts, allowing the SCSI SCRIPTS(tm) code to save processor
+;      state so that multiple threads of execution are possible, and also
+;      provide an ALU for loop control, etc.
+; 
+; - table indirect addressing for some instructions. This allows 
+;      pointers to be located relative to the DSA ((Data Structure
+;      Address) register.
+;
+; These features make it possible to implement a mailbox style interface,
+; where the same piece of code is run to handle I/O for multiple threads
+; at once minimizing our need to relocate code.  Since the NCR53c700/
+; NCR53c800 series have a unique combination of features, making a 
+; a standard ingoing/outgoing mailbox system, costly, Ive modified it.
+;
+; - Commands are stored in a linked list, rather than placed in 
+;      arbitrary mailboxes.  This simiplifies the amount of processing
+;      that must be done by the NCR53c810.
+;
+; - Mailboxes are a mixture of code and data.  This lets us greatly
+;      simplify the NCR53c810 code and do things that would otherwise
+;      not be possible.
+
+;
+; Note : the DSA structures must be aligned on 32 bit boundaries,
+; since the source and destination of MOVE MEMORY instructions 
+; must share the same alignment and this is the alignment of the
+; NCR registers.
+;
+
+ABSOLUTE dsa_temp_jump_resume = 0      ; Patch to dsa_jump_resume
+                                       ;       in current dsa
+ABSOLUTE dsa_temp_lun = 0              ; Patch to lun for current dsa
+ABSOLUTE dsa_temp_dsa_next = 0         ; Patch to dsa next for current dsa
+ABSOLUTE dsa_temp_sync = 0             ; Patch to address of per-target
+                                       ;       sync routine
+ABSOLUTE dsa_temp_target = 0           ; Patch to id for current dsa
+
+
+
+ENTRY dsa_code_template
+dsa_code_template:
+
+; Define DSA structure used for mailboxes
+
+; wrong_dsa loads the DSA register with the value of the dsa_next
+; field.
+;
+wrong_dsa:
+;              Patch the MOVE MEMORY INSTRUCTION such that 
+;              the destination address is that of the OLD next
+;              pointer.
+       MOVE MEMORY 4, dsa_temp_dsa_next, reselected_ok + 8
+
+at 0x00000000 : */     0xc0000004,0x00000000,0x00000660,
+/*
+
+       MOVE dmode_memory_to_ncr TO DMODE       
+
+at 0x00000003 : */     0x78380000,0x00000000,
+/*
+       MOVE MEMORY 4, dsa_temp_dsa_next, addr_scratch
+
+at 0x00000005 : */     0xc0000004,0x00000000,0x00000000,
+/*
+       MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000008 : */     0x78380000,0x00000000,
+/*
+       CALL scratch_to_dsa
+
+at 0x0000000a : */     0x88080000,0x00000800,
+/*
+       JUMP reselected_check_next
+
+at 0x0000000c : */     0x80080000,0x000005ac,
+/*
+
+ABSOLUTE dsa_check_reselect = 0
+; dsa_check_reselect determines weather or not the current target and
+; lun match the current DSA
+ENTRY dsa_code_check_reselect
+dsa_code_check_reselect:
+       MOVE SSID TO SFBR               ; SSID contains 3 bit target ID
+
+at 0x0000000e : */     0x720a0000,0x00000000,
+/*
+       JUMP REL (wrong_dsa), IF NOT dsa_temp_target, AND MASK 7
+
+at 0x00000010 : */     0x80840700,0x00ffffb8,
+/*
+       MOVE dmode_memory_to_ncr TO DMODE
+
+at 0x00000012 : */     0x78380000,0x00000000,
+/*
+       MOVE MEMORY 1, reselected_identify, addr_sfbr
+
+at 0x00000014 : */     0xc0000001,0x00000000,0x00000000,
+/*
+       JUMP REL (wrong_dsa), IF NOT dsa_temp_lun, AND MASK 7
+
+at 0x00000017 : */     0x80840700,0x00ffff9c,
+/*
+       MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000019 : */     0x78380000,0x00000000,
+/*
+;              Patch the MOVE MEMORY INSTRUCTION such that
+;              the source address is that of this dsas
+;              next pointer.
+       MOVE MEMORY 4, dsa_temp_dsa_next, reselected_ok + 4
+
+at 0x0000001b : */     0xc0000004,0x00000000,0x0000065c,
+/*
+       CALL reselected_ok
+
+at 0x0000001e : */     0x88080000,0x00000658,
+/*
+       CALL dsa_temp_sync      
+
+at 0x00000020 : */     0x88080000,0x00000000,
+/*
+ENTRY dsa_jump_resume
+dsa_jump_resume:
+       JUMP 0                          ; Jump to resume address
+
+at 0x00000022 : */     0x80080000,0x00000000,
+/*
+ENTRY dsa_zero
+dsa_zero:
+       MOVE dmode_ncr_to_memory TO DMODE                       ; 8
+
+at 0x00000024 : */     0x78380000,0x00000000,
+/*
+       MOVE MEMORY 4, addr_temp, dsa_temp_jump_resume          ; 16    
+
+at 0x00000026 : */     0xc0000004,0x00000000,0x00000000,
+/*
+       MOVE dmode_memory_to_memory TO DMODE                    ; 28
+
+at 0x00000029 : */     0x78380000,0x00000000,
+/*
+       JUMP dsa_schedule                                       ; 36
+
+at 0x0000002b : */     0x80080000,0x000000b4,
+/*
+ENTRY dsa_code_template_end
+dsa_code_template_end:
+
+; Perform sanity check for dsa_fields_start == dsa_code_template_end - 
+; dsa_zero, puke.
+
+ABSOLUTE dsa_fields_start =  36        ; Sanity marker
+                               ;       pad 12
+ABSOLUTE dsa_next = 48         ; len 4 Next DSA
+                               ; del 4 Previous DSA address
+ABSOLUTE dsa_cmnd = 56         ; len 4 Scsi_Cmnd * for this thread.
+ABSOLUTE dsa_select = 60       ; len 4 Device ID, Period, Offset for 
+                               ;       table indirect select
+ABSOLUTE dsa_msgout = 64       ; len 8 table indirect move parameter for 
+                               ;       select message
+ABSOLUTE dsa_cmdout = 72       ; len 8 table indirect move parameter for 
+                               ;       command
+ABSOLUTE dsa_dataout = 80      ; len 4 code pointer for dataout
+ABSOLUTE dsa_datain = 84       ; len 4 code pointer for datain
+ABSOLUTE dsa_msgin = 88                ; len 8 table indirect move for msgin
+ABSOLUTE dsa_status = 96       ; len 8 table indirect move for status byte
+ABSOLUTE dsa_msgout_other = 104        ; len 8 table indirect for normal message out
+                               ; (Synchronous transfer negotiation, etc).
+ABSOLUTE dsa_end = 112
+
+; Linked lists of DSA structures
+ABSOLUTE issue_dsa_head = 0    ; Linked list of DSAs to issue
+ABSOLUTE reconnect_dsa_head = 0        ; Link list of DSAs which can reconnect
+
+; These select the source and destination of a MOVE MEMORY instruction
+ABSOLUTE dmode_memory_to_memory = 0x0
+ABSOLUTE dmode_memory_to_ncr = 0x0
+ABSOLUTE dmode_ncr_to_memory = 0x0
+ABSOLUTE dmode_ncr_to_ncr = 0x0
+
+ABSOLUTE addr_scratch = 0x0
+ABSOLUTE addr_sfbr = 0x0
+ABSOLUTE addr_temp = 0x0
+
+
+; Interrupts - 
+; MSB indicates type
+; 0    handle error condition
+; 1    handle message 
+; 2    handle normal condition
+; 3    debugging interrupt
+; 4    testing interrupt 
+; Next byte indicates specific error
+
+; XXX not yet implemented, Im not sure if I want to - 
+; Next byte indicates the routine the error occurred in
+; The LSB indicates the specific place the error occurred
+ABSOLUTE int_err_unexpected_phase = 0x00000000 ; Unexpected phase encountered
+ABSOLUTE int_err_selected = 0x00010000         ; SELECTED (nee RESELECTED)
+ABSOLUTE int_err_unexpected_reselect = 0x00020000 
+ABSOLUTE int_err_check_condition = 0x00030000  
+ABSOLUTE int_err_no_phase = 0x00040000
+ABSOLUTE int_msg_wdtr = 0x01000000             ; WDTR message received
+ABSOLUTE int_msg_sdtr = 0x01010000             ; SDTR received
+ABSOLUTE int_msg_1 = 0x01020000                        ; single byte special message
+                                               ; received
+
+ABSOLUTE int_norm_select_complete = 0x02000000 ; Select complete, reprogram
+                                               ; registers.
+ABSOLUTE int_norm_reselect_complete = 0x02010000       ; Nexus established
+ABSOLUTE int_norm_command_complete = 0x02020000 ; Command complete
+ABSOLUTE int_norm_disconnected = 0x02030000    ; Disconnected 
+ABSOLUTE int_norm_aborted =0x02040000          ; Aborted *dsa
+ABSOLUTE int_norm_reset = 0x02050000           ; Generated BUS reset.
+ABSOLUTE int_debug_break = 0x03000000          ; Break point
+ABSOLUTE int_debug_scheduled = 0x03010000      ; new I/O scheduled 
+ABSOLUTE int_debug_idle = 0x03020000           ; scheduler is idle
+ABSOLUTE int_debug_dsa_loaded = 0x03030000     ; dsa reloaded
+ABSOLUTE int_debug_reselected = 0x03040000     ; NCR reselected
+ABSOLUTE int_debug_head = 0x03050000           ; issue head overwritten
+
+ABSOLUTE int_test_1 = 0x04000000               ; Test 1 complete
+ABSOLUTE int_test_2 = 0x04010000               ; Test 2 complete
+ABSOLUTE int_test_3 = 0x04020000               ; Test 3 complete
+                                               
+EXTERNAL NCR53c7xx_msg_abort           ; Pointer to abort message
+EXTERNAL NCR53c7xx_msg_reject          ; Pointer to reject message
+EXTERNAL NCR53c7xx_zero                        ; long with zero in it, use for source
+EXTERNAL NCR53c7xx_sink                        ; long to dump worthless data in
+
+; Pointer to final bytes of multi-byte messages
+ABSOLUTE msg_buf = 0
+
+; Pointer to holding area for reselection information
+ABSOLUTE reselected_identify = 0
+ABSOLUTE reselected_tag = 0
+
+; Request sense command pointer, its a 6 byte command, should
+; be constant for all commands since we allways want 16 bytes of 
+; sense and we dont need to change any fields as we did under 
+; SCSI-I when we actually cared about the LUN field.
+;EXTERNAL NCR53c7xx_sense              ; Request sense command
+
+
+; dsa_schedule  
+; PURPOSE : after a DISCONNECT message has been recieved, and pointers
+;      saved, insert the current DSA structure at the head of the 
+;      disconnected queue and fall through to the scheduler.
+;
+; CALLS : OK
+;
+; INPUTS : dsa - current DSA structure, reconnect_dsa_head - list
+;      of disconnected commands
+;
+; MODIFIES : SCRATCH, reconnect_dsa_head
+; 
+; EXITS : allways passes control to schedule
+
+ENTRY dsa_schedule
+dsa_schedule:
+
+;
+; Calculate the address of the next pointer within the DSA 
+; structure of the command that is currently disconnecting
+;
+    CALL dsa_to_scratch
+
+at 0x0000002d : */     0x88080000,0x000007b8,
+/*
+; XXX - we need to deal with the NCR53c710, which lacks an add with
+;      carry instruction, by moving arround the DSA alignment to avoid
+;      carry in situations like this.
+    MOVE SCRATCH0 + dsa_next TO SCRATCH0
+
+at 0x0000002f : */     0x7e343000,0x00000000,
+/*
+    MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
+
+at 0x00000031 : */     0x7f350000,0x00000000,
+/*
+    MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
+
+at 0x00000033 : */     0x7f360000,0x00000000,
+/*
+    MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
+
+at 0x00000035 : */     0x7f370000,0x00000000,
+/*
+
+; Point the next field of this DSA structure at the current disconnected 
+; list
+    MOVE dmode_ncr_to_memory TO DMODE
+
+at 0x00000037 : */     0x78380000,0x00000000,
+/*
+    MOVE MEMORY 4, addr_scratch, dsa_schedule_insert + 8
+
+at 0x00000039 : */     0xc0000004,0x00000000,0x00000100,
+/*
+    MOVE dmode_memory_to_memory TO DMODE
+
+at 0x0000003c : */     0x78380000,0x00000000,
+/*
+dsa_schedule_insert:
+    MOVE MEMORY 4, reconnect_dsa_head, 0 
+
+at 0x0000003e : */     0xc0000004,0x00000000,0x00000000,
+/*
+
+; And update the head pointer.
+    CALL dsa_to_scratch
+
+at 0x00000041 : */     0x88080000,0x000007b8,
+/*
+    MOVE dmode_ncr_to_memory TO DMODE  
+
+at 0x00000043 : */     0x78380000,0x00000000,
+/*
+    MOVE MEMORY 4, addr_scratch, reconnect_dsa_head
+
+at 0x00000045 : */     0xc0000004,0x00000000,0x00000000,
+/*
+    MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000048 : */     0x78380000,0x00000000,
+/*
+    WAIT DISCONNECT
+
+at 0x0000004a : */     0x48000000,0x00000000,
+/*
+
+; schedule
+; PURPOSE : schedule a new I/O once the bus is free by putting the 
+;      address of the next DSA structure in the DSA register.
+;
+; INPUTS : issue_dsa_head - list of new commands
+;
+; CALLS : OK
+;
+; MODIFIES : SCRATCH, DSA 
+;
+; EXITS : if the issue_dsa_head pointer is non-NULL, control
+;      is passed to select.  Otherwise, control is passed to 
+;      wait_reselect.
+
+
+ENTRY schedule
+schedule:
+    ; Point DSA at the current head of the issue queue.
+    MOVE dmode_memory_to_ncr TO DMODE
+
+at 0x0000004c : */     0x78380000,0x00000000,
+/*
+    MOVE MEMORY 4, issue_dsa_head, addr_scratch
+
+at 0x0000004e : */     0xc0000004,0x00000000,0x00000000,
+/*
+    MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000051 : */     0x78380000,0x00000000,
+/*
+
+    CALL scratch_to_dsa
+
+at 0x00000053 : */     0x88080000,0x00000800,
+/*
+
+
+
+
+    ; Check for a null pointer.
+    MOVE DSA0 TO SFBR
+
+at 0x00000055 : */     0x72100000,0x00000000,
+/*
+    JUMP select, IF NOT 0
+
+at 0x00000057 : */     0x80040000,0x00000194,
+/*
+    MOVE DSA1 TO SFBR
+
+at 0x00000059 : */     0x72110000,0x00000000,
+/*
+    JUMP select, IF NOT 0
+
+at 0x0000005b : */     0x80040000,0x00000194,
+/*
+    MOVE DSA2 TO SFBR
+
+at 0x0000005d : */     0x72120000,0x00000000,
+/*
+    JUMP select, IF NOT 0
+
+at 0x0000005f : */     0x80040000,0x00000194,
+/*
+    MOVE DSA3 TO SFBR
+
+at 0x00000061 : */     0x72130000,0x00000000,
+/*
+    JUMP wait_reselect, IF 0
+
+at 0x00000063 : */     0x800c0000,0x00000560,
+/*
+
+    
+
+
+
+;
+; select
+;
+; PURPOSE : establish a nexus for the SCSI command referenced by DSA.
+;      On success, the current DSA structure is removed from the issue 
+;      queue.  Usually, this is entered as a fall-through from schedule,
+;      although the contingent allegience handling code will write
+;      the select entry address to the DSP to restart a command as a 
+;      REQUEST SENSE.  A message is sent (usually IDENTIFY, although
+;      additional SDTR or WDTR messages may be sent).  COMMAND OUT
+;      is handled.
+;
+; INPUTS : DSA - SCSI command, issue_dsa_head
+;
+; CALLS : OK
+;
+; MODIFIES : SCRATCH, issue_dsa_head
+;
+; EXITS : on reselection or selection, go to select_failed
+;      otherwise, fall through to data_transfer.  If a MSG_IN
+;      phase occurs before 
+;
+
+ENTRY select
+select:
+
+
+
+
+    CLEAR TARGET
+
+at 0x00000065 : */     0x60000200,0x00000000,
+/*
+
+; XXX
+;
+; In effect, SELECTION operations are backgrounded, with execution
+; continuing until code which waits for REQ or a fatal interrupt is 
+; encountered.
+;
+; So, for more performance, we could overlap the code which removes 
+; the command from the NCRs issue queue with the selection, but 
+; at this point I dont want to deal with the error recovery.
+;
+
+
+    SELECT ATN FROM dsa_select, select_failed
+
+at 0x00000067 : */     0x4300003c,0x00000694,
+/*
+    JUMP select_msgout, WHEN MSG_OUT
+
+at 0x00000069 : */     0x860b0000,0x000001ac,
+/*
+ENTRY select_msgout
+select_msgout:
+    MOVE FROM dsa_msgout, WHEN MSG_OUT
+
+at 0x0000006b : */     0x1e000000,0x00000040,
+/*
+
+
+
+
+
+
+
+
+
+    ; Calculate address of dsa_next field
+
+    CALL dsa_to_scratch
+
+at 0x0000006d : */     0x88080000,0x000007b8,
+/*
+
+    MOVE SCRATCH0 + dsa_next TO SCRATCH0
+
+at 0x0000006f : */     0x7e343000,0x00000000,
+/*
+    MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
+
+at 0x00000071 : */     0x7f350000,0x00000000,
+/*
+    MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
+
+at 0x00000073 : */     0x7f360000,0x00000000,
+/*
+    MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
+
+at 0x00000075 : */     0x7f370000,0x00000000,
+/*
+
+    ; Patch memory to memory move
+    move dmode_ncr_to_memory TO DMODE
+
+at 0x00000077 : */     0x78380000,0x00000000,
+/*
+    MOVE MEMORY 4, addr_scratch, issue_remove + 4
+
+at 0x00000079 : */     0xc0000004,0x00000000,0x000001fc,
+/*
+
+
+    ; And rewrite the issue_dsa_head pointer.
+    MOVE dmode_memory_to_memory TO DMODE
+
+at 0x0000007c : */     0x78380000,0x00000000,
+/*
+issue_remove:
+;      The actual UPDATE of the issue_dsa_head variable is 
+;      atomic, with all of the setup code being irrelevant to
+;      weather the updated value being the old or new contents of 
+;      dsa_next field.
+;
+;      To insure synchronization, the host system merely needs to 
+;      do a XCHG instruction with interrupts disabled on the 
+;      issue_dsa_head memory address.
+;
+;      The net effect will be that the XCHG instruction will return
+;      either a non-NULL value, indicating that the NCR chip will not
+;      go into the idle loop when this command DISCONNECTS, or a NULL
+;      value indicating that the NCR wrote first and that the Linux
+;      code must rewrite the issue_dsa_head pointer and set SIG_P.
+;      
+
+
+    MOVE MEMORY 4, 0, issue_dsa_head
+
+at 0x0000007e : */     0xc0000004,0x00000000,0x00000000,
+/*
+
+
+
+
+
+; After a successful selection, we should get either a CMD phase or 
+; some transfer request negotiation message.
+
+    JUMP cmdout, WHEN CMD
+
+at 0x00000081 : */     0x820b0000,0x00000224,
+/*
+    INT int_err_unexpected_phase, WHEN NOT MSG_IN 
+
+at 0x00000083 : */     0x9f030000,0x00000000,
+/*
+
+select_msg_in:
+    CALL msg_in, WHEN MSG_IN
+
+at 0x00000085 : */     0x8f0b0000,0x00000354,
+/*
+    JUMP select_msg_in, WHEN MSG_IN
+
+at 0x00000087 : */     0x870b0000,0x00000214,
+/*
+
+cmdout:
+    INT int_err_unexpected_phase, WHEN NOT CMD
+
+at 0x00000089 : */     0x9a030000,0x00000000,
+/*
+
+
+
+ENTRY cmdout_cmdout
+cmdout_cmdout:
+
+    MOVE FROM dsa_cmdout, WHEN CMD
+
+at 0x0000008b : */     0x1a000000,0x00000048,
+/*
+
+
+
+
+;
+; data_transfer  
+; other_transfer
+;
+; PURPOSE : handle the main data transfer for a SCSI command in 
+;      two parts.  In the first part, data_transfer, DATA_IN
+;      and DATA_OUT phases are allowed, with the user provided
+;      code (usually dynamically generated based on the scatter/gather
+;      list associated with a SCSI command) called to handle these 
+;      phases.
+;
+;      On completion, the user code passes control to other_transfer
+;      which causes DATA_IN and DATA_OUT to result in unexpected_phase
+;      interrupts so that data overruns may be trapped.
+;
+; INPUTS : DSA - SCSI command
+;
+; CALLS : OK
+;
+; MODIFIES : SCRATCH
+;
+; EXITS : if STATUS IN is detected, signifying command completion,
+;      the NCR jumpst to command_complete.  If MSG IN occurs, a 
+;      CALL is made to msg_in.  Otherwise, other_transfer runs in 
+;      an infinite loop.
+;      
+
+data_transfer:
+    INT int_err_unexpected_phase, WHEN CMD
+
+at 0x0000008d : */     0x9a0b0000,0x00000000,
+/*
+    CALL msg_in, WHEN MSG_IN
+
+at 0x0000008f : */     0x8f0b0000,0x00000354,
+/*
+    INT int_err_unexpected_phase, WHEN MSG_OUT
+
+at 0x00000091 : */     0x9e0b0000,0x00000000,
+/*
+    JUMP do_dataout, WHEN DATA_OUT
+
+at 0x00000093 : */     0x800b0000,0x0000026c,
+/*
+    JUMP do_datain, WHEN DATA_IN
+
+at 0x00000095 : */     0x810b0000,0x000002c4,
+/*
+    JUMP command_complete, WHEN STATUS
+
+at 0x00000097 : */     0x830b0000,0x00000508,
+/*
+    JUMP data_transfer
+
+at 0x00000099 : */     0x80080000,0x00000234,
+/*
+
+;
+; On NCR53c700 and NCR53c700-66 chips, do_dataout/do_datain are fixed up 
+; whenever the nexus changes so it can point to the correct routine for 
+; that command.
+;
+
+
+; Nasty jump to dsa->dataout
+do_dataout:
+    CALL dsa_to_scratch
+
+at 0x0000009b : */     0x88080000,0x000007b8,
+/*
+    MOVE SCRATCH0 + dsa_dataout TO SCRATCH0    
+
+at 0x0000009d : */     0x7e345000,0x00000000,
+/*
+    MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY 
+
+at 0x0000009f : */     0x7f350000,0x00000000,
+/*
+    MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY 
+
+at 0x000000a1 : */     0x7f360000,0x00000000,
+/*
+    MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY 
+
+at 0x000000a3 : */     0x7f370000,0x00000000,
+/*
+    MOVE dmode_ncr_to_memory TO DMODE
+
+at 0x000000a5 : */     0x78380000,0x00000000,
+/*
+    MOVE MEMORY 4, addr_scratch, dataout_to_jump + 4
+
+at 0x000000a7 : */     0xc0000004,0x00000000,0x000002b4,
+/*
+    MOVE dmode_memory_to_memory TO DMODE
+
+at 0x000000aa : */     0x78380000,0x00000000,
+/*
+dataout_to_jump:
+    MOVE MEMORY 4, 0, dataout_jump + 4 
+
+at 0x000000ac : */     0xc0000004,0x00000000,0x000002c0,
+/*
+dataout_jump:
+    JUMP 0
+
+at 0x000000af : */     0x80080000,0x00000000,
+/*
+
+; Nasty jump to dsa->dsain
+do_datain:
+    CALL dsa_to_scratch
+
+at 0x000000b1 : */     0x88080000,0x000007b8,
+/*
+    MOVE SCRATCH0 + dsa_datain TO SCRATCH0     
+
+at 0x000000b3 : */     0x7e345400,0x00000000,
+/*
+    MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY 
+
+at 0x000000b5 : */     0x7f350000,0x00000000,
+/*
+    MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY 
+
+at 0x000000b7 : */     0x7f360000,0x00000000,
+/*
+    MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY 
+
+at 0x000000b9 : */     0x7f370000,0x00000000,
+/*
+    MOVE dmode_ncr_to_memory TO DMODE
+
+at 0x000000bb : */     0x78380000,0x00000000,
+/*
+    MOVE MEMORY 4, addr_scratch, datain_to_jump + 4
+
+at 0x000000bd : */     0xc0000004,0x00000000,0x0000030c,
+/*
+    MOVE dmode_memory_to_memory TO DMODE               
+
+at 0x000000c0 : */     0x78380000,0x00000000,
+/*
+datain_to_jump:
+    MOVE MEMORY 4, 0, datain_jump + 4
+
+at 0x000000c2 : */     0xc0000004,0x00000000,0x00000318,
+/*
+datain_jump:
+    JUMP 0
+
+at 0x000000c5 : */     0x80080000,0x00000000,
+/*
+
+
+;
+; other_transfer is exported because it is referenced by dynamically 
+; generated code.
+;
+ENTRY other_transfer
+other_transfer:
+
+
+
+    INT int_err_unexpected_phase, WHEN CMD
+
+at 0x000000c7 : */     0x9a0b0000,0x00000000,
+/*
+    CALL msg_in, WHEN MSG_IN 
+
+at 0x000000c9 : */     0x8f0b0000,0x00000354,
+/*
+    INT int_err_unexpected_phase, WHEN MSG_OUT
+
+at 0x000000cb : */     0x9e0b0000,0x00000000,
+/*
+    INT int_err_unexpected_phase, WHEN DATA_OUT
+
+at 0x000000cd : */     0x980b0000,0x00000000,
+/*
+    INT int_err_unexpected_phase, WHEN DATA_IN
+
+at 0x000000cf : */     0x990b0000,0x00000000,
+/*
+    JUMP command_complete, WHEN STATUS
+
+at 0x000000d1 : */     0x830b0000,0x00000508,
+/*
+    JUMP other_transfer
+
+at 0x000000d3 : */     0x80080000,0x0000031c,
+/*
+
+;
+; msg_in
+; munge_msg
+;
+; PURPOSE : process messages from a target.  msg_in is called when the 
+;      caller hasnt read the first byte of the message.  munge_message
+;      is called when the caller has read the first byte of the message,
+;      and left it in SFBR.
+;
+;      Various int_* interrupts are generated when the host system
+;      needs to intervene, as is the case with SDTR, WDTR, and
+;      INITIATE RECOVERY messages.
+;
+;      When the host system handles one of these interrupts,
+;      it can respond by rentering at reject_message, 
+;      which rejects the message and returns control to
+;      the caller of msg_in or munge_msg, accept_message
+;      which clears ACK and returns control, or reply_message
+;      which sends the message pointed to by the DSA 
+;      msgout_other table indirect field.
+;
+;      DISCONNECT messages are handled by moving the command
+;      to the reconnect_dsa_queue.
+;
+;      SAVE DATA POINTER and RESTORE DATA POINTERS are currently 
+;      treated as NOPS. 
+;
+; INPUTS : DSA - SCSI COMMAND, SFBR - first byte of message (munge_msg
+;      only)
+;
+; CALLS : NO.  The TEMP register isnt backed up to allow nested calls.
+;
+; MODIFIES : SCRATCH, DSA on DISCONNECT
+;
+; EXITS : On receipt of SAVE DATA POINTER, RESTORE POINTERS,
+;      and normal return from message handlers running under
+;      Linux, control is returned to the caller.  Receipt
+;      of DISCONNECT messages pass control to dsa_schedule.
+;
+ENTRY msg_in
+msg_in:
+    MOVE 1, msg_buf, WHEN MSG_IN
+
+at 0x000000d5 : */     0x0f000001,0x00000000,
+/*
+
+munge_msg:
+    JUMP munge_extended, IF 0x01               ; EXTENDED MESSAGE
+
+at 0x000000d7 : */     0x800c0001,0x00000428,
+/*
+    JUMP munge_2, IF 0x20, AND MASK 0xdf       ; two byte message
+
+at 0x000000d9 : */     0x800cdf20,0x0000039c,
+/*
+;
+; Ive seen a handful of broken SCSI devices which fail to issue
+; a SAVE POINTERS message before disconnecting in the middle of 
+; a transfer, assuming that the DATA POINTER will be implicitly 
+; restored.  So, we treat the SAVE DATA POINTER message as a NOP.
+;
+; Ive also seen SCSI devices which dont issue a RESTORE DATA
+; POINTER message and assume that thats implicit.
+;
+    JUMP accept_message, IF 0x02               ; SAVE DATA POINTER
+
+at 0x000000db : */     0x800c0002,0x000004d8,
+/*
+    JUMP accept_message, IF 0x03               ; RESTORE POINTERS 
+
+at 0x000000dd : */     0x800c0003,0x000004d8,
+/*
+    JUMP munge_disconnect, IF 0x04             ; DISCONNECT
+
+at 0x000000df : */     0x800c0004,0x000003b4,
+/*
+    INT int_msg_1, IF 0x07                     ; MESSAGE REJECT
+
+at 0x000000e1 : */     0x980c0007,0x01020000,
+/*
+    INT int_msg_1, IF 0x0f                     ; INITIATE RECOVERY
+
+at 0x000000e3 : */     0x980c000f,0x01020000,
+/*
+    JUMP reject_message
+
+at 0x000000e5 : */     0x80080000,0x000004b8,
+/*
+
+munge_2:
+    JUMP reject_message
+
+at 0x000000e7 : */     0x80080000,0x000004b8,
+/*
+
+munge_save_data_pointer:
+    CLEAR ACK
+
+at 0x000000e9 : */     0x60000040,0x00000000,
+/*
+    RETURN
+
+at 0x000000eb : */     0x90080000,0x00000000,
+/*
+
+munge_disconnect:
+    MOVE SCNTL2 & 0x7f TO SCNTL2
+
+at 0x000000ed : */     0x7c027f00,0x00000000,
+/*
+    CLEAR ACK
+
+at 0x000000ef : */     0x60000040,0x00000000,
+/*
+
+
+; Pass control to the DSA routine.  Note that we can not call
+; dsa_to_scratch here because that would clobber temp, which 
+; we must preserve.
+    MOVE DSA0 TO SFBR
+
+at 0x000000f1 : */     0x72100000,0x00000000,
+/*
+    MOVE SFBR TO SCRATCH0
+
+at 0x000000f3 : */     0x6a340000,0x00000000,
+/*
+    MOVE DSA1 TO SFBR
+
+at 0x000000f5 : */     0x72110000,0x00000000,
+/*
+    MOVE SFBR TO SCRATCH1
+
+at 0x000000f7 : */     0x6a350000,0x00000000,
+/*
+    MOVE DSA2 TO SFBR
+
+at 0x000000f9 : */     0x72120000,0x00000000,
+/*
+    MOVE SFBR TO SCRATCH2
+
+at 0x000000fb : */     0x6a360000,0x00000000,
+/*
+    MOVE DSA3 TO SFBR
+
+at 0x000000fd : */     0x72130000,0x00000000,
+/*
+    MOVE SFBR TO SCRATCH3
+
+at 0x000000ff : */     0x6a370000,0x00000000,
+/*
+
+    MOVE dmode_ncr_to_memory TO DMODE
+
+at 0x00000101 : */     0x78380000,0x00000000,
+/*
+    MOVE MEMORY 4, addr_scratch, jump_to_dsa + 4
+
+at 0x00000103 : */     0xc0000004,0x00000000,0x00000424,
+/*
+    MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000106 : */     0x78380000,0x00000000,
+/*
+jump_to_dsa:
+    JUMP 0
+
+at 0x00000108 : */     0x80080000,0x00000000,
+/*
+
+
+
+
+
+munge_extended:
+    CLEAR ACK
+
+at 0x0000010a : */     0x60000040,0x00000000,
+/*
+    INT int_err_unexpected_phase, WHEN NOT MSG_IN
+
+at 0x0000010c : */     0x9f030000,0x00000000,
+/*
+    MOVE 1, msg_buf + 1, WHEN MSG_IN
+
+at 0x0000010e : */     0x0f000001,0x00000001,
+/*
+    JUMP munge_extended_2, IF 0x02
+
+at 0x00000110 : */     0x800c0002,0x00000458,
+/*
+    JUMP munge_extended_3, IF 0x03 
+
+at 0x00000112 : */     0x800c0003,0x00000488,
+/*
+    JUMP reject_message
+
+at 0x00000114 : */     0x80080000,0x000004b8,
+/*
+
+munge_extended_2:
+    CLEAR ACK
+
+at 0x00000116 : */     0x60000040,0x00000000,
+/*
+    MOVE 1, msg_buf + 2, WHEN MSG_IN
+
+at 0x00000118 : */     0x0f000001,0x00000002,
+/*
+    JUMP reject_message, IF NOT 0x02   ; Must be WDTR
+
+at 0x0000011a : */     0x80040002,0x000004b8,
+/*
+    CLEAR ACK
+
+at 0x0000011c : */     0x60000040,0x00000000,
+/*
+    MOVE 1, msg_buf + 3, WHEN MSG_IN
+
+at 0x0000011e : */     0x0f000001,0x00000003,
+/*
+    INT int_msg_wdtr
+
+at 0x00000120 : */     0x98080000,0x01000000,
+/*
+
+munge_extended_3:
+    CLEAR ACK
+
+at 0x00000122 : */     0x60000040,0x00000000,
+/*
+    MOVE 1, msg_buf + 2, WHEN MSG_IN
+
+at 0x00000124 : */     0x0f000001,0x00000002,
+/*
+    JUMP reject_message, IF NOT 0x01   ; Must be SDTR
+
+at 0x00000126 : */     0x80040001,0x000004b8,
+/*
+    CLEAR ACK
+
+at 0x00000128 : */     0x60000040,0x00000000,
+/*
+    MOVE 2, msg_buf + 3, WHEN MSG_IN
+
+at 0x0000012a : */     0x0f000002,0x00000003,
+/*
+    INT int_msg_sdtr
+
+at 0x0000012c : */     0x98080000,0x01010000,
+/*
+
+ENTRY reject_message
+reject_message:
+    SET ATN
+
+at 0x0000012e : */     0x58000008,0x00000000,
+/*
+    CLEAR ACK
+
+at 0x00000130 : */     0x60000040,0x00000000,
+/*
+    MOVE 1, NCR53c7xx_msg_reject, WHEN MSG_OUT
+
+at 0x00000132 : */     0x0e000001,((unsigned long)&NCR53c7xx_msg_reject),
+/*
+    RETURN
+
+at 0x00000134 : */     0x90080000,0x00000000,
+/*
+
+ENTRY accept_message
+accept_message:
+    CLEAR ACK
+
+at 0x00000136 : */     0x60000040,0x00000000,
+/*
+    RETURN
+
+at 0x00000138 : */     0x90080000,0x00000000,
+/*
+
+ENTRY respond_message
+msg_respond:
+    SET ATN
+
+at 0x0000013a : */     0x58000008,0x00000000,
+/*
+    CLEAR ACK
+
+at 0x0000013c : */     0x60000040,0x00000000,
+/*
+    MOVE FROM dsa_msgout_other, WHEN MSG_OUT
+
+at 0x0000013e : */     0x1e000000,0x00000068,
+/*
+    RETURN
+
+at 0x00000140 : */     0x90080000,0x00000000,
+/*
+
+;
+; command_complete
+;
+; PURPOSE : handle command termination when STATUS IN is detected by reading
+;      a status byte followed by a command termination message. 
+;
+;      Normal termination results in an INTFLY instruction, and 
+;      the host system can pick out which command terminated by 
+;      examining the MESSAGE and STATUS buffers of all currently 
+;      executing commands;
+;
+;      Abnormal (CHECK_CONDITION) termination results in an
+;      int_err_check_condition interrupt so that a REQUEST SENSE
+;      command can be issued out-of-order so that no other command
+;      clears the contingent allegience condition.
+;      
+;
+; INPUTS : DSA - command       
+;
+; CALLS : OK
+;
+; EXITS : On successful termination, control is passed to schedule.
+;      On abnormal termination, the user will usually modify the 
+;      DSA fields and corresponding buffers and return control
+;      to select.
+;
+
+ENTRY command_complete
+command_complete:
+    MOVE FROM dsa_status, WHEN STATUS
+
+at 0x00000142 : */     0x1b000000,0x00000060,
+/*
+
+    MOVE SFBR TO SCRATCH0              ; Save status
+
+at 0x00000144 : */     0x6a340000,0x00000000,
+/*
+
+ENTRY command_complete_msgin
+command_complete_msgin:
+    MOVE FROM dsa_msgin, WHEN MSG_IN
+
+at 0x00000146 : */     0x1f000000,0x00000058,
+/*
+; Indicate that we should be expecting a disconnect
+    MOVE SCNTL2 & 0x7f TO SCNTL2
+
+at 0x00000148 : */     0x7c027f00,0x00000000,
+/*
+    CLEAR ACK
+
+at 0x0000014a : */     0x60000040,0x00000000,
+/*
+
+    MOVE SCRATCH0 TO SFBR                      
+
+at 0x0000014c : */     0x72340000,0x00000000,
+/*
+
+;
+; The SCSI specification states that when a UNIT ATTENTION condition
+; is pending, as indicated by a CHECK CONDITION status message,
+; the target shall revert to asynchronous transfers.  Since
+; synchronous transfers parameters are maintained on a per INITIATOR/TARGET 
+; basis, and returning control to our scheduler could work on a command
+; running on another lun on that target using the old parameters, we must
+; interrupt the host processor to get them changed, or change them ourselves.
+;
+; Once SCSI-II tagged queueing is implemented, things will be even more
+; hairy, since contingent allegience conditions exist on a per-target/lun
+; basis, and issuing a new command with a different tag would clear it.
+; In these cases, we must interrupt the host processor to get a request 
+; added to the HEAD of the queue with the request sense command, or we
+; must automatically issue the request sense command.
+
+
+
+
+    INTFLY
+
+at 0x0000014e : */     0x98180000,0x00000000,
+/*
+
+    WAIT DISCONNECT
+
+at 0x00000150 : */     0x48000000,0x00000000,
+/*
+
+    JUMP schedule
+
+at 0x00000152 : */     0x80080000,0x00000130,
+/*
+command_failed:
+    WAIT DISCONNECT
+
+at 0x00000154 : */     0x48000000,0x00000000,
+/*
+    INT int_err_check_condition
+
+at 0x00000156 : */     0x98080000,0x00030000,
+/*
+
+
+
+
+;
+; wait_reselect
+;
+; PURPOSE : This is essentially the idle routine, where control lands
+;      when there are no new processes to schedule.  wait_reselect
+;      waits for reselection, selection, and new commands.
+;
+;      When a successful reselection occurs, with the aid 
+;      of fixedup code in each DSA, wait_reselect walks the 
+;      reconnect_dsa_queue, asking each dsa if the target ID
+;      and LUN match its.
+;
+;      If a match is found, a call is made back to reselected_ok,
+;      which through the miracles of self modifying code, extracts
+;      the found DSA from the reconnect_dsa_queue and then 
+;      returns control to the DSAs thread of execution.
+;
+; INPUTS : NONE
+;
+; CALLS : OK
+;
+; MODIFIES : DSA,
+;
+; EXITS : On successful reselection, control is returned to the 
+;      DSA which called reselected_ok.  If the WAIT RESELECT
+;      was interrupted by a new commands arival signalled by 
+;      SIG_P, control is passed to schedule.  If the NCR is 
+;      selected, the host system is interrupted with an 
+;      int_err_selected which is usually responded to by
+;      setting DSP to the target_abort address.
+    
+wait_reselect:
+
+
+
+    WAIT RESELECT wait_reselect_failed
+
+at 0x00000158 : */     0x50000000,0x0000067c,
+/*
+
+reselected:
+    ; Read all data needed to restablish the nexus - 
+    MOVE 1, reselected_identify, WHEN MSG_IN
+
+at 0x0000015a : */     0x0f000001,0x00000000,
+/*
+
+    ; Well add a jump to here after some how determining that 
+    ; tagged queueing isnt in use on this device.
+reselected_notag:    
+    MOVE MEMORY 1, NCR53c7xx_zero, reselected_tag
+
+at 0x0000015c : */     0xc0000001,((unsigned long)&NCR53c7xx_zero),0x00000000,
+/*
+
+
+
+
+
+    ; Point DSA at the current head of the disconnected queue.
+    MOVE dmode_memory_to_ncr  TO DMODE
+
+at 0x0000015f : */     0x78380000,0x00000000,
+/*
+    MOVE MEMORY 4, reconnect_dsa_head, addr_scratch
+
+at 0x00000161 : */     0xc0000004,0x00000000,0x00000000,
+/*
+    MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000164 : */     0x78380000,0x00000000,
+/*
+    CALL scratch_to_dsa
+
+at 0x00000166 : */     0x88080000,0x00000800,
+/*
+
+    ; Fix the update-next pointer so that the reconnect_dsa_head
+    ; pointer is the one that will be updated if this DSA is a hit 
+    ; and we remove it from the queue.
+
+    MOVE MEMORY 4, reconnect_dsa_head, reselected_ok + 8
+
+at 0x00000168 : */     0xc0000004,0x00000000,0x00000660,
+/*
+
+ENTRY reselected_check_next
+reselected_check_next:
+    ; Check for a NULL pointer.
+    MOVE DSA0 TO SFBR
+
+at 0x0000016b : */     0x72100000,0x00000000,
+/*
+    JUMP reselected_not_end, IF NOT 0
+
+at 0x0000016d : */     0x80040000,0x000005f4,
+/*
+    MOVE DSA1 TO SFBR
+
+at 0x0000016f : */     0x72110000,0x00000000,
+/*
+    JUMP reselected_not_end, IF NOT 0
+
+at 0x00000171 : */     0x80040000,0x000005f4,
+/*
+    MOVE DSA2 TO SFBR
+
+at 0x00000173 : */     0x72120000,0x00000000,
+/*
+    JUMP reselected_not_end, IF NOT 0
+
+at 0x00000175 : */     0x80040000,0x000005f4,
+/*
+    MOVE DSA3 TO SFBR
+
+at 0x00000177 : */     0x72130000,0x00000000,
+/*
+    JUMP reselected_not_end, IF NOT 0
+
+at 0x00000179 : */     0x80040000,0x000005f4,
+/*
+    INT int_err_unexpected_reselect
+
+at 0x0000017b : */     0x98080000,0x00020000,
+/*
+
+reselected_not_end:
+    MOVE DSA0 TO SFBR
+
+at 0x0000017d : */     0x72100000,0x00000000,
+/*
+    ;
+    ; XXX the ALU is only eight bits wide, and the assembler
+    ; wont do the dirt work for us.  As long as dsa_check_reselect
+    ; is negative, we need to sign extend with 1 bits to the full
+    ; 32 bit width oof the address.
+    ;
+    ; A potential work arround would be to have a known alignment 
+    ; of the DSA structure such that the base address plus 
+    ; dsa_check_reselect doesnt require carryin from bytes 
+    ; higher than the LSB.
+    ;
+
+    MOVE SFBR + dsa_check_reselect TO SCRATCH0
+
+at 0x0000017f : */     0x6e340000,0x00000000,
+/*
+    MOVE DSA1 TO SFBR
+
+at 0x00000181 : */     0x72110000,0x00000000,
+/*
+    MOVE SFBR + 0xff TO SCRATCH1 WITH CARRY
+
+at 0x00000183 : */     0x6f35ff00,0x00000000,
+/*
+    MOVE DSA2 TO SFBR
+
+at 0x00000185 : */     0x72120000,0x00000000,
+/*
+    MOVE SFBR + 0xff TO SCRATCH2 WITH CARRY
+
+at 0x00000187 : */     0x6f36ff00,0x00000000,
+/*
+    MOVE DSA3 TO SFBR
+
+at 0x00000189 : */     0x72130000,0x00000000,
+/*
+    MOVE SFBR + 0xff TO SCRATCH3 WITH CARRY
+
+at 0x0000018b : */     0x6f37ff00,0x00000000,
+/*
+
+    MOVE dmode_ncr_to_memory TO DMODE
+
+at 0x0000018d : */     0x78380000,0x00000000,
+/*
+    MOVE MEMORY 4, addr_scratch, reselected_check + 4
+
+at 0x0000018f : */     0xc0000004,0x00000000,0x00000654,
+/*
+    MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000192 : */     0x78380000,0x00000000,
+/*
+reselected_check:
+    JUMP 0
+
+at 0x00000194 : */     0x80080000,0x00000000,
+/*
+
+
+;
+;
+reselected_ok:
+    MOVE MEMORY 4, 0, 0                                ; Patched : first word
+
+at 0x00000196 : */     0xc0000004,0x00000000,0x00000000,
+/*
+                                               ;       is this successful 
+                                               ;       dsa_next
+                                               ; Second word is 
+                                               ;       unsuccessful dsa_next
+    CLEAR ACK                                  ; Accept last message
+
+at 0x00000199 : */     0x60000040,0x00000000,
+/*
+    RETURN                                     ; Return control to where
+
+at 0x0000019b : */     0x90080000,0x00000000,
+/*
+
+
+
+
+selected:
+    INT int_err_selected;
+
+at 0x0000019d : */     0x98080000,0x00010000,
+/*
+
+;
+; A select or reselect failure can be caused by one of two conditions : 
+; 1.  SIG_P was set.  This will be the case if the user has written
+;      a new value to a previously NULL head of the issue queue.
+;
+; 2.  The NCR53c810 was selected or reselected by another device.
+; 
+
+wait_reselect_failed:
+; Reading CTEST2 clears the SIG_P bit in the ISTAT register.
+    MOVE CTEST2 & 0x40 TO SFBR 
+
+at 0x0000019f : */     0x741a4000,0x00000000,
+/*
+    JUMP selected, IF NOT 0x40
+
+at 0x000001a1 : */     0x80040040,0x00000674,
+/*
+    JUMP schedule
+
+at 0x000001a3 : */     0x80080000,0x00000130,
+/*
+
+select_failed:
+    MOVE ISTAT & 0x20 TO SFBR
+
+at 0x000001a5 : */     0x74142000,0x00000000,
+/*
+    JUMP reselected, IF NOT 0x20
+
+at 0x000001a7 : */     0x80040020,0x00000568,
+/*
+    MOVE ISTAT & 0xdf TO ISTAT
+
+at 0x000001a9 : */     0x7c14df00,0x00000000,
+/*
+    JUMP schedule
+
+at 0x000001ab : */     0x80080000,0x00000130,
+/*
+
+;
+; test_1
+; test_2
+;
+; PURPOSE : run some verification tests on the NCR.  test_1
+;      copies test_src to test_dest and interrupts the host
+;      processor, testing for cache coherency and interrupt
+;      problems in the processes.
+;
+;      test_2 runs a command with offsets relative to the 
+;      DSA on entry, and is useful for miscellaneous experimentation.
+;
+
+; Verify that interrupts are working correctly and that we dont 
+; have a cache invalidation problem.
+
+ABSOLUTE test_src = 0, test_dest = 0
+ENTRY test_1
+test_1:
+    MOVE MEMORY 4, test_src, test_dest
+
+at 0x000001ad : */     0xc0000004,0x00000000,0x00000000,
+/*
+    INT int_test_1
+
+at 0x000001b0 : */     0x98080000,0x04000000,
+/*
+
+;
+; Run arbitrary commands, with test code establishing a DSA
+;
+ENTRY test_2
+test_2:
+    CLEAR TARGET
+
+at 0x000001b2 : */     0x60000200,0x00000000,
+/*
+    SELECT ATN FROM 0, test_2_fail
+
+at 0x000001b4 : */     0x43000000,0x00000720,
+/*
+    JUMP test_2_msgout, WHEN MSG_OUT
+
+at 0x000001b6 : */     0x860b0000,0x000006e0,
+/*
+ENTRY test_2_msgout
+test_2_msgout:
+    MOVE FROM 8, WHEN MSG_OUT
+
+at 0x000001b8 : */     0x1e000000,0x00000008,
+/*
+    MOVE FROM 16, WHEN CMD 
+
+at 0x000001ba : */     0x1a000000,0x00000010,
+/*
+    MOVE FROM 24, WHEN DATA_IN
+
+at 0x000001bc : */     0x19000000,0x00000018,
+/*
+    MOVE FROM 32, WHEN STATUS
+
+at 0x000001be : */     0x1b000000,0x00000020,
+/*
+    MOVE FROM 40, WHEN MSG_IN
+
+at 0x000001c0 : */     0x1f000000,0x00000028,
+/*
+    MOVE SCNTL2 & 0x7f TO SCNTL2
+
+at 0x000001c2 : */     0x7c027f00,0x00000000,
+/*
+    CLEAR ACK
+
+at 0x000001c4 : */     0x60000040,0x00000000,
+/*
+    WAIT DISCONNECT
+
+at 0x000001c6 : */     0x48000000,0x00000000,
+/*
+test_2_fail:
+    INT int_test_2
+
+at 0x000001c8 : */     0x98080000,0x04010000,
+/*
+
+ENTRY debug_break
+debug_break:
+    INT int_debug_break
+
+at 0x000001ca : */     0x98080000,0x03000000,
+/*
+
+;
+; initiator_abort
+; target_abort
+;
+; PURPOSE : Abort the currently established nexus from with initiator
+;      or target mode.
+;
+;  
+
+ENTRY target_abort
+target_abort:
+    SET TARGET
+
+at 0x000001cc : */     0x58000200,0x00000000,
+/*
+    DISCONNECT
+
+at 0x000001ce : */     0x48000000,0x00000000,
+/*
+    CLEAR TARGET
+
+at 0x000001d0 : */     0x60000200,0x00000000,
+/*
+    JUMP schedule
+
+at 0x000001d2 : */     0x80080000,0x00000130,
+/*
+    
+ENTRY initiator_abort
+initiator_abort:
+    SET ATN
+
+at 0x000001d4 : */     0x58000008,0x00000000,
+/*
+; In order to abort the currently established nexus, we 
+; need to source/sink up to one byte of data in any SCSI phase, 
+; since the phase cannot change until REQ transitions 
+; false->true
+    JUMP no_eat_cmd, WHEN NOT CMD
+
+at 0x000001d6 : */     0x82030000,0x00000768,
+/*
+    MOVE 1, NCR53c7xx_zero, WHEN CMD
+
+at 0x000001d8 : */     0x0a000001,((unsigned long)&NCR53c7xx_zero),
+/*
+no_eat_cmd:
+    JUMP no_eat_msg, WHEN NOT MSG_IN
+
+at 0x000001da : */     0x87030000,0x00000778,
+/*
+    MOVE 1, NCR53c7xx_sink, WHEN MSG_IN
+
+at 0x000001dc : */     0x0f000001,((unsigned long)&NCR53c7xx_sink),
+/*
+no_eat_msg:
+    JUMP no_eat_data, WHEN NOT DATA_IN
+
+at 0x000001de : */     0x81030000,0x00000788,
+/*
+    MOVE 1, NCR53c7xx_sink, WHEN DATA_IN
+
+at 0x000001e0 : */     0x09000001,((unsigned long)&NCR53c7xx_sink),
+/*
+no_eat_data:
+    JUMP no_eat_status, WHEN NOT STATUS
+
+at 0x000001e2 : */     0x83030000,0x00000798,
+/*
+    MOVE 1, NCR53c7xx_sink, WHEN STATUS
+
+at 0x000001e4 : */     0x0b000001,((unsigned long)&NCR53c7xx_sink),
+/*
+no_eat_status:
+    JUMP no_source_data, WHEN NOT DATA_OUT
+
+at 0x000001e6 : */     0x80030000,0x000007a8,
+/*
+    MOVE 1, NCR53c7xx_zero, WHEN DATA_OUT
+
+at 0x000001e8 : */     0x08000001,((unsigned long)&NCR53c7xx_zero),
+/*
+no_source_data:
+;
+; If DSP points here, and a phase mismatch is encountered, we need to 
+; do a bus reset.
+;
+    MOVE 1, NCR53c7xx_msg_abort, WHEN MSG_OUT
+
+at 0x000001ea : */     0x0e000001,((unsigned long)&NCR53c7xx_msg_abort),
+/*
+    INT int_norm_aborted
+
+at 0x000001ec : */     0x98080000,0x02040000,
+/*
+
+;
+; dsa_to_scratch
+; scratch_to_dsa
+;
+; PURPOSE :
+;      The NCR chips cannot do a move memory instruction with the DSA register 
+;      as the source or destination.  So, we provide a couple of subroutines
+;      that let us switch between the DSA register and scratch register.
+;
+;      Memory moves to/from the DSPS  register also dont work, but we 
+;      dont use them.
+;
+;
+
+dsa_to_scratch:
+    MOVE DSA0 TO SFBR
+
+at 0x000001ee : */     0x72100000,0x00000000,
+/*
+    MOVE SFBR TO SCRATCH0
+
+at 0x000001f0 : */     0x6a340000,0x00000000,
+/*
+    MOVE DSA1 TO SFBR
+
+at 0x000001f2 : */     0x72110000,0x00000000,
+/*
+    MOVE SFBR TO SCRATCH1
+
+at 0x000001f4 : */     0x6a350000,0x00000000,
+/*
+    MOVE DSA2 TO SFBR
+
+at 0x000001f6 : */     0x72120000,0x00000000,
+/*
+    MOVE SFBR TO SCRATCH2
+
+at 0x000001f8 : */     0x6a360000,0x00000000,
+/*
+    MOVE DSA3 TO SFBR
+
+at 0x000001fa : */     0x72130000,0x00000000,
+/*
+    MOVE SFBR TO SCRATCH3
+
+at 0x000001fc : */     0x6a370000,0x00000000,
+/*
+    RETURN
+
+at 0x000001fe : */     0x90080000,0x00000000,
+/*
+
+scratch_to_dsa:
+    MOVE SCRATCH0 TO SFBR
+
+at 0x00000200 : */     0x72340000,0x00000000,
+/*
+    MOVE SFBR TO DSA0
+
+at 0x00000202 : */     0x6a100000,0x00000000,
+/*
+    MOVE SCRATCH1 TO SFBR
+
+at 0x00000204 : */     0x72350000,0x00000000,
+/*
+    MOVE SFBR TO DSA1
+
+at 0x00000206 : */     0x6a110000,0x00000000,
+/*
+    MOVE SCRATCH2 TO SFBR
+
+at 0x00000208 : */     0x72360000,0x00000000,
+/*
+    MOVE SFBR TO DSA2
+
+at 0x0000020a : */     0x6a120000,0x00000000,
+/*
+    MOVE SCRATCH3 TO SFBR
+
+at 0x0000020c : */     0x72370000,0x00000000,
+/*
+    MOVE SFBR TO DSA3
+
+at 0x0000020e : */     0x6a130000,0x00000000,
+/*
+    RETURN
+
+at 0x00000210 : */     0x90080000,0x00000000,
+};
+
+#define A_addr_scratch 0x00000000
+unsigned long A_addr_scratch_used[] = {
+       0x00000007,
+       0x0000003a,
+       0x00000046,
+       0x00000050,
+       0x0000007a,
+       0x000000a8,
+       0x000000be,
+       0x00000104,
+       0x00000163,
+       0x00000190,
+};
+
+#define A_addr_sfbr    0x00000000
+unsigned long A_addr_sfbr_used[] = {
+       0x00000016,
+};
+
+#define A_addr_temp    0x00000000
+unsigned long A_addr_temp_used[] = {
+       0x00000027,
+};
+
+#define A_dmode_memory_to_memory       0x00000000
+unsigned long A_dmode_memory_to_memory_used[] = {
+       0x00000008,
+       0x00000019,
+       0x00000029,
+       0x0000003c,
+       0x00000048,
+       0x00000051,
+       0x0000007c,
+       0x000000aa,
+       0x000000c0,
+       0x00000106,
+       0x00000164,
+       0x00000192,
+};
+
+#define A_dmode_memory_to_ncr  0x00000000
+unsigned long A_dmode_memory_to_ncr_used[] = {
+       0x00000003,
+       0x00000012,
+       0x0000004c,
+       0x0000015f,
+};
+
+#define A_dmode_ncr_to_memory  0x00000000
+unsigned long A_dmode_ncr_to_memory_used[] = {
+       0x00000024,
+       0x00000037,
+       0x00000043,
+       0x00000077,
+       0x000000a5,
+       0x000000bb,
+       0x00000101,
+       0x0000018d,
+};
+
+#define A_dmode_ncr_to_ncr     0x00000000
+unsigned long A_dmode_ncr_to_ncr_used[] = {
+};
+
+#define A_dsa_check_reselect   0x00000000
+unsigned long A_dsa_check_reselect_used[] = {
+       0x0000017f,
+};
+
+#define A_dsa_cmdout   0x00000048
+unsigned long A_dsa_cmdout_used[] = {
+       0x0000008c,
+};
+
+#define A_dsa_cmnd     0x00000038
+unsigned long A_dsa_cmnd_used[] = {
+};
+
+#define A_dsa_datain   0x00000054
+unsigned long A_dsa_datain_used[] = {
+       0x000000b3,
+};
+
+#define A_dsa_dataout  0x00000050
+unsigned long A_dsa_dataout_used[] = {
+       0x0000009d,
+};
+
+#define A_dsa_end      0x00000070
+unsigned long A_dsa_end_used[] = {
+};
+
+#define A_dsa_fields_start     0x00000024
+unsigned long A_dsa_fields_start_used[] = {
+};
+
+#define A_dsa_msgin    0x00000058
+unsigned long A_dsa_msgin_used[] = {
+       0x00000147,
+};
+
+#define A_dsa_msgout   0x00000040
+unsigned long A_dsa_msgout_used[] = {
+       0x0000006c,
+};
+
+#define A_dsa_msgout_other     0x00000068
+unsigned long A_dsa_msgout_other_used[] = {
+       0x0000013f,
+};
+
+#define A_dsa_next     0x00000030
+unsigned long A_dsa_next_used[] = {
+       0x0000002f,
+       0x0000006f,
+};
+
+#define A_dsa_select   0x0000003c
+unsigned long A_dsa_select_used[] = {
+       0x00000067,
+};
+
+#define A_dsa_status   0x00000060
+unsigned long A_dsa_status_used[] = {
+       0x00000143,
+};
+
+#define A_dsa_temp_dsa_next    0x00000000
+unsigned long A_dsa_temp_dsa_next_used[] = {
+       0x00000001,
+       0x00000006,
+       0x0000001c,
+};
+
+#define A_dsa_temp_jump_resume 0x00000000
+unsigned long A_dsa_temp_jump_resume_used[] = {
+       0x00000028,
+};
+
+#define A_dsa_temp_lun 0x00000000
+unsigned long A_dsa_temp_lun_used[] = {
+       0x00000017,
+};
+
+#define A_dsa_temp_sync        0x00000000
+unsigned long A_dsa_temp_sync_used[] = {
+       0x00000021,
+};
+
+#define A_dsa_temp_target      0x00000000
+unsigned long A_dsa_temp_target_used[] = {
+       0x00000010,
+};
+
+#define A_int_debug_break      0x03000000
+unsigned long A_int_debug_break_used[] = {
+       0x000001cb,
+};
+
+#define A_int_debug_dsa_loaded 0x03030000
+unsigned long A_int_debug_dsa_loaded_used[] = {
+};
+
+#define A_int_debug_head       0x03050000
+unsigned long A_int_debug_head_used[] = {
+};
+
+#define A_int_debug_idle       0x03020000
+unsigned long A_int_debug_idle_used[] = {
+};
+
+#define A_int_debug_reselected 0x03040000
+unsigned long A_int_debug_reselected_used[] = {
+};
+
+#define A_int_debug_scheduled  0x03010000
+unsigned long A_int_debug_scheduled_used[] = {
+};
+
+#define A_int_err_check_condition      0x00030000
+unsigned long A_int_err_check_condition_used[] = {
+       0x00000157,
+};
+
+#define A_int_err_no_phase     0x00040000
+unsigned long A_int_err_no_phase_used[] = {
+};
+
+#define A_int_err_selected     0x00010000
+unsigned long A_int_err_selected_used[] = {
+       0x0000019e,
+};
+
+#define A_int_err_unexpected_phase     0x00000000
+unsigned long A_int_err_unexpected_phase_used[] = {
+       0x00000084,
+       0x0000008a,
+       0x0000008e,
+       0x00000092,
+       0x000000c8,
+       0x000000cc,
+       0x000000ce,
+       0x000000d0,
+       0x0000010d,
+};
+
+#define A_int_err_unexpected_reselect  0x00020000
+unsigned long A_int_err_unexpected_reselect_used[] = {
+       0x0000017c,
+};
+
+#define A_int_msg_1    0x01020000
+unsigned long A_int_msg_1_used[] = {
+       0x000000e2,
+       0x000000e4,
+};
+
+#define A_int_msg_sdtr 0x01010000
+unsigned long A_int_msg_sdtr_used[] = {
+       0x0000012d,
+};
+
+#define A_int_msg_wdtr 0x01000000
+unsigned long A_int_msg_wdtr_used[] = {
+       0x00000121,
+};
+
+#define A_int_norm_aborted     0x02040000
+unsigned long A_int_norm_aborted_used[] = {
+       0x000001ed,
+};
+
+#define A_int_norm_command_complete    0x02020000
+unsigned long A_int_norm_command_complete_used[] = {
+};
+
+#define A_int_norm_disconnected        0x02030000
+unsigned long A_int_norm_disconnected_used[] = {
+};
+
+#define A_int_norm_reselect_complete   0x02010000
+unsigned long A_int_norm_reselect_complete_used[] = {
+};
+
+#define A_int_norm_reset       0x02050000
+unsigned long A_int_norm_reset_used[] = {
+};
+
+#define A_int_norm_select_complete     0x02000000
+unsigned long A_int_norm_select_complete_used[] = {
+};
+
+#define A_int_test_1   0x04000000
+unsigned long A_int_test_1_used[] = {
+       0x000001b1,
+};
+
+#define A_int_test_2   0x04010000
+unsigned long A_int_test_2_used[] = {
+       0x000001c9,
+};
+
+#define A_int_test_3   0x04020000
+unsigned long A_int_test_3_used[] = {
+};
+
+#define A_issue_dsa_head       0x00000000
+unsigned long A_issue_dsa_head_used[] = {
+       0x0000004f,
+       0x00000080,
+};
+
+#define A_msg_buf      0x00000000
+unsigned long A_msg_buf_used[] = {
+       0x000000d6,
+       0x0000010f,
+       0x00000119,
+       0x0000011f,
+       0x00000125,
+       0x0000012b,
+};
+
+#define A_reconnect_dsa_head   0x00000000
+unsigned long A_reconnect_dsa_head_used[] = {
+       0x0000003f,
+       0x00000047,
+       0x00000162,
+       0x00000169,
+};
+
+#define A_reselected_identify  0x00000000
+unsigned long A_reselected_identify_used[] = {
+       0x00000015,
+       0x0000015b,
+};
+
+#define A_reselected_tag       0x00000000
+unsigned long A_reselected_tag_used[] = {
+       0x0000015e,
+};
+
+#define A_test_dest    0x00000000
+unsigned long A_test_dest_used[] = {
+       0x000001af,
+};
+
+#define A_test_src     0x00000000
+unsigned long A_test_src_used[] = {
+       0x000001ae,
+};
+
+#define Ent_accept_message     0x000004d8
+#define Ent_cmdout_cmdout      0x0000022c
+#define Ent_command_complete   0x00000508
+#define Ent_command_complete_msgin     0x00000518
+#define Ent_debug_break        0x00000728
+#define Ent_dsa_code_check_reselect    0x00000038
+#define Ent_dsa_code_template  0x00000000
+#define Ent_dsa_code_template_end      0x000000b4
+#define Ent_dsa_jump_resume    0x00000088
+#define Ent_dsa_schedule       0x000000b4
+#define Ent_dsa_zero   0x00000090
+#define Ent_initiator_abort    0x00000750
+#define Ent_msg_in     0x00000354
+#define Ent_other_transfer     0x0000031c
+#define Ent_reject_message     0x000004b8
+#define Ent_reselected_check_next      0x000005ac
+#define Ent_respond_message    0x00000000
+#define Ent_schedule   0x00000130
+#define Ent_select     0x00000194
+#define Ent_select_msgout      0x000001ac
+#define Ent_target_abort       0x00000730
+#define Ent_test_1     0x000006b4
+#define Ent_test_2     0x000006c8
+#define Ent_test_2_msgout      0x000006e0
+unsigned long LABELPATCHES[] = {
+       0x00000002,
+       0x0000000b,
+       0x0000000d,
+       0x0000001d,
+       0x0000001f,
+       0x0000002c,
+       0x0000002e,
+       0x0000003b,
+       0x00000042,
+       0x00000054,
+       0x00000058,
+       0x0000005c,
+       0x00000060,
+       0x00000064,
+       0x00000068,
+       0x0000006a,
+       0x0000006e,
+       0x0000007b,
+       0x00000082,
+       0x00000086,
+       0x00000088,
+       0x00000090,
+       0x00000094,
+       0x00000096,
+       0x00000098,
+       0x0000009a,
+       0x0000009c,
+       0x000000a9,
+       0x000000ae,
+       0x000000b2,
+       0x000000bf,
+       0x000000c4,
+       0x000000ca,
+       0x000000d2,
+       0x000000d4,
+       0x000000d8,
+       0x000000da,
+       0x000000dc,
+       0x000000de,
+       0x000000e0,
+       0x000000e6,
+       0x000000e8,
+       0x00000105,
+       0x00000111,
+       0x00000113,
+       0x00000115,
+       0x0000011b,
+       0x00000127,
+       0x00000153,
+       0x00000159,
+       0x00000167,
+       0x0000016a,
+       0x0000016e,
+       0x00000172,
+       0x00000176,
+       0x0000017a,
+       0x00000191,
+       0x000001a2,
+       0x000001a4,
+       0x000001a8,
+       0x000001ac,
+       0x000001b5,
+       0x000001b7,
+       0x000001d3,
+       0x000001d7,
+       0x000001db,
+       0x000001df,
+       0x000001e3,
+       0x000001e7,
+};
+
+unsigned long INSTRUCTIONS     = 0x000000fe;
+unsigned long PATCHES  = 0x00000045;
diff --git a/drivers/scsi/53c8xx_u.h b/drivers/scsi/53c8xx_u.h
new file mode 100644 (file)
index 0000000..a6645b3
--- /dev/null
@@ -0,0 +1,79 @@
+#undef A_addr_scratch
+#undef A_addr_sfbr
+#undef A_addr_temp
+#undef A_dmode_memory_to_memory
+#undef A_dmode_memory_to_ncr
+#undef A_dmode_ncr_to_memory
+#undef A_dmode_ncr_to_ncr
+#undef A_dsa_check_reselect
+#undef A_dsa_cmdout
+#undef A_dsa_cmnd
+#undef A_dsa_datain
+#undef A_dsa_dataout
+#undef A_dsa_end
+#undef A_dsa_fields_start
+#undef A_dsa_msgin
+#undef A_dsa_msgout
+#undef A_dsa_msgout_other
+#undef A_dsa_next
+#undef A_dsa_select
+#undef A_dsa_status
+#undef A_dsa_temp_dsa_next
+#undef A_dsa_temp_jump_resume
+#undef A_dsa_temp_lun
+#undef A_dsa_temp_sync
+#undef A_dsa_temp_target
+#undef A_int_debug_break
+#undef A_int_debug_dsa_loaded
+#undef A_int_debug_head
+#undef A_int_debug_idle
+#undef A_int_debug_reselected
+#undef A_int_debug_scheduled
+#undef A_int_err_check_condition
+#undef A_int_err_no_phase
+#undef A_int_err_selected
+#undef A_int_err_unexpected_phase
+#undef A_int_err_unexpected_reselect
+#undef A_int_msg_1
+#undef A_int_msg_sdtr
+#undef A_int_msg_wdtr
+#undef A_int_norm_aborted
+#undef A_int_norm_command_complete
+#undef A_int_norm_disconnected
+#undef A_int_norm_reselect_complete
+#undef A_int_norm_reset
+#undef A_int_norm_select_complete
+#undef A_int_test_1
+#undef A_int_test_2
+#undef A_int_test_3
+#undef A_issue_dsa_head
+#undef A_msg_buf
+#undef A_reconnect_dsa_head
+#undef A_reselected_identify
+#undef A_reselected_tag
+#undef A_test_dest
+#undef A_test_src
+#undef Ent_accept_message
+#undef Ent_cmdout_cmdout
+#undef Ent_command_complete
+#undef Ent_command_complete_msgin
+#undef Ent_debug_break
+#undef Ent_dsa_code_check_reselect
+#undef Ent_dsa_code_template
+#undef Ent_dsa_code_template_end
+#undef Ent_dsa_jump_resume
+#undef Ent_dsa_schedule
+#undef Ent_dsa_zero
+#undef Ent_initiator_abort
+#undef Ent_msg_in
+#undef Ent_other_transfer
+#undef Ent_reject_message
+#undef Ent_reselected_check_next
+#undef Ent_respond_message
+#undef Ent_schedule
+#undef Ent_select
+#undef Ent_select_msgout
+#undef Ent_target_abort
+#undef Ent_test_1
+#undef Ent_test_2
+#undef Ent_test_2_msgout
index d620594822ce421548dc88c5d0432982d798231e..7dbf1f08d40e4156fffa17e2cecffbce13a00d65 100644 (file)
@@ -1,4 +1,4 @@
-#
+
 # Makefile for kernel/blk_drv/scsi
 #
 # Note! Dependencies are done automagically by 'make dep', which also
@@ -24,8 +24,8 @@ SCSI_SRCS =
 
 ifdef CONFIG_SCSI
 
-SCSI_OBJS := hosts.o scsi.o scsi_ioctl.o constants.o
-SCSI_SRCS := hosts.c scsi.c scsi_ioctl.c constants.c
+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
@@ -87,6 +87,11 @@ SCSI_OBJS := $(SCSI_OBJS) g_NCR5380.o
 SCSI_SRCS := $(SCSI_SRCS) g_NCR5380.c
 endif
 
+#ifdef CONFIG_SCSI_NCR53C7xx
+SCSI_OBJS := $(SCSI_OBJS) 53c7,8xx.o 
+SCSI_SRCS := $(SCSI_SRCS) 53c7,8xx.c
+#endif
+
 ifdef CONFIG_SCSI_PAS16
 SCSI_OBJS := $(SCSI_OBJS) pas16.o
 SCSI_SRCS := $(SCSI_SRCS) pas16.c
@@ -131,6 +136,17 @@ aha152x.o: aha152x.c
 seagate.o: seagate.c
        $(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -c seagate.c 
 
+# For debugging, use the -g flag
+53c7,8xx.o : 53c7,8xx.c
+       $(CC) $(CFLAGS) -g -c 53c7,8xx.c
+
+53c8xx_d.h 53c8xx_u.h : 53c7,8xx.scr script_asm.pl
+       ln 53c7,8xx.scr fake.c
+       $(CPP) -DCHIP=810 fake.c | grep -v ^# | perl script_asm.pl 
+       mv script.h 53c8xx_d.h
+       mv scriptu.h 53c8xx_u.h
+       rm fake.c
+
 dep:
        $(CPP) -M $(AHA152X) $(SCSI_SRCS) > .depend
 
index a7e7ff04109194b0f835e11a8f8f79ba1da57155..2791df25a2bae2817c15e0fef87cd6f1e1003b66 100644 (file)
@@ -1,3 +1,4 @@
+#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
@@ -11,7 +12,7 @@
  *     drew@colorado.edu
  *     +1 (303) 666-5836
  *
- * DISTRIBUTION REALEASE 4
+ * DISTRIBUTION REALEASE 6
  *
  * For more information, please consult 
  *
@@ -688,13 +689,65 @@ static void NCR5380_print_options (struct Scsi_Host *instance) {
     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.  
+ */
+
+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)
+ * Function : void NCR5380_init (struct Scsi_Host *instance, flags)
  *
- * Purpose : initializies *instance and corresponding 5380 chip.
+ * Purpose : initializies *instance and corresponding 5380 chip,
+ *     with flags OR'd into the initial flags value.
  *
  * Inputs : instance - instantiation of the 5380 driver.  
  *
@@ -703,15 +756,25 @@ static void NCR5380_print_options (struct Scsi_Host *instance) {
  * 
  */
 
-static void NCR5380_init (struct Scsi_Host *instance) {
+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)
@@ -721,10 +784,11 @@ static void NCR5380_init (struct Scsi_Host *instance) {
 #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;
+    hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT | flags;
 
     if (!the_template) {
        the_template = instance->hostt;
@@ -748,6 +812,11 @@ static void NCR5380_init (struct Scsi_Host *instance) {
     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
 }
 
 /* 
@@ -860,8 +929,8 @@ static void NCR5380_main (void) {
     do {
        cli(); /* Freeze request queues */
        done = 1;
-       for (instance = first_instance; instance && instance->hostt == the_template; 
-           instance=instance->next) {
+       for (instance = first_instance; instance && 
+           instance->hostt == the_template; instance=instance->next) {
            hostdata = (struct NCR5380_hostdata *) instance->hostdata;
            cli();
            if (!hostdata->connected) {
@@ -989,12 +1058,16 @@ static void NCR5380_intr (int irq) {
 #endif
                        NCR5380_reselect(instance);
                        (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
-                   } else if (basr & 
-                       BASR_PARITY_ERROR) {
+                   } 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 
@@ -1023,9 +1096,19 @@ static void NCR5380_intr (int irq) {
                            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));
+                             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);
@@ -1086,11 +1169,13 @@ static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
     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 
@@ -1099,12 +1184,35 @@ static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
 
     NCR5380_write(TARGET_COMMAND_REG, 0);
 
-    /* Start arbitration */ 
+
+    /* 
+     * 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);
@@ -1133,6 +1241,8 @@ static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
        return -1;
     }
 
+
+
     NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_SEL);
     
     if (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) {
@@ -1156,6 +1266,7 @@ static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
     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.
@@ -1184,7 +1295,7 @@ static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
        ICR_ASSERT_ATN | ICR_ASSERT_SEL));
 
     /* 
-     * Something weird happens when we cease to drive BSY - looks
+     * Something wierd happens when we cease to drive BSY - looks
      * like the board/chip is letting us do another read before the 
      * appropriate propogation delay has expired, and we're confusing
      * a BSY signal from ourselves as the target's response to SELECTION.
@@ -1216,12 +1327,33 @@ static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
      * and it's detecting as true.  Sigh.
      */
 
-    while ((jiffies < timeout) && !(NCR5380_read(STATUS_REG) & SR_BSY));
+    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 : wierdness\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);
@@ -1229,9 +1361,12 @@ static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
        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
@@ -1294,6 +1429,7 @@ static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
 
     initialize_SCp(cmd);
 
+
     return 0;
 }
 
@@ -1404,10 +1540,23 @@ static int NCR5380_transfer_pio (struct Scsi_Host *instance,
            printk("scsi%d : req false, handshake complete\n", instance->host_no);
 #endif
 
-       if (!(p == PHASE_MSGOUT && c > 1))
-           NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-       else
-           NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+/*
+ * 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) 
@@ -1510,11 +1659,32 @@ static int NCR5380_transfer_dma (struct Scsi_Host *instance,
     printk("scsi%d : mode reg = 0x%X\n", instance->host_no, NCR5380_read(MODE_REG));
 #endif
 
-    if (p & SR_IO)
+/* 
+ * FOO stuff. For some UNAPPARANT reason, I'm getting 
+ * watchdog timers fired on bootup for NO APPARANT 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_RECIEVE_REG, 0);
-    else {
+    } 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)
@@ -1738,9 +1908,10 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) {
     struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) 
        instance->hostdata;
     unsigned char msgout = NOP;
+    int sink = 0;
     int len, transfersize;
     unsigned char *data;
-    unsigned char phase, tmp, old_phase=0xff;
+    unsigned char phase, tmp, extended_msg[10], old_phase=0xff;
     Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
     NCR5380_setup(instance);
 
@@ -1755,12 +1926,26 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) {
                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;
@@ -1814,7 +1999,9 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) {
                        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
@@ -1824,12 +2011,6 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) {
                    &cmd->SCp.ptr);
                break;
            case PHASE_MSGIN:
-               /* 
-                * XXX - we don't handle multi-byte messages here, since we 
-                * shouldn't get them after the I_T_L_Q nexus is established
-                * for tagged queuing, and the host should initiate any 
-                * negotiations for sync. SCSI, etc.
-                */
                len = 1;
                data = &tmp;
                NCR5380_transfer_pio(instance, &phase, &len, &data);
@@ -1849,6 +2030,9 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) {
 #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);
@@ -1862,6 +2046,7 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) {
                    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);
@@ -1882,6 +2067,8 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) {
 #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",
@@ -1942,11 +2129,18 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) {
                        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:
@@ -1958,6 +2152,8 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) {
                        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 *) 
@@ -1970,7 +2166,12 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) {
                           "  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 interupts */
                    NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
                    /* Wait for bus free to avoid nasty timeouts */
@@ -1989,24 +2190,94 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) {
                 */
                case SAVE_POINTERS:
                case RESTORE_POINTERS:
+                   /* Accept message by clearing ACK */
+                   NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
                    break;
-               default:
+               case EXTENDED_MESSAGE:
 /* 
- * XXX rejected messages should be handled in the pio data transfer phase,
- * since ATN should be raised before ACK goes false when we reject a 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);
 
-                   printk("Unknown message!\n");
+#if (NDEBUG & NDEBUG_EXTENDED)
+                   printk("scsi%d : receiving extended message\n",
+                       instance->host_no);
+#endif
 
-#ifdef notyet
+                   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 recieving 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 wierd 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, legnth %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);
-#endif 
                    break;
                } /* switch (tmp) */
                break;
@@ -2105,6 +2376,14 @@ static void NCR5380_reselect (struct Scsi_Host *instance) {
     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)
@@ -2131,30 +2410,11 @@ static void NCR5380_reselect (struct Scsi_Host *instance) {
 
     while (!(NCR5380_read(STATUS_REG) & SR_REQ));
 
-    len = 3;
+    len = 1;
     data = msg;
     phase = PHASE_MSGIN;
     NCR5380_transfer_pio(instance, &phase, &len, &data);
 
-#ifdef SCSI2
-    /*
-     * If there was no residual from the attempt to transfer three bytes, then
-     * the target sent the one byte IDENTIFY message followed by a two byte
-     * queue message.
-     *
-     * If there were two bytes of residual, we got the IDENTIFY message
-     * only.
-     *
-     * If there was one byte of residual, we got the IDENTIFY message
-     * followed by a RESTORE pointers message (which was ignored - 
-     * see MSGIN phase of the NCR5380_information_transfer() function.
-     */
-
-    if (!len) 
-       tag = msg[2];
-    else
-       tag = 0;
-#endif
 
     if (!msg[0] & 0x80) {
        printk("scsi%d : expecting IDENTIFY message, got ",
@@ -2162,14 +2422,26 @@ static void NCR5380_reselect (struct Scsi_Host *instance) {
        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 restablished, 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)
@@ -2212,7 +2484,7 @@ static void NCR5380_reselect (struct Scsi_Host *instance) {
        hostdata->connected = tmp;
 #if (NDEBUG & NDEBUG_RESELECTION)
        printk"scsi%d : nexus established, target = %d, lun = %d, tag = %d\n",
-           instance->host_no, cmd->target, cmd->lun, cmd->tag);
+           instance->host_no, tmp->target, tmp->lun, tmp->tag);
 #endif
     }
 }
@@ -2294,6 +2566,11 @@ int NCR5380_abort (Scsi_Cmnd *cmd) {
     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);
     
@@ -2302,8 +2579,42 @@ int NCR5380_abort (Scsi_Cmnd *cmd) {
     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);
 /* 
- * Case 1 : If the command hasn't been issued yet, we simply remove it 
+ * 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), 
@@ -2324,7 +2635,7 @@ int NCR5380_abort (Scsi_Cmnd *cmd) {
        }
 
 /* 
- * Case 2 : If any commands are connected, we're going to fail the abort
+ * 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.
  *
@@ -2343,7 +2654,7 @@ int NCR5380_abort (Scsi_Cmnd *cmd) {
     }
 
 /*
- * Case 3: If the command is currently disconnected from the bus, and 
+ * 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.
@@ -2405,7 +2716,7 @@ int NCR5380_abort (Scsi_Cmnd *cmd) {
        }
 
 /*
- * Case 4 : If we reached this point, the command was not found in any of 
+ * 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
@@ -2437,11 +2748,14 @@ 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(1);
+    udelay(25);
     NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
     sti();
 
     return 0;
 }
+
index 0daf072f67128d5465dfe0971ee651aafdd54a15..746bbadb22592853151ff65f920b02e7dbfa326c 100644 (file)
@@ -7,7 +7,7 @@
  *     drew@colorado.edu
  *      +1 (303) 666-5836
  *
- * DISTRIBUTION RELEASE 4
+ * DISTRIBUTION RELEASE 6
  *
  * For more information, please consult 
  *
 
 /*
  * $Log: NCR5380.h,v $
- * Revision 1.3  1994/01/19  05:24:40  drew
- * Added support for TCR LAST_BYTE_SENT bit.
- *
- * 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
  */
 
 #ifndef NCR5380_H
 #define NCR5380_H
 
-#define NCR5380_PUBLIC_RELEASE 4
+#define NCR5380_PUBLIC_RELEASE 6
+#ifdef NCR53C400
+#define NCR53C400_PUBLIC_RELEASE 1
+#endif
 
 #define NDEBUG_ARBITRATION     0x1
 #define NDEBUG_AUTOSENSE       0x2
@@ -59,6 +51,8 @@
 #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
 /* Write any value to this register to start an ini mode DMA recieve */
 #define START_DMA_INITIATOR_RECIEVE_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 interupts */
+#define CSR_SHARED_INTR                0x08    /* rw  Interupt 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
+#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 
 
 #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;             /* implmenentation 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 */
@@ -228,6 +253,10 @@ struct NCR5380_hostdata {
     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 */
@@ -241,7 +270,7 @@ 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);
+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);
 static void NCR5380_main (void);
index ca439864d7d4ab21b26b8f0b1c65d1fc51005851..962ddbf49a44e539766435353b994ea3e25b1848 100644 (file)
@@ -7,18 +7,22 @@
 #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)
+#define CONSTANTS (CONST_COMMAND | CONST_STATUS | CONST_SENSE | CONST_XSENSE | CONST_CMND | CONST_MSG)
 #endif
 
 #if (CONSTANTS & CONST_COMMAND)
@@ -85,7 +89,7 @@ static void print_opcode(int opcode) {
        printk("%s(0x%02x) ", vendor, opcode); 
        break;
   default:
-       printk("%s ",table[opcode & 0x31]);
+       printk("%s ",table[opcode & 0x1f]);
   }
 }
 #else /* CONST & CONST_COMMAND */
@@ -451,14 +455,14 @@ static const char *one_byte_msgs[] = {
 /* 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 *queue_tag_msgs[] = {
+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 *))
 
@@ -475,15 +479,30 @@ int print_msg (const unsigned char *msg) {
     if (msg[0] == EXTENDED_MESSAGE) {
        len = 3 + msg[1];
 #if (CONSTANTS & CONST_MSG)
-       printk("Extended Message code %s arguments ", 
-           (msg[2] < NO_EXTENDED_MESSAGES) ?
-           printk("%s " extended_msgs[msg[2]]),
-           reserved);
-       for (i = 3; i < msg[1]; ++i) 
+       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 < msg[1]; ++i)
-#endif
+       for (i = 0; i < len; ++i)
            printk("%02x ", msg[i]);
+#endif
     /* Identify */
     } else if (msg[0] & 0x80) {
 #if (CONSTANTS & CONST_MSG)
@@ -509,7 +528,7 @@ int print_msg (const unsigned char *msg) {
     /* Two byte */
     } else if (msg[0] <= 0x2f) {
 #if (CONSTANTS & CONST_MSG)
-       if ((msg[0] - 0x20) < NO_TWO_BYTE_MESSAGES) 
+       if ((msg[0] - 0x20) < NO_TWO_BYTE_MSGS)
            printk("%s %02x ", two_byte_msgs[msg[0] - 0x20], 
                msg[1]);
        else 
@@ -527,3 +546,12 @@ int print_msg (const unsigned char *msg) {
 #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);
+}
index edd1e987ba60b167c334b7931c90473030e013d2..8ca2af382934f865e1d01cef7359fbcd3d95849d 100644 (file)
@@ -1,10 +1,10 @@
 /* fdomain.c -- Future Domain TMC-16x0 SCSI driver
  * Created: Sun May  3 18:53:19 1992 by faith@cs.unc.edu
- * Revised: Thu Apr  7 20:30:09 1994 by faith@cs.unc.edu
+ * Revised: Sat Jul 30 22:06:37 1994 by faith@cs.unc.edu
  * Author: Rickard E. Faith, faith@cs.unc.edu
  * Copyright 1992, 1993, 1994 Rickard E. Faith
  *
- * $Id: fdomain.c,v 5.16 1994/04/08 00:30:15 root Exp $
+ * $Id: fdomain.c,v 5.18 1994/07/31 03:09:15 faith Exp $
 
  * 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
  DESCRIPTION:
 
  This is the Linux low-level SCSI driver for Future Domain TMC-1660/1680
- and TMC-1650/1670 SCSI host adapters.  The 1650 and 1670 have a 25-pin
- external connector, whereas the 1660 and 1680 have a SCSI-2 50-pin
+ TMC-1650/1670, and TMC-3260 SCSI host adapters.  The 1650 and 1670 have a
25-pin external connector, whereas the 1660 and 1680 have a SCSI-2 50-pin
  high-density external connector.  The 1670 and 1680 have floppy disk
- controllers built in.
+ controllers built in.  The TMC-3260 is a PCI bus card.
 
  Future Domain's older boards are based on the TMC-1800 chip, and this
- driver was originally written for a TMC-1680 board with the TMC-1800
- chip.  More recently, boards are being produced with the TMC-18C50 chip.
- The latest and greatest board may not work with this driver.  If you have
- to patch this driver so that it will recognize your board's BIOS
+ driver was originally written for a TMC-1680 board with the TMC-1800 chip.
+ More recently, boards are being produced with the TMC-18C50 and TMC-18C30
+ chips.  The latest and greatest board may not work with this driver.  If
you have to patch this driver so that it will recognize your board's BIOS
  signature, then the driver may fail to function after the board is
  detected.
 
  The following BIOS versions are supported: 2.0, 3.0, 3.2, and 3.4.
- The following chips are supported: TMC-1800, TMC-18C50.
- Reports suggest that the driver will also work with the TMC-18C30 chip.
- The support for the version 3.4 BIOS is new, as of March 1994, and may not
- be stable.
+ The following chips are supported: TMC-1800, TMC-18C50, TMC-18C30.
+ Reports suggest that the driver will also work with the 36C70 chip.
 
  If you have a TMC-8xx or TMC-9xx board, then this is not the driver for
  your board.  Please refer to the Seagate driver for more information and
  Thanks to Dave Newman (dnewman@crl.com) for providing initial patches for
  the version 3.4 BIOS.
 
- All of the alpha testers deserve much thanks.
+ Thanks to James T. McKinley (mckinley@msupa.pa.msu.edu) for providing
+ patches that support the TMC-3260, a PCI bus card with the 36C70 chip.
+ The 36C70 chip appears to be "completely compatible" with the 18C30 chip.
  
+ All of the alpha testers deserve much thanks.
+
+
 
  NOTES ON USER DEFINABLE OPTIONS:
 
  DEBUG: This turns on the printing of various debug informaiton.
 #include <linux/string.h>
 #include <linux/ioport.h>
 
-#define VERSION          "$Revision: 5.16 $"
+#define VERSION          "$Revision: 5.18 $"
 
 /* START OF USER DEFINABLE OPTIONS */
 
@@ -257,6 +259,7 @@ static int               port_base         = 0;
 static void              *bios_base        = NULL;
 static int               bios_major        = 0;
 static int               bios_minor        = 0;
+static int               PCI_bus           = 0;
 static int               interrupt_level   = 0;
 static volatile int      in_command        = 0;
 static Scsi_Cmnd         *current_SC       = NULL;
@@ -329,15 +332,17 @@ struct signature {
    int  sig_length;
    int  major_bios_version;
    int  minor_bios_version;
+   int  PCI_bus;
 } signatures[] = {
    /*          1         2         3         4         5         6 */
    /* 123456789012345678901234567890123456789012345678901234567890 */
-   { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89", 5, 50,  2,  0 },
-   { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V1.07/28/89", 5, 50,  2,  0 },
-   { "FUTURE DOMAIN CORP. (C) 1992 V3.00.004/02/92",       5, 44,  3,  0 },
-   { "FUTURE DOMAIN TMC-18XX (C) 1993 V3.203/12/93",       5, 44,  3,  2 },
-   { "Future Domain Corp. V1.0008/18/93",                  5, 33,  3,  4 }, 
-   { "FUTURE DOMAIN TMC-18XX",                             5, 22, -1, -1 },
+   { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89",  5, 50,  2,  0, 0 },
+   { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V1.07/28/89",  5, 50,  2,  0, 0 },
+   { "FUTURE DOMAIN CORP. (C) 1992 V3.00.004/02/92",        5, 44,  3,  0, 0 },
+   { "FUTURE DOMAIN TMC-18XX (C) 1993 V3.203/12/93",        5, 44,  3,  2, 0 },
+   { "Future Domain Corp. V1.0008/18/93",                   5, 33,  3,  4, 0 },
+   { "Future Domain Corp. V1.0008/18/93",                  26, 33,  3,  4, 1 },
+   { "FUTURE DOMAIN TMC-18XX",                              5, 22, -1, -1, 0 },
 
    /* READ NOTICE ABOVE *BEFORE* YOU WASTE YOUR TIME ADDING A SIGANTURE
     Also, fix the disk geometry code for your signature and send your
@@ -479,7 +484,7 @@ static int fdomain_test_loopback( void )
    return 0;
 }
 
-int fdomain_16x0_detect(Scsi_Host_Template * tpnt)
+int fdomain_16x0_detect( Scsi_Host_Template *tpnt )
 {
    int              i, j;
    int              flag = 0;
@@ -508,7 +513,8 @@ int fdomain_16x0_detect(Scsi_Host_Template * tpnt)
                      signatures[j].signature, signatures[j].sig_length )) {
            bios_major = signatures[j].major_bios_version;
            bios_minor = signatures[j].minor_bios_version;
-           bios_base = addresses[i];
+           PCI_bus    = signatures[j].PCI_bus;
+           bios_base  = addresses[i];
         }
       }
    }
@@ -557,29 +563,54 @@ int fdomain_16x0_detect(Scsi_Host_Template * tpnt)
       if (bios_major != 2) printk( " RAM FAILED, " );
 #endif
 
-      /* Anyway, the alternative to finding the address in the RAM is
-        to just search through every possible port address for one
-        that is attached to the Future Domain card.  Don't panic,
-        though, about reading all these random port addresses--there
-        are rumors that the Future Domain BIOS does something very
-        similar.
+      /* Anyway, the alternative to finding the address in the RAM is to
+        just search through every possible port address for one that is
+        attached to the Future Domain card.  Don't panic, though, about
+        reading all these random port addresses -- there are rumors that
+        the Future Domain BIOS does something very similar.
 
         Do not, however, check ports which the kernel knows are being used
-         by another driver.
-       */
+        by another driver. */
 
-      for (i = 0; !flag && i < PORT_COUNT; i++) {
-        port_base = ports[i];
-        if (check_region( port_base, 0x10 )) {
+      if (!PCI_bus) {
+        for (i = 0; !flag && i < PORT_COUNT; i++) {
+           port_base = ports[i];
+           if (check_region( port_base, 0x10 )) {
 #if DEBUG_DETECT
-           printk( " (%x inuse),", port_base );
+              printk( " (%x inuse),", port_base );
 #endif
-           continue;
+              continue;
+           }
+#if DEBUG_DETECT
+           printk( " %x,", port_base );
+#endif
+           flag = fdomain_is_valid_port( port_base );
         }
+      } else {
+
+        /* The proper way of doing this is to use the PCI BIOS call
+            (interrupt 0x1a) to determine the device IRQ and interrupt
+            level.  Then the port_base will be in configuration register
+            0x10 (and configuration register 0x30 will contain the value of
+            bios_base).
+
+           Until the Linux kernel supports this sort of PCI bus query, we
+           scan down a bunch of addresses (Future Domain folks say we
+           should find the address before we get to 0xf800).  This works
+           fine on some systems -- other systems may have to scan more
+           addresses.  If you have to modify this section for your
+           installation, please send mail to faith@cs.unc.edu. */
+
+        for (i = 0xff00; !flag && i > 0xf000; i -= 8) {
+           port_base = i;
+           if (check_region( port_base, 0x10 )) {
 #if DEBUG_DETECT
-        printk( " %x,", port_base );
+              printk( " (%x inuse)," , port_base );
 #endif
-        flag = fdomain_is_valid_port( port_base );
+              continue;
+           }
+           flag = fdomain_is_valid_port( port_base );
+        }
       }
    }
 
@@ -674,7 +705,7 @@ int fdomain_16x0_detect(Scsi_Host_Template * tpnt)
    printk( "Future Domain detection routine scanning for devices:\n" );
    for (i = 0; i < 8; i++) {
       SCinit.target = i;
-      if (i == tpnt->this_id) /* Skip host adapter */
+      if (i == tpnt->this_id)  /* Skip host adapter */
            continue;
       memcpy(SCinit.cmnd, do_request_sense, sizeof(do_request_sense));
       retcode = fdomain_16x0_command(&SCinit);
@@ -1453,11 +1484,11 @@ int fdomain_16x0_reset( Scsi_Cmnd *SCpnt )
 #include "sd.h"
 #include "scsi_ioctl.h"
 
-int fdomain_16x0_biosparam(Scsi_Disk * disk, int dev, int *info_array )
+int fdomain_16x0_biosparam( Scsi_Disk *disk, int dev, int *info_array )
 {
    int              drive;
-   int             size = disk->capacity;
    unsigned char    buf[512 + sizeof( int ) * 2];
+   int             size      = disk->capacity;
    int              *sizes    = (int *)buf;
    unsigned char    *data     = (unsigned char *)(sizes + 2);
    unsigned char    do_read[] = { READ_6, 0, 0, 0, 1, 0 };
index 1a34172edc62a4ac0006fb8100b5c130dcde3699..d1ce605e0cd98579f34c83b2c2d96744124409ca 100644 (file)
@@ -1,10 +1,10 @@
 /* fdomain.h -- Header for Future Domain TMC-16x0 driver
  * Created: Sun May  3 18:47:33 1992 by faith@cs.unc.edu
- * Revised: Sat Mar 19 16:07:14 1994 by faith@cs.unc.edu
+ * Revised: Sat Jul 30 20:20:31 1994 by faith@cs.unc.edu
  * Author: Rickard E. Faith, faith@cs.unc.edu
  * Copyright 1992, 1993, 1994 Rickard E. Faith
  *
- * $Id: fdomain.h,v 5.5 1994/03/19 21:07:38 root Exp $
+ * $Id: fdomain.h,v 5.7 1994/07/31 03:09:15 faith Exp $
 
  * 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
 
 int        fdomain_16x0_detect( Scsi_Host_Template * );
 int        fdomain_16x0_command( Scsi_Cmnd * );
-int        fdomain_16x0_abort( Scsi_Cmnd *);
+int        fdomain_16x0_abort( Scsi_Cmnd * );
 const char *fdomain_16x0_info( void );
 int        fdomain_16x0_reset( Scsi_Cmnd * ); 
 int        fdomain_16x0_queue( Scsi_Cmnd *, void (*done)(Scsi_Cmnd *) );
-
-int        fdomain_16x0_biosparam(Disk *, int, int * );
-
-#define FDOMAIN_16X0 { NULL,                             \
-                        "Future Domain TMC-16x0",        \
-                       fdomain_16x0_detect,              \
-                       NULL,                             \
-                       fdomain_16x0_info,                \
-                       fdomain_16x0_command,             \
-                       fdomain_16x0_queue,               \
-                       fdomain_16x0_abort,               \
-                       fdomain_16x0_reset,               \
-                       NULL,                             \
-                       fdomain_16x0_biosparam,           \
-                       1,                                \
-                       6,                                \
-                       64 /* SG_NONE */,                 \
-                       1,                                \
-                       0,                                \
-                       0,                                \
-                        DISABLE_CLUSTERING}
+int        fdomain_16x0_biosparam( Disk *, int, int * );
+
+#define FDOMAIN_16X0 { NULL,                             \
+                      "Future Domain TMC-16x0",         \
+                      fdomain_16x0_detect,              \
+                      NULL,                             \
+                      fdomain_16x0_info,                \
+                      fdomain_16x0_command,             \
+                      fdomain_16x0_queue,               \
+                      fdomain_16x0_abort,               \
+                      fdomain_16x0_reset,               \
+                      NULL,                             \
+                      fdomain_16x0_biosparam,           \
+                      1,                                \
+                      6,                                \
+                      64,                               \
+                      1,                                \
+                      0,                                \
+                      0,                                \
+                      DISABLE_CLUSTERING }
 #endif
index ce5b7951d88096a63403399951a9d6ab7116098d..088537239548ff5e944db4048e9c61f83c677a1b 100644 (file)
@@ -79,6 +79,10 @@ int generic_NCR5380_reset(Scsi_Cmnd *);
 #define NCR5380_abort generic_NCR5380_abort
 #define NCR5380_reset generic_NCR5380_reset
 
+#define BOARD_NORMAL   0
+#define BOARD_NCR53C400        1
+
 #endif /* else def HOSTS_C */
 #endif /* ndef ASM */
 #endif /* GENERIC_NCR5380_H */
+
index 8fb35f9006465ba0fd7c460129c3ec84f0c1d943..a926d2b243b5473d888e9d874772af677dbf7d0a 100644 (file)
 #include "t128.h"
 #endif
 
+#ifdef CONFIG_SCSI_NCR53C7xx
+#include "53c7,8xx.h"
+#endif
+
 #ifdef CONFIG_SCSI_ULTRASTOR
 #include "ultrastor.h"
 #endif
@@ -146,6 +150,9 @@ static Scsi_Host_Template builtin_scsi_hosts[] =
 #ifdef CONFIG_SCSI_T128
         TRANTOR_T128,
 #endif
+#ifdef CONFIG_SCSI_NCR53C7xx
+       NCR53c7xx,
+#endif
 #ifdef CONFIG_SCSI_7000FASST
        WD7000,
 #endif
index 8cfbf8c38e6fdc9a3987049e2b224685b4622ef5..9cef9c38df30087fdfb7d535bc3938baf79bab12 100644 (file)
  *
  * 2.  With command line overrides - pas16=port,irq may be 
  *     used on the LILO command line to override the defaults.
- *     NOTE:  untested.
  *
  * 3.  With the PAS16_OVERRIDE compile time define.  This is 
  *     specified as an array of address, irq tupples.  Ie, for
  *     one board at the default 0x388 address, IRQ10, I could say 
  *     -DPAS16_OVERRIDE={{0x388, 10}}
- *     NOTE:  Also untested.
+ *     NOTE:  Untested.
  *     
  *     Note that if the override methods are used, place holders must
  *     be specified for other boards in the system.
- * 
+ *
+ *
+ * Configuration notes :
+ *   The current driver does not support interrupt sharing with the
+ *   sound portion of the card.  If you use the same irq for the
+ *   scsi port and sound you will have problems.  Either use
+ *   a different irq for the scsi port or don't use interrupts
+ *   for the scsi port.
+ *
+ *   If you have problems with your card not being recognized, use
+ *   the LILO command line override.  Try to get it recognized without
+ *   interrupts.  Ie, for a board at the default 0x388 base port,
+ *   boot: linux pas16=0x388,255
+ *
+ *     (255 is the IRQ_NONE constant in NCR5380.h)
  */
  
 #include <asm/system.h>
@@ -174,12 +187,14 @@ void      enable_board( int  board_num,  unsigned short port )
  *
  * Inputs : port - base address of the board,
  *         irq - irq to assign to the SCSI port
+ *         force_irq - set it even if it conflicts with sound driver
  *
  */
 
-void   init_board( unsigned short io_port, int irq )
+void   init_board( unsigned short io_port, int irq, int force_irq )
 {
        unsigned int    tmp;
+       unsigned int    pas_irq_code;
 
         /* Initialize the SCSI part of the board */
 
@@ -192,12 +207,25 @@ void      init_board( unsigned short io_port, int irq )
        /* Set the SCSI interrupt pointer without mucking up the sound
         * interrupt pointer in the same byte.
         */
+       pas_irq_code = ( irq < 16 ) ? scsi_irq_translate[irq] : 0;
        tmp = inb( io_port + IO_CONFIG_3 );
-       tmp = (  tmp & 0x0f ) | ( scsi_irq_translate[irq] << 4 );
-       outb( tmp, io_port + IO_CONFIG_3 );
 
-       /* Set up the drive parameters and enable 5380 interrupts */
-       outb( 0x6d, io_port + SYS_CONFIG_4 );
+       if( (( tmp & 0x0f ) == pas_irq_code) && pas_irq_code > 0 
+           && !force_irq )
+       {
+           printk( "pas16: WARNING: Can't use same irq as sound "
+                   "driver -- interrupts diabled\n" );
+           /* Set up the drive parameters, disable 5380 interrupts */
+           outb( 0x4d, io_port + SYS_CONFIG_4 );
+       }
+       else
+       {
+           tmp = (  tmp & 0x0f ) | ( pas_irq_code << 4 );
+           outb( tmp, io_port + IO_CONFIG_3 );
+
+           /* Set up the drive parameters and enable 5380 interrupts */
+           outb( 0x6d, io_port + SYS_CONFIG_4 );
+       }
 }
 
 
@@ -304,7 +332,7 @@ int pas16_detect(Scsi_Host_Template * tpnt) {
        {
            io_port = overrides[current_override].io_port;
            enable_board( current_override, io_port );
-           init_board( io_port, overrides[current_override].irq );
+           init_board( io_port, overrides[current_override].irq, 1 );
        }
        else
            for (; !io_port && (current_base < NO_BASES); ++current_base) {
@@ -314,7 +342,7 @@ int pas16_detect(Scsi_Host_Template * tpnt) {
                if ( !bases[current_base].noauto &&
                     pas16_hw_detect( current_base ) ){
                        io_port = bases[current_base].io_port;
-                       init_board( io_port, default_irqs[ current_base ] ); 
+                       init_board( io_port, default_irqs[ current_base ], 0 ); 
 #if (PDEBUG & PDEBUG_INIT)
                        printk("scsi-pas16 : detected board.\n");
 #endif
@@ -349,6 +377,8 @@ int pas16_detect(Scsi_Host_Template * tpnt) {
        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);
+            /* Disable 5380 interrupts, leave drive params the same */
+            outb( 0x4d, io_port + SYS_CONFIG_4 );
        }
 
 #if defined(PDEBUG) && (PDEBUG & PDEBUG_INIT)
index ed4b34d744db915e4803fac5145aff4606406ad5..2ff99327eb6ebea97afbfebf2ca1bedd7b3cf142 100644 (file)
@@ -36,7 +36,7 @@
 #ifndef PAS16_H
 #define PAS16_H
 
-#define PAS16_PUBLIC_RELEASE 1
+#define PAS16_PUBLIC_RELEASE 2
 
 #define PDEBUG_INIT    0x1
 #define PDEBUG_TRANSFER 0x2
diff --git a/drivers/scsi/script_asm.pl b/drivers/scsi/script_asm.pl
new file mode 100644 (file)
index 0000000..ade2251
--- /dev/null
@@ -0,0 +1,926 @@
+#! /usr/local/bin/perl
+
+# NCR 53c810 script assembler
+# Sponsored by 
+#       iX Multiuser Multitasking Magazine
+#
+# Copyright 1993, Drew Eckhardt
+#      Visionary Computing 
+#      (Unix and Linux consulting and custom programming)
+#      drew@Colorado.EDU
+#      +1 (303) 786-7975 
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software
+#   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation.
+#
+
+# 
+# Basically, I follow the NCR syntax documented in the NCR53c710 
+# Programmer's guide, with the new instructions, registers, etc.
+# from the NCR53c810.
+#
+# Differences between this assembler and NCR's are that 
+# 1.  PASS, REL (data, JUMPs work fine), and the option to start a new 
+#      script,  are unimplemented, since I didn't use them in my scripts.
+# 
+# 2.  I also emit a script_u.h file, which will undefine all of 
+#      the A_*, E_*, etc. symbols defined in the script.  This 
+#      makes including multiple scripts in one program easier
+#      
+# 3.  This is a single pass assembler, which only emits 
+#      .h files.
+#
+
+
+# XXX - set these with command line options
+$debug = 0;            # Print general debugging messages
+$debug_external = 0;   # Print external/forward reference messages
+$list_in_array = 1;    # Emit original SCRIPTS assembler in comments in
+                       # script.h
+$prefix = '';          # define all arrays having this prefix so we 
+                       # don't have name space collisions after 
+                       # assembling this file in different ways for
+                       # different host adapters
+
+# Constants
+
+
+# Table of the SCSI phase encodings
+%scsi_phases = (                       
+    'DATA_OUT', 0x00_00_00_00, 'DATA_IN', 0x01_00_00_00, 'CMD', 0x02_00_00_00,
+    'STATUS', 0x03_00_00_00, 'MSG_OUT', 0x06_00_00_00, 'MSG_IN', 0x07_00_00_00
+);
+
+# XXX - replace references to the *_810 constants with general constants
+# assigned at compile time based on chip type.
+
+# Table of operatoor encodings
+# XXX - NCR53c710 only implements 
+#      move (nop) = 0x00_00_00_00
+#      or = 0x02_00_00_00
+#      and = 0x04_00_00_00
+#      add = 0x06_00_00_00
+
+%operators_810 = (
+    'SHL',  0x01_00_00_00, 
+    '|', 0x02_00_00_00, 'OR', 0x02_00_00_00, 
+    'XOR', 0x03_00_00_00, 
+    '&', 0x04_00_00_00, 'AND', 0x04_00_00_00, 
+    'SHR', 0x05_00_00_00, 
+    # Note : low bit of the operator bit should be set for add with 
+    # carry.
+    '+', 0x06_00_00_00 
+);
+
+
+# Table of register addresses
+%registers_810 = (
+    'SCNTL0', 0, 'SCNTL1', 1, 'SCNTL2', 2, 'SCNTL3', 3,
+    'SCID', 4, 'SXFER', 5, 'SDID', 6, 'GPREG', 7,
+    'SFBR', 8, 'SOCL', 9, 'SSID', 10, 'SBCL', 11,
+    'DSTAT', 12, 'SSTAT0', 13, 'SSTAT1', 14, 'SSTAT2', 15,
+    'DSA0', 16, 'DSA1', 17, 'DSA2', 18, 'DSA3', 19,
+    'ISTAT', 20,
+    'CTEST0', 24, 'CTEST1', 25, 'CTEST2', 26, 'CTEST3', 27,
+    'TEMP0', 28, 'TEMP1', 29, 'TEMP2', 30, 'TEMP3', 31,
+    'DFIFO', 32, 'CTEST4', 33, 'CTEST5', 34, 'CTEST6', 35,
+    'DBC0', 36, 'DBC1', 37, 'DBC2', 38, 'DCMD', 39,
+    'DNAD0', 40, 'DNAD1', 41, 'DNAD2', 42, 'DNAD3', 43,
+    'DSP0', 44, 'DSP1', 45, 'DSP2', 46, 'DSP3', 47,
+    'DSPS0', 48, 'DSPS1', 49, 'DSPS2', 50, 'DSPS3', 51,
+    'SCRATCH0', 52, 'SCRATCH1', 53, 'SCRATCH2', 54, 'SCRATCH3', 55,
+    'SCRATCHA0', 52, 'SCRATCHA1', 53, 'SCRATCHA2', 54, 'SCRATCHA3', 55,
+    'DMODE', 56, 'DIEN', 57, 'DWT', 58, 'DCNTL', 59,
+    'ADDER0', 60, 'ADDER1', 61, 'ADDER2', 62, 'ADDER3', 63,
+    'SIEN0', 64, 'SIEN1', 65, 'SIST0', 66, 'SIST1', 67,
+    'SLPAR', 68,             'MACNTL', 70, 'GPCNTL', 71,
+    'STIME0', 72, 'STIME1', 73, 'RESPID', 74, 
+    'STEST0', 76, 'STEST1', 77, 'STEST2', 78, 'STEST3', 79,
+    'SIDL', 80,
+    'SODL', 84,
+    'SBDL', 88,
+    'SCRATCHB0', 92, 'SCRATCHB1', 93, 'SCRATCHB2', 94, 'SCRATCHB3', 95
+);
+
+# Parsing regular expressions
+$identifier = '[A-Za-z_][A-Za-z_0-9]*';                
+$decnum = '-?\\d+';
+$hexnum = '0[xX][0-9A-Fa-f]+';         
+$constant = "$hexnum|$decnum";
+
+# yucky - since we can't control grouping of # $constant, we need to 
+# expand out each alternative for $value.
+
+$value = "$identifier|$identifier\\s*[+\-]\\s*$decnum|".
+    "$identifier\\s*[+-]\s*$hexnum|$constant";
+
+print STDERR "value regex = $value\n" if ($debug);
+
+$phase = join ('|', keys %scsi_phases);
+print STDERR "phase regex = $phase\n" if ($debug);
+$register = join ('|', keys %registers_810);
+
+# yucky - since %operators_810 includes meta-characters which must
+# be escaped, I can't use the join() trick I used for the register
+# regex
+
+$operator = '\||OR|AND|XOR|\&|\+';
+
+# Global variables
+
+%symbol_values = (%registers_810) ;    # Traditional symbol table
+
+%symbol_references = () ;              # Table of symbol references, where
+                                       # the index is the symbol name, 
+                                       # and the contents a white space 
+                                       # delimited list of address,size
+                                       # tupples where size is in bytes.
+
+@code = ();                            # Array of 32 bit words for SIOP 
+
+@entry = ();                           # Array of entry point names
+
+@label = ();                           # Array of label names
+
+@absolute = ();                                # Array of absolute names
+
+@relative = ();                                # Array of relative names
+
+@external = ();                                # Array of external names
+
+$address = 0;                          # Address of current instruction
+
+$lineno = 0;                           # Line number we are parsing
+
+$output = 'script.h';                  # Output file
+$outputu = 'scriptu.h';
+
+# &patch ($address, $offset, $length, $value) patches $code[$address]
+#      so that the $length bytes at $offset have $value added to
+#      them.  
+
+@inverted_masks = (0x00_00_00_00, 0x00_00_00_ff, 0x00_00_ff_ff, 0x00_ff_ff_ff, 
+    0xff_ff_ff_ff);
+
+sub patch {
+    local ($address, $offset, $length, $value) = @_;
+    if ($debug) {
+       print STDERR "Patching $address at offset $offset, length $length to $value\n";
+       printf STDERR "Old code : %08x\n", $code[$address];
+     }
+
+    $mask = ($inverted_masks[$length] << ($offset * 8));
+   
+    $code[$address] = ($code[$address] & ~$mask) | 
+       (($code[$address] & $mask) + ($value << ($offset * 8)) & 
+       $mask);
+    
+    printf STDERR "New code : %08x\n", $code[$address] if ($debug);
+}
+
+# &parse_value($value, $word, $offset, $length) where $value is 
+#      an identifier or constant, $word is the word offset relative to 
+#      $address, $offset is the starting byte within that word, and 
+#      $length is the length of the field in bytes.
+#
+# Side effects are that the bytes are combined into the @code array
+#      relative to $address, and that the %symbol_references table is 
+#      updated as appropriate.
+
+sub parse_value {
+    local ($value, $word, $offset, $length) = @_;
+    local ($tmp);
+
+    $symbol = '';
+
+    if ($value =~ /^REL\s*\(\s*($identifier)\s*\)\s*(.*)/i) {
+       $relative = 'REL';
+       $symbol = $1;
+       $value = $2;
+print STDERR "Relative reference $symbol\n" if ($debug);
+    } elsif ($value =~ /^($identifier)\s*(.*)/) {
+       $relative = 'ABS';
+       $symbol = $1;
+       $value = $2;
+print STDERR "Absolute reference $symbol\n" if ($debug);
+    } 
+
+    if ($symbol ne '') {
+print STDERR "Referencing symbol $1, length = $length in $_\n" if ($debug);
+       $tmp = ($address + $word) * 4 + $offset;
+       if ($symbol_references{$symbol} ne undef) {
+           $symbol_references{$symbol} = 
+               "$symbol_references{$symbol} $relative,$tmp,$length";
+       } else {
+           if (!defined($symbol_values{$symbol})) {
+print STDERR "forward $1\n" if ($debug_external);
+               $forward{$symbol} = "line $lineno : $_";
+           } 
+           $symbol_references{$symbol} = "$relative,$tmp,$length";
+       }
+    } 
+
+    $value = eval $value;
+    &patch ($address + $word, $offset, $length, $value);
+}
+
+# &parse_conditional ($conditional) where $conditional is the conditional
+# clause from a transfer control instruction (RETURN, CALL, JUMP, INT).
+
+sub parse_conditional {
+    local ($conditional) = @_;
+    if ($conditional =~ /^\s*(IF|WHEN)\s*(.*)/i) {
+       $if = $1;
+       $conditional = $2;
+       if ($if =~ /WHEN/i) {
+           $allow_atn = 0;
+           $code[$address] |= 0x00_01_00_00;
+           $allow_atn = 0;
+           print STDERR "$0 : parsed WHEN\n" if ($debug);
+       } else {
+           $allow_atn = 1;
+           print STDERR "$0 : parsed IF\n" if ($debug);
+       }
+    } else {
+           die "$0 : syntax error in line $lineno : $_
+       expected IF or WHEN
+";
+    }
+
+    if ($conditional =~ /^NOT\s+(.*)$/i) {
+       $not = 'NOT ';
+       $other = 'OR';
+       $conditional = $1;
+       print STDERR "$0 : parsed NOT\n" if ($debug);
+    } else {
+       $code[$address] |= 0x00_08_00_00;
+       $not = '';
+       $other = 'AND'
+    }
+
+    $need_data = 0;
+    if ($conditional =~ /^ATN\s*(.*)/i) {#
+       die "$0 : syntax error in line $lineno : $_
+       WHEN conditional is incompatable with ATN 
+" if (!$allow_atn);
+       $code[$address] |= 0x00_02_00_00;
+       $conditional = $1;
+       print STDERR "$0 : parsed ATN\n" if ($debug);
+    } elsif ($conditional =~ /^($phase)\s*(.*)/i) {
+       $1 = "\U$1\E";
+       $p = $scsi_phases{$1};
+       $code[$address] |= $p | 0x00_02_00_00;
+       $conditional = $2;
+       print STDERR "$0 : parsed phase $1\n" if ($debug);
+    } else {
+       $other = '';
+       $need_data = 1;
+    }
+
+print STDERR "Parsing conjunction, expecting $other\n" if ($debug);
+    if ($conditional =~ /^(AND|OR)\s*(.*)/i) {
+       $conjunction = $1;
+       $conditional = $2;
+       $need_data = 1;
+       die "$0 : syntax error in line $lineno : $_
+           Illegal use of $1.  Valid uses are 
+           ".$not."<phase> $1 data
+           ".$not."ATN $1 data
+" if ($other eq '');
+       die "$0 : syntax error in line $lineno : $_
+       Illegal use of $conjunction.  Valid syntaxes are 
+               NOT <phase>|ATN OR data
+               <phase>|ATN AND data
+" if ($conjunction !~ /\s*$other\s*/i);
+       print STDERR "$0 : parsed $1\n" if ($debug);
+    }
+
+    if ($need_data) {
+print STDERR "looking for data in $conditional\n" if ($debug);
+       if ($conditional=~ /^($value)\s*(.*)/i) {
+           $code[$address] |= 0x00_04_00_00;
+           $conditional = $2;
+           &parse_value($1, 0, 0, 1);
+           print STDERR "$0 : parsed data\n" if ($debug);
+       } else {
+       die "$0 : syntax error in line $lineno : $_
+       expected <data>.
+";
+       }
+    }
+
+    if ($conditional =~ /^\s*,\s*(.*)/) {
+       $conditional = $1;
+       if ($conditional =~ /^AND\s\s*MASK\s\s*($value)\s*(.*)/i) {
+           &parse_value ($1, 0, 1, 1);
+           print STDERR "$0 parsed AND MASK $1\n" if ($debug);
+           die "$0 : syntax error in line $lineno : $_
+       expected end of line, not \"$2\"
+" if ($2 ne '');
+       } else {
+           die "$0 : syntax error in line $lineno : $_
+       expected \",AND MASK <data>\", not \"$2\"
+";
+       }
+    } elsif ($conditional !~ /^\s*$/) { 
+       die "$0 : syntax error in line $lineno : $_
+       expected end of line" . (($need_data) ? " or \"AND MASK <data>\"" : "") . "
+       not \"$conditional\"
+";
+    }
+}
+
+# Parse command line 
+foreach $arg (@argv) {
+    if ($arg =~ /^-prefix\s*=\s*([_a-zA-Z][_a-zA-Z0-9]*)$/i) {
+       $prefix = $1
+    }
+}
+    
+# Main loop
+while (<STDIN>) {
+    $lineno = $lineno + 1;
+    $list[$address] = $list[$address].$_;
+    s/;.*$//;                          # Strip comments
+
+
+    chop;                              # Leave new line out of error messages
+
+# Handle symbol definitions of the form label:
+    if (/^\s*($identifier)\s*:(.*)/) {
+       if (!defined($symbol_values{$1})) {
+           $symbol_values{$1} = $address * 4;  # Address is an index into
+           delete $forward{$1};                # an array of longs
+           push (@label, $1);
+           $_ = $2;
+       } else {
+           die "$0 : redefinition of symbol $1 in line $lineno : $_\n";
+       }
+    }
+
+# Handle symbol definitions of the form ABSOLUTE or RELATIVE identifier = 
+# value
+    if (/^\s*(ABSOLUTE|RELATIVE)\s+(.*)/i) {
+       $is_absolute = $1;
+       $rest = $2;
+       foreach $rest (split (/\s*,\s*/, $rest)) {
+           if ($rest =~ /^($identifier)\s*=\s*($constant)\s*$/) {
+               if ($symbol_values{$1} eq undef) {
+                   $symbol_values{$1} = eval $2;
+                   delete $forward{$1};
+                   if ($is_absolute =~ /ABSOLUTE/i) {
+                       push (@absolute , $1);
+                   } else {
+                       push (@relative, $1);
+                   }
+               } else {
+                   die "$0 : redefinition of symbol $1 in line $lineno : $_\n";
+               }
+           } else {
+               die 
+"$0 : syntax error in line $lineno : $_
+           expected <identifier> = <value>
+";
+           }
+       }
+    } elsif (/^\s*EXTERNAL\s+(.*)/i) {
+       $externals = $1;
+       foreach $external (split (/,/,$externals)) {
+           if ($external =~ /\s*($identifier)\s*$/) {
+               $external = $1;
+               push (@external, $external);
+               delete $forward{$external};
+               if (defined($symbol_values{$external})) {
+                       die "$0 : redefinition of symbol $1 in line $lineno : $_\n";
+               }
+               $symbol_values{$external} = $external;
+print STDERR "defined external $1 to $external\n" if ($debug_external);
+           } else {
+               die 
+"$0 : syntax error in line $lineno : $_
+       expected <identifier>, got $external
+";
+           }
+       }
+# Process ENTRY identifier declarations
+    } elsif (/^\s*ENTRY\s+(.*)/i) {
+       if ($1 =~ /^($identifier)\s*$/) {
+           push (@entry, $1);
+       } else {
+"$0 : syntax error in line $lineno : $_
+       expected ENTRY <identifier>
+";
+       }
+# Process MOVE length, address, WITH|WHEN phase instruction
+    } elsif (/^\s*MOVE\s+(.*)/i) {
+       $rest = $1;
+       if ($rest =~ /^FROM\s+($value)\s*,\s*(WITH|WHEN)\s+($phase)\s*$/i) {
+           $transfer_addr = $1;
+           $with_when = $2;
+           $scsi_phase = $3;
+print STDERR "Parsing MOVE FROM $transfer_addr, $with_when $3\n" if ($debug);
+           $code[$address] = 0x18_00_00_00 | (($with_when =~ /WITH/i) ? 
+               0x00_00_00_00 : 0x08_00_00_00) | $scsi_phases{$scsi_phase};
+           &parse_value ($transfer_addr, 1, 0, 4);
+           $address += 2;
+       } elsif ($rest =~ /^($value)\s*,\s*(PTR\s+|)($value)\s*,\s*(WITH|WHEN)\s+($phase)\s*$/i) {
+           $transfer_len = $1;
+           $ptr = $2;
+           $transfer_addr = $3;
+           $with_when = $4;
+           $scsi_phase = $5;
+           $code[$address] = (($with_when =~ /WITH/i) ? 0x00_00_00_00 : 
+               0x08_00_00_00)  | (($ptr =~ /PTR/i) ? (1 << 29) : 0) | 
+               $scsi_phases{$scsi_phase};
+           &parse_value ($transfer_len, 0, 0, 3);
+           &parse_value ($transfer_addr, 1, 0, 4);
+           $address += 2;
+       } elsif ($rest =~ /^MEMORY\s+(.*)/i) {
+           $rest = $1;
+           $code[$address] = 0xc0_00_00_00; 
+           if ($rest =~ /^($value)\s*,\s*($value)\s*,\s*($value)\s*$/) {
+               $count = $1;
+               $source = $2;
+               $dest =  $3;
+print STDERR "Parsing MOVE MEMORY $count, $source, $dest\n" if ($debug);
+               &parse_value ($count, 0, 0, 3);
+               &parse_value ($source, 1, 0, 4);
+               &parse_value ($dest, 2, 0, 4);
+printf STDERR "Move memory instruction = %08x,%08x,%08x\n", 
+               $code[$address], $code[$address+1], $code[$address +2] if
+               ($debug);
+               $address += 3;
+       
+           } else {
+               die 
+"$0 : syntax error in line $lineno : $_
+       expected <count>, <source>, <destination>
+"
+           }
+       } elsif ($1 =~ /^(.*)\s+(TO|SHL|SHR)\s+(.*)/i) {
+print STDERR "Parsing register to register move\n" if ($debug);
+           $src = $1;
+           $op = "\U$2\E";
+           $rest = $3;
+
+           $code[$address] = 0x40_00_00_00;
+       
+           $force = ($op !~ /TO/i); 
+
+
+print STDERR "Forcing register source \n" if ($force && $debug);
+
+           if (!$force && $src =~ 
+               /^($register)\s+(-|$operator)\s+($value)\s*$/i) {
+print STDERR "register operand  data8 source\n" if ($debug);
+               $src_reg = "\U$1\E";
+               $op = "\U$2\E";
+               if ($op ne '-') {
+                   $data8 = $3;
+               } else {
+                   die "- is not implemented yet.\n"
+               }
+           } elsif ($src =~ /^($register)\s*$/i) {
+print STDERR "register source\n" if ($debug);
+               $src_reg = "\U$1\E";
+               # Encode register to register move as a register | 0 
+               # move to register.
+               if (!$force) {
+                   $op = '|';
+               }
+               $data8 = 0;
+           } elsif (!$force && $src =~ /^($value)\s*$/i) {
+print STDERR "data8 source\n" if ($debug);
+               $src_reg = undef;
+               $op = 'NONE';
+               $data8 = $1;
+           } else {
+               if (!$force) {
+                   die 
+"$0 : syntax error in line $lineno : $_
+       expected <register>
+               <data8>
+               <register> <operand> <data8>
+";
+               } else {
+                   die
+"$0 : syntax error in line $lineno : $_
+       expected <register>
+";
+               }
+           }
+           if ($rest =~ /^($register)\s*(.*)$/i) {
+               $dst_reg = "\U$1\E";
+               $rest = $2;
+           } else {
+           die 
+"$0 : syntax error in $lineno : $_
+       expected <register>, got $rest
+";
+           }
+
+           if ($rest =~ /^WITH\s+CARRY\s*(.*)/i) {
+               $rest = $1;
+               if ($op eq '+') {
+                   $code[$address] |= 0x01_00_00_00;
+               } else {
+                   die
+"$0 : syntax error in $lineno : $_
+       WITH CARRY option is incompatable with the $op operator.
+";
+               }
+           }
+
+           if ($rest !~ /^\s*$/) {
+               die
+"$0 : syntax error in $lineno : $_
+       Expected end of line, got $rest
+";
+           }
+
+           print STDERR "source = $src_reg, data = $data8 , destination = $dst_reg\n"
+               if ($debug);
+           # Note that Move data8 to reg is encoded as a read-modify-write
+           # instruction.
+           if (($src_reg eq undef) || ($src_reg eq $dst_reg)) {
+               $code[$address] |= 0x38_00_00_00 | 
+                   ($registers_810{$dst_reg} << 16);
+           } elsif ($dst_reg =~ /SFBR/i) {
+               $code[$address] |= 0x30_00_00_00 |
+                   ($registers_810{$src_reg} << 16);
+           } elsif ($src_reg =~ /SFBR/i) {
+               $code[$address] |= 0x28_00_00_00 |
+                   ($registers_810{$dst_reg} << 16);
+           } else {
+               die
+"$0 : Illegal combination of registers in line $lineno : $_
+       Either source and destination registers must be the same,
+       or either source or destination register must be SFBR.
+";
+           }
+
+           $code[$address] |= $operators_810{$op};
+           
+           &parse_value ($data8, 0, 1, 1);
+           $code[$address] |= $operators_810{$op};
+           $code[$address + 1] = 0x00_00_00_00;# Reserved
+           $address += 2;
+       } else {
+           die 
+"$0 : syntax error in line $lineno : $_
+       expected (initiator) <length>, <address>, WHEN <phase>
+                (target) <length>, <address>, WITH <phase>
+                MEMORY <length>, <source>, <destination>
+                <expression> TO <register>
+";
+       }
+# Process SELECT {ATN|} id, fail_address
+    } elsif (/^\s*(SELECT|RESELECT)\s+(.*)/i) {
+       $rest = $2;
+       if ($rest =~ /^(ATN|)\s*($value)\s*,\s*($identifier)\s*$/i) {
+           $atn = $1;
+           $id = $2;
+           $alt_addr = $3;
+           $code[$address] = 0x40_00_00_00 | 
+               (($atn =~ /ATN/i) ? 0x01_00_00_00 : 0);
+           $code[$address + 1] = 0x00_00_00_00;
+           &parse_value($id, 0, 2, 1);
+           &parse_value($alt_addr, 1, 0, 4);
+           $address += 2;
+       } elsif ($rest =~ /^(ATN|)\s*FROM\s+($value)\s*,\s*($identifier)\s*$/i) {
+           $atn = $1;
+           $addr = $2;
+           $alt_addr = $3;
+           $code[$address] = 0x42_00_00_00 | 
+               (($atn =~ /ATN/i) ? 0x01_00_00_00 : 0);
+           $code[$address + 1] = 0x00_00_00_00;
+           &parse_value($addr, 0, 0, 3);
+           &parse_value($alt_addr, 1, 0, 4);
+           $address += 2;
+        } else {
+           die 
+"$0 : syntax error in line $lineno : $_
+       expected SELECT id, alternate_address or 
+               SELECT FROM address, alternate_address or 
+               RESELECT id, alternate_address or
+               RESELECT FROM address, alternate_address
+";
+       }
+    } elsif (/^\s*WAIT\s+(.*)/i) {
+           $rest = $1;
+print STDERR "Parsing WAIT $rest\n" if ($debug);
+       if ($rest =~ /^DISCONNECT\s*$/i) {
+           $code[$address] = 0x48_00_00_00;
+           $code[$address + 1] = 0x00_00_00_00;
+           $address += 2;
+       } elsif ($rest =~ /^(RESELECT|SELECT)\s+($identifier)\s*$/i) {
+           $alt_addr = $2;
+           $code[$address] = 0x50_00_00_00;
+           &parse_value ($alt_addr, 1, 0, 4);
+           $address += 2;
+       } else {
+           die
+"$0 : syntax error in line $lineno : $_
+       expected (initiator) WAIT DISCONNECT or 
+                (initiator) WAIT RESELECT alternate_address or
+                (target) WAIT SELECT alternate_address
+";
+       }
+# Handle SET and CLEAR instructions.  Note that we should also do something
+# with this syntax to set target mode.
+    } elsif (/^\s*(SET|CLEAR)\s+(.*)/i) {
+       $set = $1;
+       $list = $2;
+       $code[$address] = ($set =~ /SET/i) ?  0x58_00_00_00 : 
+           0x60_00_00_00;
+       foreach $arg (split (/\s+AND\s+/i,$list)) {
+           if ($arg =~ /ATN/i) {
+               $code[$address] |= 0x00_00_00_08;
+           } elsif ($arg =~ /ACK/i) {
+               $code[$address] |= 0x00_00_00_40;
+           } elsif ($arg =~ /TARGET/i) {
+               $code[$address] |= 0x00_00_02_00;
+           } elsif ($arg =~ /CARRY/i) {
+               $code[$address] |= 0x00_00_04_00;
+           } else {
+               die 
+"$0 : syntax error in line $lineno : $_
+       expected $set followed by a AND delimited list of one or 
+       more strings from the list ACK, ATN, CARRY, TARGET.
+";
+           }
+       }
+       $code[$address + 1] = 0x00_00_00_00;
+       $address += 2;
+    } elsif (/^\s*(JUMP|CALL|INT)\s+(.*)/i) {
+       $instruction = $1;
+       $rest = $2;
+       if ($instruction =~ /JUMP/i) {
+           $code[$address] = 0x80_00_00_00;
+       } elsif ($instruction =~ /CALL/i) {
+           $code[$address] = 0x88_00_00_00;
+       } else {
+           $code[$address] = 0x98_00_00_00;
+       }
+print STDERR "parsing JUMP, rest = $rest\n" if ($debug);
+
+# Relative jump. 
+       if ($rest =~ /^(REL\s*\(\s*$identifier\s*\))\s*(.*)/i) { 
+           $addr = $1;
+           $rest = $2;
+print STDERR "parsing JUMP REL, addr = $addr, rest = $rest\n" if ($debug);
+           $code[$address]  |= 0x00_80_00_00;
+           &parse_value($addr, 1, 0, 4);
+# Absolute jump, requires no more gunk
+       } elsif ($rest =~ /^($value)\s*(.*)/) {
+           $addr = $1;
+           $rest = $2;
+           &parse_value($addr, 1, 0, 4);
+       } else {
+           die
+"$0 : syntax error in line $lineno : $_
+       expected <address> or REL (address)
+";
+       }
+
+       if ($rest =~ /^,\s*(.*)/) {
+           &parse_conditional($1);
+       } elsif ($rest =~ /^\s*$/) {
+           $code[$address] |= (1 << 19);
+       } else {
+           die
+"$0 : syntax error in line $lineno : $_
+       expected , <conditional> or end of line, got $1
+";
+       }
+       
+       $address += 2;
+    } elsif (/^\s*(RETURN|INTFLY)\s*(.*)/i) {
+       $instruction = $1;
+       $conditional = $2; 
+print STDERR "Parsing $instruction\n" if ($debug);
+       $code[$address] = ($instruction =~ /RETURN/i) ? 0x90_00_00_00 :
+           0x98_10_00_00;
+       if ($conditional =~ /^,\s*(.*)/) {
+           $conditional = $1;
+           &parse_conditional ($conditional);
+       } elsif ($conditional !~ /^\s*$/) {
+           die
+"$0 : syntax error in line $lineno : $_
+       expected , <conditional> 
+";
+       } else {
+           $code[$address] |= 0x00_08_00_00;
+       }
+          
+       $code[$address + 1] = 0x00_00_00_00;
+       $address += 2;
+    } elsif (/^\s*DISCONNECT\s*$/) {
+       $code[$address] = 0x48_00_00_00;
+       $code[$address + 1] = 0x00_00_00_00;
+       $address += 2;
+# I'm not sure that I should be including this extension, but 
+# what the hell?
+    } elsif (/^\s*NOP\s*$/i) {
+       $code[$address] = 0x80_88_00_00;
+       $code[$address + 1] = 0x00_00_00_00;
+       $address += 2;
+# Ignore lines consisting entirely of white space
+    } elsif (/^\s*$/) {
+    } else {
+       die 
+"$0 : syntax error in line $lineno: $_
+       expected label:, ABSOLUTE, CLEAR, DISCONNECT, EXTERNAL, MOVE, RESELECT,
+           SELECT SET, or WAIT
+";
+    }
+}
+
+# Fill in label references
+
+@undefined = keys %forward;
+if ($#undefined >= 0) {
+    print STDERR "Undefined symbols : \n";
+    foreach $undef (@undefined) {
+       print STDERR "$undef in $forward{$undef}\n";
+    }
+    exit 1;
+}
+
+@label_patches = ();
+
+@absolute = sort @absolute;
+
+foreach $i (@absolute) {
+    foreach $j (split (/\s+/,$symbol_references{$i})) {
+       $j =~ /(REL|ABS),(.*),(.*)/;
+       $type = $1;
+       $address = $2;
+       $length = $3;
+       die 
+"$0 : $symbol $i has illegal relative reference at address $address,
+    size $length\n"
+       if ($type eq 'REL');
+           
+       &patch ($address / 4, $address % 4, $length, $symbol_values{$i});
+    }
+}
+
+foreach $external (@external) {
+print STDERR "checking external $external \n" if ($debug_external);
+    if ($symbol_references{$external} ne undef) {
+       for $reference (split(/\s+/,$symbol_references{$external})) {
+           $reference =~ /(REL|ABS),(.*),(.*)/;
+           $type = $1;
+           $address = $2;
+           $length = $3;
+           
+           die 
+"$0 : symbol $label is external, has illegal relative reference at $address, 
+    size $length\n"
+               if ($type eq 'REL');
+
+           die 
+"$0 : symbol $label has illegal reference at $address, size $length\n"
+               if ((($address % 4) !=0) || ($length != 4));
+
+           $symbol = $symbol_values{$external};
+           $add = $code[$address / 4];
+           if ($add eq 0) {
+               $code[$address / 4] = $symbol;
+           } else {
+               $add = sprintf ("0x%08x", $add);
+               $code[$address / 4] = "$symbol + $add";
+           }
+               
+print STDERR "referenced external $external at $1\n" if ($debug_external);
+       }
+    }
+}
+
+foreach $label (@label) {
+    if ($symbol_references{$label} ne undef) {
+       for $reference (split(/\s+/,$symbol_references{$label})) {
+           $reference =~ /(REL|ABS),(.*),(.*)/;
+           $type = $1;
+           $address = $2;
+           $length = $3;
+
+           if ((($address % 4) !=0) || ($length != 4)) {
+               die "$0 : symbol $label has illegal reference at $1, size $2\n";
+           }
+
+           if ($type eq 'ABS') {
+               $code[$address / 4] += $symbol_values{$label};
+               push (@label_patches, $address / 4);
+           } else {
+# 
+# - The address of the reference should be in the second and last word
+#      of an instruction
+# - Relative jumps, etc. are relative to the DSP of the _next_ instruction
+#
+# So, we need to add four to the address of the reference, to get 
+# the address of the next instruction, when computing the reference.
+  
+               $tmp = $symbol_values{$label} - 
+                   ($address + 4);
+               die 
+# Relative addressing is limited to 24 bits.
+"$0 : symbol $label is too far ($tmp) from $address to reference as 
+    relative/\n" if (($tmp >= 0x80_00_00) || ($tmp < -0x80_00_00));
+               $code[$address / 4] = $tmp & 0x00_ff_ff_ff;
+           }
+       }
+    }
+}
+
+# Output SCRIPT[] array, one instruction per line.  Optionally 
+# print the original code too.
+
+open (OUTPUT, ">$output") || die "$0 : can't open $output for writing\n";
+open (OUTPUTU, ">$outputu") || die "$0 : can't open $outputu for writing\n";
+
+print OUTPUT "unsigned long ".$prefix."SCRIPT[] = {\n";
+$instructions = 0;
+for ($i = 0; $i < $#code; ) {
+    if ($list_in_array) {
+       printf OUTPUT "/*\n$list[$i]\nat 0x%08x : */", $i;
+    }
+    printf OUTPUT "\t0x%08x,", $code[$i];
+    printf STDERR "Address $i = %x\n", $code[$i] if ($debug);
+    if ($code[$i + 1] =~ /\s*($identifier)(.*)$/) {
+       printf OUTPUT "((unsigned long)&%s)%s,", $1, $2
+    } else {
+       printf OUTPUT "0x%08x,",$code[$i+1];
+    }
+
+    if (($code[$i] & 0xff_00_00_00) == 0xc0_00_00_00) {
+       if ($code[$i + 2] =~ /$identifier/) {
+           printf OUTPUT "(unsigned long)&%s,\n",$code[$i+2];
+       } else {
+           printf OUTPUT "0x%08x,\n",$code[$i+2];
+       }
+       $i += 3;
+    } else {
+       printf OUTPUT "\n";
+       $i += 2;
+    }
+    $instructions += 1;
+}
+print OUTPUT "};\n\n";
+
+foreach $i (@absolute) {
+    printf OUTPUT "#define A_$i\t0x%08x\n", $symbol_values{$i};
+    if (defined($prefix) && $prefix ne '') {
+       printf OUTPUT "#define A_".$i."_used ".$prefix."A_".$i."_used\n";
+       printf OUTPUTU "#undef A_".$i."_used\n";
+    }
+    printf OUTPUTU "#undef A_$i\n";
+
+    printf OUTPUT "unsigned long A_".$i."_used\[\] = {\n";
+printf STDERR "$i is used $symbol_references{$i}\n" if ($debug);
+    foreach $j (split (/\s+/,$symbol_references{$i})) {
+       $j =~ /(ABS|REL),(.*),(.*)/;
+       if ($1 eq 'ABS') {
+           $address = $2;
+           $length = $3;
+           printf OUTPUT "\t0x%08x,\n", $address / 4;
+       }
+    }
+    printf OUTPUT "};\n\n";
+}
+
+foreach $i (sort @entry) {
+    printf OUTPUT "#define Ent_$i\t0x%08x\n", $symbol_values{$i};
+    printf OUTPUTU "#undef Ent_$i\n", $symbol_values{$i};
+}
+
+#
+# NCR assembler outputs label patches in the form of indecies into 
+# the code.
+#
+printf OUTPUT "unsigned long ".$prefix."LABELPATCHES[] = {\n";
+for $patch (sort {$a <=> $b} @label_patches) {
+    printf OUTPUT "\t0x%08x,\n", $patch;
+}
+printf OUTPUT "};\n\n";
+
+printf OUTPUT "unsigned long ".$prefix."INSTRUCTIONS\t= 0x%08x;\n", 
+    $instructions;
+printf OUTPUT "unsigned long ".$prefix."PATCHES\t= 0x%08x;\n", 
+    $#label_patches+1;
+close OUTPUT;
+close OUTPUTU;
index 7213bcda1d9e45cc8580f993aebfd7a065dab316..38bb5c6ae55a2fc4a5cbc473268ca40629bfdbc4 100644 (file)
@@ -67,9 +67,12 @@ const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] =
        scsi_devices an array of these specifing 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. */
@@ -152,6 +155,7 @@ static struct blist blacklist[] =
                                 * 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 */
    {NULL, NULL, NULL}};        
 
 static int blacklisted(unsigned char * response_data){
@@ -180,7 +184,7 @@ static int blacklisted(unsigned char * response_data){
  *     scsi_do_cmd() function.
  */
 
-static volatile int in_scan = 0;
+volatile int in_scan_scsis = 0;
 static int the_result;
 static void scan_scsis_done (Scsi_Cmnd * SCpnt)
        {
@@ -221,7 +225,7 @@ static void scan_scsis (struct Scsi_Host * shpnt)
   struct Scsi_Device_Template * sdtpnt;
   Scsi_Cmnd  SCmd;
   
-  ++in_scan;
+  ++in_scan_scsis;
   lun = 0;
   type = -1;
   SCmd.next = NULL;
@@ -492,7 +496,7 @@ static void scan_scsis (struct Scsi_Host * shpnt)
   /* Last device block does not exist.  Free memory. */
   scsi_init_free((char *) SDpnt, sizeof(Scsi_Device));
   
-  in_scan = 0;
+  in_scan_scsis = 0;
 }       /* scan_scsis  ends */
 
 /*
@@ -514,9 +518,11 @@ static void scsi_times_out (Scsi_Cmnd * SCpnt)
        switch (SCpnt->internal_timeout & (IN_ABORT | IN_RESET))
                {
                case NORMAL_TIMEOUT:
-                       if (!in_scan) {
-                         printk("SCSI host %d timed out - aborting command\n",
-                                SCpnt->host->host_no);
+                       if (!in_scan_scsis) {
+                             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);
 #ifdef DEBUG_TIMEOUT
                          scsi_dump_status();
 #endif
@@ -866,6 +872,8 @@ void scsi_do_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
        ourselves.
 */
 
+       SCpnt->pid = scsi_pid++; 
+
        while (1==1){
          cli();
          if (host->hostt->can_queue
index 182771c260c4cfbccad9373d5a280af763695b47..6804bf9684c4f146d2cfaffff66a1d604ec21a09 100644 (file)
@@ -76,6 +76,7 @@
 #define MODE_SELECT_10         0x55
 #define MODE_SENSE_10          0x5a
 
+extern volatile int in_scan_scsis;
 extern const unsigned char scsi_command_size[8];
 #define COMMAND_SIZE(opcode) scsi_command_size[((opcode) >> 5) & 7]
 
@@ -85,6 +86,10 @@ extern const unsigned char scsi_command_size[8];
 
 #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
@@ -97,6 +102,9 @@ extern const unsigned char scsi_command_size[8];
 #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
@@ -265,6 +273,7 @@ typedef struct scsi_device {
        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;
@@ -280,6 +289,9 @@ typedef struct scsi_device {
        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
@@ -479,6 +491,7 @@ typedef struct scsi_cmnd {
        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;             
 
 /*
index 9046f608098c629f6a00128b675550e04c8d7d87..7d43157027a65c159cfa72117ea7b1ac066e80a4 100644 (file)
@@ -5,6 +5,7 @@
 #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 */
diff --git a/drivers/scsi/scsicam.c b/drivers/scsi/scsicam.c
new file mode 100644 (file)
index 0000000..3ae85e9
--- /dev/null
@@ -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,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=-1;                 /* Give error if 0 cylinders */ 
+
+    *cyls = (unsigned int) cylinders;  /* Stuff return values */ 
+    *secs = (unsigned int) sectors; 
+    *hds  = (unsigned int) heads; 
+    return(rv); 
+} 
index 0b56fd558e607f576da54bf7fe16eed6d86a5592..a921bbed89880bd5a6d56f40df87c9f078ef0a8f 100644 (file)
@@ -161,7 +161,7 @@ static void rw_intr (Scsi_Cmnd *SCpnt)
   int this_count = SCpnt->bufflen >> 9;
 
 #ifdef DEBUG
-  printk("sd%d : rw_intr(%d, %d)\n", MINOR(SCpnt->request.dev), SCpnt->host->host_no, result);
+  printk("sd%c : rw_intr(%d, %d)\n", 'a' + MINOR(SCpnt->request.dev), SCpnt->host->host_no, result);
 #endif
 
 /*
@@ -173,7 +173,7 @@ static void rw_intr (Scsi_Cmnd *SCpnt)
   if (!result) {
 
 #ifdef DEBUG
-    printk("sd%d : %d sectors remain.\n", MINOR(SCpnt->request.dev), SCpnt->request.nr_sectors);
+    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) {
@@ -215,8 +215,8 @@ static void rw_intr (Scsi_Cmnd *SCpnt)
        if (!SCpnt->request.bh)
          {
 #ifdef DEBUG
-           printk("sd%d : handling page request, no buffer\n",
-                  MINOR(SCpnt->request.dev));
+           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,
@@ -441,7 +441,7 @@ repeat:
                }
 
 #ifdef DEBUG
-       printk("sd%d : real dev = /dev/sd%d, block = %d\n", MINOR(SCpnt->request.dev), dev, block);
+       printk("sd%c : real dev = /dev/sd%c, block = %d\n", 'a' + MINOR(SCpnt->request.dev), dev, block);
 #endif
 
        switch (SCpnt->request.cmd)
@@ -689,7 +689,7 @@ repeat:
          };
        };
 #ifdef DEBUG
-       printk("sd%d : %s %d/%d 512 byte blocks.\n", MINOR(SCpnt->request.dev),
+       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
@@ -839,7 +839,7 @@ static int sd_init_onedisk(int i)
         SCpnt->sense_buffer[2] == NOT_READY) {
        int time1;
        if(!spintime){
-         printk( "sd%d: Spinning up disk...", i );
+         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 */
@@ -925,20 +925,20 @@ static int sd_init_onedisk(int i)
 
   if (the_result)
     {
-      printk ("sd%d : READ CAPACITY failed.\n"
-             "sd%d : status = %x, message = %02x, host = %d, driver = %02x \n",
-             i,i,
+      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%d : extended sense code = %1x \n", i, SCpnt->sense_buffer[2] & 0xf);
+       printk("sd%c : extended sense code = %1x \n", 'a' + i, SCpnt->sense_buffer[2] & 0xf);
       else
-       printk("sd%d : sense not available. \n", i);
+       printk("sd%c : sense not available. \n", 'a' + i);
 
-      printk("sd%d : block size assumed to be 512 bytes, disk size 1GB.  \n", 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;
 
@@ -963,8 +963,8 @@ static int sd_init_onedisk(int i)
          rscsi_disks[i].sector_size != 1024 &&
          rscsi_disks[i].sector_size != 256)
        {
-         printk ("sd%d : unsupported sector size %d.\n",
-                 i, rscsi_disks[i].sector_size);
+         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 {
index c2255dc8a060b400b30dfe61ef3cd6ce21eafbc1..e2e7cd10c5ca9125df198a2cca6f31cf91df04e4 100644 (file)
@@ -672,8 +672,8 @@ static int internal_command(unsigned char target, unsigned char lun, const void
  */
                
                for (clock = jiffies + 10, temp = 0; (jiffies < clock) &&
-                    !(STATUS & STAT_IO););
-               
+                   !((temp = STATUS) & STAT_IO) || (STAT & STAT_BSY););
+
                if (jiffies >= clock)
                        {
 #if (DEBUG & PHASE_RESELECT)
index c921b4aa5fe52d56ef22de8be01b73c3b639103d..b0bb647a8b62980b391c961f35c004226fd8ac4b 100644 (file)
@@ -277,7 +277,7 @@ static int sr_open(struct inode * inode, struct file * filp)
           !scsi_CDs[MINOR(inode->i_rdev)].device) return -ENXIO;   /* No such device */
 
        if (filp->f_mode & 2)  
-           return -EACCES;
+           return -EROFS;
 
         check_disk_change(inode->i_rdev);
 
index 6d8c1f4ddc1a9a99d266111ca2fa292a35ded1fc..4d8eedc1b06721c815bed3512c0f92124dfd2ddb 100644 (file)
@@ -231,7 +231,7 @@ int t128_detect(Scsi_Host_Template * tpnt) {
        instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
        instance->base = base;
 
-       NCR5380_init(instance);
+       NCR5380_init(instance, 0);
 
        if (overrides[current_override].irq != IRQ_AUTO)
            instance->irq = overrides[current_override].irq;
@@ -319,10 +319,16 @@ static inline int NCR5380_pread (struct Scsi_Host *instance, unsigned char *dst,
        T_DATA_REG_OFFSET), *d = dst;
     register i = len;
 
-    while (!(instance->base[T_STATUS_REG_OFFSET]) & T_ST_RDY);
 
-    for (; i; --i) 
+#if 0
+    for (; i; --i) {
+       while (!(instance->base[T_STATUS_REG_OFFSET]) & T_ST_RDY);
+#else
+    while (!(instance->base[T_STATUS_REG_OFFSET]) & T_ST_RDY);
+    for (; i; --i) {
+#endif
        *d++ = *reg;
+    }
 
     if (*(instance->base + T_STATUS_REG_OFFSET) & T_ST_TIM) {
        unsigned char tmp;
@@ -331,7 +337,7 @@ static inline int NCR5380_pread (struct Scsi_Host *instance, unsigned char *dst,
        tmp = *foo;
        *foo = tmp | T_CR_CT;
        *foo = tmp;
-       printk("scsi%d : watchdog timer fired in NCR5480_pread()\n",
+       printk("scsi%d : watchdog timer fired in NCR5380_pread()\n",
            instance->host_no);
        return -1;
     } else
@@ -357,9 +363,15 @@ static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src
        T_DATA_REG_OFFSET), *s = src;
     register i = len;
 
+#if 0
+    for (; i; --i) {
+       while (!(instance->base[T_STATUS_REG_OFFSET]) & T_ST_RDY);
+#else
     while (!(instance->base[T_STATUS_REG_OFFSET]) & T_ST_RDY);
-    for (; i; --i)
+    for (; i; --i) {
+#endif
        *reg = *s++;
+    }
 
     if (*(instance->base + T_STATUS_REG_OFFSET) & T_ST_TIM) {
        unsigned char tmp;
@@ -368,7 +380,7 @@ static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src
        tmp = *foo;
        *foo = tmp | T_CR_CT;
        *foo = tmp;
-       printk("scsi%d : watchdog timer fired in NCR5480_pwrite()\n",
+       printk("scsi%d : watchdog timer fired in NCR5380_pwrite()\n",
            instance->host_no);
        return -1;
     } else 
index 6a4629d0c0b48d07865d967ebe93a977448933fd..00be726661f9c7daee1dc960c59db6e133526a6a 100644 (file)
@@ -169,7 +169,7 @@ static inline void move_to_level2(struct dir_cache_entry * old_de, struct hash_l
        add_hash(de, hash);
 }
 
-unsigned long dcache_lookup(struct inode * dir, const char * name, int len)
+int dcache_lookup(struct inode * dir, const char * name, int len, unsigned long * ino)
 {
        struct hash_list * hash;
        struct dir_cache_entry *de;
@@ -180,8 +180,9 @@ unsigned long dcache_lookup(struct inode * dir, const char * name, int len)
        de = find_entry(dir, name, len, hash);
        if (!de)
                return 0;
+       *ino = de->ino;
        move_to_level2(de, hash);
-       return de->ino;
+       return 1;
 }
 
 void dcache_add(struct inode * dir, const char * name, int len, unsigned long ino)
index c023b3dc467ccbc5d8848a14a1aa1838156c5ad5..0ad8c5e293b139a64852c900744f25ec31f81a76 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -847,7 +847,7 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
                if (ex.a_text) {
                        error = do_mmap(file, N_TXTADDR(ex), ex.a_text,
                                PROT_READ | PROT_EXEC,
-                               MAP_FIXED | MAP_SHARED | MAP_DENYWRITE,
+                               MAP_FIXED | MAP_SHARED | MAP_DENYWRITE | MAP_EXECUTABLE,
                                fd_offset);
 
                        if (error != N_TXTADDR(ex)) {
@@ -859,7 +859,7 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
                
                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_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
                                fd_offset + ex.a_text);
                sys_close(fd);
                if (error != N_TXTADDR(ex) + ex.a_text) {
index ab5b1195e46d1029a21898ebd1dcb722f329b49c..78fc27bc5c641adc6672ce3367efdeb449698519 100644 (file)
@@ -181,15 +181,28 @@ int ext2_lookup (struct inode * dir, const char * name, int len,
                iput (dir);
                return -ENOENT;
        }
-       if (!(ino = dcache_lookup(dir, name, len))) {
-               if (!(bh = ext2_find_entry (dir, name, len, &de))) {
-                       iput (dir);
+       if (dcache_lookup(dir, name, len, &ino)) {
+               if (!ino) {
+                       iput(dir);
                        return -ENOENT;
                }
-               ino = de->inode;
-               dcache_add(dir, de->name, de->name_len, ino);
-               brelse (bh);
+               if (!(*result = iget (dir->i_sb, ino))) {
+                       iput (dir);
+                       return -EACCES;
+               }
+               iput (dir);
+               return 0;
        }
+       ino = dir->i_version;
+       if (!(bh = ext2_find_entry (dir, name, len, &de))) {
+               if (ino == dir->i_version)
+                       dcache_add(dir, name, len, 0);
+               iput (dir);
+               return -ENOENT;
+       }
+       ino = de->inode;
+       dcache_add(dir, name, len, ino);
+       brelse (bh);
        if (!(*result = iget (dir->i_sb, ino))) {
                iput (dir);
                return -EACCES;
index 9e622775d485bdc19d3a8f2a4aacdb392239c45a..52b391a6fee1f380465aead0bb2abb36bca3b39b 100644 (file)
@@ -375,8 +375,22 @@ next_lock:
        }
 
        if (! added) {
-               if (caller->fl_type == F_UNLCK)
+               if (caller->fl_type == F_UNLCK) {
+/*
+ * XXX - under iBCS-2, attempting to unlock a not-locked region is 
+ *     not considered an error condition, although I'm not sure if this 
+ *     should be a default behavior (it makes porting to native Linux easy)
+ *     or a personality option.
+ *
+ *     Does Xopen/1170 say anything about this?
+ *     - drew@Colorado.EDU
+ */
+#if 0
+                       return -EINVAL;
+#else
                        return 0;
+#endif
+               }
                if (! (caller = alloc_lock(before, caller, fd)))
                        return -ENOLCK;
        }
index 7401869b25db6ee6f6df071a9911296d65dd1670..b1eb8e8db0409824a049b1f25b0f133d7303d40e 100644 (file)
@@ -74,7 +74,7 @@ static int proc_follow_link(struct inode * dir, struct inode * inode,
                case 6: {
                        struct vm_area_struct * vma = p->mm->mmap;
                        while (vma) {
-                               if (vma->vm_flags & VM_DENYWRITE) {
+                               if (vma->vm_flags & VM_EXECUTABLE) {
                                        inode = vma->vm_inode;
                                        break;
                                }
index a016a75840e4a23a23d3b180f50d92c6a7ede25e..e7902c55b51ab8110ee0a366b43e63f120139e9d 100644 (file)
@@ -648,6 +648,9 @@ void mount_root(void)
        if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {
                printk(KERN_NOTICE "VFS: Insert root floppy and press ENTER\n");
                wait_for_keypress();
+               /* ugly, ugly */
+               if (floppy_grab_irq_and_dma())
+                       printk("Unable to gram floppy IRQ/DMA for mounting root floppy\n");
        }
        for (fs_type = file_systems ; fs_type ; fs_type = fs_type->next) {
                if (!fs_type->requires_dev)
diff --git a/include/linux/bios32.h b/include/linux/bios32.h
new file mode 100644 (file)
index 0000000..d106467
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * BIOS32, PCI BIOS functions and defines
+ * Copyright 1994, Drew Eckhardt
+ * 
+ * For more information, please consult 
+ * 
+ * PCI BIOS Specification Revision
+ * PCI Local Bus Specification
+ * PCI System Design Guide
+ *
+ * PCI Special Interest Group
+ * M/S HF3-15A
+ * 5200 N.E. Elam Young Parkway
+ * Hillsboro, Oregon 97124-6497
+ * +1 (503) 696-2000 
+ * +1 (800) 433-5177
+ * 
+ * Manuals are $25 each or $50 for all three, plus $7 shipping 
+ * within the United States, $35 abroad.
+ */
+
+#ifndef BIOS32_H
+#define BIOS32_H
+
+long bios32_init (long memory_start, long memory_end);
+
+extern int pcibios_find_class (unsigned long class_code, unsigned short index, 
+    unsigned char *bus, unsigned char *device_fn);
+extern int pcibios_find_device (unsigned short vendor, unsigned short device_id, 
+    unsigned short index, unsigned char *bus, unsigned char *device_fn);
+extern int pcibios_read_config_byte (unsigned char bus,
+    unsigned char device_fn, unsigned char where, unsigned char *value);
+extern int pcibios_read_config_word (unsigned char bus,
+    unsigned char device_fn, unsigned char where, unsigned short *value);
+extern int pcibios_read_config_dword (unsigned char bus,
+    unsigned char device_fn, unsigned char where, unsigned long *value);
+extern int pcibios_present (void);
+extern int pcibios_write_config_byte (unsigned char bus,
+    unsigned char device_fn, unsigned char where, unsigned char value);
+extern int pcibios_write_config_word (unsigned char bus,
+    unsigned char device_fn, unsigned char where, unsigned short value);
+extern pcibios_write_config_dword (unsigned char bus,
+    unsigned char device_fn, unsigned char where, unsigned long value);
+#endif /* ndef BIOS32_H */
index 38e7c02209536591d9a29a030e36cae34d47a6a9..06c5ebd8a16bb25bd8659518d6dca2bf74dcb07b 100644 (file)
@@ -471,7 +471,7 @@ extern int block_fsync(struct inode *, struct file *);
 extern int file_fsync(struct inode *, struct file *);
 
 extern void dcache_add(struct inode *, const char *, int, unsigned long);
-extern unsigned long dcache_lookup(struct inode *, const char *, int);
+extern int dcache_lookup(struct inode *, const char *, int, unsigned long *);
 
 extern inline struct inode * iget(struct super_block * sb,int nr)
 {
index 85a579a0bdb21b3306567cbba90d17da1f0f688c..e64cb78eff721333060a933cf3a2a538529a45ad 100644 (file)
@@ -62,7 +62,7 @@ asmlinkage int printk(const char * fmt, ...)
  * permissions checks first, and check suser() last.
  */
 #define suser() (current->euid == 0)
-
+extern int splx (int new_ipl);
 #endif /* __KERNEL__ */
 
 #define SI_LOAD_SHIFT  16
index 65b7af0bdd3077a5c4c129f9185a11bf2570565d..15c391e4ec6253f50e459d1f463dc7d07116d6d0 100644 (file)
@@ -58,6 +58,8 @@ struct vm_area_struct {
 #define VM_SHM         0x0400
 #define VM_DENYWRITE   0x0800  /* ETXTBSY on write attempts.. */
 
+#define VM_EXECUTABLE  0x1000
+
 #define VM_STACK_FLAGS 0x0177
 
 /*
index 11027e0dad4b44b8a0493f075fe18c240b1e401d..082fd88b736440106a0b4d66097ad499769f9674 100644 (file)
@@ -14,5 +14,6 @@
 
 #define MAP_GROWSDOWN  0x0400          /* stack-like segment */
 #define MAP_DENYWRITE  0x0800          /* ETXTBSY */
+#define MAP_EXECUTABLE 0x1000          /* mark it as a executable */
 
 #endif /* _LINUX_MMAN_H */
diff --git a/include/linux/pci.h b/include/linux/pci.h
new file mode 100644 (file)
index 0000000..29f2629
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * PCI defines and function prototypes
+ * Copyright 1994, Drew Eckhardt
+ *
+ * For more information, please consult 
+ * 
+ * PCI BIOS Specification Revision
+ * PCI Local Bus Specification
+ * PCI System Design Guide
+ *
+ * PCI Special Interest Group
+ * M/S HF3-15A
+ * 5200 N.E. Elam Young Parkway
+ * Hillsboro, Oregon 97124-6497
+ * +1 (503) 696-2000 
+ * +1 (800) 433-5177
+ * 
+ * Manuals are $25 each or $50 for all three, plus $7 shipping 
+ * within the United States, $35 abroad.
+ */
+
+#ifndef PCI_H
+#define PCI_H
+
+/* Configuration method #1 */
+#define PCI_CONFIG1_ADDRESS_REG  0xcf8
+#define PCI_CONFIG1_ENABLE 0x80000000
+#define PCI_CONFIG1_TUPPLE (bus, device, function, register)   \
+        (PCI_CONFIG1_ENABLE | ((bus) << 16) & 0xff0000 |       \
+        ((device) << 11) & 0xf800 | ((function) << 8) & 0x700 | \
+        ((register) << 2) & 0xfc)
+#define PCI_CONFIG1_DATA_REG     0xcfc
+
+/* Configuration method #2, deprecated */
+#define PCI_CONFIG2_ENABLE_REG 0xcf8
+#define PCI_CONFIG2_ENABLE     0xf0
+#define PCI_CONFIG2_TUPPLE (function)                          \
+       (PCI_CONFIG2_ENABLE | ((function) << 1) & 0xe)
+#define PCI_CONFIG2_FORWARD_REG        0xcfa
+
+/*
+ * Under PCI, each device has 256 bytes of configuration address space,
+ * of which the first 64 bytes is standardized as follows : 
+ */
+
+#define PCI_VENDOR_ID          0x00    /* 16 bits */
+#define PCI_DEVICE_ID          0x02    /* 16 bits */
+#define PCI_COMMAND            0x04    /* 16 bits */
+#define  PCI_COMMAND_IO                0x1     /* Enable response in I/O space */
+#define  PCI_COMMAND_MEMORY    0x2     /* Enable response in I/O space */
+#define  PCI_COMMAND_MASTER    0x4     /* Enable bus mastering */
+#define  PCI_COMMAND_SPECIAL   0x8     /* Enable response to special cycles */
+#define  PCI_COMMAND_INVALIDATE        0x10    /* Use memory write and invalidate */
+#define  PCI_COMMAND_VGA_PALETTE       0x20    /* Enable palette snooping */
+#define  PCI_COMMAND_PARITY    0x40    /* Enable parity checking */
+#define  PCI_COMMAND_WAIT      0x80    /* Enable address/data stepping */
+#define  PCI_COMMAND_SERR      0x100   /* Enable SERR */
+#define  PCI_COMMAND_FAST_BACK 0x200   /* Enable back-to-back writes */
+
+#define PCI_STATUS             0x06    /* 16 bits */
+#define  PCI_STATUS_FAST_BACK  0x80    /* Accept fast-back to back */
+#define  PCI_STATUS_PARITY     0x100   /* Detected parity error */
+#define  PCI_STATUS_DEVSEL_MASK        0x600   /* DEVSEL timing */
+#define  PCI_STATUS_DEVSEL_FAST        0x000   
+#define  PCI_STATUS_DEVSEL_MEDIUM 0x200
+#define  PCI_STATUS_DEVESEL_SLOW 0x400
+#define  PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */
+#define  PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */
+#define  PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */
+#define  PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */
+#define  PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */
+
+#define PCI_CLASS_REVISION     0x08    /* High 24 bits are class, low 8
+                                          revision */
+#define PCI_CACHE_LINE_SIZE    0x0c    /* 8 bits */
+#define PCI_LATENCY_TIMER      0x0d    /* 8 bits */
+#define PCI_HEADER_TYPE                0x0e    /* 8 bits */
+#define PCI_BIST               0x0f    /* 8 bits */
+#define PCI_BIST_CODE_MASK     0x0f    /* Return result */
+#define PCI_BIST_START         0x40    /* 1 to start BIST, 2 secs or less */
+#define PCI_BIST_CAPABLE       0x80    /* 1 if BIST capable */
+
+/*
+ * Base adddresses specify locations in memory or I/O space.
+ * Decoded size can be determined by writing a value of 
+ * 0xffffffff to the register, and reading it back.  Only 
+ * 1 bits are decoded.
+ */
+
+#define PCI_BASE_ADDRESS_0     0x10    /* 32 bits */
+#define PCI_BASE_ADDRESS_1     0x14    /* 32 bits */
+#define PCI_BASE_ADDRESS_2     0x18    /* 32 bits */
+#define PCI_BASE_ADDRESS_3     0x1c    /* 32 bits */
+#define PCI_BASE_ADDRESS_4     0x20    /* 32 bits */
+#define PCI_BASE_ADDRESS_5     0x24    /* 32 bits */
+#define  PCI_BASE_ADDRESS_SPACE        0x01    /* 0 = memory, 1 = I/O */
+#define  PCI_BASE_ADDRESS_SPACE_IO 0x01
+#define  PCI_BASE_ADDRESS_SPACE_MEMORY 0x00
+#define  PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06
+#define  PCI_BASE_ADDRESS_MEM_TYPE_32  0x00    /* 32 bit address */
+#define  PCI_BASE_ADDRESS_MEM_TYPE_1M  0x02    /* Below 1M */
+#define  PCI_BASE_ADDRESS_MEM_TYPE_64  0x04    /* 64 bit address */
+#define  PCI_BASE_ADDRESS_MEM_MASK     ~7
+#define  PCI_BASE_ADDRESS_IO_MASK      ~3
+/* bit 1 is reserved if address_space = 1 */
+
+/* 0x28-0x2f are reserved */
+#define PCI_ROM_ADDRESS                0x30    /* 32 bits */
+#define  PCI_ROM_ADDRESS_ENABLE        0x01    /* Write 1 to enable ROM,
+                                          bits 31..11 are address,
+                                          10..2 are reserved */
+/* 0x34-0x3b are reserved */
+#define PCI_INTERRUPT_LINE     0x3c    /* 8 bits */
+#define PCI_INTERRUPT_PIN      0x3d    /* 8 bits */
+#define PCI_MIN_GNT            0x3e    /* 8 bits */
+#define PCI_MAX_LAT            0x3f    /* 8 bits */
+
+#define PCI_VENDOR_ID_NCR              0x1000
+#define PCI_DEVICE_ID_NCR_53C810       0x0001
+#define PCI_DEVICE_ID_NCR_53C820       0x0002
+#define PCI_DEVICE_ID_NCR_53C825       0x0003
+
+/* PCI BIOS */
+
+extern int pcibios_present (void);
+
+#define PCIBIOS_SUCCESFUL              0x00
+#define PCIBIOS_FUNC_NOT_SUPPORTED     0x81
+#define PCIBIOS_BAD_VENDOR_ID          0x83
+#define PCIBIOS_DEVICE_NOT_FOUND       0x86
+#define PCIBIOS_BAD_REGISTER_NUMBER    0x87
+
+/*
+ * The PCIBIOS calls all bit-field the device_function variable such that 
+ * the bit fielding matches that of the bl register used in the actual
+ * calls.
+ */
+
+extern int pcibios_find_class (unsigned long class_code, unsigned short index, 
+    unsigned char *bus, unsigned char *device_fn);
+extern int pcibios_find_device (unsigned short vendor, unsigned short device_id, 
+    unsigned short index, unsigned char *bus, unsigned char *device_fn);
+extern int pcibios_read_config_byte (unsigned char bus,
+    unsigned char device_fn, unsigned char where, unsigned char *value);
+extern int pcibios_read_config_word (unsigned char bus,
+    unsigned char device_fn, unsigned char where, unsigned short *value);
+extern int pcibios_read_config_dword (unsigned char bus,
+    unsigned char device_fn, unsigned char where, unsigned long *value);
+extern char *pcibios_strerror (int error);
+extern int pcibios_write_config_byte (unsigned char bus,
+    unsigned char device_fn, unsigned char where, unsigned char value);
+extern int pcibios_write_config_word (unsigned char bus,
+    unsigned char device_fn, unsigned char where, unsigned short value);
+extern pcibios_write_config_dword (unsigned char bus,
+    unsigned char device_fn, unsigned char where, unsigned long value);
+#endif /* ndef PCI_H */
index 0b7da5152fe63b2c00e4c764ad225b4d83745bc8..c4881eb955c6577dce2c143fc4d867a94766f4db 100644 (file)
 #define SBPRO     1
 #endif
 
+/*
+ * If you have a "compatible" soundcard of type "SBPRO 0" or "SBPRO 2",
+ * enter your sound card's base address here if you want sbpcd to turn
+ * the CD sound channels on.
+ *
+ * Example: #define SOUND_BASE 0x220 enables the sound card's CD channels
+ *          #define SOUND_BASE 0     leaves the soundcard untouched
+ */
+#define SOUND_BASE 0
+
 /* ignore the rest if you have only one interface board & driver */
 
 #if !(SBPCD_ISSUE-2) /* second interface board: */
@@ -419,15 +429,15 @@ Read XA Parameter:
  */
 #define OUT(x,y) outb(y,x)
 
-
-#define MIXER_CD_Volume        0x28
-
-/*==========================================================================*/
 /*
  * use "REP INSB" for strobing the data in:
  */
 #define READ_DATA(port, buf, nr) insb(port, buf, nr)
 
+/*==========================================================================*/
+
+#define MIXER_CD_Volume        0x28 /* internal SB Pro register address */
+
 /*==========================================================================*/
 /*
  * Creative Labs Programmers did this:
index cabf82f58e0476d3967fc19d78cea12b91870dc6..e417e98910a1853774b7ba3ad07203b80c9e1cbb 100644 (file)
 #define HZ 100
 
 /*
- * System setup flags..
+ * System setup and hardware bug flags..
  */
 extern int hard_math;
 extern int x86;
 extern int ignore_irq13;
-extern int wp_works_ok;
+extern int wp_works_ok;                /* doesn't work on a 386 */
+extern int hlt_works_ok;       /* problems on some 486Dx4's and old 386's */
 
 extern unsigned long intr_count;
 extern unsigned long event;
index 409640075478e547825484dac3858211ec05c058..de08f38e5e765610dfd47482720cdf287511ffec 100644 (file)
@@ -132,5 +132,8 @@ struct async_struct {
 #define RS_EVENT_WRITE_WAKEUP  0
 #define RS_EVENT_HANGUP                1
 
+/* Export to allow PCMCIA to use this - Dave Hinds */
+extern int register_serial(struct serial_struct *req);
+extern void unregister_serial(int line);
 #endif /* __KERNEL__ */
 #endif /* _LINUX_SERIAL_H */
index 518cbd7aa08de9747101fb7e66ffd245ad0f4f57..fcba989291bea5de926009a3d5b9930bc3a1acca 100644 (file)
@@ -427,7 +427,7 @@ extern void sysv_put_inode(struct inode *);
 extern void sysv_statfs(struct super_block *, struct statfs *);
 extern int sysv_sync_inode(struct inode *);
 extern int sysv_sync_file(struct inode *, struct file *);
-extern int sysv_mmap(struct inode *, struct file *, unsigned long, size_t, int, unsigned long);
+extern int sysv_mmap(struct inode *, struct file *, struct vm_area_struct *);
 
 extern struct inode_operations sysv_file_inode_operations;
 extern struct inode_operations sysv_file_inode_operations_with_bmap;
index 91ade4b40c0cdce6d52fa0b9d9eb525c7f8805ce..2e7cb65e799296d817c8db1a2520ae353854aa22 100644 (file)
@@ -79,6 +79,7 @@ extern void floppy_init(void);
 extern void sock_init(void);
 extern long rd_init(long mem_start, int length);
 unsigned long net_dev_init(unsigned long, unsigned long);
+extern long bios32_init(long, long);
 
 extern void hd_setup(char *str, int *ints);
 extern void bmouse_setup(char *str, int *ints);
@@ -89,6 +90,7 @@ extern void st_setup(char *str, int *ints);
 extern void st0x_setup(char *str, int *ints);
 extern void tmc8xx_setup(char *str, int *ints);
 extern void t128_setup(char *str, int *ints);
+extern void pas16_setup(char *str, int *ints);
 extern void generic_NCR5380_setup(char *str, int *intr);
 extern void aha152x_setup(char *str, int *ints);
 extern void scsi_luns_setup(char *str, int *ints);
@@ -195,6 +197,9 @@ struct {
 #ifdef CONFIG_SCSI_T128
        { "t128=", t128_setup },
 #endif
+#ifdef CONFIG_SCSI_PAS16
+       { "pas16=", pas16_setup },
+#endif
 #ifdef CONFIG_SCSI_GENERIC_NCR5380
        { "ncr5380=", generic_NCR5380_setup },
 #endif
@@ -310,6 +315,8 @@ static void parse_options(char *line)
                        root_mountflags &= ~MS_RDONLY;
                else if (!strcmp(line,"debug"))
                        console_loglevel = 10;
+               else if (!strcmp(line,"no-hlt"))
+                       hlt_works_ok = 0;
                else if (!strcmp(line,"no387")) {
                        hard_math = 0;
                        __asm__("movl %%cr0,%%eax\n\t"
@@ -407,11 +414,17 @@ asmlinkage void start_kernel(void)
        prof_len >>= 2;
        memory_start += prof_len * sizeof(unsigned long);
 #endif
+       memory_start = bios32_init(memory_start,memory_end);
        memory_start = kmalloc_init(memory_start,memory_end);
        memory_start = chr_dev_init(memory_start,memory_end);
        memory_start = blk_dev_init(memory_start,memory_end);
        sti();
        calibrate_delay();
+       if (hlt_works_ok) {
+               printk("Checking 'hlt' ...");
+               __asm__ __volatile__("hlt");
+               printk(" ok\n");
+       }
 #ifdef CONFIG_INET
        memory_start = net_dev_init(memory_start,memory_end);
 #endif
index 1894421131bf5abee081a7a74bc1db96947e6735..fbef4b81886a3528e66da98dc2277ff68fbdb0ae 100644 (file)
@@ -19,7 +19,7 @@
 OBJS  = sched.o sys_call.o traps.o irq.o dma.o fork.o exec_domain.o \
        panic.o printk.o vsprintf.o sys.o module.o ksyms.o exit.o \
        signal.o ptrace.o ioport.o itimer.o \
-       info.o ldt.o time.o tqueue.o vm86.o
+       info.o ldt.o time.o tqueue.o vm86.o bios32.o splx.o
 
 all: kernel.o
 
diff --git a/kernel/bios32.c b/kernel/bios32.c
new file mode 100644 (file)
index 0000000..2522ebf
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+ * bios32.c - BIOS32, PCI BIOS functions.
+ *
+ * Sponsored by 
+ *     iX Multiuser Multitasking Magazine
+ *     Hannover, Germany
+ *     hm@ix.de
+ *
+ * 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 
+ * 
+ * PCI BIOS Specification Revision
+ * PCI Local Bus Specification
+ * PCI System Design Guide
+ *
+ * PCI Special Interest Group
+ * M/S HF3-15A
+ * 5200 N.E. Elam Young Parkway
+ * Hillsboro, Oregon 97124-6497
+ * +1 (503) 696-2000 
+ * +1 (800) 433-5177
+ * 
+ * Manuals are $25 each or $50 for all three, plus $7 shipping 
+ * within the United States, $35 abroad.
+ *
+ *
+ * CHANGELOG : 
+ * Jun 17, 1994 : Modified to accomodate the broken pre-PCI BIOS SPECIFICATION
+ *     Revision 2.0 present on <thys@dennis.ee.up.ac.za>'s ASUS mainboard.
+ */
+
+#include <linux/kernel.h>
+#include <linux/segment.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+
+#define PCIBIOS_PCI_FUNCTION_ID        0xb1
+#define PCIBIOS_PCI_BIOS_PRESENT       0x01
+#define PCIBIOS_FIND_PCI_DEVICE                0x02
+#define PCIBIOS_FIND_PCI_CLASS_CODE    0x03
+#define PCIBIOS_GENERATE_SPECIAL_CYCLE 0x06
+#define PCIBIOS_READ_CONFIG_BYTE       0x08            
+#define PCIBIOS_READ_CONFIG_WORD       0x09
+#define PCIBIOS_READ_CONFIG_DWORD      0x0a
+#define PCIBIOS_WRITE_CONFIG_BYTE      0x0b
+#define PCIBIOS_WRITE_CONFIG_WORD      0x0c
+#define PCIBIOS_WRITE_CONFIG_DWORD     0x0d
+
+
+union signature {
+    char chars[4];
+    unsigned long scalar;
+};
+
+/*
+ * This is the standard structure used to identify the entry point 
+ * to the BIOS32 Service Directory, as documented in 
+ *     Standard BIOS 32-bit Service Directory Proposal
+ *     Revision 0.4 May 24, 1993
+ *     Phoenix Technologies Ltd.
+ *     Norwood, MA
+ * and the PCI BIOS specfication.
+ */
+
+static union signature bios32_signature = {"_32_",};
+
+union bios32 {
+    struct {
+       union signature signature;      /* _32_ */
+       long entry;                     /* 32 bit physical address */
+       unsigned char revision;         /* Revision level, 0 */
+       unsigned char length;           /* Length in paragraphs should be 01 */
+       unsigned char checksum;         /* All bytes must add up to zero */
+       unsigned char reserved[5];      /* Must be zero */
+    } fields;
+    char chars[16];
+};
+
+/* 
+ * Physical address of the service directory.  I don't know if we're
+ * allowed to have more than one of these or not, so just in case 
+ * we'll make bios32_init() take a memory start parameter and store
+ * the array there.
+ */
+
+static long bios32_entry = 0;
+static unsigned char bios32_indirect[6]; 
+
+static long pcibios_init (long, long);
+long bios32_init (long memory_start, long memory_end) {
+    union bios32 *check;
+    unsigned char sum;
+    int i, length;
+
+    /*
+     * Follow the standard procedure for locating the BIOS32 Serivce
+     * directory by scanning the permissable address range from 
+     * 0xe0000 through 0xfffff for a valid BIOS32 structure.
+     */
+
+    for (check = (union bios32 *) 0xe0000; check <= (union bios32 *) 0xffff0; 
+       ++check) {
+       if (check->fields.signature.scalar == bios32_signature.scalar) {
+           for (i = sum = 0, length = check->fields.length * 16;
+               i < check->fields.length * 16; ++i) 
+               sum += check->chars[i];
+           if (sum != 0)
+               continue;
+           if (check->fields.revision != 0) {
+               printk ("bios32_init : unsupported revision %d at 0x%x, mail drew@colorado.edu\n",
+                   check->fields.revision, (unsigned) check);
+               continue;
+           }
+           printk ("bios32_init : BIOS32 Service Directory structure at 0x%x\n", 
+               (unsigned) check);
+           if (!bios32_entry) {
+               *((long *) bios32_indirect) = bios32_entry = check->fields.entry;
+               *((short *) (bios32_indirect + 4)) = KERNEL_CS;
+               printk ("bios32_init : BIOS32 Service Directory entry at 0x%x\n", 
+                   (unsigned) bios32_entry);
+           } else 
+               printk ("bios32_init : multiple entries, mail drew@colorado.edu\n");
+       }
+    }
+    if (bios32_entry) {
+       memory_start = pcibios_init (memory_start, memory_end);
+    }
+    return memory_start;
+}
+
+/* 
+ * Returns the entry point for the given service, NULL on error 
+ */
+
+static long bios32_service (long service) {
+    unsigned char return_code; /* %al */
+    long address;              /* %ebx */
+    long length;               /* %ecx */
+    long entry;                        /* %edx */
+
+    __asm__ ("
+       movl $0, %%ebx
+       lcall (%%edi)
+" 
+       : "=al" (return_code), "=ebx" (address), "=ecx" (length), "=edx" (entry)
+       : "eax" (service), "D" (bios32_indirect) 
+       : "ebx"
+    );
+
+    switch (return_code) {
+    case 0:    
+       return address + entry;
+    case 0x80: /* Not present */
+       printk ("bios32_service(%ld) : not present\n", service);
+       return 0;
+    default: /* Shouldn't happen */
+       printk ("bios32_service(%ld) : returned 0x%x, mail drew@colorado.edu\n",
+           service, (int) return_code );
+       return 0;
+    }
+
+}
+
+static union signature pci_signature = {"PCI ",};
+static union signature pci_service = {"$PCI",};
+static long pcibios_entry = 0;
+static unsigned char pci_indirect[6];
+
+void NCR53c810_test(void);
+
+static long pcibios_init (long memory_start, long memory_end) {
+    union signature signature;
+    unsigned char present_status;
+    unsigned char major_revision;
+    unsigned char minor_revision;
+    int pack;
+
+    if ((pcibios_entry = bios32_service (pci_service.scalar))) {
+       *((long *) pci_indirect) = pcibios_entry;
+       *((short *) (pci_indirect + 4)) = KERNEL_CS;
+       __asm__ ("
+           movw $0xb101, %%eax
+           lcall (%%edi)
+           jc 1f
+           xor %%ah, %%ah
+1:
+           shl $8, %%eax;
+           movw %%bx, %%ax;
+"              
+           : "=edx" (signature.scalar), "=eax" (pack)
+           : "D" (pci_indirect) 
+           : "cx"
+       );
+
+       present_status = (pack >> 16) & 0xff;
+       major_revision = (pack >> 8) & 0xff;
+       minor_revision = pack & 0xff;
+       if (present_status || (signature.scalar != pci_signature.scalar)) {
+           printk ("pcibios_init : %s : BIOS32 Service Directory says PCI BIOS is present,\n"
+                   "    but PCI_BIOS_PRESENT subfunction fails with present status of 0x%x\n"
+                   "    and signature of 0x%08lx (%c%c%c%c).  mail drew@Colorado.EDU\n", 
+                   (signature.scalar == pci_signature.scalar) ?  "WARNING" : "ERROR",
+                   (int) present_status, signature.scalar , signature.chars[0],
+                   signature.chars[1], signature.chars[2], signature.chars[3]);
+
+           if (signature.scalar != pci_signature.scalar) 
+               pcibios_entry = 0;
+       } 
+       if (pcibios_entry) {
+           printk ("pcibios_init : PCI BIOS revision %x.%02x entry at 0x%lx\n",
+               (int) major_revision, (int) minor_revision, pcibios_entry);
+       }
+    }
+
+#if 0
+    NCR53c810_test(); 
+#endif
+    return memory_start;
+}
+
+int pcibios_present (void) {
+    return pcibios_entry ? 1 : 0;
+}
+
+int pcibios_find_class_code (unsigned long class_code, unsigned short index, 
+       unsigned char *bus, unsigned char *device_fn) {
+       unsigned short bx;
+       unsigned short ret;
+       __asm__ ("
+           movw $0xb103, %%ax
+           lcall (%%edi)
+           jc 1f
+           xor %%ah, %%ah
+1:
+" 
+           : "=bx" (bx), "=ax" (ret)
+           : "cx" (class_code), "S" ((int) index), "D" (pci_indirect)
+       );
+       *bus = (bx >> 8) & 0xff;
+       *device_fn = bx & 0xff;
+       return (int) (ret & 0xff00) >> 8;
+}
+
+
+int pcibios_find_device (unsigned short vendor, unsigned short device_id, 
+       unsigned short index, unsigned char *bus, unsigned char *device_fn) {
+       unsigned short bx;
+       unsigned short ret;
+       __asm__ ("
+           movw $0xb102, %%ax
+           lcall (%%edi)
+           jc 1f
+           xor %%ah, %%ah
+1:
+" 
+           : "=bx" (bx), "=ax" (ret)
+           : "cx" (device_id), "dx" (vendor), "S" ((int) index), "D" (pci_indirect)
+       );
+       *bus = (bx >> 8) & 0xff;
+       *device_fn = bx & 0xff;
+       return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_read_config_byte (unsigned char bus,
+    unsigned char device_fn, unsigned char where, unsigned char *value) {
+    unsigned long ret;
+    unsigned short bx = (bus << 8) | device_fn;
+    __asm__ ("
+       movw $0xb108, %%ax
+       lcall (%%esi)
+       jc 1f
+       xor %%ah, %%ah
+1:
+"  
+       : "=cl" (*value), "=eax" (ret)  
+       : "bx" (bx), "D" ((long) where), "S" (pci_indirect)
+    );
+    return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_read_config_word (unsigned char bus,
+    unsigned char device_fn, unsigned char where, unsigned short *value) {
+    unsigned short ret;
+    unsigned short bx = (bus << 8) | device_fn;
+    __asm__ ("
+       movw $0xb109, %%ax
+       lcall (%%esi)
+       jc 1f
+       xor %%ah, %%ah
+1:
+"  
+       : "=cx" (*value), "=ax" (ret)  
+       : "bx" (bx), "D" ((long) where), "S" (pci_indirect)
+    );
+    return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_read_config_dword (unsigned char bus,
+    unsigned char device_fn, unsigned char where, unsigned long *value) {
+    unsigned short ret;
+    unsigned short bx = (bus << 8) | device_fn;
+    __asm__ ("
+       movw $0xb10a, %%ax
+       lcall (%%esi)
+       jc 1f
+       xor %%ah, %%ah
+1:
+"  
+       : "=ecx" (*value), "=ax" (ret)  
+       : "bx" (bx), "D" ((long) where), "S" (pci_indirect)
+    );
+    return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_write_config_byte (unsigned char bus,
+    unsigned char device_fn, unsigned char where, unsigned char value) {
+    unsigned short ret;
+    unsigned short bx = (bus << 8) | device_fn;
+    __asm__ ("
+       movw $0xb108, %%ax
+       lcall (%%esi)
+       jc 1f
+       xor %%ah, %%ah
+1:
+"  
+       : "=ax" (ret)  
+       : "cl" (value), "bx" (bx), "D" ((long) where), "S" (pci_indirect)
+    );
+    return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_write_config_word (unsigned char bus,
+    unsigned char device_fn, unsigned char where, unsigned short value) {
+    unsigned short ret;
+    unsigned short bx = (bus << 8) | device_fn;
+    __asm__ ("
+       movw $0xb109, %%ax
+       lcall (%%esi)
+       jc 1f
+       xor %%ah, %%ah
+1:
+"  
+       : "=ax" (ret)  
+       : "cx" (value), "bx" (bx), "D" ((long) where), "S" (pci_indirect)
+    );
+    return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_write_config_dword (unsigned char bus,
+    unsigned char device_fn, unsigned char where, unsigned long value) {
+    unsigned short ret;
+    unsigned short bx = (bus << 8) | device_fn;
+    __asm__ ("
+       movw $0xb10a, %%ax
+       lcall (%%esi)
+       jc 1f
+       xor %%ah, %%ah
+1:
+"  
+       : "=ax" (ret)  
+       : "ecx" (value), "bx" (bx), "D" ((long) where), "S" (pci_indirect)
+    );
+    return (int) (ret & 0xff00) >> 8;
+}
+
+void NCR53c810_test(void) {
+    unsigned char bus, device_fn;
+    unsigned short index;
+    int ret;
+    unsigned char row, col;
+    unsigned long val;
+
+    for (index = 0; index < 4; ++index) {
+       ret = pcibios_find_device ((unsigned short) PCI_VENDOR_ID_NCR, (unsigned short)
+               PCI_DEVICE_ID_NCR_53C810, index, &bus, &device_fn);
+       if (ret) 
+           break;
+       printk ("ncr53c810 : at PCI bus %d, device %d, function %d.",
+           (int) bus, (int) ((device_fn & 0xf8) >> 3), (int) (device_fn & 7));
+       for (row = 0; row < 0x3c; row += 0x10) {
+           printk ("\n    reg 0x%02x    ", row);
+           for (col = 0; col < 0x10; col += 4) {
+               if (!(ret = pcibios_read_config_dword (bus, device_fn, row+col, &val))) 
+                   printk ("0x%08lx      ", val);
+               else
+                   printk ("error 0x%02x ", ret);
+           }
+       }
+       printk ("\n");
+    }
+}
+
+char *pcibios_strerror (int error) {
+    char buf[80];
+    switch (error) {
+    case PCIBIOS_SUCCESFUL:
+        return "SUCCESFUL";
+    case PCIBIOS_FUNC_NOT_SUPPORTED:
+        return "FUNC_NOT_SUPPORTED";
+    case PCIBIOS_BAD_VENDOR_ID:
+        return "BAD_VENDOR_ID";
+    case PCIBIOS_DEVICE_NOT_FOUND:
+        return "DEVICE_NOT_FOUND";
+    case PCIBIOS_BAD_REGISTER_NUMBER:
+        return "BAD_REGISTER_NUMBER";
+    default:
+       sprintf (buf, "UNKNOWN RETURN 0x%x", error);
+       return buf;
+    }
+}
index 83c2fcd12542effe3e6ce0783fa167b5d4c1fdc9..42a6701dd4c010ba6e3bb19a0477ac319782c303 100644 (file)
@@ -21,6 +21,9 @@
 #include <linux/binfmts.h>
 #include <linux/personality.h>
 #include <linux/module.h>
+#include <linux/termios.h>
+#include <linux/tqueue.h>
+#include <linux/serial.h>
 #ifdef CONFIG_INET
 #include <linux/netdevice.h>
 #endif
@@ -64,6 +67,7 @@ extern struct device *irq2dev_map[];
 extern void dev_kfree_skb(struct sk_buff *, int);
 #endif
 
+
 struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */
        {
        /* stackable module support */
@@ -102,6 +106,9 @@ struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */
        X(unregister_chrdev),
        X(register_blkdev),
        X(unregister_blkdev),
+       /* Module creation of serial units */
+       X(register_serial),
+       X(unregister_serial),
 
        /* filesystem registration */
        X(register_filesystem),
index f331b76628827e10f5dc3690387a63e50b37d1e9..10f60f99874545fc4143ade560009fb40737df01 100644 (file)
@@ -71,6 +71,7 @@ int hard_math = 0;            /* set by boot/head.S */
 int x86 = 0;                   /* set by boot/head.S to 3 or 4 */
 int ignore_irq13 = 0;          /* set if exception 16 works */
 int wp_works_ok = 0;           /* set if paging hardware honours WP */ 
+int hlt_works_ok = 1;          /* set if the "hlt" instruction works */
 
 /*
  * Bus types ..
diff --git a/kernel/splx.c b/kernel/splx.c
new file mode 100644 (file)
index 0000000..5869cfa
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * splx.c - SYSV DDI/DKI ipl manipulation functions
+ *
+ * Internally, many unices use a range of different interrupt 
+ * priveledge levels, ie from "allow all interrupts" (7) to 
+ * "allow no interrupts." (0) under SYSV.
+ *
+ * This a simple splx() function behaves as the SYSV DDI/DKI function does,
+ * although since Linux only implements the equivalent of level 0 (cli) and
+ * level 7 (sti), this implementation only implements those levels.
+ * 
+ * Also, unlike the current Linux routines, splx() also returns the 
+ * old priveledge level so that it can be restored.
+ */
+
+#include <asm/system.h>
+
+int splx (int new_level) {
+    register int old_level, tmp;
+    save_flags(tmp);
+    old_level = (tmp & 0x200) ? 7 : 0;
+    if (new_level)
+       sti();
+    else 
+       cli();
+    return old_level;
+}
index 1e4d57aa5b20d34100d8842317dd0ab050991c57..956e97e2c53d51f8a4ad7da84f0c68d92d88fb9b 100644 (file)
@@ -49,7 +49,7 @@ asmlinkage int sys_idle(void)
        /* endless idle loop with no priority at all */
        current->counter = -100;
        for (;;) {
-               if (!need_resched)
+               if (hlt_works_ok && !need_resched)
                        __asm__("hlt");
                schedule();
        }
@@ -212,8 +212,8 @@ asmlinkage int sys_setregid(gid_t rgid, gid_t egid)
        int old_rgid = current->gid;
 
        if (rgid != (gid_t) -1) {
-               if ((current->egid==rgid) ||
-                   (old_rgid == rgid) || 
+               if ((old_rgid == rgid) ||
+                   (current->egid==rgid) ||
                    suser())
                        current->gid = rgid;
                else
@@ -222,6 +222,7 @@ asmlinkage int sys_setregid(gid_t rgid, gid_t egid)
        if (egid != (gid_t) -1) {
                if ((old_rgid == egid) ||
                    (current->egid == egid) ||
+                   (current->sgid == egid) ||
                    suser())
                        current->egid = egid;
                else {
index 92ecbc66dac6429cb4eba77d28a5533faf2939f8..4258ffda58d4a8c38583d16936f7c05eaf2210e4 100644 (file)
@@ -337,7 +337,7 @@ asmlinkage int sys_adjtimex(struct timex *txc_p)
        /* Now we validate the data before disabling interrupts
         */
 
-       if (txc.mode & ADJ_OFFSET)
+       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)))
index a7bae5e44b3392b2911c2b9417148cc0e54467f6..bf088fd94f1051df4d31372e51f23afd53adc27c 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -130,7 +130,7 @@ int do_mmap(struct file * file, unsigned long addr, unsigned long len,
        vma->vm_end = addr + len;
        vma->vm_page_prot = mask;
        vma->vm_flags = prot & (VM_READ | VM_WRITE | VM_EXEC);
-       vma->vm_flags |= flags & (VM_GROWSDOWN | VM_DENYWRITE);
+       vma->vm_flags |= flags & (VM_GROWSDOWN | VM_DENYWRITE | VM_EXECUTABLE);
 
        if (file) {
                if (file->f_mode & 1)
index 6a4ba8ff09b43b2981ff9888f8e12524c09303b5..5875fb7dc34efce9019555698c94f5783e1ea1ca 100644 (file)
@@ -615,7 +615,7 @@ static int inet_create(struct socket *sock, int protocol)
        sk->timeout = 0;
        sk->broadcast = 0;
        sk->localroute = 0;
-       sk->timer.next = sk->timer.prev = NULL;
+       init_timer(&sk->timer);
        sk->timer.data = (unsigned long)sk;
        sk->timer.function = &net_timer;
        skb_queue_head_init(&sk->back_log);
index 61e989e495e69946ed32e507681f138453e7c0f2..24123358754fda12c3aa200f5b37fadb80212e6f 100644 (file)
@@ -28,6 +28,7 @@
  *              Ross Martin     :       Rewrote arp_rcv() and arp_get_info()
  *             Stephen Henson  :       Add AX25 support to arp_get_info()
  *             Alan Cox        :       Drop data when a device is downed.
+ *             Alan Cox        :       Use init_timer()
  */
 
 #include <linux/types.h>
@@ -746,7 +747,7 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
                entry->hlen = hlen;
                entry->htype = htype;
                entry->flags = ATF_COM;
-               entry->timer.next = entry->timer.prev = NULL;
+               init_timer(&entry->timer);
                memcpy(entry->ha, sha, hlen);
                entry->last_used = jiffies;
                entry->dev = skb->dev;
@@ -839,7 +840,7 @@ int arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev,
                memset(entry->ha, 0, dev->addr_len);
                entry->dev = dev;
                entry->last_used = jiffies;
-               entry->timer.next = entry->timer.prev = NULL;
+               init_timer(&entry->timer);
                entry->timer.function = arp_expire_request;
                entry->timer.data = (unsigned long)entry;
                entry->timer.expires = ARP_RES_TIME;
@@ -1048,7 +1049,7 @@ static int arp_req_set(struct arpreq *req)
                entry->ip = ip;
                entry->hlen = hlen;
                entry->htype = htype;
-               entry->timer.next = entry->timer.prev = NULL;
+               init_timer(&entry->timer);
                entry->next = arp_tables[hash];
                arp_tables[hash] = entry;
                skb_queue_head_init(&entry->skb);
index bc11f64b2a3ae351ec786ff519e3e4f5f88d1697..090e7a711017b0b2e3879b7951f0c70f7008d036 100644 (file)
@@ -422,16 +422,19 @@ void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
        restore_flags(flags);
 
        /* copy outgoing packets to any sniffer packet handlers */
-       for (nitcount = dev_nit, ptype = ptype_base; nitcount > 0 && ptype != NULL; ptype = ptype->next) {
-               if (ptype->type == htons(ETH_P_ALL)) {
-                       struct sk_buff *skb2;
-                       if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)
-                               break;
-                       ptype->func(skb2, skb->dev, ptype);
-                       nitcount--;
+       if(!where)
+       {
+               for (nitcount = dev_nit, ptype = ptype_base; nitcount > 0 && ptype != NULL; ptype = ptype->next) 
+               {
+                       if (ptype->type == htons(ETH_P_ALL)) {
+                               struct sk_buff *skb2;
+                               if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)
+                                       break;
+                               ptype->func(skb2, skb->dev, ptype);
+                               nitcount--;
+                       }
                }
        }
-
        if (dev->hard_start_xmit(skb, dev) == 0) {
                /*
                 *      Packet is now solely the responsibility of the driver
index 7c6222b3a49b3301113d2abf09ae58527d801da8..25835cbad00a921d57c7db35860d2cd09548e1d0 100644 (file)
@@ -21,9 +21,9 @@
  *             Alan Cox        :       Frames to bad broadcast subnets are dumped
  *                                     We used to process them non broadcast and
  *                                     boy could that cause havoc.
- *             Alan Cox        :       ip_forward sets the free flag on the 
+ *             Alan Cox        :       ip_forward sets the free flag on the
  *                                     new frame it queues. Still crap because
- *                                     it copies the frame but at least it 
+ *                                     it copies the frame but at least it
  *                                     doesn't eat memory too.
  *             Alan Cox        :       Generic queue code and memory fixes.
  *             Fred Van Kempen :       IP fragment support (borrowed from NET2E)
@@ -41,7 +41,7 @@
  *             Alan Cox        :       Silly ip bug when an overlength
  *                                     fragment turns up. Now frees the
  *                                     queue.
- *             Linus Torvalds/ :       Memory leakage on fragmentation 
+ *             Linus Torvalds/ :       Memory leakage on fragmentation
  *             Alan Cox        :       handling.
  *             Gerhard Koerting:       Forwarding uses IP priority hints
  *             Teemu Rantanen  :       Fragment problems.
@@ -52,6 +52,7 @@
  *                                     if you do things the wrong way.
  *             Alan Cox        :       Always defrag, moved IP_FORWARD to the config.in file
  *             Alan Cox        :       IP options adjust sk->priority.
+ *             Pedro Roque     :       Fix mtu/length error in ip_forward.
  *
  * To Fix:
  *             IP option processing is mostly not needed. ip_forward needs to know about routing rules
@@ -96,22 +97,22 @@ extern void sort_send(struct sock *sk);
 /*
  *     SNMP management statistics
  */
+
 struct ip_mib ip_statistics={1,64,};   /* Forwarding=Yes, Default TTL=64 */
+
 /*
- *     Handle the issuing of an ioctl() request 
+ *     Handle the issuing of an ioctl() request
  *     for the ip device. This is scheduled to
  *     disappear
  */
 
 int ip_ioctl(struct sock *sk, int cmd, unsigned long arg)
 {
-       switch(cmd) 
-       {
+       switch(cmd)
+       {
                default:
                        return(-EINVAL);
-       }
+       }
 }
 
 
@@ -153,28 +154,28 @@ build_options(struct iphdr *iph, struct options *opt)
 
 
 /*
- *     Take an skb, and fill in the MAC header. 
+ *     Take an skb, and fill in the MAC header.
  */
+
 static int ip_send(struct sk_buff *skb, unsigned long daddr, int len, struct device *dev, unsigned long saddr)
 {
        int mac = 0;
 
        skb->dev = dev;
        skb->arp = 1;
-       if (dev->hard_header) 
+       if (dev->hard_header)
        {
-               /*
-                *      Build a hardware header. Source address is our mac, destination unknown
-                *      (rebuild header will sort this out) 
-                */
+               /*
+                *      Build a hardware header. Source address is our mac, destination unknown
+                *      (rebuild header will sort this out)
+                */
                mac = dev->hard_header(skb->data, dev, ETH_P_IP, NULL, NULL, len, skb);
-               if (mac < 0) 
+               if (mac < 0)
                {
                        mac = -mac;
                        skb->arp = 0;
-                       skb->raddr = daddr;     /* next routing address */                      
-               }       
+                       skb->raddr = daddr;     /* next routing address */
+               }
        }
        return mac;
 }
@@ -190,39 +191,39 @@ int ip_id_count = 0;
 int ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long daddr,
                struct device **dev, int type, struct options *opt, int len, int tos, int ttl)
 {
-       static struct options optmem;
-       struct iphdr *iph;
-       struct rtable *rt;
-       unsigned char *buff;
-       unsigned long raddr;
-       int tmp;
-       unsigned long src;
+       static struct options optmem;
+       struct iphdr *iph;
+       struct rtable *rt;
+       unsigned char *buff;
+       unsigned long raddr;
+       int tmp;
+       unsigned long src;
 
        /*
         *      If there is no 'from' address as yet, then make it our loopback
         */
-        
-       if (saddr == 0) 
-               saddr = ip_my_addr();
-       
+
+       if (saddr == 0)
+               saddr = ip_my_addr();
+
        buff = skb->data;
 
-       /* 
-        *      See if we need to look up the device. 
+       /*
+        *      See if we need to look up the device.
         */
-        
-       if (*dev == NULL) 
+
+       if (*dev == NULL)
        {
                if(skb->localroute)
                        rt = ip_rt_local(daddr, &optmem, &src);
                else
                        rt = ip_rt_route(daddr, &optmem, &src);
-               if (rt == NULL) 
+               if (rt == NULL)
                {
                        ip_statistics.IpOutNoRoutes++;
                        return(-ENETUNREACH);
                }
-       
+
                *dev = rt->rt_dev;
                /*
                 *      If the frame is from us and going off machine it MUST MUST MUST
@@ -233,11 +234,11 @@ int ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long dadd
                raddr = rt->rt_gateway;
 
                opt = &optmem;
-       } 
-       else 
+       }
+       else
        {
-               /* 
-                *      We still need the address of the first hop. 
+               /*
+                *      We still need the address of the first hop.
                 */
                if(skb->localroute)
                        rt = ip_rt_local(daddr, &optmem, &src);
@@ -252,19 +253,19 @@ int ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long dadd
 
                raddr = (rt == NULL) ? 0 : rt->rt_gateway;
        }
-  
-         /*
-          *    No gateway so aim at the real destination
-          */
-       if (raddr == 0)
-               raddr = daddr;
-
-       /* 
-        *      Now build the MAC header. 
-        */
-        
-       tmp = ip_send(skb, raddr, len, *dev, saddr);
-       buff += tmp;
+
+       /*
+        *      No gateway so aim at the real destination
+        */
+       if (raddr == 0)
+               raddr = daddr;
+
+       /*
+        *      Now build the MAC header.
+        */
+
+       tmp = ip_send(skb, raddr, len, *dev, saddr);
+       buff += tmp;
        len -= tmp;
 
        /*
@@ -273,20 +274,20 @@ int ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long dadd
 
        skb->dev = *dev;
        skb->saddr = saddr;
-       if (skb->sk) 
+       if (skb->sk)
                skb->sk->saddr = saddr;
 
        /*
-        *      Now build the IP header. 
+        *      Now build the IP header.
         */
 
-       /* 
-        *      If we are using IPPROTO_RAW, then we don't need an IP header, since
-        *      one is being supplied to us by the user 
-        */
+       /*
+        *      If we are using IPPROTO_RAW, then we don't need an IP header, since
+        *      one is being supplied to us by the user
+        */
 
-       if(type == IPPROTO_RAW) 
-               return (tmp);
+       if(type == IPPROTO_RAW)
+               return (tmp);
 
        iph = (struct iphdr *)buff;
        iph->version  = 4;
@@ -297,7 +298,7 @@ int ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long dadd
        iph->saddr    = saddr;
        iph->protocol = type;
        iph->ihl      = 5;
-  
+
        /* Setup the IP options. */
 #ifdef Not_Yet_Avail
        build_options(iph, opt);
@@ -348,10 +349,10 @@ do_options(struct iphdr *iph, struct options *opt)
                buff += 2;
                opt->handling = ntohs(*(unsigned short *)buff);
                buff += 2;
-               opt->tcc = ((*buff) << 16) + ntohs(*(unsigned short *)(buff+1));
-               buff += 3;
-               len += 11;
-               break;
+               opt->tcc = ((*buff) << 16) + ntohs(*(unsigned short *)(buff+1));
+               buff += 3;
+               len += 11;
+               break;
        case IPOPT_LSRR:
                buff++;
                if ((*buff - 3)% 4 != 0) return(1);
@@ -454,18 +455,18 @@ do_options(struct iphdr *iph, struct options *opt)
   return(0);
 }
 
-/* 
+/*
  *     This is a version of ip_compute_csum() optimized for IP headers, which
- *     always checksum on 4 octet boundaries. 
+ *     always checksum on 4 octet boundaries.
  */
+
 static inline unsigned short ip_fast_csum(unsigned char * buff, int wlen)
 {
        unsigned long sum = 0;
 
-       if (wlen) 
-       {
-       unsigned long bogus;
+       if (wlen)
+       {
+       unsigned long bogus;
         __asm__("clc\n"
                "1:\t"
                "lodsl\n\t"
@@ -479,7 +480,7 @@ static inline unsigned short ip_fast_csum(unsigned char * buff, int wlen)
                "adcw $0, %w0"
            : "=r" (sum), "=S" (buff), "=r" (wlen), "=a" (bogus)
            : "0"  (sum),  "1" (buff),  "2" (wlen));
-       }
+       }
        return (~sum) & 0xffff;
 }
 
@@ -493,56 +494,56 @@ unsigned short ip_compute_csum(unsigned char * buff, int len)
        unsigned long sum = 0;
 
        /* Do the first multiple of 4 bytes and convert to 16 bits. */
-       if (len > 3) 
+       if (len > 3)
        {
                __asm__("clc\n"
-               "1:\t"
-               "lodsl\n\t"
-               "adcl %%eax, %%ebx\n\t"
-               "loop 1b\n\t"
-               "adcl $0, %%ebx\n\t"
-               "movl %%ebx, %%eax\n\t"
-               "shrl $16, %%eax\n\t"
-               "addw %%ax, %%bx\n\t"
-               "adcw $0, %%bx"
-               : "=b" (sum) , "=S" (buff)
-               : "0" (sum), "c" (len >> 2) ,"1" (buff)
-               : "ax", "cx", "si", "bx" );
-       }
-       if (len & 2) 
-       {
+               "1:\t"
+               "lodsl\n\t"
+               "adcl %%eax, %%ebx\n\t"
+               "loop 1b\n\t"
+               "adcl $0, %%ebx\n\t"
+               "movl %%ebx, %%eax\n\t"
+               "shrl $16, %%eax\n\t"
+               "addw %%ax, %%bx\n\t"
+               "adcw $0, %%bx"
+               : "=b" (sum) , "=S" (buff)
+               : "0" (sum), "c" (len >> 2) ,"1" (buff)
+               : "ax", "cx", "si", "bx" );
+       }
+       if (len & 2)
+       {
                __asm__("lodsw\n\t"
-               "addw %%ax, %%bx\n\t"
-               "adcw $0, %%bx"
-               : "=b" (sum), "=S" (buff)
-               : "0" (sum), "1" (buff)
-               : "bx", "ax", "si");
-       }
-       if (len & 1) 
-       {
+               "addw %%ax, %%bx\n\t"
+               "adcw $0, %%bx"
+               : "=b" (sum), "=S" (buff)
+               : "0" (sum), "1" (buff)
+               : "bx", "ax", "si");
+       }
+       if (len & 1)
+       {
                __asm__("lodsb\n\t"
-               "movb $0, %%ah\n\t"
-               "addw %%ax, %%bx\n\t"
-               "adcw $0, %%bx"
-               : "=b" (sum), "=S" (buff)
-               : "0" (sum), "1" (buff)
-               : "bx", "ax", "si");
-       }
-       sum =~sum;
-       return(sum & 0xffff);
+               "movb $0, %%ah\n\t"
+               "addw %%ax, %%bx\n\t"
+               "adcw $0, %%bx"
+               : "=b" (sum), "=S" (buff)
+               : "0" (sum), "1" (buff)
+               : "bx", "ax", "si");
+       }
+       sum =~sum;
+       return(sum & 0xffff);
 }
 
-/* 
- *     Check the header of an incoming IP datagram.  This version is still used in slhc.c. 
+/*
+ *     Check the header of an incoming IP datagram.  This version is still used in slhc.c.
  */
+
 int ip_csum(struct iphdr *iph)
 {
-       return ip_fast_csum((unsigned char *)iph, iph->ihl);
+       return ip_fast_csum((unsigned char *)iph, iph->ihl);
 }
 
-/* 
- *     Generate a checksym for an outgoing IP datagram. 
+/*
+ *     Generate a checksym for an outgoing IP datagram.
  */
 
 static void ip_send_check(struct iphdr *iph)
@@ -558,63 +559,63 @@ static void ip_send_check(struct iphdr *iph)
  *     This fragment handler is a bit of a heap. On the other hand it works quite
  *     happily and handles things quite well.
  */
+
 static struct ipq *ipqueue = NULL;             /* IP fragment queue    */
 
 /*
- *     Create a new fragment entry. 
+ *     Create a new fragment entry.
  */
+
 static struct ipfrag *ip_frag_create(int offset, int end, struct sk_buff *skb, unsigned char *ptr)
 {
-       struct ipfrag *fp;
-       fp = (struct ipfrag *) kmalloc(sizeof(struct ipfrag), GFP_ATOMIC);
-       if (fp == NULL) 
-       {
-               printk("IP: frag_create: no memory left !\n");
-               return(NULL);
-       }
-       memset(fp, 0, sizeof(struct ipfrag));
-
-        /* Fill in the structure. */
+       struct ipfrag *fp;
+
+       fp = (struct ipfrag *) kmalloc(sizeof(struct ipfrag), GFP_ATOMIC);
+       if (fp == NULL)
+       {
+               printk("IP: frag_create: no memory left !\n");
+               return(NULL);
+       }
+       memset(fp, 0, sizeof(struct ipfrag));
+
+       /* Fill in the structure. */
        fp->offset = offset;
        fp->end = end;
        fp->len = end - offset;
        fp->skb = skb;
        fp->ptr = ptr;
+
        return(fp);
 }
+
+
 /*
  *     Find the correct entry in the "incomplete datagrams" queue for
  *     this IP datagram, and return the queue entry address if found.
  */
+
 static struct ipq *ip_find(struct iphdr *iph)
 {
        struct ipq *qp;
        struct ipq *qplast;
+
        cli();
        qplast = NULL;
-       for(qp = ipqueue; qp != NULL; qplast = qp, qp = qp->next) 
+       for(qp = ipqueue; qp != NULL; qplast = qp, qp = qp->next)
        {
-               if (iph->id== qp->iph->id && iph->saddr == qp->iph->saddr &&
-                       iph->daddr == qp->iph->daddr && iph->protocol == qp->iph->protocol) 
+               if (iph->id== qp->iph->id && iph->saddr == qp->iph->saddr &&
+                       iph->daddr == qp->iph->daddr && iph->protocol == qp->iph->protocol)
                {
                        del_timer(&qp->timer);  /* So it doesn't vanish on us. The timer will be reset anyway */
-                       sti();
-                       return(qp);
-               }
-       }
+                       sti();
+                       return(qp);
+               }
+       }
        sti();
        return(NULL);
 }
+
+
 /*
  *     Remove an entry from the "incomplete datagrams" queue, either
  *     because we completed, reassembled and processed it, or because
@@ -627,78 +628,78 @@ static void ip_free(struct ipq *qp)
        struct ipfrag *xp;
 
        /*
-        * Stop the timer for this entry. 
+        * Stop the timer for this entry.
         */
-        
+
        del_timer(&qp->timer);
 
        /* Remove this entry from the "incomplete datagrams" queue. */
        cli();
-       if (qp->prev == NULL) 
+       if (qp->prev == NULL)
+       {
+               ipqueue = qp->next;
+               if (ipqueue != NULL)
+                       ipqueue->prev = NULL;
+       }
+       else
        {
-               ipqueue = qp->next;
-               if (ipqueue != NULL) 
-                       ipqueue->prev = NULL;
-       } 
-       else 
-       {
-               qp->prev->next = qp->next;
-               if (qp->next != NULL) 
-                       qp->next->prev = qp->prev;
-       }
-       /* Release all fragment data. */
-
-       fp = qp->fragments;
-       while (fp != NULL) 
-       {
-               xp = fp->next;
-               IS_SKB(fp->skb);
-               kfree_skb(fp->skb,FREE_READ);
-               kfree_s(fp, sizeof(struct ipfrag));
-               fp = xp;
-       }
-       
-       /* Release the MAC header. */
-       kfree_s(qp->mac, qp->maclen);
-       /* Release the IP header. */
-       kfree_s(qp->iph, qp->ihlen + 8);
-       /* Finally, release the queue descriptor itself. */
-       kfree_s(qp, sizeof(struct ipq));
-       sti();
- }
+               qp->prev->next = qp->next;
+               if (qp->next != NULL)
+                       qp->next->prev = qp->prev;
+       }
+
+       /* Release all fragment data. */
+
+       fp = qp->fragments;
+       while (fp != NULL)
+       {
+               xp = fp->next;
+               IS_SKB(fp->skb);
+               kfree_skb(fp->skb,FREE_READ);
+               kfree_s(fp, sizeof(struct ipfrag));
+               fp = xp;
+       }
+
+       /* Release the MAC header. */
+       kfree_s(qp->mac, qp->maclen);
+
+       /* Release the IP header. */
+       kfree_s(qp->iph, qp->ihlen + 8);
+
+       /* Finally, release the queue descriptor itself. */
+       kfree_s(qp, sizeof(struct ipq));
+       sti();
+}
+
+
 /*
- *     Oops- a fragment queue timed out.  Kill it and send an ICMP reply. 
+ *     Oops- a fragment queue timed out.  Kill it and send an ICMP reply.
  */
+
 static void ip_expire(unsigned long arg)
 {
-       struct ipq *qp;
-       qp = (struct ipq *)arg;
+       struct ipq *qp;
+
+       qp = (struct ipq *)arg;
 
-       /*
-        *      Send an ICMP "Fragment Reassembly Timeout" message. 
-        */
+       /*
+        *      Send an ICMP "Fragment Reassembly Timeout" message.
+        */
 
        ip_statistics.IpReasmTimeout++;
-       ip_statistics.IpReasmFails++;            
-       /* This if is always true... shrug */
-       if(qp->fragments!=NULL)
-               icmp_send(qp->fragments->skb,ICMP_TIME_EXCEEDED,
-                               ICMP_EXC_FRAGTIME, qp->dev);
-       /* 
-        *      Nuke the fragment queue. 
-        */
+       ip_statistics.IpReasmFails++;   
+       /* This if is always true... shrug */
+       if(qp->fragments!=NULL)
+               icmp_send(qp->fragments->skb,ICMP_TIME_EXCEEDED,
+                               ICMP_EXC_FRAGTIME, qp->dev);
+
+       /*
+        *      Nuke the fragment queue.
+        */
        ip_free(qp);
 }
+
+
 /*
  *     Add an entry to the 'ipq' queue for a newly received IP datagram.
  *     We will (hopefully :-) receive all other fragments of this datagram
@@ -708,186 +709,186 @@ static void ip_expire(unsigned long arg)
 
 static struct ipq *ip_create(struct sk_buff *skb, struct iphdr *iph, struct device *dev)
 {
-       struct ipq *qp;
-       int maclen;
-       int ihlen;
+       struct ipq *qp;
+       int maclen;
+       int ihlen;
 
-       qp = (struct ipq *) kmalloc(sizeof(struct ipq), GFP_ATOMIC);
-       if (qp == NULL) 
-       {
+       qp = (struct ipq *) kmalloc(sizeof(struct ipq), GFP_ATOMIC);
+       if (qp == NULL)
+       {
                printk("IP: create: no memory left !\n");
                return(NULL);
-               skb->dev = qp->dev;
-       }
-       memset(qp, 0, sizeof(struct ipq));
-
-       /*
-        *      Allocate memory for the MAC header. 
-        *
-        *      FIXME: We have a maximum MAC address size limit and define 
-        *      elsewhere. We should use it here and avoid the 3 kmalloc() calls
-        */
-        
-       maclen = ((unsigned long) iph) - ((unsigned long) skb->data);
-       qp->mac = (unsigned char *) kmalloc(maclen, GFP_ATOMIC);
-       if (qp->mac == NULL) 
-       {
+               skb->dev = qp->dev;
+       }
+       memset(qp, 0, sizeof(struct ipq));
+
+       /*
+        *      Allocate memory for the MAC header.
+        *
+        *      FIXME: We have a maximum MAC address size limit and define
+        *      elsewhere. We should use it here and avoid the 3 kmalloc() calls
+        */
+
+       maclen = ((unsigned long) iph) - ((unsigned long) skb->data);
+       qp->mac = (unsigned char *) kmalloc(maclen, GFP_ATOMIC);
+       if (qp->mac == NULL)
+       {
                printk("IP: create: no memory left !\n");
                kfree_s(qp, sizeof(struct ipq));
                return(NULL);
-       }
-
-       /* 
-        *      Allocate memory for the IP header (plus 8 octects for ICMP). 
-        */
-        
-       ihlen = (iph->ihl * sizeof(unsigned long));
-       qp->iph = (struct iphdr *) kmalloc(ihlen + 8, GFP_ATOMIC);
-       if (qp->iph == NULL) 
-       {
+       }
+
+       /*
+        *      Allocate memory for the IP header (plus 8 octects for ICMP).
+        */
+
+       ihlen = (iph->ihl * sizeof(unsigned long));
+       qp->iph = (struct iphdr *) kmalloc(ihlen + 8, GFP_ATOMIC);
+       if (qp->iph == NULL)
+       {
                printk("IP: create: no memory left !\n");
                kfree_s(qp->mac, maclen);
                kfree_s(qp, sizeof(struct ipq));
                return(NULL);
-       }
-
-       /* Fill in the structure. */
-       memcpy(qp->mac, skb->data, maclen);
-       memcpy(qp->iph, iph, ihlen + 8);
-       qp->len = 0;
-       qp->ihlen = ihlen;
-       qp->maclen = maclen;
-       qp->fragments = NULL;
-       qp->dev = dev;
-       
-       /* Start a timer for this entry. */
-       qp->timer.expires = IP_FRAG_TIME;               /* about 30 seconds     */
-       qp->timer.data = (unsigned long) qp;            /* pointer to queue     */
-       qp->timer.function = ip_expire;                 /* expire function      */
-       add_timer(&qp->timer);
-
-       /* Add this entry to the queue. */
-       qp->prev = NULL;
-       cli();
-       qp->next = ipqueue;
-       if (qp->next != NULL) 
-               qp->next->prev = qp;
-       ipqueue = qp;
-       sti();
-       return(qp);
+       }
+
+       /* Fill in the structure. */
+       memcpy(qp->mac, skb->data, maclen);
+       memcpy(qp->iph, iph, ihlen + 8);
+       qp->len = 0;
+       qp->ihlen = ihlen;
+       qp->maclen = maclen;
+       qp->fragments = NULL;
+       qp->dev = dev;
+
+       /* Start a timer for this entry. */
+       qp->timer.expires = IP_FRAG_TIME;               /* about 30 seconds     */
+       qp->timer.data = (unsigned long) qp;            /* pointer to queue     */
+       qp->timer.function = ip_expire;                 /* expire function      */
+       add_timer(&qp->timer);
+
+       /* Add this entry to the queue. */
+       qp->prev = NULL;
+       cli();
+       qp->next = ipqueue;
+       if (qp->next != NULL)
+               qp->next->prev = qp;
+       ipqueue = qp;
+       sti();
+       return(qp);
 }
+
+
 /*
- *     See if a fragment queue is complete. 
+ *     See if a fragment queue is complete.
  */
+
 static int ip_done(struct ipq *qp)
 {
        struct ipfrag *fp;
        int offset;
-       /* Only possible if we received the final fragment. */
-       if (qp->len == 0) 
-               return(0);
-       /* Check all fragment offsets to see if they connect. */
-       fp = qp->fragments;
-       offset = 0;
-       while (fp != NULL) 
-       {
-               if (fp->offset > offset) 
-                       return(0);      /* fragment(s) missing */
-               offset = fp->end;
-               fp = fp->next;
-       }
-       /* All fragments are present. */
-       return(1);
- }
-/* 
- *     Build a new IP datagram from all its fragments. 
+
+       /* Only possible if we received the final fragment. */
+       if (qp->len == 0)
+               return(0);
+
+       /* Check all fragment offsets to see if they connect. */
+       fp = qp->fragments;
+       offset = 0;
+       while (fp != NULL)
+       {
+               if (fp->offset > offset)
+                       return(0);      /* fragment(s) missing */
+               offset = fp->end;
+               fp = fp->next;
+       }
+
+       /* All fragments are present. */
+       return(1);
+}
+
+
+/*
+ *     Build a new IP datagram from all its fragments.
  *
  *     FIXME: We copy here because we lack an effective way of handling lists
  *     of bits on input. Until the new skb data handling is in I'm not going
  *     to touch this with a bargepole. This also causes a 4Kish limit on
  *     packet sizes.
  */
+
 static struct sk_buff *ip_glue(struct ipq *qp)
 {
        struct sk_buff *skb;
-       struct iphdr *iph;
-       struct ipfrag *fp;
-       unsigned char *ptr;
-       int count, len;
-       /*
-        *      Allocate a new buffer for the datagram. 
-        */
-        
-       len = qp->maclen + qp->ihlen + qp->len;
-
-       if ((skb = alloc_skb(len,GFP_ATOMIC)) == NULL) 
-       {
-               ip_statistics.IpReasmFails++;
-               printk("IP: queue_glue: no memory for glueing queue 0x%X\n", (int) qp);
-               ip_free(qp);
-               return(NULL);
-       }
-       /* Fill in the basic details. */
-       skb->len = (len - qp->maclen);
-       skb->h.raw = skb->data;
-       skb->free = 1;
-       /* Copy the original MAC and IP headers into the new buffer. */
-       ptr = (unsigned char *) skb->h.raw;
-       memcpy(ptr, ((unsigned char *) qp->mac), qp->maclen);
-       ptr += qp->maclen;
-       memcpy(ptr, ((unsigned char *) qp->iph), qp->ihlen);
-       ptr += qp->ihlen;
-       skb->h.raw += qp->maclen;
-       
-       count = 0;
-       /* Copy the data portions of all fragments into the new buffer. */
-       fp = qp->fragments;
-       while(fp != NULL) 
-       {
-               if(count+fp->len>skb->len)
-               {
-                       printk("Invalid fragment list: Fragment over size.\n");
-                       ip_free(qp);
-                       kfree_skb(skb,FREE_WRITE);
-                       ip_statistics.IpReasmFails++;
-                       return NULL;
-               }
-               memcpy((ptr + fp->offset), fp->ptr, fp->len);
-               count += fp->len;
-               fp = fp->next;
-       }
-       /* We glued together all fragments, so remove the queue entry. */
-       ip_free(qp);
-       /* Done with all fragments. Fixup the new IP header. */
-       iph = skb->h.iph;
-       iph->frag_off = 0;
-       iph->tot_len = htons((iph->ihl * sizeof(unsigned long)) + count);
-       skb->ip_hdr = iph;
-       
-       ip_statistics.IpReasmOKs++;
-       return(skb);
+       struct iphdr *iph;
+       struct ipfrag *fp;
+       unsigned char *ptr;
+       int count, len;
+
+       /*
+        *      Allocate a new buffer for the datagram.
+        */
+
+       len = qp->maclen + qp->ihlen + qp->len;
+
+       if ((skb = alloc_skb(len,GFP_ATOMIC)) == NULL)
+       {
+               ip_statistics.IpReasmFails++;
+               printk("IP: queue_glue: no memory for glueing queue 0x%X\n", (int) qp);
+               ip_free(qp);
+               return(NULL);
+       }
+
+       /* Fill in the basic details. */
+       skb->len = (len - qp->maclen);
+       skb->h.raw = skb->data;
+       skb->free = 1;
+
+       /* Copy the original MAC and IP headers into the new buffer. */
+       ptr = (unsigned char *) skb->h.raw;
+       memcpy(ptr, ((unsigned char *) qp->mac), qp->maclen);
+       ptr += qp->maclen;
+       memcpy(ptr, ((unsigned char *) qp->iph), qp->ihlen);
+       ptr += qp->ihlen;
+       skb->h.raw += qp->maclen;
+
+       count = 0;
+
+       /* Copy the data portions of all fragments into the new buffer. */
+       fp = qp->fragments;
+       while(fp != NULL)
+       {
+               if(count+fp->len>skb->len)
+               {
+                       printk("Invalid fragment list: Fragment over size.\n");
+                       ip_free(qp);
+                       kfree_skb(skb,FREE_WRITE);
+                       ip_statistics.IpReasmFails++;
+                       return NULL;
+               }
+               memcpy((ptr + fp->offset), fp->ptr, fp->len);
+               count += fp->len;
+               fp = fp->next;
+       }
+
+       /* We glued together all fragments, so remove the queue entry. */
+       ip_free(qp);
+
+       /* Done with all fragments. Fixup the new IP header. */
+       iph = skb->h.iph;
+       iph->frag_off = 0;
+       iph->tot_len = htons((iph->ihl * sizeof(unsigned long)) + count);
+       skb->ip_hdr = iph;
+
+       ip_statistics.IpReasmOKs++;
+       return(skb);
 }
+
 
 /*
- *     Process an incoming IP datagram fragment. 
+ *     Process an incoming IP datagram fragment.
  */
+
 static struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device *dev)
 {
        struct ipfrag *prev, *next;
@@ -899,43 +900,43 @@ static struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct
        int i, ihl, end;
 
        ip_statistics.IpReasmReqds++;
-       
+
        /* Find the entry of this IP datagram in the "incomplete datagrams" queue. */
-       qp = ip_find(iph);
-       /* Is this a non-fragmented datagram? */
-       offset = ntohs(iph->frag_off);
-       flags = offset & ~IP_OFFSET;
-       offset &= IP_OFFSET;
-       if (((flags & IP_MF) == 0) && (offset == 0)) 
-       {
+       qp = ip_find(iph);
+
+       /* Is this a non-fragmented datagram? */
+       offset = ntohs(iph->frag_off);
+       flags = offset & ~IP_OFFSET;
+       offset &= IP_OFFSET;
+       if (((flags & IP_MF) == 0) && (offset == 0))
+       {
                if (qp != NULL)
-                       ip_free(qp);    /* Huh? How could this exist?? */
-               return(skb);
-       }
-
-       offset <<= 3;           /* offset is in 8-byte chunks */
-       /*
-        * If the queue already existed, keep restarting its timer as long
-        * as we still are receiving fragments.  Otherwise, create a fresh
-        * queue entry.
-        */
-
-       if (qp != NULL) 
+                       ip_free(qp);    /* Huh? How could this exist?? */
+               return(skb);
+       }
+
+       offset <<= 3;           /* offset is in 8-byte chunks */
+
+       /*
+        * If the queue already existed, keep restarting its timer as long
+        * as we still are receiving fragments.  Otherwise, create a fresh
+        * queue entry.
+        */
+
+       if (qp != NULL)
        {
                del_timer(&qp->timer);
                qp->timer.expires = IP_FRAG_TIME;       /* about 30 seconds */
                qp->timer.data = (unsigned long) qp;    /* pointer to queue */
                qp->timer.function = ip_expire;         /* expire function */
                add_timer(&qp->timer);
-       } 
-       else 
+       }
+       else
        {
                /*
                 *      If we failed to create it, then discard the frame
                 */
-               if ((qp = ip_create(skb, iph, dev)) == NULL) 
+               if ((qp = ip_create(skb, iph, dev)) == NULL)
                {
                        skb->sk = NULL;
                        kfree_skb(skb, FREE_READ);
@@ -944,219 +945,218 @@ static struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct
                }
        }
 
-       /*
-        *      Determine the position of this fragment. 
-        */
-        
-       ihl = (iph->ihl * sizeof(unsigned long));
-       end = offset + ntohs(iph->tot_len) - ihl;
-
-       /*
-        *      Point into the IP datagram 'data' part. 
-        */
-
-       ptr = skb->data + dev->hard_header_len + ihl;
-       /* 
-        *      Is this the final fragment? 
-        */
-
-       if ((flags & IP_MF) == 0) 
-               qp->len = end;
-       /*
-        *      Find out which fragments are in front and at the back of us
-        *      in the chain of fragments so far.  We must know where to put
-        *      this fragment, right?
-        */
-        
-       prev = NULL;
-       for(next = qp->fragments; next != NULL; next = next->next) 
-       {
-               if (next->offset > offset) 
-                       break;  /* bingo! */
-               prev = next;
-       }       
-       /*
-        *      We found where to put this one.
-        *      Check for overlap with preceeding fragment, and, if needed,
-        *      align things so that any overlaps are eliminated.
-        */
-       if (prev != NULL && offset < prev->end) 
-       {
-               i = prev->end - offset;
-               offset += i;    /* ptr into datagram */
-               ptr += i;       /* ptr into fragment data */
-       }       
-       /*
-        * Look for overlap with succeeding segments.
-        * If we can merge fragments, do it.
-        */
-   
-       for(; next != NULL; next = tfp) 
-       {
-               tfp = next->next;
-               if (next->offset >= end) 
-                       break;          /* no overlaps at all */
-               i = end - next->offset;                 /* overlap is 'i' bytes */
-               next->len -= i;                         /* so reduce size of    */
-               next->offset += i;                      /* next fragment        */
-               next->ptr += i;
-               
-               /* 
-                *      If we get a frag size of <= 0, remove it and the packet
-                *      that it goes with.
-                */
-               if (next->len <= 0) 
-               {
-                       if (next->prev != NULL) 
-                               next->prev->next = next->next;
-                       else 
-                               qp->fragments = next->next;
-               
-                       if (tfp->next != NULL) 
-                               next->next->prev = next->prev;
-                               
-                       kfree_skb(next->skb,FREE_READ);                         
-                       kfree_s(next, sizeof(struct ipfrag));
-               }
-       }
-       /* 
-        *      Insert this fragment in the chain of fragments. 
-        */
-        
-       tfp = NULL;
-       tfp = ip_frag_create(offset, end, skb, ptr);
-       
-       /*
-        *      No memory to save the fragment - so throw the lot
-        */
-       
-       if (!tfp) 
-       {
-               skb->sk = NULL;
-               kfree_skb(skb, FREE_READ);
-               return NULL;
-       }
-       tfp->prev = prev;
-       tfp->next = next;
-       if (prev != NULL) 
-               prev->next = tfp;
-       else 
-               qp->fragments = tfp;
-   
-       if (next != NULL) 
-               next->prev = tfp;
-       /*
-        *      OK, so we inserted this new fragment into the chain.
-        *      Check if we now have a full IP datagram which we can
-        *      bump up to the IP layer...
-        */
-   
-       if (ip_done(qp)) 
-       {
-               skb2 = ip_glue(qp);             /* glue together the fragments */
-               return(skb2);
-       }
-       return(NULL);
- }
- /*
-  *    This IP datagram is too large to be sent in one piece.  Break it up into
-  *    smaller pieces (each of size equal to the MAC header plus IP header plus
-  *    a block of the data of the original IP data part) that will yet fit in a
-  *    single device frame, and queue such a frame for sending by calling the
-  *    ip_queue_xmit().  Note that this is recursion, and bad things will happen
-  *    if this function causes a loop...
-  *
-  *    Yes this is inefficient, feel free to submit a quicker one.
-  *
-  *    **Protocol Violation**
-  *    We copy all the options to each fragment. !FIXME!
-  */
-  
- void ip_fragment(struct sock *sk, struct sk_buff *skb, struct device *dev, int is_frag)
- {
-       struct iphdr *iph;
-       unsigned char *raw;
-       unsigned char *ptr;
-       struct sk_buff *skb2;
-       int left, mtu, hlen, len;
-       int offset;
-       unsigned long flags;
-       /* 
-        *      Point into the IP datagram header. 
-        */
-        
-       raw = skb->data;
-       iph = (struct iphdr *) (raw + dev->hard_header_len);
+       /*
+        *      Determine the position of this fragment.
+        */
+
+       ihl = (iph->ihl * sizeof(unsigned long));
+       end = offset + ntohs(iph->tot_len) - ihl;
+
+       /*
+        *      Point into the IP datagram 'data' part.
+        */
+
+       ptr = skb->data + dev->hard_header_len + ihl;
+
+       /*
+        *      Is this the final fragment?
+        */
+
+       if ((flags & IP_MF) == 0)
+               qp->len = end;
+
+       /*
+        *      Find out which fragments are in front and at the back of us
+        *      in the chain of fragments so far.  We must know where to put
+        *      this fragment, right?
+        */
+
+       prev = NULL;
+       for(next = qp->fragments; next != NULL; next = next->next)
+       {
+               if (next->offset > offset)
+                       break;  /* bingo! */
+               prev = next;
+       }
+
+       /*
+        *      We found where to put this one.
+        *      Check for overlap with preceeding fragment, and, if needed,
+        *      align things so that any overlaps are eliminated.
+        */
+       if (prev != NULL && offset < prev->end)
+       {
+               i = prev->end - offset;
+               offset += i;    /* ptr into datagram */
+               ptr += i;       /* ptr into fragment data */
+       }
+
+       /*
+        * Look for overlap with succeeding segments.
+        * If we can merge fragments, do it.
+        */
+
+       for(; next != NULL; next = tfp)
+       {
+               tfp = next->next;
+               if (next->offset >= end)
+                       break;          /* no overlaps at all */
+
+               i = end - next->offset;                 /* overlap is 'i' bytes */
+               next->len -= i;                         /* so reduce size of    */
+               next->offset += i;                      /* next fragment        */
+               next->ptr += i;
+
+               /*
+                *      If we get a frag size of <= 0, remove it and the packet
+                *      that it goes with.
+                */
+               if (next->len <= 0)
+               {
+                       if (next->prev != NULL)
+                               next->prev->next = next->next;
+                       else
+                               qp->fragments = next->next;
+
+                       if (tfp->next != NULL)
+                               next->next->prev = next->prev;
+
+                       kfree_skb(next->skb,FREE_READ);
+                       kfree_s(next, sizeof(struct ipfrag));
+               }
+       }
+
+       /*
+        *      Insert this fragment in the chain of fragments.
+        */
+
+       tfp = NULL;
+       tfp = ip_frag_create(offset, end, skb, ptr);
+
+       /*
+        *      No memory to save the fragment - so throw the lot
+        */
+
+       if (!tfp)
+       {
+               skb->sk = NULL;
+               kfree_skb(skb, FREE_READ);
+               return NULL;
+       }
+       tfp->prev = prev;
+       tfp->next = next;
+       if (prev != NULL)
+               prev->next = tfp;
+       else
+               qp->fragments = tfp;
+
+       if (next != NULL)
+               next->prev = tfp;
+
+       /*
+        *      OK, so we inserted this new fragment into the chain.
+        *      Check if we now have a full IP datagram which we can
+        *      bump up to the IP layer...
+        */
+
+       if (ip_done(qp))
+       {
+               skb2 = ip_glue(qp);             /* glue together the fragments */
+               return(skb2);
+       }
+       return(NULL);
+}
+
+
+/*
+ *     This IP datagram is too large to be sent in one piece.  Break it up into
+ *     smaller pieces (each of size equal to the MAC header plus IP header plus
+ *     a block of the data of the original IP data part) that will yet fit in a
+ *     single device frame, and queue such a frame for sending by calling the
+ *     ip_queue_xmit().  Note that this is recursion, and bad things will happen
+ *     if this function causes a loop...
+ *
+ *     Yes this is inefficient, feel free to submit a quicker one.
+ *
+ *     **Protocol Violation**
+ *     We copy all the options to each fragment. !FIXME!
+ */
+void ip_fragment(struct sock *sk, struct sk_buff *skb, struct device *dev, int is_frag)
+{
+       struct iphdr *iph;
+       unsigned char *raw;
+       unsigned char *ptr;
+       struct sk_buff *skb2;
+       int left, mtu, hlen, len;
+       int offset;
+       unsigned long flags;
+
+       /*
+        *      Point into the IP datagram header.
+        */
+
+       raw = skb->data;
+       iph = (struct iphdr *) (raw + dev->hard_header_len);
 
        skb->ip_hdr = iph;
-               
-       /* 
-        *      Setup starting values. 
-        */
-        
-       hlen = (iph->ihl * sizeof(unsigned long));
-       left = ntohs(iph->tot_len) - hlen;      /* Space per frame */
-       hlen += dev->hard_header_len;           /* Total header size */
-       mtu = (dev->mtu - hlen);                /* Size of data space */
-       ptr = (raw + hlen);                     /* Where to start from */
-       
-       /*
-        *      Check for any "DF" flag. [DF means do not fragment]
-        */
-        
-       if (ntohs(iph->frag_off) & IP_DF) 
-       {
-               ip_statistics.IpFragFails++;
-               icmp_send(skb,ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev); 
-               return;
-       }
-       /*
-        *      The protocol doesn't seem to say what to do in the case that the
-        *      frame + options doesn't fit the mtu. As it used to fall down dead
-        *      in this case we were fortunate it didn't happen
-        */
-        
-       if(mtu<8)
-       {
-               /* It's wrong but its better than nothing */
-               icmp_send(skb,ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED,dev);
-               ip_statistics.IpFragFails++;
-               return;
-       }
-       
-       /* 
-        *      Fragment the datagram. 
-        */
-        
-       /*
-        *      The initial offset is 0 for a complete frame. When
-        *      fragmenting fragments its wherever this one starts.
-        */
+
+       /*
+        *      Setup starting values.
+        */
+
+       hlen = (iph->ihl * sizeof(unsigned long));
+       left = ntohs(iph->tot_len) - hlen;      /* Space per frame */
+       hlen += dev->hard_header_len;           /* Total header size */
+       mtu = (dev->mtu - hlen);                /* Size of data space */
+       ptr = (raw + hlen);                     /* Where to start from */
+
+       /*
+        *      Check for any "DF" flag. [DF means do not fragment]
+        */
+
+       if (ntohs(iph->frag_off) & IP_DF)
+       {
+               ip_statistics.IpFragFails++;
+               icmp_send(skb,ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev);
+               return;
+       }
+
+       /*
+        *      The protocol doesn't seem to say what to do in the case that the
+        *      frame + options doesn't fit the mtu. As it used to fall down dead
+        *      in this case we were fortunate it didn't happen
+        */
+
+       if(mtu<8)
+       {
+               /* It's wrong but its better than nothing */
+               icmp_send(skb,ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED,dev);
+               ip_statistics.IpFragFails++;
+               return;
+       }
+
+       /*
+        *      Fragment the datagram.
+        */
+
+       /*
+        *      The initial offset is 0 for a complete frame. When
+        *      fragmenting fragments its wherever this one starts.
+        */
 
        if (is_frag & 2)
                offset = (ntohs(iph->frag_off) & 0x1fff) << 3;
        else
-               offset = 0;
+               offset = 0;
 
 
        /*
         *      Keep copying data until we run out.
         */
-                       
-       while(left > 0) 
-       {
-               len = left;
+
+       while(left > 0)
+       {
+               len = left;
                /* IF: it doesn't fit, use 'mtu' - the data space left */
                if (len > mtu)
                        len = mtu;
@@ -1167,87 +1167,87 @@ static struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct
                        len/=8;
                        len*=8;
                }
-               /*
-                *      Allocate buffer. 
-                */
-                
-               if ((skb2 = alloc_skb(len + hlen,GFP_ATOMIC)) == NULL) 
-               {
-                       printk("IP: frag: no memory for new fragment!\n");
-                       ip_statistics.IpFragFails++;
-                       return;
-               }
-               
-               /*
-                *      Set up data on packet
-                */
-
-               skb2->arp = skb->arp;
-               if(skb->free==0)
-                       printk("IP fragmenter: BUG free!=1 in fragmenter\n");
-               skb2->free = 1;
-               skb2->len = len + hlen;
-               skb2->h.raw=(char *) skb2->data;
+               /*
+                *      Allocate buffer.
+                */
+
+               if ((skb2 = alloc_skb(len + hlen,GFP_ATOMIC)) == NULL)
+               {
+                       printk("IP: frag: no memory for new fragment!\n");
+                       ip_statistics.IpFragFails++;
+                       return;
+               }
+
+               /*
+                *      Set up data on packet
+                */
+
+               skb2->arp = skb->arp;
+               if(skb->free==0)
+                       printk("IP fragmenter: BUG free!=1 in fragmenter\n");
+               skb2->free = 1;
+               skb2->len = len + hlen;
+               skb2->h.raw=(char *) skb2->data;
                /*
                 *      Charge the memory for the fragment to any owner
                 *      it might posess
                 */
-                
+
                save_flags(flags);
-               if (sk) 
-               {
-                       cli();
-                       sk->wmem_alloc += skb2->mem_len;
-                       skb2->sk=sk;
-               }
-               restore_flags(flags);
-               skb2->raddr = skb->raddr;       /* For rebuild_header - must be here */ 
-
-               /* 
-                *      Copy the packet header into the new buffer. 
-                */
-                
-               memcpy(skb2->h.raw, raw, hlen);
-               /*
-                *      Copy a block of the IP datagram. 
-                */
-               memcpy(skb2->h.raw + hlen, ptr, len);
-               left -= len;
-
-               skb2->h.raw+=dev->hard_header_len; 
-
-               /*
-                *      Fill in the new header fields. 
-                */
-               iph = (struct iphdr *)(skb2->h.raw/*+dev->hard_header_len*/);
-               iph->frag_off = htons((offset >> 3));
-               /* 
-                *      Added AC : If we are fragmenting a fragment thats not the
-                *                 last fragment then keep MF on each bit 
-                */
-               if (left > 0 || (is_frag & 1)) 
-                       iph->frag_off |= htons(IP_MF);
-               ptr += len;
-               offset += len;
-               /* 
-                *      Put this fragment into the sending queue. 
-                */
-                
-               ip_statistics.IpFragCreates++;
-               
-               ip_queue_xmit(sk, dev, skb2, 2);
-       }
-       ip_statistics.IpFragOKs++;
+               if (sk)
+               {
+                       cli();
+                       sk->wmem_alloc += skb2->mem_len;
+                       skb2->sk=sk;
+               }
+               restore_flags(flags);
+               skb2->raddr = skb->raddr;       /* For rebuild_header - must be here */
+
+               /*
+                *      Copy the packet header into the new buffer.
+                */
+
+               memcpy(skb2->h.raw, raw, hlen);
+
+               /*
+                *      Copy a block of the IP datagram.
+                */
+               memcpy(skb2->h.raw + hlen, ptr, len);
+               left -= len;
+
+               skb2->h.raw+=dev->hard_header_len;
+
+               /*
+                *      Fill in the new header fields.
+                */
+               iph = (struct iphdr *)(skb2->h.raw/*+dev->hard_header_len*/);
+               iph->frag_off = htons((offset >> 3));
+               /*
+                *      Added AC : If we are fragmenting a fragment thats not the
+                *                 last fragment then keep MF on each bit
+                */
+               if (left > 0 || (is_frag & 1))
+                       iph->frag_off |= htons(IP_MF);
+               ptr += len;
+               offset += len;
+
+               /*
+                *      Put this fragment into the sending queue.
+                */
+
+               ip_statistics.IpFragCreates++;
+
+               ip_queue_xmit(sk, dev, skb2, 2);
+       }
+       ip_statistics.IpFragOKs++;
 }
+
 
 
 #ifdef CONFIG_IP_FORWARD
 
-/*     
- *     Forward an IP datagram to its next destination. 
+/*
+ *     Forward an IP datagram to its next destination.
  */
 
 static void ip_forward(struct sk_buff *skb, struct device *dev, int is_frag)
@@ -1259,41 +1259,41 @@ static void ip_forward(struct sk_buff *skb, struct device *dev, int is_frag)
        unsigned char *ptr;     /* Data pointer */
        unsigned long raddr;    /* Router IP address */
 
-       /*
-        *      According to the RFC, we must first decrease the TTL field. If
-        *      that reaches zero, we must reply an ICMP control message telling
-        *      that the packet's lifetime expired.
-        *
-        *      Exception:
-        *      We may not generate an ICMP for an ICMP. icmp_send does the
-        *      enforcement of this so we can forget it here. It is however
-        *      sometimes VERY important.
-        */
+       /*
+        *      According to the RFC, we must first decrease the TTL field. If
+        *      that reaches zero, we must reply an ICMP control message telling
+        *      that the packet's lifetime expired.
+        *
+        *      Exception:
+        *      We may not generate an ICMP for an ICMP. icmp_send does the
+        *      enforcement of this so we can forget it here. It is however
+        *      sometimes VERY important.
+        */
 
        iph = skb->h.iph;
        iph->ttl--;
-       if (iph->ttl <= 0) 
+       if (iph->ttl <= 0)
        {
                /* Tell the sender its packet died... */
                icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, dev);
                return;
        }
 
-       /* 
-        *      Re-compute the IP header checksum. 
+       /*
+        *      Re-compute the IP header checksum.
         *      This is inefficient. We know what has happened to the header
         *      and could thus adjust the checksum as Phil Karn does in KA9Q
         */
-        
+
        ip_send_check(iph);
 
        /*
         * OK, the packet is still valid.  Fetch its destination address,
-        * and give it to the IP sender for further processing.
+        * and give it to the IP sender for further processing.
         */
 
        rt = ip_rt_route(iph->daddr, NULL, NULL);
-       if (rt == NULL) 
+       if (rt == NULL)
        {
                /*
                 *      Tell the sender its packet cannot be delivered. Again
@@ -1314,30 +1314,30 @@ static void ip_forward(struct sk_buff *skb, struct device *dev, int is_frag)
 
        raddr = rt->rt_gateway;
 
-       if (raddr != 0) 
+       if (raddr != 0)
        {
                /*
                 *      There is a gateway so find the correct route for it.
                 *      Gateways cannot in turn be gatewayed.
                 */
                rt = ip_rt_route(raddr, NULL, NULL);
-               if (rt == NULL) 
+               if (rt == NULL)
                {
-                       /* 
-                        *      Tell the sender its packet cannot be delivered... 
+                       /*
+                        *      Tell the sender its packet cannot be delivered...
                         */
                        icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, dev);
                        return;
                }
-               if (rt->rt_gateway != 0) 
+               if (rt->rt_gateway != 0)
                        raddr = rt->rt_gateway;
-       } 
-       else 
-               raddr = iph->daddr;
-               
-       /*
-        *      Having picked a route we can now send the frame out.
-        */
+       }
+       else
+               raddr = iph->daddr;
+
+       /*
+        *      Having picked a route we can now send the frame out.
+        */
 
        dev2 = rt->rt_dev;
 
@@ -1356,21 +1356,21 @@ static void ip_forward(struct sk_buff *skb, struct device *dev, int is_frag)
         * If the indicated interface is up and running, kick it.
         */
 
-       if (dev2->flags & IFF_UP) 
+       if (dev2->flags & IFF_UP)
        {
-       
+
                /*
                 *      Current design decrees we copy the packet. For identical header
                 *      lengths we could avoid it. The new skb code will let us push
                 *      data so the problem goes away then.
                 */
-                
+
                skb2 = alloc_skb(dev2->hard_header_len + skb->len, GFP_ATOMIC);
                /*
                 *      This is rare and since IP is tolerant of network failures
                 *      quite harmless.
                 */
-               if (skb2 == NULL) 
+               if (skb2 == NULL)
                {
                        printk("\nIP: No memory available for IP forward\n");
                        return;
@@ -1380,11 +1380,11 @@ static void ip_forward(struct sk_buff *skb, struct device *dev, int is_frag)
                skb2->len = skb->len + dev2->hard_header_len;
                skb2->h.raw = ptr;
 
-               /* 
-                *      Copy the packet data into the new buffer. 
+               /*
+                *      Copy the packet data into the new buffer.
                 */
                memcpy(ptr + dev2->hard_header_len, skb->h.raw, skb->len);
-               
+
                /* Now build the MAC header. */
                (void) ip_send(skb2, raddr, skb->len, dev2, dev2->pa_addr);
 
@@ -1395,8 +1395,8 @@ static void ip_forward(struct sk_buff *skb, struct device *dev, int is_frag)
                 *      the fragment type. This must be right so that
                 *      the fragmenter does the right thing.
                 */
-                
-               if(skb2->len > dev2->mtu)
+
+               if(skb2->len > dev2->mtu + dev2->hard_header_len)
                {
                        ip_fragment(NULL,skb2,dev2, is_frag);
                        kfree_skb(skb2,FREE_WRITE);
@@ -1422,9 +1422,9 @@ static void ip_forward(struct sk_buff *skb, struct device *dev, int is_frag)
 #endif
 
 /*
- *     This function receives all incoming IP datagrams. 
+ *     This function receives all incoming IP datagrams.
  */
+
 int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
 {
        struct iphdr *iph = skb->h.iph;
@@ -1439,15 +1439,15 @@ int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
 
 
        ip_statistics.IpInReceives++;
-       
+
        /*
         *      Tag the ip header of this packet so we can find it
         */
-        
+
        skb->ip_hdr = iph;
 
        /*
-        *      Is the datagram acceptable? 
+        *      Is the datagram acceptable?
         *
         *      1.      Length at least the size of an ip header
         *      2.      Version of 4
@@ -1455,31 +1455,31 @@ int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
         *      (4.     We ought to check for IP multicast addresses and undefined types.. does this matter ?)
         */
 
-       if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4 || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0) 
-       {
-               ip_statistics.IpInHdrErrors++;
+       if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4 || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0)
+       {
+               ip_statistics.IpInHdrErrors++;
                kfree_skb(skb, FREE_WRITE);
                return(0);
        }
-       
+
        /*
         *      Our transport medium may have padded the buffer out. Now we know it
         *      is IP we can trim to the true length of the frame.
         */
-        
+
        skb->len=ntohs(iph->tot_len);
 
        /*
         *      Next anaylse the packet for options. Studies show under one packet in
         *      a thousand have options....
         */
-          
-       if (iph->ihl != 5) 
+
+       if (iph->ihl != 5)
        {       /* Fast path for the typical optionless IP packet. */
-               memset((char *) &opt, 0, sizeof(opt));
-               if (do_options(iph, &opt) != 0)
-                       return 0;
-               opts_p = 1;
+               memset((char *) &opt, 0, sizeof(opt));
+               if (do_options(iph, &opt) != 0)
+                       return 0;
+               opts_p = 1;
        }
 
        /*
@@ -1487,103 +1487,103 @@ int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
         */
 
        if (iph->frag_off & 0x0020)
-               is_frag|=1;
-       
-       /*
-        *      Last fragment ?
-        */
-        
-       if (ntohs(iph->frag_off) & 0x1fff)
-               is_frag|=2;
-       
-       /* 
-        *      Do any IP forwarding required.  chk_addr() is expensive -- avoid it someday. 
-        *
-        *      This is inefficient. While finding out if it is for us we could also compute
-        *      the routing table entry. This is where the great unified cache theory comes
-        *      in as and when someone impliments it
-        */
-
-       if ((brd = ip_chk_addr(iph->daddr)) == 0) 
+               is_frag|=1;
+
+       /*
+        *      Last fragment ?
+        */
+
+       if (ntohs(iph->frag_off) & 0x1fff)
+               is_frag|=2;
+
+       /*
+        *      Do any IP forwarding required.  chk_addr() is expensive -- avoid it someday.
+        *
+        *      This is inefficient. While finding out if it is for us we could also compute
+        *      the routing table entry. This is where the great unified cache theory comes
+        *      in as and when someone impliments it
+        */
+
+       if ((brd = ip_chk_addr(iph->daddr)) == 0)
        {
                /*
                 *      Don't forward multicast or broadcast frames.
                 */
-       
+
                if(skb->pkt_type!=PACKET_HOST)
                {
                        kfree_skb(skb,FREE_WRITE);
                        return 0;
                }
-               
+
                /*
                 *      The packet is for another target. Forward the frame
                 */
-                
+
 #ifdef CONFIG_IP_FORWARD
                ip_forward(skb, dev, is_frag);
 #else
 /*             printk("Machine %lx tried to use us as a forwarder to %lx but we have forwarding disabled!\n",
                        iph->saddr,iph->daddr);*/
                ip_statistics.IpInAddrErrors++;
-#endif                 
+#endif
                /*
-                *      The forwarder is inefficient and copies the packet. We 
+                *      The forwarder is inefficient and copies the packet. We
                 *      free the original now.
                 */
-                
+
                kfree_skb(skb, FREE_WRITE);
                return(0);
-       }
+       }
 
-       /*
-        * Reassemble IP fragments. 
-        */
+       /*
+        * Reassemble IP fragments.
+        */
 
-       if(is_frag)
-       {
+       if(is_frag)
+       {
                /* Defragment. Obtain the complete packet if there is one */
                skb=ip_defrag(iph,skb,dev);
-               if(skb==NULL)
-                       return 0;
-               iph=skb->h.iph;
+               if(skb==NULL)
+                       return 0;
+               iph=skb->h.iph;
        }
 
        /*
-        *      Point into the IP datagram, just past the header. 
+        *      Point into the IP datagram, just past the header.
         */
 
        skb->ip_hdr = iph;
        skb->h.raw += iph->ihl*4;
-       
+
        /*
         *      skb->h.raw now points at the protocol beyond the IP header.
         */
-        
+
        hash = iph->protocol & (MAX_INET_PROTOS -1);
        for (ipprot = (struct inet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next)
        {
-               struct sk_buff *skb2;
+               struct sk_buff *skb2;
 
-                       if (ipprot->protocol != iph->protocol) 
-                               continue;
+               if (ipprot->protocol != iph->protocol)
+                       continue;
        /*
        *       See if we need to make a copy of it.  This will
-       *       only be set if more than one protocol wants it. 
+       *       only be set if more than one protocol wants it.
        *       and then not for the last one.
        *
-       *       This is an artifact of poor upper protocol design. 
+       *       This is an artifact of poor upper protocol design.
        *       Because the upper protocols damage the actual packet
        *       we must do copying. In actual fact it's even worse
        *       than this as TCP may hold on to the buffer.
        */
-               if (ipprot->copy) 
+               if (ipprot->copy)
                {
                        skb2 = skb_clone(skb, GFP_ATOMIC);
                        if(skb2==NULL)
                                continue;
-               } 
-               else 
+               }
+               else
                {
                        skb2 = skb;
                }
@@ -1607,7 +1607,7 @@ int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
         * ICMP reply messages get queued up for transmission...)
         */
 
-       if (!flag) 
+       if (!flag)
        {
                if (brd != IS_BROADCAST)
                        icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, dev);
@@ -1626,30 +1626,30 @@ int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
  * This routine also needs to put in the total length,
  * and compute the checksum
  */
-void ip_queue_xmit(struct sock *sk, struct device *dev, 
+
+void ip_queue_xmit(struct sock *sk, struct device *dev,
              struct sk_buff *skb, int free)
 {
-       struct iphdr *iph;
-       unsigned char *ptr;
+       struct iphdr *iph;
+       unsigned char *ptr;
 
-       /* Sanity check */
-       if (dev == NULL) 
-       {
+       /* Sanity check */
+       if (dev == NULL)
+       {
                printk("IP: ip_queue_xmit dev = NULL\n");
                return;
-       }
-  
-       IS_SKB(skb);
-       
-       /*
-        *      Do some book-keeping in the packet for later
-        */
-
-
-       skb->dev = dev;
-       skb->when = jiffies;
-  
+       }
+
+       IS_SKB(skb);
+
+       /*
+        *      Do some book-keeping in the packet for later
+        */
+
+
+       skb->dev = dev;
+       skb->when = jiffies;
+
        /*
         *      Find the IP header and set the length. This is bad
         *      but once we get the skb data handling code in the
@@ -1657,7 +1657,7 @@ void ip_queue_xmit(struct sock *sk, struct device *dev,
         *      set skb->ip_hdr to avoid this mess and the fixed
         *      header length problem
         */
-        
+
        ptr = skb->data;
        ptr += dev->hard_header_len;
        iph = (struct iphdr *)ptr;
@@ -1667,38 +1667,38 @@ void ip_queue_xmit(struct sock *sk, struct device *dev,
        /*
         *      No reassigning numbers to fragments...
         */
-        
+
        if(free!=2)
                iph->id      = htons(ip_id_count++);
        else
                free=1;
-               
+
        /* All buffers without an owner socket get freed */
-       if (sk == NULL) 
-               free = 1;
-       
-       skb->free = free;               
+       if (sk == NULL)
+               free = 1;
+
+       skb->free = free;
 
        /*
-        *      Do we need to fragment. Again this is inefficient. 
+        *      Do we need to fragment. Again this is inefficient.
         *      We need to somehow lock the original buffer and use
         *      bits of it.
         */
-        
-       if(skb->len > dev->mtu)
-       {
+
+       if(skb->len > dev->mtu + dev->hard_header_len)
+       {
                ip_fragment(sk,skb,dev,0);
                IS_SKB(skb);
-               kfree_skb(skb,FREE_WRITE);
-               return;
+               kfree_skb(skb,FREE_WRITE);
+               return;
        }
-  
-       /*
-        *      Add an IP checksum
-        */
-        
+
+       /*
+        *      Add an IP checksum
+        */
+
        ip_send_check(iph);
-       
+
        /*
         *      Print the frame when debugging
         */
@@ -1707,7 +1707,7 @@ void ip_queue_xmit(struct sock *sk, struct device *dev,
         *      More debugging. You cannot queue a packet already on a list
         *      Spot this and moan loudly.
         */
-       if (skb->next != NULL) 
+       if (skb->next != NULL)
        {
                printk("ip_queue_xmit: next != NULL\n");
                skb_unlink(skb);
@@ -1719,70 +1719,70 @@ void ip_queue_xmit(struct sock *sk, struct device *dev,
         *      in the TCP level since nobody elses uses it. BUT
         *      remember IPng might change all the rules.
         */
-        
-       if (!free) 
+
+       if (!free)
        {
                unsigned long flags;
                /* The socket now has more outstanding blocks */
-               
+
                sk->packets_out++;
-               
+
                /* Protect the list for a moment */
                save_flags(flags);
                cli();
-               
-               if (skb->link3 != NULL) 
+
+               if (skb->link3 != NULL)
                {
                        printk("ip.c: link3 != NULL\n");
                        skb->link3 = NULL;
                }
-               if (sk->send_head == NULL) 
+               if (sk->send_head == NULL)
                {
                        sk->send_tail = skb;
                        sk->send_head = skb;
                }
-               else 
+               else
                {
                        sk->send_tail->link3 = skb;
                        sk->send_tail = skb;
                }
                /* skb->link3 is NULL */
-               
+
                /* Interrupt restore */
                restore_flags(flags);
                /* Set the IP write timeout to the round trip time for the packet.
-                  If an acknowledge has not arrived by then we may wish to act */      
+                  If an acknowledge has not arrived by then we may wish to act */
                reset_timer(sk, TIME_WRITE, sk->rto);
-       } 
-       else 
+       }
+       else
                /* Remember who owns the buffer */
                skb->sk = sk;
 
        /*
-        *      If the indicated interface is up and running, send the packet. 
+        *      If the indicated interface is up and running, send the packet.
         */
        ip_statistics.IpOutRequests++;
-        
-       if (dev->flags & IFF_UP) 
+
+       if (dev->flags & IFF_UP)
        {
-               /* 
+               /*
                 *      If we have an owner use its priority setting,
                 *      otherwise use NORMAL
                 */
-               
-               if (sk != NULL) 
+
+               if (sk != NULL)
                {
                        dev_queue_xmit(skb, dev, sk->priority);
                }
-               else 
+               else
                {
                        dev_queue_xmit(skb, dev, SOPRI_NORMAL);
                }
-       } 
-       else 
+       }
+       else
        {
                ip_statistics.IpOutDiscards++;
-               if (free) 
+               if (free)
                        kfree_skb(skb, FREE_WRITE);
        }
 }
@@ -1795,27 +1795,27 @@ void ip_queue_xmit(struct sock *sk, struct device *dev,
 
 void ip_do_retransmit(struct sock *sk, int all)
 {
-       struct sk_buff * skb;
-       struct proto *prot;
-       struct device *dev;
-       int retransmits;
+       struct sk_buff * skb;
+       struct proto *prot;
+       struct device *dev;
+       int retransmits;
 
        prot = sk->prot;
        skb = sk->send_head;
        retransmits = sk->retransmits;
-       
-       while (skb != NULL) 
+
+       while (skb != NULL)
        {
                dev = skb->dev;
                IS_SKB(skb);
                skb->when = jiffies;
 
-               /* 
+               /*
                 * In general it's OK just to use the old packet.  However we
-                * need to use the current ack and window fields.  Urg and 
-                * urg_ptr could possibly stand to be updated as well, but we 
+                * need to use the current ack and window fields.  Urg and
+                * urg_ptr could possibly stand to be updated as well, but we
                 * don't keep the necessary data.  That shouldn't be a problem,
-                * if the other end is doing the right thing.  Since we're 
+                * if the other end is doing the right thing.  Since we're
                 * changing the packet, we have to issue a new IP identifier.
                 */
 
@@ -1837,11 +1837,11 @@ void ip_do_retransmit(struct sock *sk, int all)
                  tcp_send_check(th, sk->saddr, sk->daddr, size, sk);
                }
 
-               /* 
-                *      If the interface is (still) up and running, kick it. 
+               /*
+                *      If the interface is (still) up and running, kick it.
                 */
-               
-               if (dev->flags & IFF_UP) 
+
+               if (dev->flags & IFF_UP)
                {
                        /*
                         *      If the packet is still being sent by the device/protocol
@@ -1860,23 +1860,23 @@ void ip_do_retransmit(struct sock *sk, int all)
                                dev_queue_xmit(skb, dev, sk->priority);
                        }
                }
-               
+
                /*
                 *      Count retransmissions
                 */
                retransmits++;
                sk->prot->retransmits ++;
-               
+
                /*
                 *      Only one retransmit requested.
                 */
-               if (!all) 
+               if (!all)
                        break;
 
                /*
-                *      This should cut it off before we send too many packets. 
+                *      This should cut it off before we send too many packets.
                 */
-               if (sk->retransmits > sk->cong_window) 
+               if (sk->retransmits > sk->cong_window)
                        break;
                skb = skb->link3;
        }
@@ -1891,20 +1891,20 @@ void ip_do_retransmit(struct sock *sk, int all)
 
 void ip_retransmit(struct sock *sk, int all)
 {
-       ip_do_retransmit(sk, all);
-
-       /*
-        * Increase the timeout each time we retransmit.  Note that
-        * we do not increase the rtt estimate.  rto is initialized
-        * from rtt, but increases here.  Jacobson (SIGCOMM 88) suggests
-        * that doubling rto each time is the least we can get away with.
-        * In KA9Q, Karn uses this for the first few times, and then
-        * goes to quadratic.  netBSD doubles, but only goes up to *64,
-        * and clamps at 1 to 64 sec afterwards.  Note that 120 sec is
-        * defined in the protocol as the maximum possible RTT.  I guess
-        * we'll have to use something other than TCP to talk to the
-        * University of Mars.
-        */
+       ip_do_retransmit(sk, all);
+
+       /*
+        * Increase the timeout each time we retransmit.  Note that
+        * we do not increase the rtt estimate.  rto is initialized
+        * from rtt, but increases here.  Jacobson (SIGCOMM 88) suggests
+        * that doubling rto each time is the least we can get away with.
+        * In KA9Q, Karn uses this for the first few times, and then
+        * goes to quadratic.  netBSD doubles, but only goes up to *64,
+        * and clamps at 1 to 64 sec afterwards.  Note that 120 sec is
+        * defined in the protocol as the maximum possible RTT.  I guess
+        * we'll have to use something other than TCP to talk to the
+        * University of Mars.
+        */
 
        sk->retransmits++;
        sk->backoff++;
@@ -1920,19 +1920,19 @@ void ip_retransmit(struct sock *sk, int all)
  *
  *     Next release we will sort out IP_OPTIONS since for some people are kind of important.
  */
+
 int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
 {
        int val,err;
-       
-       if (optval == NULL) 
-               return(-EINVAL);
 
-       err=verify_area(VERIFY_READ, optval, sizeof(int));
-       if(err)
-               return err;
-       
-       val = get_fs_long((unsigned long *)optval);
+       if (optval == NULL)
+               return(-EINVAL);
+
+       err=verify_area(VERIFY_READ, optval, sizeof(int));
+       if(err)
+               return err;
+
+       val = get_fs_long((unsigned long *)optval);
 
        if(level!=SOL_IP)
                return -EOPNOTSUPP;
@@ -1963,14 +1963,14 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
  *     Get the options. Note for future reference. The GET of IP options gets the
  *     _received_ ones. The set sets the _sent_ ones.
  */
+
 int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen)
 {
        int val,err;
-       
+
        if(level!=SOL_IP)
                return -EOPNOTSUPP;
-               
+
        switch(optname)
        {
                case IP_TOS:
@@ -1984,22 +1984,22 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *op
        }
        err=verify_area(VERIFY_WRITE, optlen, sizeof(int));
        if(err)
-               return err;
-       put_fs_long(sizeof(int),(unsigned long *) optlen);
+               return err;
+       put_fs_long(sizeof(int),(unsigned long *) optlen);
 
-       err=verify_area(VERIFY_WRITE, optval, sizeof(int));
-       if(err)
-               return err;
-       put_fs_long(val,(unsigned long *)optval);
+       err=verify_area(VERIFY_WRITE, optval, sizeof(int));
+       if(err)
+               return err;
+       put_fs_long(val,(unsigned long *)optval);
 
-       return(0);
+       return(0);
 }
 
 /*
  *     IP protocol layer initialiser
  */
-static struct packet_type ip_packet_type = 
+
+static struct packet_type ip_packet_type =
 {
        0,      /* MUTTER ntohs(ETH_P_IP),*/
        0,              /* copy */
@@ -2007,12 +2007,12 @@ static struct packet_type ip_packet_type =
        NULL,
        NULL,
 };
+
+
 /*
  *     IP registers the packet type and then calls the subprotocol initialisers
  */
+
 void ip_init(void)
 {
        ip_packet_type.type=htons(ETH_P_IP);
index 9de5d3eec8f73ec9b94bca887a902e8b6b49b413..b52260628289ff6e563aeaeed68b6dbb47e7c3bf 100644 (file)
@@ -27,6 +27,7 @@
  *                     multiple datalinks <Greg Page>
  *     Revision 0.26:  Device drop kills IPX routes via it. (needed for modules)
  *     Revision 0.27:  Autobind <Mark Evans>
+ *     Revision 0.28:  Small fix for multiple local networks <Thomas Winder>
  *
  *                     
  *
@@ -216,7 +217,7 @@ ipxrtr_get_local_net(struct device *dev, unsigned short datalink)
                        restore_flags(flags);
                        return r;
                }
-               r=r->next;
+               r=r->nextlocal;
        }
        restore_flags(flags);
        return NULL;
@@ -1347,7 +1348,7 @@ void ipx_proto_init(struct net_proto *pro)
        if ((p8022_datalink = register_8022_client(val, ipx_rcv)) == NULL)
                printk("IPX: Unable to register with 802.2\n");
        
-       printk("Swansea University Computer Society IPX 0.26 BETA for NET3.016\n");
+       printk("Swansea University Computer Society IPX 0.28 BETA for NET3.016\n");
        
 }
 #endif
index 7f972b275324d7f7805198180f1fe12069823c92..467d7cda265440430c4810054dcdf2b8d3c02c4a 100644 (file)
@@ -22,6 +22,7 @@
  *             Alan Cox        :       Added NULL's for socket options.
  *             Alan Cox        :       Re-commented the code.
  *             Alan Cox        :       Use new kernel side addressing
+ *             Rob Janssen     :       Correct MTU usage.
  *
  *
  *             This program is free software; you can redistribute it and/or
@@ -162,7 +163,7 @@ static int packet_sendto(struct sock *sk, unsigned char *from, int len,
         *      raw protocol and you must do your own fragmentation at this level.
         */
         
-       if(len>dev->mtu)
+       if(len>dev->mtu+dev->hard_header_len)
                return -EMSGSIZE;
 
        skb = sk->prot->wmalloc(sk, len, 0, GFP_KERNEL);
index d8742cf2eebb4bd0bb3365c4b44c7ad0a60da7cb..04932324ab8a2ff31847e792a9968d937b432151 100644 (file)
@@ -710,7 +710,7 @@ void tcp_enqueue_partial(struct sk_buff * skb, struct sock * sk)
        if (tmp)
                del_timer(&sk->partial_timer);
        sk->partial = skb;
-       sk->partial_timer.next = sk->partial_timer.prev = NULL;
+       init_timer(&sk->partial_timer);
        sk->partial_timer.expires = HZ;
        sk->partial_timer.function = (void (*)(unsigned long)) tcp_send_partial;
        sk->partial_timer.data = (unsigned long) sk;
@@ -1987,7 +1987,7 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb,
        newsk->urg_data = 0;
        newsk->retransmits = 0;
        newsk->destroy = 0;
-       newsk->timer.next = newsk->timer.prev = NULL;
+       init_timer(&newsk->timer);
        newsk->timer.data = (unsigned long)newsk;
        newsk->timer.function = &net_timer;
        newsk->dummy_th.source = skb->h.th->dest;
index 1802cf545a468ee855d618a1a54038df18869ccb..ba1340185391940a2865ee33b0b7027252ca2b0f 100644 (file)
@@ -18,4 +18,4 @@ struct new_utsname system_utsname = {
 
 char *linux_banner = 
        "Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@"
-       LINUX_COMPILE_HOST ") " UTS_VERSION "\n";
+       LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n";