diff -u --recursive --new-file v1.1.37/linux/Makefile linux/Makefile --- v1.1.37/linux/Makefile Tue Aug 2 11:27:40 1994 +++ linux/Makefile Tue Aug 2 11:29:14 1994 @@ -1,6 +1,6 @@ VERSION = 1 PATCHLEVEL = 1 -SUBLEVEL = 37 +SUBLEVEL = 38 all: Version zImage @@ -153,6 +153,7 @@ @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 $@ $< diff -u --recursive --new-file v1.1.37/linux/config.in linux/config.in --- v1.1.37/linux/config.in Mon Jul 25 17:56:37 1994 +++ linux/config.in Tue Aug 2 12:47:07 1994 @@ -53,6 +53,7 @@ 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 diff -u --recursive --new-file v1.1.37/linux/drivers/FPU-emu/Makefile linux/drivers/FPU-emu/Makefile --- v1.1.37/linux/drivers/FPU-emu/Makefile Thu Jun 9 18:56:02 1994 +++ linux/drivers/FPU-emu/Makefile Mon Aug 1 08:19:12 1994 @@ -20,12 +20,13 @@ 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 diff -u --recursive --new-file v1.1.37/linux/drivers/FPU-emu/README linux/drivers/FPU-emu/README --- v1.1.37/linux/drivers/FPU-emu/README Thu Jun 9 18:56:02 1994 +++ linux/drivers/FPU-emu/README Mon Aug 1 08:19:12 1994 @@ -23,10 +23,10 @@ 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 @@ 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 @@ --Bill Metzenthen - June 1994 + August 1994 ----------------------- Internals of wm-FPU-emu ----------------------- @@ -96,7 +97,7 @@ ----------------------- 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 @@ ----------------------- 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) @@ -302,6 +307,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 ------------------------------- diff -u --recursive --new-file v1.1.37/linux/drivers/FPU-emu/div_Xsig.S linux/drivers/FPU-emu/div_Xsig.S --- v1.1.37/linux/drivers/FPU-emu/div_Xsig.S Thu Jan 1 02:00:00 1970 +++ linux/drivers/FPU-emu/div_Xsig.S Mon Aug 1 08:19:13 1994 @@ -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 diff -u --recursive --new-file v1.1.37/linux/drivers/FPU-emu/errors.c linux/drivers/FPU-emu/errors.c --- v1.1.37/linux/drivers/FPU-emu/errors.c Thu Jun 9 18:56:03 1994 +++ linux/drivers/FPU-emu/errors.c Mon Aug 1 08:19:13 1994 @@ -235,10 +235,8 @@ 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 @@ 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 @@ 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 @@ 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) diff -u --recursive --new-file v1.1.37/linux/drivers/FPU-emu/fpu_emu.h linux/drivers/FPU-emu/fpu_emu.h --- v1.1.37/linux/drivers/FPU-emu/fpu_emu.h Thu Jun 9 18:56:03 1994 +++ linux/drivers/FPU-emu/fpu_emu.h Mon Aug 1 08:19:13 1994 @@ -143,13 +143,6 @@ /*----- 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, diff -u --recursive --new-file v1.1.37/linux/drivers/FPU-emu/fpu_proto.h linux/drivers/FPU-emu/fpu_proto.h --- v1.1.37/linux/drivers/FPU-emu/fpu_proto.h Thu Jun 9 18:56:04 1994 +++ linux/drivers/FPU-emu/fpu_proto.h Mon Aug 1 08:19:13 1994 @@ -78,18 +78,18 @@ 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, diff -u --recursive --new-file v1.1.37/linux/drivers/FPU-emu/fpu_trig.c linux/drivers/FPU-emu/fpu_trig.c --- v1.1.37/linux/drivers/FPU-emu/fpu_trig.c Thu Jun 9 18:56:05 1994 +++ linux/drivers/FPU-emu/fpu_trig.c Mon Aug 1 08:19:13 1994 @@ -25,8 +25,9 @@ #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 @@ reg_move(&tmp, X); } +#ifdef FPTAN if ( even == FPTAN ) { if ( ((X->exp >= EXP_BIAS) || @@ -73,6 +75,7 @@ else even = 0; } +#endif FPTAN if ( (even && !(q & 1)) || (!even && (q & 1)) ) { @@ -234,43 +237,27 @@ { 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 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 @@ 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; - - 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); + poly_atan(st0_ptr, st1_ptr, st1_ptr); - 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 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 -u --recursive --new-file v1.1.37/linux/drivers/FPU-emu/mul_Xsig.S linux/drivers/FPU-emu/mul_Xsig.S --- v1.1.37/linux/drivers/FPU-emu/mul_Xsig.S Thu Jan 1 02:00:00 1970 +++ linux/drivers/FPU-emu/mul_Xsig.S Mon Aug 1 08:19:13 1994 @@ -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 -u --recursive --new-file v1.1.37/linux/drivers/FPU-emu/poly.h linux/drivers/FPU-emu/poly.h --- v1.1.37/linux/drivers/FPU-emu/poly.h Thu Jan 1 02:00:00 1970 +++ linux/drivers/FPU-emu/poly.h Mon Aug 1 08:19:14 1994 @@ -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 diff -u --recursive --new-file v1.1.37/linux/drivers/FPU-emu/poly_2xm1.c linux/drivers/FPU-emu/poly_2xm1.c --- v1.1.37/linux/drivers/FPU-emu/poly_2xm1.c Wed Dec 1 14:44:16 1993 +++ linux/drivers/FPU-emu/poly_2xm1.c Mon Aug 1 08:19:14 1994 @@ -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 | | | @@ -13,43 +13,54 @@ #include "exception.h" #include "reg_constant.h" #include "fpu_emu.h" +#include "control_w.h" +#include "poly.h" +#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); -#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 }, - }; +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 @@ } #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; diff -u --recursive --new-file v1.1.37/linux/drivers/FPU-emu/poly_atan.c linux/drivers/FPU-emu/poly_atan.c --- v1.1.37/linux/drivers/FPU-emu/poly_atan.c Thu Jun 9 18:56:06 1994 +++ linux/drivers/FPU-emu/poly_atan.c Mon Aug 1 08:19:14 1994 @@ -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 | | | @@ -13,184 +13,185 @@ #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 */ + shr_Xsig(&Numer, -1-exponent); + negate_Xsig(&Numer); + + shr_Xsig(&Denom, -exponent); + Denom.msw |= 0x80000000; + + div_Xsig(&Numer, &Denom, &argSignif); - arg->exp = numerator.exp; - reg_u_div(&numerator, &denom, arg, FULL_PRECISION); - - exponent = arg->exp - EXP_BIAS; + 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); - - /* 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, - (unsigned short (*)[4])oddplterms, HIPOWERop-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, - (unsigned short (*)[4])oddnegterms, HIPOWERon-1); - - /* Subtract the mantissas */ - significand(&pos_poly) -= significand(&neg_poly); - - reg_move(&pos_poly, &odd_poly); - poly_add_1(&odd_poly); - - /* will be a valid positive nr with expon = 0 */ - *(short *)&(even_poly.sign) = 0; - - mul64(&significand(&argSq), &denomterm, &significand(&even_poly)); - - poly_add_1(&even_poly); + argSqSq.lsw = argSq.lsw; argSqSq.midw = argSq.midw; argSqSq.msw = argSq.msw; + mul_Xsig_Xsig(&argSqSq, &argSqSq); - reg_div(&odd_poly, &even_poly, &ratio, FULL_PRECISION); + accumulatore.lsw = argSq.lsw; + XSIG_LL(accumulatore) = XSIG_LL(argSq); - reg_u_mul(&ratio, arg, arg, FULL_PRECISION); + shr_Xsig(&argSq, 2*(-1-exponent-1)); + shr_Xsig(&argSqSq, 4*(-1-exponent-1)); - if ( recursions ) - reg_sub(&CONST_PI4, arg, arg, FULL_PRECISION); - -} + /* Now have argSq etc with binary point at the left + .1xxxxxxxx */ + /* Do the basic fixed point polynomial evaluation */ + 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); + + mul64_Xsig(&accumulatore, &denomterm); + shr_Xsig(&accumulatore, 1 + 2*(-1-exponent)); + accumulatore.msw |= 0x80000000; + + div_Xsig(&accumulator, &accumulatore, &accumulator); + + mul_Xsig_Xsig(&accumulator, &argSignif); + mul_Xsig_Xsig(&accumulator, &argSq); + + shr_Xsig(&accumulator, 3); + negate_Xsig(&accumulator); + add_Xsig_Xsig(&accumulator, &argSignif); -/* 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; } Only in v1.1.37/linux/drivers/FPU-emu: poly_div.S diff -u --recursive --new-file v1.1.37/linux/drivers/FPU-emu/poly_l2.c linux/drivers/FPU-emu/poly_l2.c --- v1.1.37/linux/drivers/FPU-emu/poly_l2.c Wed Dec 1 14:44:16 1993 +++ linux/drivers/FPU-emu/poly_l2.c Mon Aug 1 08:19:14 1994 @@ -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 | | | @@ -15,275 +15,241 @@ #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 + { + /* 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 { - accum.sign = 0; /* set the sign to positive */ - num.sigl = arg->sigl; /* copy the mantissa */ - num.sigh = arg->sigh; + log2_kernel(&x, &accumulator, &expon); } + 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); - /* 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); - - 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 ) + { + reg_move(&CONST_Z, y); + } + else { - /* 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); + 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; + } - 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); - } + return; +} - result->sign = sign; - return; - } + +/*--- 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; - mul64(&significand(&accum), - &significand(&Xx), &significand(&accum)); - significand(&accum) += significand(&Xx); + sign = arg->sign; - if ( Xx.sigh > accum.sigh ) + if ( arg->exp < EXP_BIAS ) { - /* There was an overflow */ + log2_kernel(arg, &accumulator, &exponent); - poly_div2(&significand(&accum)); - accum.sigh |= 0x80000000; - accum.exp++; - } + yaccum.lsw = 0; + XSIG_LL(yaccum) = significand(y); + mul_Xsig_Xsig(&accumulator, &yaccum); - /* 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)) ) - { - /* signs are different */ + exponent += round_Xsig(&accumulator); - accum.sign = !accum.sign; + 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; - /* 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--; - } + return 0; } - - shift = exponent >= 0 ? exponent : -exponent ; - bits = 0; - if ( shift ) + 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; -} + +#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; -/*--- poly_l2p1() -----------------------------------------------------------+ + +/*--- 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 ) + { + div_Xsig(&Numer, &Denom, &argSignif); + exponent ++; + } + else { - /* The argument is large */ - poly_l2(&arg_pl1, result); return 0; + /* 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)); +#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 - mul64(&significand(&poly_arg), &significand(&poly_arg), &Xsq); - poly_div16(&Xsq); + 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; } Only in v1.1.37/linux/drivers/FPU-emu: poly_mul64.S diff -u --recursive --new-file v1.1.37/linux/drivers/FPU-emu/poly_sin.c linux/drivers/FPU-emu/poly_sin.c --- v1.1.37/linux/drivers/FPU-emu/poly_sin.c Tue Jan 25 10:54:23 1994 +++ linux/drivers/FPU-emu/poly_sin.c Mon Aug 1 08:19:15 1994 @@ -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, | @@ -15,136 +16,393 @@ #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() -----------------------------------------------------------+ | | +---------------------------------------------------------------------------*/ 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 ) - { - /* shift the argument right by the required places */ - if ( shrx(&(fixed_arg.sigl), -1-exponent) >= 0x80000000U ) - significand(&fixed_arg)++; /* round up */ - } - - 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 ) - { - /* 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 */ - ) - { -#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); + + exponent = arg->exp - EXP_BIAS; + + accumulator.lsw = accumulator.midw = accumulator.msw = 0; + + if ( (exponent < -1) || ((exponent == -1) && (arg->sigh <= 0xb00d6f54)) ) + { + /* 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); -#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 + /* will be a valid positive nr with expon = -1 */ + *(short *)&(result->sign) = 0; + result->exp = EXP_BIAS - 1; + } + } + else + { + fixed_arg = significand(arg); + + if ( exponent == 0 ) + { + /* 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; + + 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); - result->sigl = 0; /* Truncate the result to 1.00 */ + 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 } diff -u --recursive --new-file v1.1.37/linux/drivers/FPU-emu/poly_tan.c linux/drivers/FPU-emu/poly_tan.c --- v1.1.37/linux/drivers/FPU-emu/poly_tan.c Wed Dec 1 14:44:16 1993 +++ linux/drivers/FPU-emu/poly_tan.c Mon Aug 1 08:19:15 1994 @@ -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 | | | @@ -14,48 +14,51 @@ #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 @@ { 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); } - - mul64(&arg_signif, &arg_signif, &significand(&argSq)); - mul64(&significand(&argSq), &significand(&argSq), &argSqSq); + 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 */ + } + } - /* will be a valid positive nr with expon = 0 */ - *(short *)&(pos_poly.sign) = 0; - pos_poly.exp = EXP_BIAS; + 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); + + /* 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); - /* Do the basic fixed point polynomial evaluation */ - polynomial(&pos_poly.sigl, (unsigned *)&argSqSq, oddplterms, HIPOWERop-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 */ - /* will be a valid positive nr with expon = 0 */ - *(short *)&(neg_poly.sign) = 0; - neg_poly.exp = EXP_BIAS; + /* 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); + } - /* 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)); + /* 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)); - /* Subtract the mantissas */ - significand(&pos_poly) -= significand(&neg_poly); + /* tan(arg) = arg + accum */ + add_two_Xsig(&accum, &argSignif, &exponent); - /* Convert to 64 bit signed-compatible */ - pos_poly.exp -= 1; + if ( invert ) + { + /* 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 */ - 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); - - /* 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; - - reg_move(&neg_poly, &even_poly); - normalize(&even_poly); - - 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); + 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; + } - /* 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); } + /* Transfer the result */ + round_Xsig(&accum); + *(short *)&(result->sign) = 0; + significand(result) = XSIG_LL(accum); + result->exp = EXP_BIAS + exponent; } diff -u --recursive --new-file v1.1.37/linux/drivers/FPU-emu/polynom_Xsig.S linux/drivers/FPU-emu/polynom_Xsig.S --- v1.1.37/linux/drivers/FPU-emu/polynom_Xsig.S Thu Jan 1 02:00:00 1970 +++ linux/drivers/FPU-emu/polynom_Xsig.S Mon Aug 1 08:19:15 1994 @@ -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 Only in v1.1.37/linux/drivers/FPU-emu: polynomial.S diff -u --recursive --new-file v1.1.37/linux/drivers/FPU-emu/reg_ld_str.c linux/drivers/FPU-emu/reg_ld_str.c --- v1.1.37/linux/drivers/FPU-emu/reg_ld_str.c Thu Jun 9 18:56:08 1994 +++ linux/drivers/FPU-emu/reg_ld_str.c Mon Aug 1 08:19:15 1994 @@ -825,7 +825,7 @@ #ifdef PARANOID else { - EXCEPTION(EX_INTERNAL|0x106); + EXCEPTION(EX_INTERNAL|0x163); return 0; } #endif @@ -1389,16 +1389,16 @@ { 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 -u --recursive --new-file v1.1.37/linux/drivers/FPU-emu/round_Xsig.S linux/drivers/FPU-emu/round_Xsig.S --- v1.1.37/linux/drivers/FPU-emu/round_Xsig.S Thu Jan 1 02:00:00 1970 +++ linux/drivers/FPU-emu/round_Xsig.S Mon Aug 1 08:19:15 1994 @@ -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 -u --recursive --new-file v1.1.37/linux/drivers/FPU-emu/shr_Xsig.S linux/drivers/FPU-emu/shr_Xsig.S --- v1.1.37/linux/drivers/FPU-emu/shr_Xsig.S Thu Jan 1 02:00:00 1970 +++ linux/drivers/FPU-emu/shr_Xsig.S Mon Aug 1 08:19:16 1994 @@ -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 diff -u --recursive --new-file v1.1.37/linux/drivers/FPU-emu/version.h linux/drivers/FPU-emu/version.h --- v1.1.37/linux/drivers/FPU-emu/version.h Thu Jun 9 18:56:08 1994 +++ linux/drivers/FPU-emu/version.h Mon Aug 1 08:19:16 1994 @@ -9,4 +9,4 @@ | | +---------------------------------------------------------------------------*/ -#define FPU_VERSION "wm-FPU-emu version 1.12" +#define FPU_VERSION "wm-FPU-emu version 1.20" diff -u --recursive --new-file v1.1.37/linux/drivers/block/README.sbpcd linux/drivers/block/README.sbpcd --- v1.1.37/linux/drivers/block/README.sbpcd Sat Jul 16 19:51:20 1994 +++ linux/drivers/block/README.sbpcd Mon Aug 1 10:54:37 1994 @@ -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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 diff -u --recursive --new-file v1.1.37/linux/drivers/block/cdu31a.c linux/drivers/block/cdu31a.c --- v1.1.37/linux/drivers/block/cdu31a.c Fri Jul 22 16:40:59 1994 +++ linux/drivers/block/cdu31a.c Mon Aug 1 11:49:49 1994 @@ -1773,7 +1773,7 @@ if (filp->f_mode & 2) - return -EACCES; + return -EROFS; if (!sony_spun_up) { diff -u --recursive --new-file v1.1.37/linux/drivers/block/floppy.c linux/drivers/block/floppy.c --- v1.1.37/linux/drivers/block/floppy.c Tue Jul 19 10:18:54 1994 +++ linux/drivers/block/floppy.c Mon Aug 1 11:48:46 1994 @@ -1417,7 +1417,7 @@ if (filp->f_mode & 2) { if (1 & (read_only >> drive)) { floppy_release(inode, filp); - return -EACCES; + return -EROFS; } } } diff -u --recursive --new-file v1.1.37/linux/drivers/block/mcd.c linux/drivers/block/mcd.c --- v1.1.37/linux/drivers/block/mcd.c Fri Jul 22 16:41:00 1994 +++ linux/drivers/block/mcd.c Mon Aug 1 11:49:21 1994 @@ -1031,7 +1031,7 @@ return -ENXIO; /* no hardware */ if (fp->f_mode & 2) /* write access? */ - return -EACCES; + return -EROFS; if (!mcd_open_count && mcd_state == MCD_S_IDLE) { diff -u --recursive --new-file v1.1.37/linux/drivers/block/sbpcd.c linux/drivers/block/sbpcd.c --- v1.1.37/linux/drivers/block/sbpcd.c Sat Jul 23 17:38:36 1994 +++ linux/drivers/block/sbpcd.c Mon Aug 1 11:49:35 1994 @@ -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. @@ -119,6 +119,12 @@ * * 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 * @@ -195,7 +201,7 @@ #include "blk.h" -#define VERSION "2.4 Eberhard Moenkeberg " +#define VERSION "2.5 Eberhard Moenkeberg " #define SBPCD_DEBUG @@ -211,6 +217,8 @@ /* * 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 @@ 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 @@ 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 @@ do i=yy_LockDoor(0); while (i!=0); +#ifdef EJECT + if (new_drive) yy_SpinDown(); +#endif } } } @@ -3559,12 +3575,22 @@ 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); diff -u --recursive --new-file v1.1.37/linux/drivers/net/Space.c linux/drivers/net/Space.c --- v1.1.37/linux/drivers/net/Space.c Thu Jul 21 08:18:08 1994 +++ linux/drivers/net/Space.c Mon Aug 1 10:16:49 1994 @@ -58,7 +58,7 @@ 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 @@ 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 = ðer_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. */ @@ -185,17 +153,6 @@ # undef NEXT_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 diff -u --recursive --new-file v1.1.37/linux/drivers/net/depca.c linux/drivers/net/depca.c --- v1.1.37/linux/drivers/net/depca.c Mon Jun 27 16:47:00 1994 +++ linux/drivers/net/depca.c Mon Aug 1 10:15:51 1994 @@ -114,6 +114,29 @@ 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: ------ @@ -125,7 +148,7 @@ 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. @@ -136,11 +159,12 @@ 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 #include @@ -159,12 +183,18 @@ #include #include #include + +#ifdef MODULE +#include +#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 @@ #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 @@ 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 @@ } 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 @@ /* 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 @@ 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 @@ printk("nicsr: 0x%02x\n",inb(DEPCA_NICSR)); } +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + return 0; /* Always succeed */ } @@ -1091,6 +1143,10 @@ irq2dev_map[dev->irq] = 0; +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + return 0; } @@ -1229,6 +1285,7 @@ #endif /* HAVE_MULTICAST */ +#ifndef MODULE /* ** ISA bus I/O device probe */ @@ -1242,11 +1299,6 @@ *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 @@ ioaddr+=0x1000; /* get the first slot address */ for (status = -ENODEV, i=1; i 0) { /* only gets here in autoprobe */ dev = alloc_device(dev, ioaddr); @@ -1340,6 +1388,7 @@ return dev; } +#endif /* MODULE */ /* ** Look for a particular board name in the on-board Remote Diagnostics @@ -1473,12 +1522,38 @@ 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 */ + /* * 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: */ - - - diff -u --recursive --new-file v1.1.37/linux/drivers/net/net_init.c linux/drivers/net/net_init.c --- v1.1.37/linux/drivers/net/net_init.c Tue Jun 21 14:16:21 1994 +++ linux/drivers/net/net_init.c Mon Aug 1 10:16:49 1994 @@ -194,26 +194,27 @@ { 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 */ diff -u --recursive --new-file v1.1.37/linux/drivers/scsi/53c7,8xx.c linux/drivers/scsi/53c7,8xx.c --- v1.1.37/linux/drivers/scsi/53c7,8xx.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/scsi/53c7,8xx.c Tue Aug 2 11:29:15 1994 @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#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<= 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,,,\n" + " and 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 - clear breakpoint\n" +"bl - list breakpoints\n" +"bs - set breakpoint\n" +"g - start\n" +"h - halt\n" +"? - this message\n" +"i - info\n" +"mp - print memory\n" +"ms - store memory\n" +"rp - print register\n" +"rs - 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 -u --recursive --new-file v1.1.37/linux/drivers/scsi/53c7,8xx.h linux/drivers/scsi/53c7,8xx.h --- v1.1.37/linux/drivers/scsi/53c7,8xx.h Thu Jan 1 02:00:00 1970 +++ linux/drivers/scsi/53c7,8xx.h Tue Aug 2 11:29:16 1994 @@ -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 +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 -u --recursive --new-file v1.1.37/linux/drivers/scsi/53c7,8xx.scr linux/drivers/scsi/53c7,8xx.scr --- v1.1.37/linux/drivers/scsi/53c7,8xx.scr Thu Jan 1 02:00:00 1970 +++ linux/drivers/scsi/53c7,8xx.scr Tue Aug 2 11:29:16 1994 @@ -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 -u --recursive --new-file v1.1.37/linux/drivers/scsi/53c8xx_d.h linux/drivers/scsi/53c8xx_d.h --- v1.1.37/linux/drivers/scsi/53c8xx_d.h Thu Jan 1 02:00:00 1970 +++ linux/drivers/scsi/53c8xx_d.h Tue Aug 2 11:29:17 1994 @@ -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 -u --recursive --new-file v1.1.37/linux/drivers/scsi/53c8xx_u.h linux/drivers/scsi/53c8xx_u.h --- v1.1.37/linux/drivers/scsi/53c8xx_u.h Thu Jan 1 02:00:00 1970 +++ linux/drivers/scsi/53c8xx_u.h Tue Aug 2 11:29:17 1994 @@ -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 diff -u --recursive --new-file v1.1.37/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile --- v1.1.37/linux/drivers/scsi/Makefile Fri May 27 10:49:06 1994 +++ linux/drivers/scsi/Makefile Tue Aug 2 11:29:18 1994 @@ -1,4 +1,4 @@ -# + # Makefile for kernel/blk_drv/scsi # # Note! Dependencies are done automagically by 'make dep', which also @@ -24,8 +24,8 @@ 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_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 @@ -130,6 +135,17 @@ 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 diff -u --recursive --new-file v1.1.37/linux/drivers/scsi/NCR5380.c linux/drivers/scsi/NCR5380.c --- v1.1.37/linux/drivers/scsi/NCR5380.c Thu Jul 14 00:48:21 1994 +++ linux/drivers/scsi/NCR5380.c Tue Aug 2 11:29:18 1994 @@ -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 @@ 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_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 @@ #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 @@ 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 @@ 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 @@ #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 @@ hostdata->dmalen = 0; (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG); - - while (NCR5380_read(BUS_AND_STATUS_REG) & - BASR_ACK)); +#if NCR_TIMEOUT + { + unsigned long timeout = jiffies + NCR_TIMEOUT; + + while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK + && jiffies < timeout) + ; + if (jiffies >= timeout) + printk("scsi: timeout at %d\n", __LINE__); + } +#else /* NCR_TIMEOUT */ + while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK); +#endif NCR5380_write(MODE_REG, MR_BASE); NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); @@ -1086,11 +1169,13 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ * 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 @@ 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 @@ initialize_SCp(cmd); + return 0; } @@ -1404,10 +1540,23 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ &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 @@ #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 @@ 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 @@ #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 @@ 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 @@ 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 @@ " 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 @@ */ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ printk(" basr 0x%X, sr 0x%X\n", NCR5380_read(BUS_AND_STATUS_REG), NCR5380_read(STATUS_REG)); #endif + +#if 0 +/* + * Case 1 : If the command is the currently executing command, + * we'll set the aborted flag and return control so that + * information transfer routine can exit cleanly. + */ + + if (hostdata->connected == cmd) { +#if (NDEBUG & NDEBUG_ABORT) + printk("scsi%d : aborting connected command\n", instance->host_no); +#endif + hostdata->aborted = 1; +/* + * We should perform BSY checking, and make sure we haven't slipped + * into BUS FREE. + */ + + NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ATN); +/* + * Since we can't change phases until we've completed the current + * handshake, we have to source or sink a byte of data if the current + * phase is not MSGOUT. + */ + +/* + * Return control to the executing NCR drive so we can clear the + * aborted flag and get back into our main loop. + */ + + return 0; + } +#endif + /* - * Case 1 : If the command hasn't been issued yet, we simply remove it + * 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 @@ } /* - * 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 @@ } /* - * 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 @@ } /* - * 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 @@ 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; } + diff -u --recursive --new-file v1.1.37/linux/drivers/scsi/NCR5380.h linux/drivers/scsi/NCR5380.h --- v1.1.37/linux/drivers/scsi/NCR5380.h Fri May 27 10:49:06 1994 +++ linux/drivers/scsi/NCR5380.h Tue Aug 2 11:29:19 1994 @@ -7,7 +7,7 @@ * drew@colorado.edu * +1 (303) 666-5836 * - * DISTRIBUTION RELEASE 4 + * DISTRIBUTION RELEASE 6 * * For more information, please consult * @@ -23,23 +23,15 @@ /* * $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 @@ -164,16 +158,43 @@ /* 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 @@ -215,11 +236,15 @@ #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 @@ 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 @@ #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); diff -u --recursive --new-file v1.1.37/linux/drivers/scsi/constants.c linux/drivers/scsi/constants.c --- v1.1.37/linux/drivers/scsi/constants.c Fri May 27 10:49:09 1994 +++ linux/drivers/scsi/constants.c Tue Aug 2 11:29:19 1994 @@ -7,18 +7,22 @@ #include "../block/blk.h" #include #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 @@ 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 @@ /* 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 @@ 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 @@ /* 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 @@ -526,4 +545,13 @@ printk("%02x ", msg[0]); #endif return len; +} + +void print_Scsi_Cmnd (Scsi_Cmnd *cmd) { + printk("scsi%d : destination target %d, lun %d\n", + cmd->host->host_no, + cmd->target, + cmd->lun); + printk(" command = "); + print_command (cmd->cmnd); } diff -u --recursive --new-file v1.1.37/linux/drivers/scsi/fdomain.c linux/drivers/scsi/fdomain.c --- v1.1.37/linux/drivers/scsi/fdomain.c Thu Jul 14 00:48:22 1994 +++ linux/drivers/scsi/fdomain.c Mon Aug 1 08:05:56 1994 @@ -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 @@ -25,24 +25,22 @@ 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 @@ -118,10 +116,14 @@ 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. @@ -167,7 +169,7 @@ #include #include -#define VERSION "$Revision: 5.16 $" +#define VERSION "$Revision: 5.18 $" /* START OF USER DEFINABLE OPTIONS */ @@ -257,6 +259,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ #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 }; diff -u --recursive --new-file v1.1.37/linux/drivers/scsi/fdomain.h linux/drivers/scsi/fdomain.h --- v1.1.37/linux/drivers/scsi/fdomain.h Thu Jul 14 00:48:23 1994 +++ linux/drivers/scsi/fdomain.h Mon Aug 1 08:05:56 1994 @@ -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 @@ -27,29 +27,28 @@ 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 * ); -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} +#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 diff -u --recursive --new-file v1.1.37/linux/drivers/scsi/g_NCR5380.h linux/drivers/scsi/g_NCR5380.h --- v1.1.37/linux/drivers/scsi/g_NCR5380.h Thu Jul 14 00:48:23 1994 +++ linux/drivers/scsi/g_NCR5380.h Tue Aug 2 11:29:19 1994 @@ -79,6 +79,10 @@ #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 */ + diff -u --recursive --new-file v1.1.37/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c --- v1.1.37/linux/drivers/scsi/hosts.c Tue Aug 2 11:27:41 1994 +++ linux/drivers/scsi/hosts.c Tue Aug 2 11:29:20 1994 @@ -67,6 +67,10 @@ #include "t128.h" #endif +#ifdef CONFIG_SCSI_NCR53C7xx +#include "53c7,8xx.h" +#endif + #ifdef CONFIG_SCSI_ULTRASTOR #include "ultrastor.h" #endif @@ -145,6 +149,9 @@ #endif #ifdef CONFIG_SCSI_T128 TRANTOR_T128, +#endif +#ifdef CONFIG_SCSI_NCR53C7xx + NCR53c7xx, #endif #ifdef CONFIG_SCSI_7000FASST WD7000, diff -u --recursive --new-file v1.1.37/linux/drivers/scsi/pas16.c linux/drivers/scsi/pas16.c --- v1.1.37/linux/drivers/scsi/pas16.c Thu Jul 14 00:48:23 1994 +++ linux/drivers/scsi/pas16.c Sat Jul 30 09:51:52 1994 @@ -67,17 +67,30 @@ * * 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 @@ -174,12 +187,14 @@ * * 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 @@ /* 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 ); + + 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 ); + /* Set up the drive parameters and enable 5380 interrupts */ + outb( 0x6d, io_port + SYS_CONFIG_4 ); + } } @@ -304,7 +332,7 @@ { 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 @@ 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 @@ 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) diff -u --recursive --new-file v1.1.37/linux/drivers/scsi/pas16.h linux/drivers/scsi/pas16.h --- v1.1.37/linux/drivers/scsi/pas16.h Thu Jul 14 00:48:23 1994 +++ linux/drivers/scsi/pas16.h Sat Jul 30 09:51:52 1994 @@ -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 -u --recursive --new-file v1.1.37/linux/drivers/scsi/script_asm.pl linux/drivers/scsi/script_asm.pl --- v1.1.37/linux/drivers/scsi/script_asm.pl Thu Jan 1 02:00:00 1970 +++ linux/drivers/scsi/script_asm.pl Tue Aug 2 11:29:20 1994 @@ -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." $1 data + ".$not."ATN $1 data +" if ($other eq ''); + die "$0 : syntax error in line $lineno : $_ + Illegal use of $conjunction. Valid syntaxes are + NOT |ATN OR data + |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 . +"; + } + } + + 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 \", not \"$2\" +"; + } + } elsif ($conditional !~ /^\s*$/) { + die "$0 : syntax error in line $lineno : $_ + expected end of line" . (($need_data) ? " or \"AND MASK \"" : "") . " + 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 () { + $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 = +"; + } + } + } 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 , 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 +"; + } +# 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 , , +" + } + } 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 + + +"; + } else { + die +"$0 : syntax error in line $lineno : $_ + expected +"; + } + } + if ($rest =~ /^($register)\s*(.*)$/i) { + $dst_reg = "\U$1\E"; + $rest = $2; + } else { + die +"$0 : syntax error in $lineno : $_ + expected , 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) ,
, WHEN + (target) ,
, WITH + MEMORY , , + TO +"; + } +# 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
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 , 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 , +"; + } 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; diff -u --recursive --new-file v1.1.37/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c --- v1.1.37/linux/drivers/scsi/scsi.c Tue Aug 2 11:27:41 1994 +++ linux/drivers/scsi/scsi.c Tue Aug 2 11:29:21 1994 @@ -67,9 +67,12 @@ 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 @@ * 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 @@ * 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 @@ struct Scsi_Device_Template * sdtpnt; Scsi_Cmnd SCmd; - ++in_scan; + ++in_scan_scsis; lun = 0; type = -1; SCmd.next = NULL; @@ -492,7 +496,7 @@ /* 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 @@ 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 @@ -865,6 +871,8 @@ time we check for the host being not busy, and the time we mark it busy ourselves. */ + + SCpnt->pid = scsi_pid++; while (1==1){ cli(); diff -u --recursive --new-file v1.1.37/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h --- v1.1.37/linux/drivers/scsi/scsi.h Tue Aug 2 11:27:42 1994 +++ linux/drivers/scsi/scsi.h Tue Aug 2 11:29:21 1994 @@ -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 @@ #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 @@ #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 @@ 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 @@ 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 @@ 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; /* diff -u --recursive --new-file v1.1.37/linux/drivers/scsi/scsi_ioctl.h linux/drivers/scsi/scsi_ioctl.h --- v1.1.37/linux/drivers/scsi/scsi_ioctl.h Tue Apr 19 10:52:48 1994 +++ linux/drivers/scsi/scsi_ioctl.h Tue Aug 2 11:29:21 1994 @@ -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 -u --recursive --new-file v1.1.37/linux/drivers/scsi/scsicam.c linux/drivers/scsi/scsicam.c --- v1.1.37/linux/drivers/scsi/scsicam.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/scsi/scsicam.c Tue Aug 2 11:29:21 1994 @@ -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 +#include +#include +#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); +} diff -u --recursive --new-file v1.1.37/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c --- v1.1.37/linux/drivers/scsi/sd.c Tue Aug 2 11:27:42 1994 +++ linux/drivers/scsi/sd.c Tue Aug 2 11:29:21 1994 @@ -161,7 +161,7 @@ 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 @@ 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 @@ 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 @@ } #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 @@ }; }; #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 @@ 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 @@ 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 @@ 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 { diff -u --recursive --new-file v1.1.37/linux/drivers/scsi/seagate.c linux/drivers/scsi/seagate.c --- v1.1.37/linux/drivers/scsi/seagate.c Tue Aug 2 11:27:42 1994 +++ linux/drivers/scsi/seagate.c Tue Aug 2 11:29:22 1994 @@ -672,8 +672,8 @@ */ 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) diff -u --recursive --new-file v1.1.37/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c --- v1.1.37/linux/drivers/scsi/sr.c Tue Aug 2 11:27:43 1994 +++ linux/drivers/scsi/sr.c Mon Aug 1 11:50:23 1994 @@ -277,7 +277,7 @@ !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); diff -u --recursive --new-file v1.1.37/linux/drivers/scsi/t128.c linux/drivers/scsi/t128.c --- v1.1.37/linux/drivers/scsi/t128.c Thu Jul 14 00:48:25 1994 +++ linux/drivers/scsi/t128.c Tue Aug 2 11:29:22 1994 @@ -231,7 +231,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 diff -u --recursive --new-file v1.1.37/linux/fs/dcache.c linux/fs/dcache.c --- v1.1.37/linux/fs/dcache.c Tue Aug 2 11:27:43 1994 +++ linux/fs/dcache.c Tue Aug 2 12:24:09 1994 @@ -169,7 +169,7 @@ 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 @@ 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) diff -u --recursive --new-file v1.1.37/linux/fs/exec.c linux/fs/exec.c --- v1.1.37/linux/fs/exec.c Tue Jul 26 21:39:45 1994 +++ linux/fs/exec.c Tue Aug 2 11:43:00 1994 @@ -847,7 +847,7 @@ 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 @@ 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) { Only in v1.1.37/linux/fs/ext2: dcache.c diff -u --recursive --new-file v1.1.37/linux/fs/ext2/namei.c linux/fs/ext2/namei.c --- v1.1.37/linux/fs/ext2/namei.c Tue Aug 2 11:27:44 1994 +++ linux/fs/ext2/namei.c Tue Aug 2 13:56:05 1994 @@ -181,15 +181,28 @@ 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; diff -u --recursive --new-file v1.1.37/linux/fs/locks.c linux/fs/locks.c --- v1.1.37/linux/fs/locks.c Fri Jul 8 13:26:41 1994 +++ linux/fs/locks.c Tue Aug 2 11:29:22 1994 @@ -375,8 +375,22 @@ } 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; } diff -u --recursive --new-file v1.1.37/linux/fs/proc/link.c linux/fs/proc/link.c --- v1.1.37/linux/fs/proc/link.c Tue Jul 26 21:39:46 1994 +++ linux/fs/proc/link.c Tue Aug 2 11:41:50 1994 @@ -74,7 +74,7 @@ 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; } diff -u --recursive --new-file v1.1.37/linux/fs/super.c linux/fs/super.c --- v1.1.37/linux/fs/super.c Tue Jul 19 10:19:13 1994 +++ linux/fs/super.c Mon Aug 1 10:53:36 1994 @@ -648,6 +648,9 @@ 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 -u --recursive --new-file v1.1.37/linux/include/linux/bios32.h linux/include/linux/bios32.h --- v1.1.37/linux/include/linux/bios32.h Thu Jan 1 02:00:00 1970 +++ linux/include/linux/bios32.h Tue Aug 2 11:29:22 1994 @@ -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 */ diff -u --recursive --new-file v1.1.37/linux/include/linux/fs.h linux/include/linux/fs.h --- v1.1.37/linux/include/linux/fs.h Tue Aug 2 11:27:46 1994 +++ linux/include/linux/fs.h Tue Aug 2 12:24:32 1994 @@ -471,7 +471,7 @@ 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) { diff -u --recursive --new-file v1.1.37/linux/include/linux/kernel.h linux/include/linux/kernel.h --- v1.1.37/linux/include/linux/kernel.h Mon Apr 25 10:04:34 1994 +++ linux/include/linux/kernel.h Tue Aug 2 11:29:22 1994 @@ -62,7 +62,7 @@ * 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 diff -u --recursive --new-file v1.1.37/linux/include/linux/mm.h linux/include/linux/mm.h --- v1.1.37/linux/include/linux/mm.h Tue Jul 26 21:39:46 1994 +++ linux/include/linux/mm.h Tue Aug 2 11:40:36 1994 @@ -58,6 +58,8 @@ #define VM_SHM 0x0400 #define VM_DENYWRITE 0x0800 /* ETXTBSY on write attempts.. */ +#define VM_EXECUTABLE 0x1000 + #define VM_STACK_FLAGS 0x0177 /* diff -u --recursive --new-file v1.1.37/linux/include/linux/mman.h linux/include/linux/mman.h --- v1.1.37/linux/include/linux/mman.h Tue Jul 26 21:39:46 1994 +++ linux/include/linux/mman.h Tue Aug 2 11:41:03 1994 @@ -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 -u --recursive --new-file v1.1.37/linux/include/linux/pci.h linux/include/linux/pci.h --- v1.1.37/linux/include/linux/pci.h Thu Jan 1 02:00:00 1970 +++ linux/include/linux/pci.h Tue Aug 2 11:29:22 1994 @@ -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 */ diff -u --recursive --new-file v1.1.37/linux/include/linux/sbpcd.h linux/include/linux/sbpcd.h --- v1.1.37/linux/include/linux/sbpcd.h Sat Jul 9 16:30:47 1994 +++ linux/include/linux/sbpcd.h Mon Aug 1 10:54:37 1994 @@ -46,6 +46,16 @@ #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,14 +429,14 @@ */ #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 */ /*==========================================================================*/ /* diff -u --recursive --new-file v1.1.37/linux/include/linux/sched.h linux/include/linux/sched.h --- v1.1.37/linux/include/linux/sched.h Tue Aug 2 11:27:46 1994 +++ linux/include/linux/sched.h Sat Jul 30 10:11:45 1994 @@ -12,12 +12,13 @@ #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; diff -u --recursive --new-file v1.1.37/linux/include/linux/serial.h linux/include/linux/serial.h --- v1.1.37/linux/include/linux/serial.h Fri Jun 17 15:20:07 1994 +++ linux/include/linux/serial.h Mon Aug 1 10:16:49 1994 @@ -132,5 +132,8 @@ #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 */ diff -u --recursive --new-file v1.1.37/linux/include/linux/sysv_fs.h linux/include/linux/sysv_fs.h --- v1.1.37/linux/include/linux/sysv_fs.h Tue Aug 2 11:27:46 1994 +++ linux/include/linux/sysv_fs.h Mon Aug 1 06:58:04 1994 @@ -427,7 +427,7 @@ 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; diff -u --recursive --new-file v1.1.37/linux/init/main.c linux/init/main.c --- v1.1.37/linux/init/main.c Tue Aug 2 11:27:47 1994 +++ linux/init/main.c Tue Aug 2 11:29:23 1994 @@ -79,6 +79,7 @@ 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 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 @@ #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 @@ 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 @@ 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 diff -u --recursive --new-file v1.1.37/linux/kernel/Makefile linux/kernel/Makefile --- v1.1.37/linux/kernel/Makefile Tue Aug 2 11:27:47 1994 +++ linux/kernel/Makefile Tue Aug 2 11:29:23 1994 @@ -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 -u --recursive --new-file v1.1.37/linux/kernel/bios32.c linux/kernel/bios32.c --- v1.1.37/linux/kernel/bios32.c Thu Jan 1 02:00:00 1970 +++ linux/kernel/bios32.c Tue Aug 2 11:29:23 1994 @@ -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 's ASUS mainboard. + */ + +#include +#include +#include +#include + +#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; + } +} diff -u --recursive --new-file v1.1.37/linux/kernel/ksyms.c linux/kernel/ksyms.c --- v1.1.37/linux/kernel/ksyms.c Tue Aug 2 11:27:47 1994 +++ linux/kernel/ksyms.c Tue Aug 2 13:39:47 1994 @@ -21,6 +21,9 @@ #include #include #include +#include +#include +#include #ifdef CONFIG_INET #include #endif @@ -64,6 +67,7 @@ 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 @@ 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), diff -u --recursive --new-file v1.1.37/linux/kernel/sched.c linux/kernel/sched.c --- v1.1.37/linux/kernel/sched.c Tue Aug 2 11:27:47 1994 +++ linux/kernel/sched.c Sat Jul 30 10:14:13 1994 @@ -71,6 +71,7 @@ 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 -u --recursive --new-file v1.1.37/linux/kernel/splx.c linux/kernel/splx.c --- v1.1.37/linux/kernel/splx.c Thu Jan 1 02:00:00 1970 +++ linux/kernel/splx.c Tue Aug 2 11:29:23 1994 @@ -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 + +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; +} diff -u --recursive --new-file v1.1.37/linux/kernel/sys.c linux/kernel/sys.c --- v1.1.37/linux/kernel/sys.c Tue Aug 2 11:27:47 1994 +++ linux/kernel/sys.c Tue Aug 2 12:01:17 1994 @@ -49,7 +49,7 @@ /* 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 @@ 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 @@ if (egid != (gid_t) -1) { if ((old_rgid == egid) || (current->egid == egid) || + (current->sgid == egid) || suser()) current->egid = egid; else { diff -u --recursive --new-file v1.1.37/linux/kernel/time.c linux/kernel/time.c --- v1.1.37/linux/kernel/time.c Thu Jun 9 18:56:13 1994 +++ linux/kernel/time.c Sat Jul 30 09:58:08 1994 @@ -337,7 +337,7 @@ /* 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))) diff -u --recursive --new-file v1.1.37/linux/mm/mmap.c linux/mm/mmap.c --- v1.1.37/linux/mm/mmap.c Tue Aug 2 11:27:47 1994 +++ linux/mm/mmap.c Tue Aug 2 11:43:27 1994 @@ -130,7 +130,7 @@ 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) diff -u --recursive --new-file v1.1.37/linux/net/inet/af_inet.c linux/net/inet/af_inet.c --- v1.1.37/linux/net/inet/af_inet.c Thu Jul 21 08:18:23 1994 +++ linux/net/inet/af_inet.c Mon Aug 1 10:16:49 1994 @@ -615,7 +615,7 @@ 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); diff -u --recursive --new-file v1.1.37/linux/net/inet/arp.c linux/net/inet/arp.c --- v1.1.37/linux/net/inet/arp.c Fri Jul 22 16:41:04 1994 +++ linux/net/inet/arp.c Mon Aug 1 10:16:49 1994 @@ -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 @@ -746,7 +747,7 @@ 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 @@ 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 @@ 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); diff -u --recursive --new-file v1.1.37/linux/net/inet/dev.c linux/net/inet/dev.c --- v1.1.37/linux/net/inet/dev.c Thu Jul 21 08:18:24 1994 +++ linux/net/inet/dev.c Mon Aug 1 10:16:50 1994 @@ -422,16 +422,19 @@ 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 diff -u --recursive --new-file v1.1.37/linux/net/inet/ip.c linux/net/inet/ip.c --- v1.1.37/linux/net/inet/ip.c Mon Jun 27 16:47:03 1994 +++ linux/net/inet/ip.c Tue Aug 2 12:44:08 1994 @@ -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 @@ /* * 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 @@ /* - * 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_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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ "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 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 @@ * 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)); + 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. */ + /* 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 @@ 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; - - /* - * Send an ICMP "Fragment Reassembly Timeout" message. - */ + struct ipq *qp; + + qp = (struct ipq *)arg; + + /* + * 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 struct ipq *ip_create(struct sk_buff *skb, struct iphdr *iph, struct device *dev) { - struct ipq *qp; - int maclen; - int ihlen; - - qp = (struct ipq *) kmalloc(sizeof(struct ipq), GFP_ATOMIC); - if (qp == NULL) - { + struct ipq *qp; + int maclen; + int ihlen; + + 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). + */ - /* - * 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) - { + 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); - /* 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); + /* 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 @@ 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. - */ + 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) + 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 @@ } } - /* - * 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 @@ 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 @@ 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 @@ 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 @@ * 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 @@ 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 @@ * 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 @@ #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 @@ 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 @@ * (4. We ought to check for IP multicast addresses and undefined types.. does this matter ?) */ - if (skb->lenihl<5 || iph->version != 4 || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0) - { - ip_statistics.IpInHdrErrors++; + if (skb->lenihl<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 @@ */ 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 - */ + 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) + 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 @@ * 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 @@ * 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 - */ + } + IS_SKB(skb); + + /* + * Do some book-keeping in the packet for later + */ + + + skb->dev = dev; + skb->when = jiffies; - 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 @@ * 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 @@ /* * 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 @@ * 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 @@ * 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_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 @@ 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 @@ 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_retransmit(struct sock *sk, int all) { - ip_do_retransmit(sk, 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. - */ + /* + * 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,20 +1920,20 @@ * * 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 @@ * 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 @@ } 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 @@ 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); diff -u --recursive --new-file v1.1.37/linux/net/inet/ipx.c linux/net/inet/ipx.c --- v1.1.37/linux/net/inet/ipx.c Mon Jun 27 16:47:03 1994 +++ linux/net/inet/ipx.c Tue Aug 2 11:47:59 1994 @@ -27,6 +27,7 @@ * multiple datalinks * Revision 0.26: Device drop kills IPX routes via it. (needed for modules) * Revision 0.27: Autobind + * Revision 0.28: Small fix for multiple local networks * * * @@ -216,7 +217,7 @@ restore_flags(flags); return r; } - r=r->next; + r=r->nextlocal; } restore_flags(flags); return NULL; @@ -1347,7 +1348,7 @@ 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 diff -u --recursive --new-file v1.1.37/linux/net/inet/packet.c linux/net/inet/packet.c --- v1.1.37/linux/net/inet/packet.c Tue Jun 21 14:16:28 1994 +++ linux/net/inet/packet.c Tue Aug 2 11:32:41 1994 @@ -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 @@ * 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); diff -u --recursive --new-file v1.1.37/linux/net/inet/tcp.c linux/net/inet/tcp.c --- v1.1.37/linux/net/inet/tcp.c Thu Jul 21 08:18:26 1994 +++ linux/net/inet/tcp.c Mon Aug 1 10:16:50 1994 @@ -710,7 +710,7 @@ 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 @@ 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; diff -u --recursive --new-file v1.1.37/linux/tools/version.c linux/tools/version.c --- v1.1.37/linux/tools/version.c Fri Dec 31 08:14:05 1993 +++ linux/tools/version.c Tue Aug 2 11:29:23 1994 @@ -18,4 +18,4 @@ char *linux_banner = "Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@" - LINUX_COMPILE_HOST ") " UTS_VERSION "\n"; + LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n";