summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1995-11-14 08:00:00 +0000
committer <ralf@linux-mips.org>1995-11-14 08:00:00 +0000
commite7c2a72e2680827d6a733931273a93461c0d8d1b (patch)
treec9abeda78ef7504062bb2e816bcf3e3c9d680112 /drivers
parentec6044459060a8c9ce7f64405c465d141898548c (diff)
Import of Linux/MIPS 1.3.0
Diffstat (limited to 'drivers')
-rw-r--r--drivers/FPU-emu/Makefile50
-rw-r--r--drivers/FPU-emu/README436
-rw-r--r--drivers/FPU-emu/control_w.h45
-rw-r--r--drivers/FPU-emu/div_Xsig.S369
-rw-r--r--drivers/FPU-emu/div_small.S50
-rw-r--r--drivers/FPU-emu/errors.c671
-rw-r--r--drivers/FPU-emu/exception.h53
-rw-r--r--drivers/FPU-emu/fpu_arith.c179
-rw-r--r--drivers/FPU-emu/fpu_asm.h30
-rw-r--r--drivers/FPU-emu/fpu_aux.c184
-rw-r--r--drivers/FPU-emu/fpu_emu.h171
-rw-r--r--drivers/FPU-emu/fpu_entry.c690
-rw-r--r--drivers/FPU-emu/fpu_etc.c129
-rw-r--r--drivers/FPU-emu/fpu_proto.h137
-rw-r--r--drivers/FPU-emu/fpu_system.h82
-rw-r--r--drivers/FPU-emu/fpu_trig.c1718
-rw-r--r--drivers/FPU-emu/get_address.c423
-rw-r--r--drivers/FPU-emu/load_store.c260
-rw-r--r--drivers/FPU-emu/mul_Xsig.S182
-rw-r--r--drivers/FPU-emu/poly.h116
-rw-r--r--drivers/FPU-emu/poly_2xm1.c152
-rw-r--r--drivers/FPU-emu/poly_atan.c197
-rw-r--r--drivers/FPU-emu/poly_l2.c255
-rw-r--r--drivers/FPU-emu/poly_sin.c408
-rw-r--r--drivers/FPU-emu/poly_tan.c213
-rw-r--r--drivers/FPU-emu/polynom_Xsig.S137
-rw-r--r--drivers/FPU-emu/reg_add_sub.c318
-rw-r--r--drivers/FPU-emu/reg_compare.c378
-rw-r--r--drivers/FPU-emu/reg_constant.c116
-rw-r--r--drivers/FPU-emu/reg_constant.h31
-rw-r--r--drivers/FPU-emu/reg_div.S251
-rw-r--r--drivers/FPU-emu/reg_ld_str.c1438
-rw-r--r--drivers/FPU-emu/reg_mul.c105
-rw-r--r--drivers/FPU-emu/reg_norm.S150
-rw-r--r--drivers/FPU-emu/reg_round.S701
-rw-r--r--drivers/FPU-emu/reg_u_add.S189
-rw-r--r--drivers/FPU-emu/reg_u_div.S477
-rw-r--r--drivers/FPU-emu/reg_u_mul.S163
-rw-r--r--drivers/FPU-emu/reg_u_sub.S292
-rw-r--r--drivers/FPU-emu/round_Xsig.S148
-rw-r--r--drivers/FPU-emu/shr_Xsig.S90
-rw-r--r--drivers/FPU-emu/status_w.h65
-rw-r--r--drivers/FPU-emu/version.h12
-rw-r--r--drivers/FPU-emu/wm_shrx.S208
-rw-r--r--drivers/FPU-emu/wm_sqrt.S474
-rw-r--r--drivers/Makefile9
-rw-r--r--drivers/block/MAKEDEV.ide124
-rw-r--r--drivers/block/Makefile38
-rw-r--r--drivers/block/README.aztcd730
-rw-r--r--drivers/block/README.fd168
-rw-r--r--drivers/block/README.ide278
-rw-r--r--drivers/block/README.sbpcd823
-rw-r--r--drivers/block/README.sonycd535119
-rw-r--r--drivers/block/aztcd.c1713
-rw-r--r--drivers/block/blk.h127
-rw-r--r--drivers/block/cdu31a.c410
-rw-r--r--drivers/block/floppy.c680
-rw-r--r--drivers/block/genhd.c49
-rw-r--r--drivers/block/hd.c34
-rw-r--r--drivers/block/ide-cd.c2126
-rw-r--r--drivers/block/ide.c2438
-rw-r--r--drivers/block/ll_rw_blk.c201
-rw-r--r--drivers/block/mcd.c25
-rw-r--r--drivers/block/ramdisk.c38
-rw-r--r--drivers/block/sbpcd.c7522
-rw-r--r--drivers/block/sbpcd2.c5
-rw-r--r--drivers/block/sbpcd3.c5
-rw-r--r--drivers/block/sbpcd4.c5
-rw-r--r--drivers/block/sonycd535.c1743
-rw-r--r--drivers/block/xd.c3
-rw-r--r--drivers/char/8x16.fnt4097
-rw-r--r--drivers/char/ChangeLog192
-rw-r--r--drivers/char/Makefile40
-rw-r--r--drivers/char/README.scc933
-rw-r--r--drivers/char/atixlmouse.c2
-rw-r--r--drivers/char/busmouse.c3
-rw-r--r--drivers/char/console.c1337
-rw-r--r--drivers/char/consolemap.c286
-rw-r--r--drivers/char/consolemap.h13
-rw-r--r--drivers/char/cyclades.c2958
-rw-r--r--drivers/char/defkeymap.c2
-rw-r--r--drivers/char/defkeymap.map3
-rw-r--r--drivers/char/fnt8x16.c7
-rw-r--r--drivers/char/kbd_kern.h9
-rw-r--r--drivers/char/keyboard.c290
-rw-r--r--drivers/char/lp.c244
-rw-r--r--drivers/char/mem.c29
-rw-r--r--drivers/char/msbusmouse.c2
-rw-r--r--drivers/char/n_tty.c16
-rw-r--r--drivers/char/psaux.c4
-rw-r--r--drivers/char/scc.c2305
-rw-r--r--drivers/char/scc_config.h67
-rw-r--r--drivers/char/selection.c294
-rw-r--r--drivers/char/selection.h91
-rw-r--r--drivers/char/serial.c633
-rw-r--r--drivers/char/tpqic02.c4
-rw-r--r--drivers/char/tty_io.c174
-rw-r--r--drivers/char/tty_ioctl.c8
-rw-r--r--drivers/char/uni_to_437.c169
-rw-r--r--drivers/char/vc_screen.c212
-rw-r--r--drivers/char/vesa_blank.c246
-rw-r--r--drivers/char/vt.c176
-rw-r--r--drivers/net/3c501.c213
-rw-r--r--drivers/net/3c503.c11
-rw-r--r--drivers/net/3c505.c2324
-rw-r--r--drivers/net/3c505.h178
-rw-r--r--drivers/net/3c505dta.h119
-rw-r--r--drivers/net/3c507.c8
-rw-r--r--drivers/net/3c509.c30
-rw-r--r--drivers/net/8390.c119
-rw-r--r--drivers/net/8390.h11
-rw-r--r--drivers/net/CONFIG25
-rw-r--r--drivers/net/MODULES8
-rw-r--r--drivers/net/Makefile148
-rw-r--r--drivers/net/README.3c50539
-rw-r--r--drivers/net/README.arcnet204
-rw-r--r--drivers/net/README.arcnet-jumpers1561
-rw-r--r--drivers/net/README.de4x548
-rw-r--r--drivers/net/README.eql528
-rw-r--r--drivers/net/README.tunnel123
-rw-r--r--drivers/net/README.wavelan31
-rw-r--r--drivers/net/Space.c162
-rw-r--r--drivers/net/ac3200.c1
-rw-r--r--drivers/net/apricot.c311
-rw-r--r--drivers/net/arcnet.c2175
-rw-r--r--drivers/net/at1700.c43
-rw-r--r--drivers/net/atp.c145
-rw-r--r--drivers/net/auto_irq.c5
-rw-r--r--drivers/net/de4x5.c2513
-rw-r--r--drivers/net/de4x5.h647
-rw-r--r--drivers/net/de600.c35
-rw-r--r--drivers/net/de620.c45
-rw-r--r--drivers/net/depca.c267
-rw-r--r--drivers/net/dummy.c72
-rw-r--r--drivers/net/e2100.c3
-rw-r--r--drivers/net/eexpress.c33
-rw-r--r--drivers/net/eql.c1153
-rw-r--r--drivers/net/ewrk3.c173
-rw-r--r--drivers/net/hp-plus.c12
-rw-r--r--drivers/net/hp.c3
-rw-r--r--drivers/net/i82586.h410
-rw-r--r--drivers/net/ibmtr.c1180
-rw-r--r--drivers/net/ibmtr.h426
-rw-r--r--drivers/net/lance.c223
-rw-r--r--drivers/net/loopback.c185
-rw-r--r--drivers/net/ne.c115
-rw-r--r--drivers/net/net_init.c85
-rw-r--r--drivers/net/ni52.c728
-rw-r--r--drivers/net/ni52.h2
-rw-r--r--drivers/net/ni65.c11
-rw-r--r--drivers/net/pi2.c1693
-rw-r--r--drivers/net/pi2.h133
-rw-r--r--drivers/net/plip.c1568
-rw-r--r--drivers/net/ppp.c143
-rw-r--r--drivers/net/sk_g16.c15
-rw-r--r--drivers/net/skeleton.c7
-rw-r--r--drivers/net/slhc.c72
-rw-r--r--drivers/net/slip.c1717
-rw-r--r--drivers/net/slip.h79
-rw-r--r--drivers/net/smc-ultra.c56
-rw-r--r--drivers/net/sonic.c1120
-rw-r--r--drivers/net/sonic.h359
-rw-r--r--drivers/net/tulip.c737
-rw-r--r--drivers/net/tunnel.c311
-rw-r--r--drivers/net/wavelan.c2448
-rw-r--r--drivers/net/wavelan.h254
-rw-r--r--drivers/net/wd.c11
-rw-r--r--drivers/net/z8530.h220
-rw-r--r--drivers/net/znet.c26
-rw-r--r--drivers/pci/Makefile40
-rw-r--r--drivers/pci/pci.c752
-rw-r--r--drivers/scsi/53c7,8xx.c733
-rw-r--r--drivers/scsi/53c7,8xx.h66
-rw-r--r--drivers/scsi/53c7,8xx.scr33
-rw-r--r--drivers/scsi/53c8xx_d.h252
-rw-r--r--drivers/scsi/53c8xx_u.h4
-rw-r--r--drivers/scsi/ChangeLog964
-rw-r--r--drivers/scsi/Makefile52
-rw-r--r--drivers/scsi/NCR5380.c26
-rw-r--r--drivers/scsi/NCR5380.h2
-rw-r--r--drivers/scsi/README.st21
-rw-r--r--drivers/scsi/aha152x.c189
-rw-r--r--drivers/scsi/aha152x.h4
-rw-r--r--drivers/scsi/aha1542.c74
-rw-r--r--drivers/scsi/aha1740.c35
-rw-r--r--drivers/scsi/aha1740.h42
-rw-r--r--drivers/scsi/aha274x.c30
-rw-r--r--drivers/scsi/buslogic.c690
-rw-r--r--drivers/scsi/buslogic.h27
-rw-r--r--drivers/scsi/constants.c2
-rw-r--r--drivers/scsi/eata.c456
-rw-r--r--drivers/scsi/eata.h39
-rw-r--r--drivers/scsi/eata_dma.c1185
-rw-r--r--drivers/scsi/eata_dma.h408
-rw-r--r--drivers/scsi/fdomain.c245
-rw-r--r--drivers/scsi/fdomain.h9
-rw-r--r--drivers/scsi/hosts.c171
-rw-r--r--drivers/scsi/hosts.h65
-rw-r--r--drivers/scsi/in2000.c64
-rw-r--r--drivers/scsi/qlogic.c306
-rw-r--r--drivers/scsi/script_asm.pl19
-rw-r--r--drivers/scsi/scsi.c1615
-rw-r--r--drivers/scsi/scsi.h65
-rw-r--r--drivers/scsi/scsi_debug.c26
-rw-r--r--drivers/scsi/scsi_ioctl.c21
-rw-r--r--drivers/scsi/scsi_module.c49
-rw-r--r--drivers/scsi/sd.c218
-rw-r--r--drivers/scsi/sd_ioctl.c17
-rw-r--r--drivers/scsi/seagate.c64
-rw-r--r--drivers/scsi/seagate.h2
-rw-r--r--drivers/scsi/sg.c104
-rw-r--r--drivers/scsi/sg.h2
-rw-r--r--drivers/scsi/sr.c376
-rw-r--r--drivers/scsi/sr.h1
-rw-r--r--drivers/scsi/sr_ioctl.c53
-rw-r--r--drivers/scsi/st.c455
-rw-r--r--drivers/scsi/st.h8
-rw-r--r--drivers/scsi/t128.c8
-rw-r--r--drivers/scsi/u14-34f.c316
-rw-r--r--drivers/scsi/u14-34f.h17
-rw-r--r--drivers/scsi/ultrastor.c18
-rw-r--r--drivers/scsi/ultrastor.h2
-rw-r--r--drivers/scsi/wd7000.c25
-rw-r--r--drivers/sound/.blurb27
-rw-r--r--drivers/sound/.blurb.orig27
-rw-r--r--drivers/sound/CHANGELOG2
-rw-r--r--drivers/sound/Makefile2
-rw-r--r--drivers/sound/Readme12
-rw-r--r--drivers/sound/Readme.v302
-rw-r--r--drivers/sound/ad1848.c5
-rw-r--r--drivers/sound/configure.c2
-rw-r--r--drivers/sound/dma.h266
-rw-r--r--drivers/sound/dmabuf.c4
-rw-r--r--drivers/sound/experimental.txt2
-rw-r--r--drivers/sound/gus_card.c6
-rw-r--r--drivers/sound/gus_wave.c5
-rw-r--r--drivers/sound/mpu401.c4
-rw-r--r--drivers/sound/pas2_card.c2
-rw-r--r--drivers/sound/pss.c2
-rw-r--r--drivers/sound/sb_dsp.c4
-rw-r--r--drivers/sound/sound_calls.h6
-rw-r--r--drivers/sound/soundcard.c3
-rw-r--r--drivers/sound/uart6850.c2
243 files changed, 60985 insertions, 24829 deletions
diff --git a/drivers/FPU-emu/Makefile b/drivers/FPU-emu/Makefile
deleted file mode 100644
index 46ff0c541..000000000
--- a/drivers/FPU-emu/Makefile
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# Makefile for wm-FPU-emu
-#
-
-#DEBUG = -DDEBUGGING
-DEBUG =
-PARANOID = -DPARANOID
-CFLAGS := $(CFLAGS) $(PARANOID) $(DEBUG) -fno-builtin
-
-.c.o:
- $(CC) $(CFLAGS) $(MATH_EMULATION) -c $<
-
-.S.o:
- $(CC) -D__ASSEMBLER__ $(PARANOID) -c $<
-
-.s.o:
- $(CC) -c $<
-
-OBJS = fpu_entry.o div_small.o errors.o \
- fpu_arith.o fpu_aux.o fpu_etc.o fpu_trig.o \
- load_store.o get_address.o \
- poly_atan.o poly_l2.o poly_2xm1.o poly_sin.o poly_tan.o \
- 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 \
- div_Xsig.o polynom_Xsig.o round_Xsig.o \
- shr_Xsig.o mul_Xsig.o
-
-math.a: $(OBJS)
- rm -f math.a
- $(AR) rcs math.a $(OBJS)
- sync
-
-dep:
- $(CPP) -M *.c > .depend
- $(CPP) -D__ASSEMBLER__ -M *.S >> .depend
-
-proto:
- cproto -e -DMAKING_PROTO *.c >fpu_proto.h
-
-dummy:
-
-#
-# include a dependency file if one exists
-#
-ifeq (.depend,$(wildcard .depend))
-include .depend
-endif
diff --git a/drivers/FPU-emu/README b/drivers/FPU-emu/README
deleted file mode 100644
index 2c0acb423..000000000
--- a/drivers/FPU-emu/README
+++ /dev/null
@@ -1,436 +0,0 @@
- +---------------------------------------------------------------------------+
- | wm-FPU-emu an FPU emulator for 80386 and 80486SX microprocessors. |
- | |
- | Copyright (C) 1992,1993,1994 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | This program is free software; you can redistribute it and/or modify |
- | it under the terms of the GNU General Public License version 2 as |
- | published by the Free Software Foundation. |
- | |
- | 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. |
- | |
- +---------------------------------------------------------------------------+
-
-
-
-wm-FPU-emu is an FPU emulator for Linux. It is derived from wm-emu387
-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
-facets of the functioning of the FPU are not well covered in the
-Reference Manual. The information in the manual has been supplemented
-with measurements on real 80486's. Unfortunately, it is simply not
-possible to be sure that all of the peculiarities of the 80486 have
-been discovered, so there is always likely to be obscure differences
-in the detailed behaviour of the emulator and a real 80486.
-
-wm-FPU-emu does not implement all of the behaviour of the 80486 FPU,
-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
- or at:
- billm@jacobi.maths.monash.edu.au
-
-
---Bill Metzenthen
- August 1994
-
-
------------------------ Internals of wm-FPU-emu -----------------------
-
-Numeric algorithms:
-(1) Add, subtract, and multiply. Nothing remarkable in these.
-(2) Divide has been tuned to get reasonable performance. The algorithm
- is not the obvious one which most people seem to use, but is designed
- to take advantage of the characteristics of the 80386. I expect that
- it has been invented many times before I discovered it, but I have not
- seen it. It is based upon one of those ideas which one carries around
- for years without ever bothering to check it out.
-(3) The sqrt function has been tuned to get good performance. It is based
- upon Newton's classic method. Performance was improved by capitalizing
- upon the properties of Newton's method, and the code is once again
- structured taking account of the 80386 characteristics.
-(4) The trig, log, and exp functions are based in each case upon quasi-
- "optimal" polynomial approximations. My definition of "optimal" was
- based upon getting good accuracy with reasonable speed.
-(5) The argument reducing code for the trig function effectively uses
- a value of pi which is accurate to more than 128 bits. As a consequence,
- the reduced argument is accurate to more than 64 bits for arguments up
- to a few pi, and accurate to more than 64 bits for most arguments,
- even for arguments approaching 2^63. This is far superior to an
- 80486, which uses a value of pi which is accurate to 66 bits.
-
-The code of the emulator is complicated slightly by the need to
-account for a limited form of re-entrancy. Normally, the emulator will
-emulate each FPU instruction to completion without interruption.
-However, it may happen that when the emulator is accessing the user
-memory space, swapping may be needed. In this case the emulator may be
-temporarily suspended while disk i/o takes place. During this time
-another process may use the emulator, thereby perhaps changing static
-variables. The code which accesses user memory is confined to five
-files:
- fpu_entry.c
- reg_ld_str.c
- load_store.c
- get_address.c
- errors.c
-As from version 1.12 of the emulator, no static variables are used
-(apart from those in the kernel's per-process tables). The emulator is
-therefore now fully re-entrant, rather than having just the restricted
-form of re-entrancy which is required by the Linux kernel.
-
------------------------ Limitations of wm-FPU-emu -----------------------
-
-There are a number of differences between the current wm-FPU-emu
-(version 1.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
-functions and its 80486 value with these functions is likely to differ
-from its emulator value.
-
-In a few rare cases the Underflow flag obtained with the emulator will
-be different from that obtained with an 80486. This occurs when the
-following conditions apply simultaneously:
-(a) the operands have a higher precision than the current setting of the
- precision control (PC) flags.
-(b) the underflow exception is masked.
-(c) the magnitude of the exact result (before rounding) is less than 2^-16382.
-(d) the magnitude of the final result (after rounding) is exactly 2^-16382.
-(e) the magnitude of the exact result would be exactly 2^-16382 if the
- operands were rounded to the current precision before the arithmetic
- operation was performed.
-If all of these apply, the emulator will set the Underflow flag but a real
-80486 will not.
-
-NOTE: Certain formats of Extended Real are UNSUPPORTED. They are
-unsupported by the 80486. They are the Pseudo-NaNs, Pseudoinfinities,
-and Unnormals. None of these will be generated by an 80486 or by the
-emulator. Do not use them. The emulator treats them differently in
-detail from the way an 80486 does.
-
-The emulator treats PseudoDenormals differently from an 80486. These
-numbers are in fact properly normalised numbers with the exponent
-offset by 1, and the emulator treats them as such. Unlike the 80486,
-the emulator does not generate a Denormal Operand exception for these
-numbers. The arithmetical results produced when using such a number as
-an operand are the same for the emulator and a real 80486 (apart from
-any slight precision difference for the transcendental functions).
-Neither the emulator nor an 80486 produces one of these numbers as the
-result of any arithmetic operation. An 80486 can keep one of these
-numbers in an FPU register with its identity as a PseudoDenormal, but
-the emulator will not; they are always converted to a valid number.
-
-Self modifying code can cause the emulator to fail. An example of such
-code is:
- movl %esp,[%ebx]
- fld1
-The FPU instruction may be (usually will be) loaded into the pre-fetch
-queue of the cpu before the mov instruction is executed. If the
-destination of the 'movl' overlaps the FPU instruction then the bytes
-in the prefetch queue and memory will be inconsistent when the FPU
-instruction is executed. The emulator will be invoked but will not be
-able to find the instruction which caused the device-not-present
-exception. For this case, the emulator cannot emulate the behaviour of
-an 80486DX.
-
-Handling of the address size override prefix byte (0x67) has not been
-extensively tested yet. A major problem exists because using it in
-vm86 mode can cause a general protection fault. Address offsets
-greater than 0xffff appear to be illegal in vm86 mode but are quite
-acceptable (and work) in real mode. A small test program developed to
-check the addressing, and which runs successfully in real mode,
-crashes dosemu under Linux and also brings Windows down with a general
-protection fault message when run under the MS-DOS prompt of Windows
-3.1. (The program simply reads data from a valid address).
-
-The emulator supports 16-bit protected mode, with one difference from
-an 80486DX. A 80486DX will allow some floating point instructions to
-write a few bytes below the lowest address of the stack. The emulator
-will not allow this in 16-bit protected mode: no instructions are
-allowed to write outside the bounds set by the protection.
-
------------------------ Performance of wm-FPU-emu -----------------------
-
-Speed.
------
-
-The speed of floating point computation with the emulator will depend
-upon instruction mix. Relative performance is best for the instructions
-which require most computation. The simple instructions are adversely
-affected by the fpu instruction trap overhead.
-
-
-Timing: Some simple timing tests have been made on the emulator functions.
-The times include load/store instructions. All times are in microseconds
-measured on a 33MHz 386 with 64k cache. The Turbo C tests were under
-ms-dos, the next two columns are for emulators running with the djgpp
-ms-dos extender. The final column is for wm-FPU-emu in Linux 0.97,
-using libm4.0 (hard).
-
-function Turbo C djgpp 1.06 WM-emu387 wm-FPU-emu
-
- + 60.5 154.8 76.5 139.4
- - 61.1-65.5 157.3-160.8 76.2-79.5 142.9-144.7
- * 71.0 190.8 79.6 146.6
- / 61.2-75.0 261.4-266.9 75.3-91.6 142.2-158.1
-
- sin() 310.8 4692.0 319.0 398.5
- cos() 284.4 4855.2 308.0 388.7
- tan() 495.0 8807.1 394.9 504.7
- atan() 328.9 4866.4 601.1 419.5-491.9
-
- sqrt() 128.7 crashed 145.2 227.0
- log() 413.1-419.1 5103.4-5354.21 254.7-282.2 409.4-437.1
- exp() 479.1 6619.2 469.1 850.8
-
-
-The performance under Linux is improved by the use of look-ahead code.
-The following results show the improvement which is obtained under
-Linux due to the look-ahead code. Also given are the times for the
-original Linux emulator with the 4.1 'soft' lib.
-
- [ Linus' note: I changed look-ahead to be the default under linux, as
- there was no reason not to use it after I had edited it to be
- disabled during tracing ]
-
- wm-FPU-emu w original w
- look-ahead 'soft' lib
- + 106.4 190.2
- - 108.6-111.6 192.4-216.2
- * 113.4 193.1
- / 108.8-124.4 700.1-706.2
-
- sin() 390.5 2642.0
- cos() 381.5 2767.4
- tan() 496.5 3153.3
- atan() 367.2-435.5 2439.4-3396.8
-
- sqrt() 195.1 4732.5
- log() 358.0-387.5 3359.2-3390.3
- exp() 619.3 4046.4
-
-
-These figures are now somewhat out-of-date. The emulator has become
-progressively slower for most functions as more of the 80486 features
-have been implemented.
-
-
------------------------ Accuracy of wm-FPU-emu -----------------------
-
-
-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.
-
-
-Function Tested x range Worst result Turbo C
- (relative bits)
-
-sqrt(x) 1 .. 2 64.1 63.2
-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)
-
-cos(x) 0 .. 9.22e+18 62.0
-sin(x) 1e-16 .. 9.22e+18 62.1
-tan(x) 1e-16 .. 9.22e+18 61.8
-
-It is possible with some effort to find very large arguments which
-give much degraded precision. For example, the integer number
- 8227740058411162616.0
-is within about 10e-7 of a multiple of pi. To find the tan (for
-example) of this number to 64 bits precision it would be necessary to
-have a value of pi which had about 150 bits precision. The FPU
-emulator computes the result to about 42.6 bits precision (the correct
-result is about -9.739715e-8). On the other hand, an 80486 FPU returns
-0.01059, which in relative terms is hopelessly inaccurate.
-
-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 indicate that an Intel 486DX produces
-identical results). The tests were basically the same as those used
-to measure the emulator (the values, being random, were in general not
-the same). The total number of tests for each instruction are given
-at the end of the table, in case each about 100k tests were performed.
-Another line of figures at the end of the table shows that most of the
-instructions return results which are in error for more than 10
-percent of the arguments tested.
-
-The numbers in the body of the table give the approx number of times a
-result of the given accuracy in bits (given in the left-most column)
-was obtained per one million arguments. For three of the instructions,
-two columns of results are given: * The second column for f2xm1 gives
-the number cases where the results of the first column were for a
-positive argument, this shows that this instruction gives better
-results for positive arguments than it does for negative. * In the
-cases of fcos and fptan, the first column gives the results when all
-cases where arguments greater than 1.5 were removed from the results
-given in the second column. Unlike the emulator, an 80486 FPU returns
-results of relatively poor accuracy for these instructions when the
-argument approaches pi/2. The table does not show those cases when the
-accuracy of the results were less than 62 bits, which occurs quite
-often for fsin and fptan when the argument approaches pi/2. This poor
-accuracy is discussed above in relation to the Turbo C "emulator", and
-the accuracy of the value of pi.
-
-
-bits f2xm1 f2xm1 fpatan fcos fcos fyl2x fyl2xp1 fsin fptan fptan
-62.0 0 0 0 0 437 0 0 0 0 925
-62.1 0 0 10 0 894 0 0 0 0 1023
-62.2 14 0 0 0 1033 0 0 0 0 945
-62.3 57 0 0 0 1202 0 0 0 0 1023
-62.4 385 0 0 10 1292 0 23 0 0 1178
-62.5 1140 0 0 119 1649 0 39 0 0 1149
-62.6 2037 0 0 189 1620 0 16 0 0 1169
-62.7 5086 14 0 646 2315 10 101 35 39 1402
-62.8 8818 86 0 984 3050 59 287 131 224 2036
-62.9 11340 1355 0 2126 4153 79 605 357 321 1948
-63.0 15557 4750 0 3319 5376 246 1281 862 808 2688
-63.1 20016 8288 0 4620 6628 511 2569 1723 1510 3302
-63.2 24945 11127 10 6588 8098 1120 4470 2968 2990 4724
-63.3 25686 12382 69 8774 10682 1906 6775 4482 5474 7236
-63.4 29219 14722 79 11109 12311 3094 9414 7259 8912 10587
-63.5 30458 14936 393 13802 15014 5874 12666 9609 13762 15262
-63.6 32439 16448 1277 17945 19028 10226 15537 14657 19158 20346
-63.7 35031 16805 4067 23003 23947 18910 20116 21333 25001 26209
-63.8 33251 15820 7673 24781 25675 24617 25354 24440 29433 30329
-63.9 33293 16833 18529 28318 29233 31267 31470 27748 29676 30601
-
-Per cent with error:
- 30.9 3.2 18.5 9.8 13.1 11.6 17.4
-Total arguments tested:
- 70194 70099 101784 100641 100641 101799 128853 114893 102675 102675
-
-
-------------------------- Contributors -------------------------------
-
-A number of people have contributed to the development of the
-emulator, often by just reporting bugs, sometimes with suggested
-fixes, and a few kind people have provided me with access in one way
-or another to an 80486 machine. Contributors include (to those people
-who I may have forgotten, please forgive me):
-
-Linus Torvalds
-Tommy.Thorn@daimi.aau.dk
-Andrew.Tridgell@anu.edu.au
-Nick Holloway, alfie@dcs.warwick.ac.uk
-Hermano Moura, moura@dcs.gla.ac.uk
-Jon Jagger, J.Jagger@scp.ac.uk
-Lennart Benschop
-Brian Gallew, geek+@CMU.EDU
-Thomas Staniszewski, ts3v+@andrew.cmu.edu
-Martin Howell, mph@plasma.apana.org.au
-M Saggaf, alsaggaf@athena.mit.edu
-Peter Barker, PETER@socpsy.sci.fau.edu
-tom@vlsivie.tuwien.ac.at
-Dan Russel, russed@rpi.edu
-Daniel Carosone, danielce@ee.mu.oz.au
-cae@jpmorgan.com
-Hamish Coleman, t933093@minyos.xx.rmit.oz.au
-Bruce Evans, bde@kralizec.zeta.org.au
-Timo Korvola, Timo.Korvola@hut.fi
-Rick Lyons, rick@razorback.brisnet.org.au
-Rick, jrs@world.std.com
-
-...and numerous others who responded to my request for help with
-a real 80486.
-
diff --git a/drivers/FPU-emu/control_w.h b/drivers/FPU-emu/control_w.h
deleted file mode 100644
index ef5fced39..000000000
--- a/drivers/FPU-emu/control_w.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*---------------------------------------------------------------------------+
- | control_w.h |
- | |
- | Copyright (C) 1992,1993 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- +---------------------------------------------------------------------------*/
-
-#ifndef _CONTROLW_H_
-#define _CONTROLW_H_
-
-#ifdef __ASSEMBLER__
-#define _Const_(x) $##x
-#else
-#define _Const_(x) x
-#endif
-
-#define CW_RC _Const_(0x0C00) /* rounding control */
-#define CW_PC _Const_(0x0300) /* precision control */
-
-#define CW_Precision Const_(0x0020) /* loss of precision mask */
-#define CW_Underflow Const_(0x0010) /* underflow mask */
-#define CW_Overflow Const_(0x0008) /* overflow mask */
-#define CW_ZeroDiv Const_(0x0004) /* divide by zero mask */
-#define CW_Denormal Const_(0x0002) /* denormalized operand mask */
-#define CW_Invalid Const_(0x0001) /* invalid operation mask */
-
-#define CW_Exceptions _Const_(0x003f) /* all masks */
-
-#define RC_RND _Const_(0x0000)
-#define RC_DOWN _Const_(0x0400)
-#define RC_UP _Const_(0x0800)
-#define RC_CHOP _Const_(0x0C00)
-
-/* p 15-5: Precision control bits affect only the following:
- ADD, SUB(R), MUL, DIV(R), and SQRT */
-#define PR_24_BITS _Const_(0x000)
-#define PR_53_BITS _Const_(0x200)
-#define PR_64_BITS _Const_(0x300)
-#define PR_RESERVED_BITS _Const_(0x100)
-/* FULL_PRECISION simulates all exceptions masked */
-#define FULL_PRECISION (PR_64_BITS | RC_RND | 0x3f)
-
-#endif _CONTROLW_H_
diff --git a/drivers/FPU-emu/div_Xsig.S b/drivers/FPU-emu/div_Xsig.S
deleted file mode 100644
index 67d8be964..000000000
--- a/drivers/FPU-emu/div_Xsig.S
+++ /dev/null
@@ -1,369 +0,0 @@
- .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 --git a/drivers/FPU-emu/div_small.S b/drivers/FPU-emu/div_small.S
deleted file mode 100644
index 0225a96d4..000000000
--- a/drivers/FPU-emu/div_small.S
+++ /dev/null
@@ -1,50 +0,0 @@
- .file "div_small.S"
-/*---------------------------------------------------------------------------+
- | div_small.S |
- | |
- | Divide a 64 bit integer by a 32 bit integer & return remainder. |
- | |
- | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
-
-/*---------------------------------------------------------------------------+
- | unsigned long div_small(unsigned long long *x, unsigned long y) |
- +---------------------------------------------------------------------------*/
-
-#include "fpu_asm.h"
-
-.text
- .align 2,144
-
-.globl _div_small
-
-_div_small:
- pushl %ebp
- movl %esp,%ebp
-
- pushl %esi
-
- movl PARAM1,%esi /* pointer to num */
- movl PARAM2,%ecx /* The denominator */
-
- movl 4(%esi),%eax /* Get the current num msw */
- xorl %edx,%edx
- divl %ecx
-
- movl %eax,4(%esi)
-
- movl (%esi),%eax /* Get the num lsw */
- divl %ecx
-
- movl %eax,(%esi)
-
- movl %edx,%eax /* Return the remainder in eax */
-
- popl %esi
-
- leave
- ret
-
diff --git a/drivers/FPU-emu/errors.c b/drivers/FPU-emu/errors.c
deleted file mode 100644
index e34eec942..000000000
--- a/drivers/FPU-emu/errors.c
+++ /dev/null
@@ -1,671 +0,0 @@
-/*---------------------------------------------------------------------------+
- | errors.c |
- | |
- | The error handling functions for wm-FPU-emu |
- | |
- | Copyright (C) 1992,1993,1994 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
-
-/*---------------------------------------------------------------------------+
- | Note: |
- | The file contains code which accesses user memory. |
- | Emulator static data may change when user memory is accessed, due to |
- | other processes using the emulator while swapping is in progress. |
- +---------------------------------------------------------------------------*/
-
-#include <linux/signal.h>
-
-#include <asm/segment.h>
-
-#include "fpu_system.h"
-#include "exception.h"
-#include "fpu_emu.h"
-#include "status_w.h"
-#include "control_w.h"
-#include "reg_constant.h"
-#include "version.h"
-
-/* */
-#undef PRINT_MESSAGES
-/* */
-
-
-void Un_impl(void)
-{
- unsigned char byte1, FPU_modrm;
- unsigned long address = FPU_ORIG_EIP;
-
- RE_ENTRANT_CHECK_OFF;
- /* No need to verify_area(), we have previously fetched these bytes. */
- printk("Unimplemented FPU Opcode at eip=%p : ", (void *) address);
- if ( FPU_CS == USER_CS )
- {
- while ( 1 )
- {
- byte1 = get_fs_byte((unsigned char *) address);
- if ( (byte1 & 0xf8) == 0xd8 ) break;
- printk("[%02x]", byte1);
- address++;
- }
- printk("%02x ", byte1);
- FPU_modrm = get_fs_byte(1 + (unsigned char *) address);
-
- if (FPU_modrm >= 0300)
- printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
- else
- printk("/%d\n", (FPU_modrm >> 3) & 7);
- }
- else
- {
- printk("cs selector = %04x\n", FPU_CS);
- }
-
- RE_ENTRANT_CHECK_ON;
-
- EXCEPTION(EX_Invalid);
-
-}
-
-
-/*
- Called for opcodes which are illegal and which are known to result in a
- SIGILL with a real 80486.
- */
-void FPU_illegal(void)
-{
- math_abort(FPU_info,SIGILL);
-}
-
-
-
-void emu_printall()
-{
- int i;
- static char *tag_desc[] = { "Valid", "Zero", "ERROR", "ERROR",
- "DeNorm", "Inf", "NaN", "Empty" };
- unsigned char byte1, FPU_modrm;
- unsigned long address = FPU_ORIG_EIP;
-
- RE_ENTRANT_CHECK_OFF;
- /* No need to verify_area(), we have previously fetched these bytes. */
- printk("At %p:", (void *) address);
- if ( FPU_CS == USER_CS )
- {
-#define MAX_PRINTED_BYTES 20
- for ( i = 0; i < MAX_PRINTED_BYTES; i++ )
- {
- byte1 = get_fs_byte((unsigned char *) address);
- if ( (byte1 & 0xf8) == 0xd8 )
- {
- printk(" %02x", byte1);
- break;
- }
- printk(" [%02x]", byte1);
- address++;
- }
- if ( i == MAX_PRINTED_BYTES )
- printk(" [more..]\n");
- else
- {
- FPU_modrm = get_fs_byte(1 + (unsigned char *) address);
-
- if (FPU_modrm >= 0300)
- printk(" %02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
- else
- printk(" /%d, mod=%d rm=%d\n",
- (FPU_modrm >> 3) & 7, (FPU_modrm >> 6) & 3, FPU_modrm & 7);
- }
- }
- else
- {
- printk("%04x\n", FPU_CS);
- }
-
- partial_status = status_word();
-
-#ifdef DEBUGGING
-if ( partial_status & SW_Backward ) printk("SW: backward compatibility\n");
-if ( partial_status & SW_C3 ) printk("SW: condition bit 3\n");
-if ( partial_status & SW_C2 ) printk("SW: condition bit 2\n");
-if ( partial_status & SW_C1 ) printk("SW: condition bit 1\n");
-if ( partial_status & SW_C0 ) printk("SW: condition bit 0\n");
-if ( partial_status & SW_Summary ) printk("SW: exception summary\n");
-if ( partial_status & SW_Stack_Fault ) printk("SW: stack fault\n");
-if ( partial_status & SW_Precision ) printk("SW: loss of precision\n");
-if ( partial_status & SW_Underflow ) printk("SW: underflow\n");
-if ( partial_status & SW_Overflow ) printk("SW: overflow\n");
-if ( partial_status & SW_Zero_Div ) printk("SW: divide by zero\n");
-if ( partial_status & SW_Denorm_Op ) printk("SW: denormalized operand\n");
-if ( partial_status & SW_Invalid ) printk("SW: invalid operation\n");
-#endif DEBUGGING
-
- printk(" SW: b=%d st=%ld es=%d sf=%d cc=%d%d%d%d ef=%d%d%d%d%d%d\n",
- partial_status & 0x8000 ? 1 : 0, /* busy */
- (partial_status & 0x3800) >> 11, /* stack top pointer */
- partial_status & 0x80 ? 1 : 0, /* Error summary status */
- partial_status & 0x40 ? 1 : 0, /* Stack flag */
- partial_status & SW_C3?1:0, partial_status & SW_C2?1:0, /* cc */
- partial_status & SW_C1?1:0, partial_status & SW_C0?1:0, /* cc */
- partial_status & SW_Precision?1:0, partial_status & SW_Underflow?1:0,
- partial_status & SW_Overflow?1:0, partial_status & SW_Zero_Div?1:0,
- partial_status & SW_Denorm_Op?1:0, partial_status & SW_Invalid?1:0);
-
-printk(" CW: ic=%d rc=%ld%ld pc=%ld%ld iem=%d ef=%d%d%d%d%d%d\n",
- control_word & 0x1000 ? 1 : 0,
- (control_word & 0x800) >> 11, (control_word & 0x400) >> 10,
- (control_word & 0x200) >> 9, (control_word & 0x100) >> 8,
- control_word & 0x80 ? 1 : 0,
- control_word & SW_Precision?1:0, control_word & SW_Underflow?1:0,
- control_word & SW_Overflow?1:0, control_word & SW_Zero_Div?1:0,
- control_word & SW_Denorm_Op?1:0, control_word & SW_Invalid?1:0);
-
- for ( i = 0; i < 8; i++ )
- {
- FPU_REG *r = &st(i);
- switch (r->tag)
- {
- case TW_Empty:
- continue;
- break;
- case TW_Zero:
-#if 0
- printk("st(%d) %c .0000 0000 0000 0000 ",
- i, r->sign ? '-' : '+');
- break;
-#endif
- case TW_Valid:
- case TW_NaN:
-/* case TW_Denormal: */
- case TW_Infinity:
- printk("st(%d) %c .%04lx %04lx %04lx %04lx e%+-6ld ", i,
- r->sign ? '-' : '+',
- (long)(r->sigh >> 16),
- (long)(r->sigh & 0xFFFF),
- (long)(r->sigl >> 16),
- (long)(r->sigl & 0xFFFF),
- r->exp - EXP_BIAS + 1);
- break;
- default:
- printk("Whoops! Error in errors.c ");
- break;
- }
- printk("%s\n", tag_desc[(int) (unsigned) r->tag]);
- }
-
-#ifdef OBSOLETE
- printk("[data] %c .%04lx %04lx %04lx %04lx e%+-6ld ",
- FPU_loaded_data.sign ? '-' : '+',
- (long)(FPU_loaded_data.sigh >> 16),
- (long)(FPU_loaded_data.sigh & 0xFFFF),
- (long)(FPU_loaded_data.sigl >> 16),
- (long)(FPU_loaded_data.sigl & 0xFFFF),
- FPU_loaded_data.exp - EXP_BIAS + 1);
- printk("%s\n", tag_desc[(int) (unsigned) FPU_loaded_data.tag]);
-#endif OBSOLETE
- RE_ENTRANT_CHECK_ON;
-
-}
-
-static struct {
- int type;
- char *name;
-} exception_names[] = {
- { EX_StackOver, "stack overflow" },
- { EX_StackUnder, "stack underflow" },
- { EX_Precision, "loss of precision" },
- { EX_Underflow, "underflow" },
- { EX_Overflow, "overflow" },
- { EX_ZeroDiv, "divide by zero" },
- { EX_Denormal, "denormalized operand" },
- { EX_Invalid, "invalid operation" },
- { EX_INTERNAL, "INTERNAL BUG in "FPU_VERSION },
- { 0, NULL }
-};
-
-/*
- EX_INTERNAL is always given with a code which indicates where the
- error was detected.
-
- Internal error types:
- 0x14 in fpu_etc.c
- 0x1nn in a *.c file:
- 0x101 in reg_add_sub.c
- 0x102 in reg_mul.c
- 0x104 in poly_atan.c
- 0x105 in reg_mul.c
- 0x107 in fpu_trig.c
- 0x108 in reg_compare.c
- 0x109 in reg_compare.c
- 0x110 in reg_add_sub.c
- 0x111 in fpe_entry.c
- 0x112 in fpu_trig.c
- 0x113 in errors.c
- 0x115 in fpu_trig.c
- 0x116 in fpu_trig.c
- 0x117 in fpu_trig.c
- 0x118 in fpu_trig.c
- 0x119 in fpu_trig.c
- 0x120 in poly_atan.c
- 0x121 in reg_compare.c
- 0x122 in reg_compare.c
- 0x123 in reg_compare.c
- 0x125 in fpu_trig.c
- 0x126 in fpu_entry.c
- 0x127 in poly_2xm1.c
- 0x128 in fpu_entry.c
- 0x129 in fpu_entry.c
- 0x130 in get_address.c
- 0x131 in get_address.c
- 0x132 in get_address.c
- 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
- 0x203 in reg_u_div.S
- 0x204 in reg_u_div.S
- 0x205 in reg_u_mul.S
- 0x206 in reg_u_sub.S
- 0x207 in wm_sqrt.S
- 0x208 in reg_div.S
- 0x209 in reg_u_sub.S
- 0x210 in reg_u_sub.S
- 0x211 in reg_u_sub.S
- 0x212 in reg_u_sub.S
- 0x213 in wm_sqrt.S
- 0x214 in wm_sqrt.S
- 0x215 in wm_sqrt.S
- 0x220 in reg_norm.S
- 0x221 in reg_norm.S
- 0x230 in reg_round.S
- 0x231 in reg_round.S
- 0x232 in reg_round.S
- 0x233 in reg_round.S
- 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)
-{
- int i, int_type;
-
- int_type = 0; /* Needed only to stop compiler warnings */
- if ( n & EX_INTERNAL )
- {
- int_type = n - EX_INTERNAL;
- n = EX_INTERNAL;
- /* Set lots of exception bits! */
- partial_status |= (SW_Exc_Mask | SW_Summary | SW_Backward);
- }
- else
- {
- /* Extract only the bits which we use to set the status word */
- n &= (SW_Exc_Mask);
- /* Set the corresponding exception bit */
- partial_status |= n;
- /* Set summary bits iff exception isn't masked */
- if ( partial_status & ~control_word & CW_Exceptions )
- partial_status |= (SW_Summary | SW_Backward);
- if ( n & (SW_Stack_Fault | EX_Precision) )
- {
- if ( !(n & SW_C1) )
- /* This bit distinguishes over- from underflow for a stack fault,
- and roundup from round-down for precision loss. */
- partial_status &= ~SW_C1;
- }
- }
-
- RE_ENTRANT_CHECK_OFF;
- if ( (~control_word & n & CW_Exceptions) || (n == EX_INTERNAL) )
- {
-#ifdef PRINT_MESSAGES
- /* My message from the sponsor */
- printk(FPU_VERSION" "__DATE__" (C) W. Metzenthen.\n");
-#endif PRINT_MESSAGES
-
- /* Get a name string for error reporting */
- for (i=0; exception_names[i].type; i++)
- if ( (exception_names[i].type & n) == exception_names[i].type )
- break;
-
- if (exception_names[i].type)
- {
-#ifdef PRINT_MESSAGES
- printk("FP Exception: %s!\n", exception_names[i].name);
-#endif PRINT_MESSAGES
- }
- else
- printk("FPU emulator: Unknown Exception: 0x%04x!\n", n);
-
- if ( n == EX_INTERNAL )
- {
- printk("FPU emulator: Internal error type 0x%04x\n", int_type);
- emu_printall();
- }
-#ifdef PRINT_MESSAGES
- else
- emu_printall();
-#endif PRINT_MESSAGES
-
- /*
- * The 80486 generates an interrupt on the next non-control FPU
- * instruction. So we need some means of flagging it.
- * We use the ES (Error Summary) bit for this, assuming that
- * this is the way a real FPU does it (until I can check it out),
- * if not, then some method such as the following kludge might
- * be needed.
- */
-/* regs[0].tag |= TW_FPU_Interrupt; */
- }
- RE_ENTRANT_CHECK_ON;
-
-#ifdef __DEBUG__
- math_abort(FPU_info,SIGFPE);
-#endif __DEBUG__
-
-}
-
-
-/* Real operation attempted on two operands, one a NaN. */
-/* Returns nz if the exception is unmasked */
-asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest)
-{
- FPU_REG const *x;
- int signalling;
-
- /* The default result for the case of two "equal" NaNs (signs may
- differ) is chosen to reproduce 80486 behaviour */
- x = a;
- if (a->tag == TW_NaN)
- {
- if (b->tag == TW_NaN)
- {
- signalling = !(a->sigh & b->sigh & 0x40000000);
- /* find the "larger" */
- if ( significand(a) < significand(b) )
- x = b;
- }
- else
- {
- /* return the quiet version of the NaN in a */
- signalling = !(a->sigh & 0x40000000);
- }
- }
- else
-#ifdef PARANOID
- if (b->tag == TW_NaN)
-#endif PARANOID
- {
- signalling = !(b->sigh & 0x40000000);
- x = b;
- }
-#ifdef PARANOID
- else
- {
- signalling = 0;
- EXCEPTION(EX_INTERNAL|0x113);
- x = &CONST_QNaN;
- }
-#endif PARANOID
-
- if ( !signalling )
- {
- if ( !(x->sigh & 0x80000000) ) /* pseudo-NaN ? */
- x = &CONST_QNaN;
- reg_move(x, dest);
- return 0;
- }
-
- if ( control_word & CW_Invalid )
- {
- /* The masked response */
- if ( !(x->sigh & 0x80000000) ) /* pseudo-NaN ? */
- x = &CONST_QNaN;
- reg_move(x, dest);
- /* ensure a Quiet NaN */
- dest->sigh |= 0x40000000;
- }
-
- EXCEPTION(EX_Invalid);
-
- return !(control_word & CW_Invalid);
-}
-
-
-/* Invalid arith operation on Valid registers */
-/* Returns nz if the exception is unmasked */
-asmlinkage int arith_invalid(FPU_REG *dest)
-{
-
- EXCEPTION(EX_Invalid);
-
- if ( control_word & CW_Invalid )
- {
- /* The masked response */
- reg_move(&CONST_QNaN, dest);
- }
-
- return !(control_word & CW_Invalid);
-
-}
-
-
-/* Divide a finite number by zero */
-asmlinkage int divide_by_zero(int sign, FPU_REG *dest)
-{
-
- if ( control_word & CW_ZeroDiv )
- {
- /* The masked response */
- reg_move(&CONST_INF, dest);
- dest->sign = (unsigned char)sign;
- }
-
- EXCEPTION(EX_ZeroDiv);
-
- return !(control_word & CW_ZeroDiv);
-
-}
-
-
-/* This may be called often, so keep it lean */
-int set_precision_flag(int flags)
-{
- if ( control_word & CW_Precision )
- {
- partial_status &= ~(SW_C1 & flags);
- partial_status |= flags; /* The masked response */
- return 0;
- }
- else
- {
- exception(flags);
- return 1;
- }
-}
-
-
-/* This may be called often, so keep it lean */
-asmlinkage void set_precision_flag_up(void)
-{
- if ( control_word & CW_Precision )
- partial_status |= (SW_Precision | SW_C1); /* The masked response */
- else
- exception(EX_Precision | SW_C1);
-
-}
-
-
-/* This may be called often, so keep it lean */
-asmlinkage void set_precision_flag_down(void)
-{
- if ( control_word & CW_Precision )
- { /* The masked response */
- partial_status &= ~SW_C1;
- partial_status |= SW_Precision;
- }
- else
- exception(EX_Precision);
-}
-
-
-asmlinkage int denormal_operand(void)
-{
- if ( control_word & CW_Denormal )
- { /* The masked response */
- partial_status |= SW_Denorm_Op;
- return 0;
- }
- else
- {
- exception(EX_Denormal);
- return 1;
- }
-}
-
-
-asmlinkage int arith_overflow(FPU_REG *dest)
-{
-
- if ( control_word & CW_Overflow )
- {
- char sign;
- /* The masked response */
-/* ###### The response here depends upon the rounding mode */
- sign = dest->sign;
- reg_move(&CONST_INF, dest);
- dest->sign = sign;
- }
- else
- {
- /* Subtract the magic number from the exponent */
- dest->exp -= (3 * (1 << 13));
- }
-
- EXCEPTION(EX_Overflow);
- if ( control_word & CW_Overflow )
- {
- /* The overflow exception is masked. */
- /* By definition, precision is lost.
- The roundup bit (C1) is also set because we have
- "rounded" upwards to Infinity. */
- EXCEPTION(EX_Precision | SW_C1);
- return !(control_word & CW_Precision);
- }
-
- return !(control_word & CW_Overflow);
-
-}
-
-
-asmlinkage int arith_underflow(FPU_REG *dest)
-{
-
- if ( control_word & CW_Underflow )
- {
- /* The masked response */
- if ( dest->exp <= EXP_UNDER - 63 )
- {
- reg_move(&CONST_Z, dest);
- partial_status &= ~SW_C1; /* Round down. */
- }
- }
- else
- {
- /* Add the magic number to the exponent. */
- dest->exp += (3 * (1 << 13));
- }
-
- EXCEPTION(EX_Underflow);
- if ( control_word & CW_Underflow )
- {
- /* The underflow exception is masked. */
- EXCEPTION(EX_Precision);
- return !(control_word & CW_Precision);
- }
-
- return !(control_word & CW_Underflow);
-
-}
-
-
-void stack_overflow(void)
-{
-
- if ( control_word & CW_Invalid )
- {
- /* The masked response */
- top--;
- reg_move(&CONST_QNaN, &st(0));
- }
-
- EXCEPTION(EX_StackOver);
-
- return;
-
-}
-
-
-void stack_underflow(void)
-{
-
- if ( control_word & CW_Invalid )
- {
- /* The masked response */
- reg_move(&CONST_QNaN, &st(0));
- }
-
- EXCEPTION(EX_StackUnder);
-
- return;
-
-}
-
-
-void stack_underflow_i(int i)
-{
-
- if ( control_word & CW_Invalid )
- {
- /* The masked response */
- reg_move(&CONST_QNaN, &(st(i)));
- }
-
- EXCEPTION(EX_StackUnder);
-
- return;
-
-}
-
-
-void stack_underflow_pop(int i)
-{
-
- if ( control_word & CW_Invalid )
- {
- /* The masked response */
- reg_move(&CONST_QNaN, &(st(i)));
- pop();
- }
-
- EXCEPTION(EX_StackUnder);
-
- return;
-
-}
-
diff --git a/drivers/FPU-emu/exception.h b/drivers/FPU-emu/exception.h
deleted file mode 100644
index 2e629a30c..000000000
--- a/drivers/FPU-emu/exception.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*---------------------------------------------------------------------------+
- | exception.h |
- | |
- | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- +---------------------------------------------------------------------------*/
-
-#ifndef _EXCEPTION_H_
-#define _EXCEPTION_H_
-
-
-#ifdef __ASSEMBLER__
-#define Const_(x) $##x
-#else
-#define Const_(x) x
-#endif
-
-#ifndef SW_C1
-#include "fpu_emu.h"
-#endif SW_C1
-
-#define FPU_BUSY Const_(0x8000) /* FPU busy bit (8087 compatibility) */
-#define EX_ErrorSummary Const_(0x0080) /* Error summary status */
-/* Special exceptions: */
-#define EX_INTERNAL Const_(0x8000) /* Internal error in wm-FPU-emu */
-#define EX_StackOver Const_(0x0041|SW_C1) /* stack overflow */
-#define EX_StackUnder Const_(0x0041) /* stack underflow */
-/* Exception flags: */
-#define EX_Precision Const_(0x0020) /* loss of precision */
-#define EX_Underflow Const_(0x0010) /* underflow */
-#define EX_Overflow Const_(0x0008) /* overflow */
-#define EX_ZeroDiv Const_(0x0004) /* divide by zero */
-#define EX_Denormal Const_(0x0002) /* denormalized operand */
-#define EX_Invalid Const_(0x0001) /* invalid operation */
-
-
-#define PRECISION_LOST_UP Const_((EX_Precision | SW_C1))
-#define PRECISION_LOST_DOWN Const_(EX_Precision)
-
-
-#ifndef __ASSEMBLER__
-
-#ifdef DEBUG
-#define EXCEPTION(x) { printk("exception in %s at line %d\n", \
- __FILE__, __LINE__); exception(x); }
-#else
-#define EXCEPTION(x) exception(x)
-#endif
-
-#endif __ASSEMBLER__
-
-#endif _EXCEPTION_H_
diff --git a/drivers/FPU-emu/fpu_arith.c b/drivers/FPU-emu/fpu_arith.c
deleted file mode 100644
index 96e6bd89b..000000000
--- a/drivers/FPU-emu/fpu_arith.c
+++ /dev/null
@@ -1,179 +0,0 @@
-/*---------------------------------------------------------------------------+
- | fpu_arith.c |
- | |
- | Code to implement the FPU register/register arithmetic instructions |
- | |
- | Copyright (C) 1992,1993 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
-
-#include "fpu_system.h"
-#include "fpu_emu.h"
-#include "control_w.h"
-#include "status_w.h"
-
-
-void fadd__()
-{
- /* fadd st,st(i) */
- clear_C1();
- reg_add(&st(0), &st(FPU_rm), &st(0), control_word);
-}
-
-
-void fmul__()
-{
- /* fmul st,st(i) */
- clear_C1();
- reg_mul(&st(0), &st(FPU_rm), &st(0), control_word);
-}
-
-
-
-void fsub__()
-{
- /* fsub st,st(i) */
- clear_C1();
- reg_sub(&st(0), &st(FPU_rm), &st(0), control_word);
-}
-
-
-void fsubr_()
-{
- /* fsubr st,st(i) */
- clear_C1();
- reg_sub(&st(FPU_rm), &st(0), &st(0), control_word);
-}
-
-
-void fdiv__()
-{
- /* fdiv st,st(i) */
- clear_C1();
- reg_div(&st(0), &st(FPU_rm), &st(0), control_word);
-}
-
-
-void fdivr_()
-{
- /* fdivr st,st(i) */
- clear_C1();
- reg_div(&st(FPU_rm), &st(0), &st(0), control_word);
-}
-
-
-
-void fadd_i()
-{
- /* fadd st(i),st */
- clear_C1();
- reg_add(&st(0), &st(FPU_rm), &st(FPU_rm), control_word);
-}
-
-
-void fmul_i()
-{
- /* fmul st(i),st */
- clear_C1();
- reg_mul(&st(0), &st(FPU_rm), &st(FPU_rm), control_word);
-}
-
-
-void fsubri()
-{
- /* fsubr st(i),st */
- /* This is the sense of the 80486 manual
- reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); */
- clear_C1();
- reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word);
-}
-
-
-void fsub_i()
-{
- /* fsub st(i),st */
- /* This is the sense of the 80486 manual
- reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); */
- clear_C1();
- reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word);
-}
-
-
-void fdivri()
-{
- /* fdivr st(i),st */
- clear_C1();
- reg_div(&st(0), &st(FPU_rm), &st(FPU_rm), control_word);
-}
-
-
-void fdiv_i()
-{
- /* fdiv st(i),st */
- clear_C1();
- reg_div(&st(FPU_rm), &st(0), &st(FPU_rm), control_word);
-}
-
-
-
-void faddp_()
-{
- /* faddp st(i),st */
- clear_C1();
- if ( !reg_add(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) )
- pop();
-}
-
-
-void fmulp_()
-{
- /* fmulp st(i),st */
- clear_C1();
- if ( !reg_mul(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) )
- pop();
-}
-
-
-
-void fsubrp()
-{
- /* fsubrp st(i),st */
- /* This is the sense of the 80486 manual
- reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); */
- clear_C1();
- if ( !reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) )
- pop();
-}
-
-
-void fsubp_()
-{
- /* fsubp st(i),st */
- /* This is the sense of the 80486 manual
- reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); */
- clear_C1();
- if ( !reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word) )
- pop();
-}
-
-
-void fdivrp()
-{
- /* fdivrp st(i),st */
- clear_C1();
- if ( !reg_div(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) )
- pop();
-}
-
-
-void fdivp_()
-{
- /* fdivp st(i),st */
- clear_C1();
- if ( !reg_div(&st(FPU_rm), &st(0), &st(FPU_rm), control_word) )
- pop();
-}
-
diff --git a/drivers/FPU-emu/fpu_asm.h b/drivers/FPU-emu/fpu_asm.h
deleted file mode 100644
index 8eb60148d..000000000
--- a/drivers/FPU-emu/fpu_asm.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*---------------------------------------------------------------------------+
- | fpu_asm.h |
- | |
- | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- +---------------------------------------------------------------------------*/
-
-#ifndef _FPU_ASM_H_
-#define _FPU_ASM_H_
-
-#include "fpu_emu.h"
-
-#define EXCEPTION _exception
-
-
-#define PARAM1 8(%ebp)
-#define PARAM2 12(%ebp)
-#define PARAM3 16(%ebp)
-#define PARAM4 20(%ebp)
-
-#define SIGL_OFFSET 8
-#define SIGN(x) (x)
-#define TAG(x) 1(x)
-#define EXP(x) 4(x)
-#define SIG(x) SIGL_OFFSET##(x)
-#define SIGL(x) SIGL_OFFSET##(x)
-#define SIGH(x) 12(x)
-
-#endif _FPU_ASM_H_
diff --git a/drivers/FPU-emu/fpu_aux.c b/drivers/FPU-emu/fpu_aux.c
deleted file mode 100644
index 0d35fe19b..000000000
--- a/drivers/FPU-emu/fpu_aux.c
+++ /dev/null
@@ -1,184 +0,0 @@
-/*---------------------------------------------------------------------------+
- | fpu_aux.c |
- | |
- | Code to implement some of the FPU auxiliary instructions. |
- | |
- | Copyright (C) 1992,1993,1994 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
-
-#include "fpu_system.h"
-#include "exception.h"
-#include "fpu_emu.h"
-#include "status_w.h"
-#include "control_w.h"
-
-
-static void fnop(void)
-{
-}
-
-void fclex(void)
-{
- partial_status &= ~(SW_Backward|SW_Summary|SW_Stack_Fault|SW_Precision|
- SW_Underflow|SW_Overflow|SW_Zero_Div|SW_Denorm_Op|
- SW_Invalid);
- no_ip_update = 1;
-}
-
-/* Needs to be externally visible */
-void finit()
-{
- int r;
- control_word = 0x037f;
- partial_status = 0;
- top = 0; /* We don't keep top in the status word internally. */
- for (r = 0; r < 8; r++)
- {
- regs[r].tag = TW_Empty;
- }
- /* The behaviour is different to that detailed in
- Section 15.1.6 of the Intel manual */
- operand_address.offset = 0;
- operand_address.selector = 0;
- instruction_address.offset = 0;
- instruction_address.selector = 0;
- instruction_address.opcode = 0;
- no_ip_update = 1;
-}
-
-/*
- * These are nops on the i387..
- */
-#define feni fnop
-#define fdisi fnop
-#define fsetpm fnop
-
-static FUNC const finit_table[] = {
- feni, fdisi, fclex, finit,
- fsetpm, FPU_illegal, FPU_illegal, FPU_illegal
-};
-
-void finit_()
-{
- (finit_table[FPU_rm])();
-}
-
-
-static void fstsw_ax(void)
-{
- *(short *) &FPU_EAX = status_word();
- no_ip_update = 1;
-}
-
-static FUNC const fstsw_table[] = {
- fstsw_ax, FPU_illegal, FPU_illegal, FPU_illegal,
- FPU_illegal, FPU_illegal, FPU_illegal, FPU_illegal
-};
-
-void fstsw_()
-{
- (fstsw_table[FPU_rm])();
-}
-
-
-static FUNC const fp_nop_table[] = {
- fnop, FPU_illegal, FPU_illegal, FPU_illegal,
- FPU_illegal, FPU_illegal, FPU_illegal, FPU_illegal
-};
-
-void fp_nop()
-{
- (fp_nop_table[FPU_rm])();
-}
-
-
-void fld_i_()
-{
- FPU_REG *st_new_ptr;
-
- if ( STACK_OVERFLOW )
- { stack_overflow(); return; }
-
- /* fld st(i) */
- if ( NOT_EMPTY(FPU_rm) )
- { reg_move(&st(FPU_rm), st_new_ptr); push(); }
- else
- {
- if ( control_word & CW_Invalid )
- {
- /* The masked response */
- stack_underflow();
- }
- else
- EXCEPTION(EX_StackUnder);
- }
-
-}
-
-
-void fxch_i()
-{
- /* fxch st(i) */
- FPU_REG t;
- register FPU_REG *sti_ptr = &st(FPU_rm), *st0_ptr = &st(0);
-
- if ( st0_ptr->tag == TW_Empty )
- {
- if ( sti_ptr->tag == TW_Empty )
- {
- stack_underflow();
- stack_underflow_i(FPU_rm);
- return;
- }
- if ( control_word & CW_Invalid )
- reg_move(sti_ptr, st0_ptr); /* Masked response */
- stack_underflow_i(FPU_rm);
- return;
- }
- if ( sti_ptr->tag == TW_Empty )
- {
- if ( control_word & CW_Invalid )
- reg_move(st0_ptr, sti_ptr); /* Masked response */
- stack_underflow();
- return;
- }
- clear_C1();
- reg_move(st0_ptr, &t);
- reg_move(sti_ptr, st0_ptr);
- reg_move(&t, sti_ptr);
-}
-
-
-void ffree_()
-{
- /* ffree st(i) */
- st(FPU_rm).tag = TW_Empty;
-}
-
-
-void ffreep()
-{
- /* ffree st(i) + pop - unofficial code */
- st(FPU_rm).tag = TW_Empty;
- pop();
-}
-
-
-void fst_i_()
-{
- /* fst st(i) */
- reg_move(&st(0), &st(FPU_rm));
-}
-
-
-void fstp_i()
-{
- /* fstp st(i) */
- reg_move(&st(0), &st(FPU_rm));
- pop();
-}
-
diff --git a/drivers/FPU-emu/fpu_emu.h b/drivers/FPU-emu/fpu_emu.h
deleted file mode 100644
index 9d2c5dd13..000000000
--- a/drivers/FPU-emu/fpu_emu.h
+++ /dev/null
@@ -1,171 +0,0 @@
-/*---------------------------------------------------------------------------+
- | fpu_emu.h |
- | |
- | Copyright (C) 1992,1993,1994 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- +---------------------------------------------------------------------------*/
-
-
-#ifndef _FPU_EMU_H_
-#define _FPU_EMU_H_
-
-/*
- * Define DENORM_OPERAND to make the emulator detect denormals
- * and use the denormal flag of the status word. Note: this only
- * affects the flag and corresponding interrupt, the emulator
- * will always generate denormals and operate upon them as required.
- */
-#define DENORM_OPERAND
-
-/*
- * Define PECULIAR_486 to get a closer approximation to 80486 behaviour,
- * rather than behaviour which appears to be cleaner.
- * This is a matter of opinion: for all I know, the 80486 may simply
- * be complying with the IEEE spec. Maybe one day I'll get to see the
- * spec...
- */
-#define PECULIAR_486
-
-#ifdef __ASSEMBLER__
-#include "fpu_asm.h"
-#define Const(x) $##x
-#else
-#define Const(x) x
-#endif
-
-#define EXP_BIAS Const(0)
-#define EXP_OVER Const(0x4000) /* smallest invalid large exponent */
-#define EXP_UNDER Const(-0x3fff) /* largest invalid small exponent */
-#define EXP_Infinity EXP_OVER
-#define EXP_NaN EXP_OVER
-
-#define SIGN_POS Const(0)
-#define SIGN_NEG Const(1)
-
-/* Keep the order TW_Valid, TW_Zero, TW_Denormal */
-#define TW_Valid Const(0) /* valid */
-#define TW_Zero Const(1) /* zero */
-/* The following fold to 2 (Special) in the Tag Word */
-/* #define TW_Denormal Const(4) */ /* De-normal */
-#define TW_Infinity Const(5) /* + or - infinity */
-#define TW_NaN Const(6) /* Not a Number */
-
-#define TW_Empty Const(7) /* empty */
-
-
-#ifndef __ASSEMBLER__
-
-#include <linux/math_emu.h>
-#include <linux/linkage.h>
-
-/*
-#define RE_ENTRANT_CHECKING
- */
-
-#ifdef RE_ENTRANT_CHECKING
-extern char emulating;
-# define RE_ENTRANT_CHECK_OFF emulating = 0
-# define RE_ENTRANT_CHECK_ON emulating = 1
-#else
-# define RE_ENTRANT_CHECK_OFF
-# define RE_ENTRANT_CHECK_ON
-#endif RE_ENTRANT_CHECKING
-
-#define FWAIT_OPCODE 0x9b
-#define OP_SIZE_PREFIX 0x66
-#define ADDR_SIZE_PREFIX 0x67
-#define PREFIX_CS 0x2e
-#define PREFIX_DS 0x3e
-#define PREFIX_ES 0x26
-#define PREFIX_SS 0x36
-#define PREFIX_FS 0x64
-#define PREFIX_GS 0x65
-#define PREFIX_REPE 0xf3
-#define PREFIX_REPNE 0xf2
-#define PREFIX_LOCK 0xf0
-#define PREFIX_CS_ 1
-#define PREFIX_DS_ 2
-#define PREFIX_ES_ 3
-#define PREFIX_FS_ 4
-#define PREFIX_GS_ 5
-#define PREFIX_SS_ 6
-#define PREFIX_DEFAULT 7
-
-struct address {
- unsigned int offset;
- unsigned int selector:16;
- unsigned int opcode:11;
- unsigned int empty:5;
-};
-typedef void (*FUNC)(void);
-typedef struct fpu_reg FPU_REG;
-typedef void (*FUNC_ST0)(FPU_REG *st0_ptr);
-typedef struct { unsigned char address_size, operand_size, segment; }
- overrides;
-/* This structure is 32 bits: */
-typedef struct { overrides override;
- unsigned char default_mode; } fpu_addr_modes;
-/* PROTECTED has a restricted meaning in the emulator; it is used
- to signal that the emulator needs to do special things to ensure
- that protection is respected in a segmented model. */
-#define PROTECTED 4
-#define SIXTEEN 1 /* We rely upon this being 1 (true) */
-#define VM86 SIXTEEN
-#define PM16 (SIXTEEN | PROTECTED)
-#define SEG32 PROTECTED
-extern unsigned char const data_sizes_16[32];
-
-#define st(x) ( regs[((top+x) &7 )] )
-
-#define STACK_OVERFLOW (st_new_ptr = &st(-1), st_new_ptr->tag != TW_Empty)
-#define NOT_EMPTY(i) (st(i).tag != TW_Empty)
-#define NOT_EMPTY_ST0 (st0_tag ^ TW_Empty)
-
-#define pop() { regs[(top++ & 7 )].tag = TW_Empty; }
-#define poppop() { regs[((top + 1) & 7 )].tag \
- = regs[(top & 7 )].tag = TW_Empty; \
- top += 2; }
-
-/* push() does not affect the tags */
-#define push() { top--; }
-
-
-#define reg_move(x, y) { \
- *(short *)&((y)->sign) = *(short *)&((x)->sign); \
- *(long *)&((y)->exp) = *(long *)&((x)->exp); \
- *(long long *)&((y)->sigl) = *(long long *)&((x)->sigl); }
-
-#define significand(x) ( ((unsigned long long *)&((x)->sigl))[0] )
-
-
-/*----- Prototypes for functions written in assembler -----*/
-/* extern void reg_move(FPU_REG *a, FPU_REG *b); */
-
-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,
- FPU_REG *answ, unsigned int control_w);
-asmlinkage int reg_u_sub(FPU_REG const *arg1, FPU_REG const *arg2,
- FPU_REG *answ, unsigned int control_w);
-asmlinkage int reg_u_mul(FPU_REG const *arg1, FPU_REG const *arg2,
- FPU_REG *answ, unsigned int control_w);
-asmlinkage int reg_u_div(FPU_REG const *arg1, FPU_REG const *arg2,
- FPU_REG *answ, unsigned int control_w);
-asmlinkage int reg_u_add(FPU_REG const *arg1, FPU_REG const *arg2,
- FPU_REG *answ, unsigned int control_w);
-asmlinkage int wm_sqrt(FPU_REG *n, unsigned int control_w);
-asmlinkage unsigned shrx(void *l, unsigned x);
-asmlinkage unsigned shrxs(void *v, unsigned x);
-asmlinkage unsigned long div_small(unsigned long long *x, unsigned long y);
-asmlinkage void round_reg(FPU_REG *arg, unsigned int extent,
- unsigned int control_w);
-
-#ifndef MAKING_PROTO
-#include "fpu_proto.h"
-#endif
-
-#endif __ASSEMBLER__
-
-#endif _FPU_EMU_H_
diff --git a/drivers/FPU-emu/fpu_entry.c b/drivers/FPU-emu/fpu_entry.c
deleted file mode 100644
index b2777a722..000000000
--- a/drivers/FPU-emu/fpu_entry.c
+++ /dev/null
@@ -1,690 +0,0 @@
-/*---------------------------------------------------------------------------+
- | fpu_entry.c |
- | |
- | The entry function for wm-FPU-emu |
- | |
- | Copyright (C) 1992,1993,1994 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | See the files "README" and "COPYING" for further copyright and warranty |
- | information. |
- | |
- +---------------------------------------------------------------------------*/
-
-/*---------------------------------------------------------------------------+
- | Note: |
- | The file contains code which accesses user memory. |
- | Emulator static data may change when user memory is accessed, due to |
- | other processes using the emulator while swapping is in progress. |
- +---------------------------------------------------------------------------*/
-
-/*---------------------------------------------------------------------------+
- | math_emulate() is the sole entry point for wm-FPU-emu |
- +---------------------------------------------------------------------------*/
-
-#include <linux/signal.h>
-
-#include <asm/segment.h>
-
-#include "fpu_system.h"
-#include "fpu_emu.h"
-#include "exception.h"
-#include "control_w.h"
-#include "status_w.h"
-
-#define __BAD__ FPU_illegal /* Illegal on an 80486, causes SIGILL */
-
-#ifndef NO_UNDOC_CODE /* Un-documented FPU op-codes supported by default. */
-
-/* WARNING: These codes are not documented by Intel in their 80486 manual
- and may not work on FPU clones or later Intel FPUs. */
-
-/* Changes to support the un-doc codes provided by Linus Torvalds. */
-
-#define _d9_d8_ fstp_i /* unofficial code (19) */
-#define _dc_d0_ fcom_st /* unofficial code (14) */
-#define _dc_d8_ fcompst /* unofficial code (1c) */
-#define _dd_c8_ fxch_i /* unofficial code (0d) */
-#define _de_d0_ fcompst /* unofficial code (16) */
-#define _df_c0_ ffreep /* unofficial code (07) ffree + pop */
-#define _df_c8_ fxch_i /* unofficial code (0f) */
-#define _df_d0_ fstp_i /* unofficial code (17) */
-#define _df_d8_ fstp_i /* unofficial code (1f) */
-
-static FUNC const st_instr_table[64] = {
- fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, _df_c0_,
- fmul__, fxch_i, __BAD__, __BAD__, fmul_i, _dd_c8_, fmulp_, _df_c8_,
- fcom_st, fp_nop, __BAD__, __BAD__, _dc_d0_, fst_i_, _de_d0_, _df_d0_,
- fcompst, _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i, fcompp, _df_d8_,
- fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
- fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
- fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
- fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
-};
-
-#else /* Support only documented FPU op-codes */
-
-static FUNC const st_instr_table[64] = {
- fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, __BAD__,
- fmul__, fxch_i, __BAD__, __BAD__, fmul_i, __BAD__, fmulp_, __BAD__,
- fcom_st, fp_nop, __BAD__, __BAD__, __BAD__, fst_i_, __BAD__, __BAD__,
- fcompst, __BAD__, __BAD__, __BAD__, __BAD__, fstp_i, fcompp, __BAD__,
- fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
- fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
- fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
- fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
-};
-
-#endif NO_UNDOC_CODE
-
-
-#define _NONE_ 0 /* Take no special action */
-#define _REG0_ 1 /* Need to check for not empty st(0) */
-#define _REGI_ 2 /* Need to check for not empty st(0) and st(rm) */
-#define _REGi_ 0 /* Uses st(rm) */
-#define _PUSH_ 3 /* Need to check for space to push onto stack */
-#define _null_ 4 /* Function illegal or not implemented */
-#define _REGIi 5 /* Uses st(0) and st(rm), result to st(rm) */
-#define _REGIp 6 /* Uses st(0) and st(rm), result to st(rm) then pop */
-#define _REGIc 0 /* Compare st(0) and st(rm) */
-#define _REGIn 0 /* Uses st(0) and st(rm), but handle checks later */
-
-#ifndef NO_UNDOC_CODE
-
-/* Un-documented FPU op-codes supported by default. (see above) */
-
-static unsigned char const type_table[64] = {
- _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_,
- _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_,
- _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
- _REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
- _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
- _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
- _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
- _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
-};
-
-#else /* Support only documented FPU op-codes */
-
-static unsigned char const type_table[64] = {
- _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _null_,
- _REGI_, _REGIn, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
- _REGIc, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_,
- _REGIc, _null_, _null_, _null_, _null_, _REG0_, _REGIc, _null_,
- _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
- _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
- _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
- _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
-};
-
-#endif NO_UNDOC_CODE
-
-
-#ifdef RE_ENTRANT_CHECKING
-char emulating=0;
-#endif RE_ENTRANT_CHECKING
-
-static int valid_prefix(unsigned char *Byte, unsigned char **fpu_eip,
- overrides *override);
-
-asmlinkage void math_emulate(long arg)
-{
- unsigned char FPU_modrm, byte1;
- unsigned short code;
- fpu_addr_modes addr_modes;
- int unmasked;
- FPU_REG loaded_data;
- void *data_address;
- struct address data_sel_off;
- struct address entry_sel_off;
- unsigned long code_base = 0;
- unsigned long code_limit = 0; /* Initialized to stop compiler warnings */
- char st0_tag;
- FPU_REG *st0_ptr;
- struct desc_struct code_descriptor;
-
-#ifdef RE_ENTRANT_CHECKING
- if ( emulating )
- {
- printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n");
- }
- RE_ENTRANT_CHECK_ON;
-#endif RE_ENTRANT_CHECKING
-
- if (!current->used_math)
- {
- int i;
- for ( i = 0; i < 8; i++ )
- {
- /* Make sure that the registers are compatible
- with the assumptions of the emulator. */
- regs[i].exp = 0;
- regs[i].sigh = 0x80000000;
- }
- finit();
- current->used_math = 1;
- }
-
- SETUP_DATA_AREA(arg);
-
- FPU_ORIG_EIP = FPU_EIP;
-
- if ( (FPU_EFLAGS & 0x00020000) != 0 )
- {
- /* Virtual 8086 mode */
- addr_modes.default_mode = VM86;
- FPU_EIP += code_base = FPU_CS << 4;
- code_limit = code_base + 0xffff; /* Assumes code_base <= 0xffff0000 */
- }
- else if ( FPU_CS == USER_CS && FPU_DS == USER_DS )
- {
- addr_modes.default_mode = 0;
- }
- else if ( FPU_CS == KERNEL_CS )
- {
- printk("math_emulate: %04x:%08lx\n",FPU_CS,FPU_EIP);
- panic("Math emulation needed in kernel");
- }
- else
- {
-
- if ( (FPU_CS & 4) != 4 ) /* Must be in the LDT */
- {
- /* Can only handle segmented addressing via the LDT
- for now, and it must be 16 bit */
- printk("FPU emulator: Unsupported addressing mode\n");
- math_abort(FPU_info, SIGILL);
- }
-
- if ( SEG_D_SIZE(code_descriptor = LDT_DESCRIPTOR(FPU_CS)) )
- {
- /* The above test may be wrong, the book is not clear */
- /* Segmented 32 bit protected mode */
- addr_modes.default_mode = SEG32;
- }
- else
- {
- /* 16 bit protected mode */
- addr_modes.default_mode = PM16;
- }
- FPU_EIP += code_base = SEG_BASE_ADDR(code_descriptor);
- code_limit = code_base
- + (SEG_LIMIT(code_descriptor)+1) * SEG_GRANULARITY(code_descriptor)
- - 1;
- if ( code_limit < code_base ) code_limit = 0xffffffff;
- }
-
- FPU_lookahead = 1;
- if (current->flags & PF_PTRACED)
- FPU_lookahead = 0;
-
- if ( !valid_prefix(&byte1, (unsigned char **)&FPU_EIP,
- &addr_modes.override) )
- {
- RE_ENTRANT_CHECK_OFF;
- printk("FPU emulator: Unknown prefix byte 0x%02x, probably due to\n"
- "FPU emulator: self-modifying code! (emulation impossible)\n",
- byte1);
- RE_ENTRANT_CHECK_ON;
- EXCEPTION(EX_INTERNAL|0x126);
- math_abort(FPU_info,SIGILL);
- }
-
-do_another_FPU_instruction:
-
- no_ip_update = 0;
-
- FPU_EIP++; /* We have fetched the prefix and first code bytes. */
-
- if ( addr_modes.default_mode )
- {
- /* This checks for the minimum instruction bytes.
- We also need to check any extra (address mode) code access. */
- if ( FPU_EIP > code_limit )
- math_abort(FPU_info,SIGSEGV);
- }
-
- if ( (byte1 & 0xf8) != 0xd8 )
- {
- if ( byte1 == FWAIT_OPCODE )
- {
- if (partial_status & SW_Summary)
- goto do_the_FPU_interrupt;
- else
- goto FPU_fwait_done;
- }
-#ifdef PARANOID
- EXCEPTION(EX_INTERNAL|0x128);
- math_abort(FPU_info,SIGILL);
-#endif PARANOID
- }
-
- RE_ENTRANT_CHECK_OFF;
- FPU_code_verify_area(1);
- FPU_modrm = get_fs_byte((unsigned char *) FPU_EIP);
- RE_ENTRANT_CHECK_ON;
- FPU_EIP++;
-
- if (partial_status & SW_Summary)
- {
- /* Ignore the error for now if the current instruction is a no-wait
- control instruction */
- /* The 80486 manual contradicts itself on this topic,
- but a real 80486 uses the following instructions:
- fninit, fnstenv, fnsave, fnstsw, fnstenv, fnclex.
- */
- code = (FPU_modrm << 8) | byte1;
- if ( ! ( (((code & 0xf803) == 0xe003) || /* fnclex, fninit, fnstsw */
- (((code & 0x3003) == 0x3001) && /* fnsave, fnstcw, fnstenv,
- fnstsw */
- ((code & 0xc000) != 0xc000))) ) )
- {
- /*
- * We need to simulate the action of the kernel to FPU
- * interrupts here.
- * Currently, the "real FPU" part of the kernel (0.99.10)
- * clears the exception flags, sets the registers to empty,
- * and passes information back to the interrupted process
- * via the cs selector and operand selector, so we do the same.
- */
- do_the_FPU_interrupt:
- instruction_address.selector = status_word();
- operand_address.selector = tag_word();
- partial_status = 0;
- top = 0;
- {
- int r;
- for (r = 0; r < 8; r++)
- {
- regs[r].tag = TW_Empty;
- }
- }
-
- FPU_EIP = FPU_ORIG_EIP; /* Point to current FPU instruction. */
-
- RE_ENTRANT_CHECK_OFF;
- current->tss.trap_no = 16;
- current->tss.error_code = 0;
- send_sig(SIGFPE, current, 1);
- return;
- }
- }
-
- entry_sel_off.offset = FPU_ORIG_EIP;
- entry_sel_off.selector = FPU_CS;
- entry_sel_off.opcode = (byte1 << 8) | FPU_modrm;
-
- FPU_rm = FPU_modrm & 7;
-
- if ( FPU_modrm < 0300 )
- {
- /* All of these instructions use the mod/rm byte to get a data address */
-
- if ( (addr_modes.default_mode & SIXTEEN)
- ^ (addr_modes.override.address_size == ADDR_SIZE_PREFIX) )
- data_address = get_address_16(FPU_modrm, &FPU_EIP, &data_sel_off,
- addr_modes);
- else
- data_address = get_address(FPU_modrm, &FPU_EIP, &data_sel_off,
- addr_modes);
-
- if ( addr_modes.default_mode )
- {
- if ( FPU_EIP-1 > code_limit )
- math_abort(FPU_info,SIGSEGV);
- }
-
- if ( !(byte1 & 1) )
- {
- unsigned short status1 = partial_status;
-
- st0_ptr = &st(0);
- st0_tag = st0_ptr->tag;
-
- /* Stack underflow has priority */
- if ( NOT_EMPTY_ST0 )
- {
- if ( addr_modes.default_mode & PROTECTED )
- {
- /* This table works for 16 and 32 bit protected mode */
- if ( access_limit < data_sizes_16[(byte1 >> 1) & 3] )
- math_abort(FPU_info,SIGSEGV);
- }
-
- unmasked = 0; /* Do this here to stop compiler warnings. */
- switch ( (byte1 >> 1) & 3 )
- {
- case 0:
- unmasked = reg_load_single((float *)data_address,
- &loaded_data);
- break;
- case 1:
- reg_load_int32((long *)data_address, &loaded_data);
- break;
- case 2:
- unmasked = reg_load_double((double *)data_address,
- &loaded_data);
- break;
- case 3:
- reg_load_int16((short *)data_address, &loaded_data);
- break;
- }
-
- /* No more access to user memory, it is safe
- to use static data now */
-
- /* NaN operands have the next priority. */
- /* We have to delay looking at st(0) until after
- loading the data, because that data might contain an SNaN */
- if ( (st0_tag == TW_NaN) ||
- (loaded_data.tag == TW_NaN) )
- {
- /* Restore the status word; we might have loaded a
- denormal. */
- partial_status = status1;
- if ( (FPU_modrm & 0x30) == 0x10 )
- {
- /* fcom or fcomp */
- EXCEPTION(EX_Invalid);
- setcc(SW_C3 | SW_C2 | SW_C0);
- if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) )
- pop(); /* fcomp, masked, so we pop. */
- }
- else
- {
-#ifdef PECULIAR_486
- /* This is not really needed, but gives behaviour
- identical to an 80486 */
- if ( (FPU_modrm & 0x28) == 0x20 )
- /* fdiv or fsub */
- real_2op_NaN(&loaded_data, st0_ptr,
- st0_ptr);
- else
-#endif PECULIAR_486
- /* fadd, fdivr, fmul, or fsubr */
- real_2op_NaN(st0_ptr, &loaded_data,
- st0_ptr);
- }
- goto reg_mem_instr_done;
- }
-
- if ( unmasked && !((FPU_modrm & 0x30) == 0x10) )
- {
- /* Is not a comparison instruction. */
- if ( (FPU_modrm & 0x38) == 0x38 )
- {
- /* fdivr */
- if ( (st0_tag == TW_Zero) &&
- (loaded_data.tag == TW_Valid) )
- {
- if ( divide_by_zero(loaded_data.sign,
- st0_ptr) )
- {
- /* We use the fact here that the unmasked
- exception in the loaded data was for a
- denormal operand */
- /* Restore the state of the denormal op bit */
- partial_status &= ~SW_Denorm_Op;
- partial_status |= status1 & SW_Denorm_Op;
- }
- }
- }
- goto reg_mem_instr_done;
- }
-
- switch ( (FPU_modrm >> 3) & 7 )
- {
- case 0: /* fadd */
- clear_C1();
- reg_add(st0_ptr, &loaded_data, st0_ptr,
- control_word);
- break;
- case 1: /* fmul */
- clear_C1();
- reg_mul(st0_ptr, &loaded_data, st0_ptr,
- control_word);
- break;
- case 2: /* fcom */
- compare_st_data(&loaded_data);
- break;
- case 3: /* fcomp */
- if ( !compare_st_data(&loaded_data) && !unmasked )
- pop();
- break;
- case 4: /* fsub */
- clear_C1();
- reg_sub(st0_ptr, &loaded_data, st0_ptr,
- control_word);
- break;
- case 5: /* fsubr */
- clear_C1();
- reg_sub(&loaded_data, st0_ptr, st0_ptr,
- control_word);
- break;
- case 6: /* fdiv */
- clear_C1();
- reg_div(st0_ptr, &loaded_data, st0_ptr,
- control_word);
- break;
- case 7: /* fdivr */
- clear_C1();
- if ( st0_tag == TW_Zero )
- partial_status = status1; /* Undo any denorm tag,
- zero-divide has priority. */
- reg_div(&loaded_data, st0_ptr, st0_ptr,
- control_word);
- break;
- }
- }
- else
- {
- if ( (FPU_modrm & 0x30) == 0x10 )
- {
- /* The instruction is fcom or fcomp */
- EXCEPTION(EX_StackUnder);
- setcc(SW_C3 | SW_C2 | SW_C0);
- if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) )
- pop(); /* fcomp */
- }
- else
- stack_underflow();
- }
- reg_mem_instr_done:
- operand_address = data_sel_off;
- }
- else
- {
- if ( !(no_ip_update =
- load_store_instr(((FPU_modrm & 0x38) | (byte1 & 6)) >> 1,
- addr_modes, data_address)) )
- {
- operand_address = data_sel_off;
- }
- }
-
- }
- else
- {
- /* None of these instructions access user memory */
- unsigned char instr_index = (FPU_modrm & 0x38) | (byte1 & 7);
-
-#ifdef PECULIAR_486
- /* This is supposed to be undefined, but a real 80486 seems
- to do this: */
- operand_address.offset = 0;
- operand_address.selector = FPU_DS;
-#endif PECULIAR_486
-
- st0_ptr = &st(0);
- st0_tag = st0_ptr->tag;
- switch ( type_table[(int) instr_index] )
- {
- case _NONE_: /* also _REGIc: _REGIn */
- break;
- case _REG0_:
- if ( !NOT_EMPTY_ST0 )
- {
- stack_underflow();
- goto FPU_instruction_done;
- }
- break;
- case _REGIi:
- if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) )
- {
- stack_underflow_i(FPU_rm);
- goto FPU_instruction_done;
- }
- break;
- case _REGIp:
- if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) )
- {
- stack_underflow_pop(FPU_rm);
- goto FPU_instruction_done;
- }
- break;
- case _REGI_:
- if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) )
- {
- stack_underflow();
- goto FPU_instruction_done;
- }
- break;
- case _PUSH_: /* Only used by the fld st(i) instruction */
- break;
- case _null_:
- FPU_illegal();
- goto FPU_instruction_done;
- default:
- EXCEPTION(EX_INTERNAL|0x111);
- goto FPU_instruction_done;
- }
- (*st_instr_table[(int) instr_index])();
-
-FPU_instruction_done:
- ;
- }
-
- if ( ! no_ip_update )
- instruction_address = entry_sel_off;
-
-FPU_fwait_done:
-
-#ifdef DEBUG
- RE_ENTRANT_CHECK_OFF;
- emu_printall();
- RE_ENTRANT_CHECK_ON;
-#endif DEBUG
-
- if (FPU_lookahead && !need_resched)
- {
- FPU_ORIG_EIP = FPU_EIP - code_base;
- if ( valid_prefix(&byte1, (unsigned char **)&FPU_EIP,
- &addr_modes.override) )
- goto do_another_FPU_instruction;
- }
-
- if ( addr_modes.default_mode )
- FPU_EIP -= code_base;
-
- RE_ENTRANT_CHECK_OFF;
-}
-
-
-/* Support for prefix bytes is not yet complete. To properly handle
- all prefix bytes, further changes are needed in the emulator code
- which accesses user address space. Access to separate segments is
- important for msdos emulation. */
-static int valid_prefix(unsigned char *Byte, unsigned char **fpu_eip,
- overrides *override)
-{
- unsigned char byte;
- unsigned char *ip = *fpu_eip;
-
- *override = (overrides) { 0, 0, PREFIX_DEFAULT }; /* defaults */
-
- RE_ENTRANT_CHECK_OFF;
- FPU_code_verify_area(1);
- byte = get_fs_byte(ip);
- RE_ENTRANT_CHECK_ON;
-
- while ( 1 )
- {
- switch ( byte )
- {
- case ADDR_SIZE_PREFIX:
- override->address_size = ADDR_SIZE_PREFIX;
- goto do_next_byte;
-
- case OP_SIZE_PREFIX:
- override->operand_size = OP_SIZE_PREFIX;
- goto do_next_byte;
-
- case PREFIX_CS:
- override->segment = PREFIX_CS_;
- goto do_next_byte;
- case PREFIX_ES:
- override->segment = PREFIX_ES_;
- goto do_next_byte;
- case PREFIX_SS:
- override->segment = PREFIX_SS_;
- goto do_next_byte;
- case PREFIX_FS:
- override->segment = PREFIX_FS_;
- goto do_next_byte;
- case PREFIX_GS:
- override->segment = PREFIX_GS_;
- goto do_next_byte;
- case PREFIX_DS:
- override->segment = PREFIX_DS_;
- goto do_next_byte;
-
-/* lock is not a valid prefix for FPU instructions,
- let the cpu handle it to generate a SIGILL. */
-/* case PREFIX_LOCK: */
-
- /* rep.. prefixes have no meaning for FPU instructions */
- case PREFIX_REPE:
- case PREFIX_REPNE:
-
- do_next_byte:
- ip++;
- RE_ENTRANT_CHECK_OFF;
- FPU_code_verify_area(1);
- byte = get_fs_byte(ip);
- RE_ENTRANT_CHECK_ON;
- break;
- case FWAIT_OPCODE:
- *Byte = byte;
- return 1;
- default:
- if ( (byte & 0xf8) == 0xd8 )
- {
- *Byte = byte;
- *fpu_eip = ip;
- return 1;
- }
- else
- {
- /* Not a valid sequence of prefix bytes followed by
- an FPU instruction. */
- *Byte = byte; /* Needed for error message. */
- return 0;
- }
- }
- }
-}
-
-
-void math_abort(struct info * info, unsigned int signal)
-{
- FPU_EIP = FPU_ORIG_EIP;
- current->tss.trap_no = 16;
- current->tss.error_code = 0;
- send_sig(signal,current,1);
- RE_ENTRANT_CHECK_OFF;
- __asm__("movl %0,%%esp ; ret": :"g" (((long) info)-4));
-#ifdef PARANOID
- printk("ERROR: wm-FPU-emu math_abort failed!\n");
-#endif PARANOID
-}
diff --git a/drivers/FPU-emu/fpu_etc.c b/drivers/FPU-emu/fpu_etc.c
deleted file mode 100644
index 20e3294ca..000000000
--- a/drivers/FPU-emu/fpu_etc.c
+++ /dev/null
@@ -1,129 +0,0 @@
-/*---------------------------------------------------------------------------+
- | fpu_etc.c |
- | |
- | Implement a few FPU instructions. |
- | |
- | Copyright (C) 1992,1993,1994 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
-
-#include "fpu_system.h"
-#include "exception.h"
-#include "fpu_emu.h"
-#include "status_w.h"
-#include "reg_constant.h"
-
-
-static void fchs(FPU_REG *st0_ptr)
-{
- if ( st0_ptr->tag ^ TW_Empty )
- {
- st0_ptr->sign ^= SIGN_POS^SIGN_NEG;
- clear_C1();
- }
- else
- stack_underflow();
-}
-
-static void fabs(FPU_REG *st0_ptr)
-{
- if ( st0_ptr->tag ^ TW_Empty )
- {
- st0_ptr->sign = SIGN_POS;
- clear_C1();
- }
- else
- stack_underflow();
-}
-
-
-static void ftst_(FPU_REG *st0_ptr)
-{
- switch (st0_ptr->tag)
- {
- case TW_Zero:
- setcc(SW_C3);
- break;
- case TW_Valid:
- if (st0_ptr->sign == SIGN_POS)
- setcc(0);
- else
- setcc(SW_C0);
-
-#ifdef DENORM_OPERAND
- if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- {
-#ifdef PECULIAR_486
- /* This is weird! */
- if (st0_ptr->sign == SIGN_POS)
- setcc(SW_C3);
-#endif PECULIAR_486
- return;
- }
-#endif DENORM_OPERAND
-
- break;
- case TW_NaN:
- setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */
- EXCEPTION(EX_Invalid);
- break;
- case TW_Infinity:
- if (st0_ptr->sign == SIGN_POS)
- setcc(0);
- else
- setcc(SW_C0);
- break;
- case TW_Empty:
- setcc(SW_C0|SW_C2|SW_C3);
- EXCEPTION(EX_StackUnder);
- break;
- default:
- setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */
- EXCEPTION(EX_INTERNAL|0x14);
- break;
- }
-}
-
-static void fxam(FPU_REG *st0_ptr)
-{
- int c=0;
- switch (st0_ptr->tag)
- {
- case TW_Empty:
- c = SW_C3|SW_C0;
- break;
- case TW_Zero:
- c = SW_C3;
- break;
- case TW_Valid:
- /* This will need to be changed if TW_Denormal is ever used. */
- if ( st0_ptr->exp <= EXP_UNDER )
- c = SW_C2|SW_C3; /* Denormal */
- else
- c = SW_C2;
- break;
- case TW_NaN:
- c = SW_C0;
- break;
- case TW_Infinity:
- c = SW_C2|SW_C0;
- break;
- }
- if (st0_ptr->sign == SIGN_NEG)
- c |= SW_C1;
- setcc(c);
-}
-
-
-static FUNC_ST0 const fp_etc_table[] = {
- fchs, fabs, (FUNC_ST0)FPU_illegal, (FUNC_ST0)FPU_illegal,
- ftst_, fxam, (FUNC_ST0)FPU_illegal, (FUNC_ST0)FPU_illegal
-};
-
-void fp_etc()
-{
- (fp_etc_table[FPU_rm])(&st(0));
-}
diff --git a/drivers/FPU-emu/fpu_proto.h b/drivers/FPU-emu/fpu_proto.h
deleted file mode 100644
index b4392fe57..000000000
--- a/drivers/FPU-emu/fpu_proto.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/* errors.c */
-extern void Un_impl(void);
-extern void FPU_illegal(void);
-extern void emu_printall(void);
-extern void stack_overflow(void);
-extern void stack_underflow(void);
-extern void stack_underflow_i(int i);
-extern void stack_underflow_pop(int i);
-extern int set_precision_flag(int flags);
-asmlinkage void exception(int n);
-asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest);
-asmlinkage int arith_invalid(FPU_REG *dest);
-asmlinkage int divide_by_zero(int sign, FPU_REG *dest);
-asmlinkage void set_precision_flag_up(void);
-asmlinkage void set_precision_flag_down(void);
-asmlinkage int denormal_operand(void);
-asmlinkage int arith_overflow(FPU_REG *dest);
-asmlinkage int arith_underflow(FPU_REG *dest);
-
-/* fpu_arith.c */
-extern void fadd__(void);
-extern void fmul__(void);
-extern void fsub__(void);
-extern void fsubr_(void);
-extern void fdiv__(void);
-extern void fdivr_(void);
-extern void fadd_i(void);
-extern void fmul_i(void);
-extern void fsubri(void);
-extern void fsub_i(void);
-extern void fdivri(void);
-extern void fdiv_i(void);
-extern void faddp_(void);
-extern void fmulp_(void);
-extern void fsubrp(void);
-extern void fsubp_(void);
-extern void fdivrp(void);
-extern void fdivp_(void);
-
-/* fpu_aux.c */
-extern void fclex(void);
-extern void finit(void);
-extern void finit_(void);
-extern void fstsw_(void);
-extern void fp_nop(void);
-extern void fld_i_(void);
-extern void fxch_i(void);
-extern void ffree_(void);
-extern void ffreep(void);
-extern void fst_i_(void);
-extern void fstp_i(void);
-
-/* fpu_entry.c */
-asmlinkage void math_emulate(long arg);
-extern void math_abort(struct info *info, unsigned int signal);
-
-/* fpu_etc.c */
-extern void fp_etc(void);
-
-/* fpu_trig.c */
-extern void convert_l2reg(long const *arg, FPU_REG *dest);
-extern void trig_a(void);
-extern void trig_b(void);
-
-/* get_address.c */
-extern void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
- struct address *addr,
- fpu_addr_modes);
-extern void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip,
- struct address *addr,
- fpu_addr_modes);
-
-/* load_store.c */
-extern int load_store_instr(unsigned char type, fpu_addr_modes addr_modes,
- void *address);
-
-/* poly_2xm1.c */
-extern int poly_2xm1(FPU_REG const *arg, FPU_REG *result);
-
-/* poly_atan.c */
-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 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);
-
-/* reg_add_sub.c */
-extern int reg_add(FPU_REG const *a, FPU_REG const *b,
- FPU_REG *dest, int control_w);
-extern int reg_sub(FPU_REG const *a, FPU_REG const *b,
- FPU_REG *dest, int control_w);
-
-/* reg_compare.c */
-extern int compare(FPU_REG const *b);
-extern int compare_st_data(FPU_REG const *b);
-extern void fcom_st(void);
-extern void fcompst(void);
-extern void fcompp(void);
-extern void fucom_(void);
-extern void fucomp(void);
-extern void fucompp(void);
-
-/* reg_constant.c */
-extern void fconst(void);
-
-/* reg_ld_str.c */
-extern int reg_load_extended(long double *addr, FPU_REG *loaded_data);
-extern int reg_load_double(double *dfloat, FPU_REG *loaded_data);
-extern int reg_load_single(float *single, FPU_REG *loaded_data);
-extern void reg_load_int64(long long *_s, FPU_REG *loaded_data);
-extern void reg_load_int32(long *_s, FPU_REG *loaded_data);
-extern void reg_load_int16(short *_s, FPU_REG *loaded_data);
-extern void reg_load_bcd(char *s, FPU_REG *loaded_data);
-extern int reg_store_extended(long double *d, FPU_REG *st0_ptr);
-extern int reg_store_double(double *dfloat, FPU_REG *st0_ptr);
-extern int reg_store_single(float *single, FPU_REG *st0_ptr);
-extern int reg_store_int64(long long *d, FPU_REG *st0_ptr);
-extern int reg_store_int32(long *d, FPU_REG *st0_ptr);
-extern int reg_store_int16(short *d, FPU_REG *st0_ptr);
-extern int reg_store_bcd(char *d, FPU_REG *st0_ptr);
-extern int round_to_int(FPU_REG *r);
-extern char *fldenv(fpu_addr_modes addr_modes, char *address);
-extern void frstor(fpu_addr_modes addr_modes, char *address);
-extern unsigned short tag_word(void);
-extern char *fstenv(fpu_addr_modes addr_modes, char *address);
-extern void fsave(fpu_addr_modes addr_modes, char *address);
-
-/* reg_mul.c */
-extern int reg_mul(FPU_REG const *a, FPU_REG const *b,
- FPU_REG *dest, unsigned int control_w);
diff --git a/drivers/FPU-emu/fpu_system.h b/drivers/FPU-emu/fpu_system.h
deleted file mode 100644
index e09e08b15..000000000
--- a/drivers/FPU-emu/fpu_system.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*---------------------------------------------------------------------------+
- | fpu_system.h |
- | |
- | Copyright (C) 1992,1994 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- +---------------------------------------------------------------------------*/
-
-#ifndef _FPU_SYSTEM_H
-#define _FPU_SYSTEM_H
-
-/* system dependent definitions */
-
-#include <linux/sched.h>
-#include <linux/kernel.h>
-
-/* This sets the pointer FPU_info to point to the argument part
- of the stack frame of math_emulate() */
-#define SETUP_DATA_AREA(arg) FPU_info = (struct info *) &arg
-
-#define LDT_DESCRIPTOR(s) (current->ldt[(s) >> 3])
-#define SEG_D_SIZE(x) ((x).b & (3 << 21))
-#define SEG_G_BIT(x) ((x).b & (1 << 23))
-#define SEG_GRANULARITY(x) (((x).b & (1 << 23)) ? 4096 : 1)
-#define SEG_286_MODE(x) ((x).b & ( 0xff000000 | 0xf0000 | (1 << 23)))
-#define SEG_BASE_ADDR(s) (((s).b & 0xff000000) \
- | (((s).b & 0xff) << 16) | ((s).a >> 16))
-#define SEG_LIMIT(s) (((s).b & 0xff0000) | ((s).a & 0xffff))
-#define SEG_EXECUTE_ONLY(s) (((s).b & ((1 << 11) | (1 << 9))) == (1 << 11))
-#define SEG_WRITE_PERM(s) (((s).b & ((1 << 11) | (1 << 9))) == (1 << 9))
-#define SEG_EXPAND_DOWN(s) (((s).b & ((1 << 11) | (1 << 10))) \
- == (1 << 10))
-
-#define I387 (current->tss.i387)
-#define FPU_info (I387.soft.info)
-
-#define FPU_CS (*(unsigned short *) &(FPU_info->___cs))
-#define FPU_SS (*(unsigned short *) &(FPU_info->___ss))
-#define FPU_DS (*(unsigned short *) &(FPU_info->___ds))
-#define FPU_EAX (FPU_info->___eax)
-#define FPU_EFLAGS (FPU_info->___eflags)
-#define FPU_EIP (FPU_info->___eip)
-#define FPU_ORIG_EIP (FPU_info->___orig_eip)
-
-#define FPU_lookahead (I387.soft.lookahead)
-
-/* nz if ip_offset and cs_selector are not to be set for the current
- instruction. */
-#define no_ip_update (((char *)&(I387.soft.twd))[0])
-#define FPU_rm (((unsigned char *)&(I387.soft.twd))[1])
-
-/* Number of bytes of data which can be legally accessed by the current
- instruction. This only needs to hold a number <= 108, so a byte will do. */
-#define access_limit (((unsigned char *)&(I387.soft.twd))[2])
-
-#define partial_status (I387.soft.swd)
-#define control_word (I387.soft.cwd)
-#define regs (I387.soft.regs)
-#define top (I387.soft.top)
-
-#define instruction_address (*(struct address *)&I387.soft.fip)
-#define operand_address (*(struct address *)&I387.soft.foo)
-
-#define FPU_verify_area(x,y,z) if ( verify_area(x,y,z) ) \
- math_abort(FPU_info,SIGSEGV)
-
-#undef FPU_IGNORE_CODE_SEGV
-#ifdef FPU_IGNORE_CODE_SEGV
-/* verify_area() is very expensive, and causes the emulator to run
- about 20% slower if applied to the code. Anyway, errors due to bad
- code addresses should be much rarer than errors due to bad data
- addresses. */
-#define FPU_code_verify_area(z)
-#else
-/* A simpler test than verify_area() can probably be done for
- FPU_code_verify_area() because the only possible error is to step
- past the upper boundary of a legal code area. */
-#define FPU_code_verify_area(z) FPU_verify_area(VERIFY_READ,(void *)FPU_EIP,z)
-#endif
-
-#endif
diff --git a/drivers/FPU-emu/fpu_trig.c b/drivers/FPU-emu/fpu_trig.c
deleted file mode 100644
index 05241f700..000000000
--- a/drivers/FPU-emu/fpu_trig.c
+++ /dev/null
@@ -1,1718 +0,0 @@
-/*---------------------------------------------------------------------------+
- | fpu_trig.c |
- | |
- | Implementation of the FPU "transcendental" functions. |
- | |
- | Copyright (C) 1992,1993,1994 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
-
-#include "fpu_system.h"
-#include "exception.h"
-#include "fpu_emu.h"
-#include "status_w.h"
-#include "control_w.h"
-#include "reg_constant.h"
-
-
-static void rem_kernel(unsigned long long st0, unsigned long long *y,
- unsigned long long st1,
- unsigned long long q, int n);
-
-#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
- using a value of pi with more than 128 bits precision. */
-/* Limited measurements show no results worse than 64 bit precision
- except for the results for arguments close to 2^63, where the
- precision of the result sometimes degrades to about 63.9 bits */
-static int trig_arg(FPU_REG *X, int even)
-{
- FPU_REG tmp;
- unsigned long long q;
- int old_cw = control_word, saved_status = partial_status;
-
- if ( X->exp >= EXP_BIAS + 63 )
- {
- partial_status |= SW_C2; /* Reduction incomplete. */
- return -1;
- }
-
- control_word &= ~CW_RC;
- control_word |= RC_CHOP;
-
- reg_div(X, &CONST_PI2, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
- round_to_int(&tmp); /* Fortunately, this can't overflow
- to 2^64 */
- q = significand(&tmp);
- if ( q )
- {
- rem_kernel(significand(X),
- &significand(&tmp),
- significand(&CONST_PI2),
- q, X->exp - CONST_PI2.exp);
- tmp.exp = CONST_PI2.exp;
- normalize(&tmp);
- reg_move(&tmp, X);
- }
-
-#ifdef FPTAN
- if ( even == FPTAN )
- {
- if ( ((X->exp >= EXP_BIAS) ||
- ((X->exp == EXP_BIAS-1)
- && (X->sigh >= 0xc90fdaa2))) ^ (q & 1) )
- even = FCOS;
- else
- even = 0;
- }
-#endif FPTAN
-
- if ( (even && !(q & 1)) || (!even && (q & 1)) )
- {
- reg_sub(&CONST_PI2, X, X, FULL_PRECISION);
-#ifdef BETTER_THAN_486
- /* So far, the results are exact but based upon a 64 bit
- precision approximation to pi/2. The technique used
- now is equivalent to using an approximation to pi/2 which
- is accurate to about 128 bits. */
- if ( (X->exp <= CONST_PI2extra.exp + 64) || (q > 1) )
- {
- /* This code gives the effect of having p/2 to better than
- 128 bits precision. */
- significand(&tmp) = q + 1;
- tmp.exp = EXP_BIAS + 63;
- tmp.tag = TW_Valid;
- normalize(&tmp);
- reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION);
- reg_add(X, &tmp, X, FULL_PRECISION);
- if ( X->sign == SIGN_NEG )
- {
- /* CONST_PI2extra is negative, so the result of the addition
- can be negative. This means that the argument is actually
- in a different quadrant. The correction is always < pi/2,
- so it can't overflow into yet another quadrant. */
- X->sign = SIGN_POS;
- q++;
- }
- }
-#endif BETTER_THAN_486
- }
-#ifdef BETTER_THAN_486
- else
- {
- /* So far, the results are exact but based upon a 64 bit
- precision approximation to pi/2. The technique used
- now is equivalent to using an approximation to pi/2 which
- is accurate to about 128 bits. */
- if ( ((q > 0) && (X->exp <= CONST_PI2extra.exp + 64)) || (q > 1) )
- {
- /* This code gives the effect of having p/2 to better than
- 128 bits precision. */
- significand(&tmp) = q;
- tmp.exp = EXP_BIAS + 63;
- tmp.tag = TW_Valid;
- normalize(&tmp);
- reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION);
- reg_sub(X, &tmp, X, FULL_PRECISION);
- if ( (X->exp == CONST_PI2.exp) &&
- ((X->sigh > CONST_PI2.sigh)
- || ((X->sigh == CONST_PI2.sigh)
- && (X->sigl > CONST_PI2.sigl))) )
- {
- /* CONST_PI2extra is negative, so the result of the
- subtraction can be larger than pi/2. This means
- that the argument is actually in a different quadrant.
- The correction is always < pi/2, so it can't overflow
- into yet another quadrant. */
- reg_sub(&CONST_PI, X, X, FULL_PRECISION);
- q++;
- }
- }
- }
-#endif BETTER_THAN_486
-
- control_word = old_cw;
- partial_status = saved_status & ~SW_C2; /* Reduction complete. */
-
- return (q & 3) | even;
-}
-
-
-/* Convert a long to register */
-void convert_l2reg(long const *arg, FPU_REG *dest)
-{
- long num = *arg;
-
- if (num == 0)
- { reg_move(&CONST_Z, dest); return; }
-
- if (num > 0)
- dest->sign = SIGN_POS;
- else
- { num = -num; dest->sign = SIGN_NEG; }
-
- dest->sigh = num;
- dest->sigl = 0;
- dest->exp = EXP_BIAS + 31;
- dest->tag = TW_Valid;
- normalize(dest);
-}
-
-
-static void single_arg_error(FPU_REG *st0_ptr)
-{
- switch ( st0_ptr->tag )
- {
- case TW_NaN:
- if ( !(st0_ptr->sigh & 0x40000000) ) /* Signaling ? */
- {
- EXCEPTION(EX_Invalid);
- if ( control_word & CW_Invalid )
- st0_ptr->sigh |= 0x40000000; /* Convert to a QNaN */
- }
- break; /* return with a NaN in st(0) */
- case TW_Empty:
- stack_underflow(); /* Puts a QNaN in st(0) */
- break;
-#ifdef PARANOID
- default:
- EXCEPTION(EX_INTERNAL|0x0112);
-#endif PARANOID
- }
-}
-
-
-static void single_arg_2_error(FPU_REG *st0_ptr)
-{
- FPU_REG *st_new_ptr;
-
- switch ( st0_ptr->tag )
- {
- case TW_NaN:
- if ( !(st0_ptr->sigh & 0x40000000) ) /* Signaling ? */
- {
- EXCEPTION(EX_Invalid);
- if ( control_word & CW_Invalid )
- {
- /* The masked response */
- /* Convert to a QNaN */
- st0_ptr->sigh |= 0x40000000;
- st_new_ptr = &st(-1);
- push();
- reg_move(&st(1), st_new_ptr);
- }
- }
- else
- {
- /* A QNaN */
- st_new_ptr = &st(-1);
- push();
- reg_move(&st(1), st_new_ptr);
- }
- break; /* return with a NaN in st(0) */
-#ifdef PARANOID
- default:
- EXCEPTION(EX_INTERNAL|0x0112);
-#endif PARANOID
- }
-}
-
-
-/*---------------------------------------------------------------------------*/
-
-static void f2xm1(FPU_REG *st0_ptr)
-{
- clear_C1();
- switch ( st0_ptr->tag )
- {
- case TW_Valid:
- {
- if ( st0_ptr->exp >= 0 )
- {
- /* For an 80486 FPU, the result is undefined. */
- }
-#ifdef DENORM_OPERAND
- else if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
- else
- {
- /* poly_2xm1(x) requires 0 < x < 1. */
- poly_2xm1(st0_ptr, st0_ptr);
- }
- if ( st0_ptr->exp <= EXP_UNDER )
- {
- /* A denormal result has been produced.
- Precision must have been lost, this is always
- an underflow. */
- arith_underflow(st0_ptr);
- }
- set_precision_flag_up(); /* 80486 appears to always do this */
- return;
- }
- case TW_Zero:
- return;
- case TW_Infinity:
- if ( st0_ptr->sign == SIGN_NEG )
- {
- /* -infinity gives -1 (p16-10) */
- reg_move(&CONST_1, st0_ptr);
- st0_ptr->sign = SIGN_NEG;
- }
- return;
- default:
- single_arg_error(st0_ptr);
- }
-}
-
-
-static void fptan(FPU_REG *st0_ptr)
-{
- char st0_tag = st0_ptr->tag;
- FPU_REG *st_new_ptr;
- int q;
- char arg_sign = st0_ptr->sign;
-
- /* Stack underflow has higher priority */
- if ( st0_tag == TW_Empty )
- {
- stack_underflow(); /* Puts a QNaN in st(0) */
- if ( control_word & CW_Invalid )
- {
- st_new_ptr = &st(-1);
- push();
- stack_underflow(); /* Puts a QNaN in the new st(0) */
- }
- return;
- }
-
- if ( STACK_OVERFLOW )
- { stack_overflow(); return; }
-
- switch ( st0_tag )
- {
- case TW_Valid:
- if ( st0_ptr->exp > EXP_BIAS - 40 )
- {
- st0_ptr->sign = SIGN_POS;
- if ( (q = trig_arg(st0_ptr, 0)) != -1 )
- {
- poly_tan(st0_ptr, st0_ptr);
- st0_ptr->sign = (q & 1) ^ arg_sign;
- }
- else
- {
- /* Operand is out of range */
- st0_ptr->sign = arg_sign; /* restore st(0) */
- return;
- }
- set_precision_flag_up(); /* We do not really know if up or down */
- }
- else
- {
- /* For a small arg, the result == the argument */
- /* Underflow may happen */
-
- if ( st0_ptr->exp <= EXP_UNDER )
- {
-#ifdef DENORM_OPERAND
- if ( denormal_operand() )
- return;
-#endif DENORM_OPERAND
- /* A denormal result has been produced.
- Precision must have been lost, this is always
- an underflow. */
- if ( arith_underflow(st0_ptr) )
- return;
- }
- set_precision_flag_down(); /* Must be down. */
- }
- push();
- reg_move(&CONST_1, st_new_ptr);
- return;
- break;
- case TW_Infinity:
- /* The 80486 treats infinity as an invalid operand */
- arith_invalid(st0_ptr);
- if ( control_word & CW_Invalid )
- {
- st_new_ptr = &st(-1);
- push();
- arith_invalid(st_new_ptr);
- }
- return;
- case TW_Zero:
- push();
- reg_move(&CONST_1, st_new_ptr);
- setcc(0);
- break;
- default:
- single_arg_2_error(st0_ptr);
- break;
- }
-}
-
-
-static void fxtract(FPU_REG *st0_ptr)
-{
- char st0_tag = st0_ptr->tag;
- FPU_REG *st_new_ptr;
- register FPU_REG *st1_ptr = st0_ptr; /* anticipate */
-
- if ( STACK_OVERFLOW )
- { stack_overflow(); return; }
- clear_C1();
- if ( !(st0_tag ^ TW_Valid) )
- {
- long e;
-
-#ifdef DENORM_OPERAND
- if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
-
- push();
- reg_move(st1_ptr, st_new_ptr);
- st_new_ptr->exp = EXP_BIAS;
- e = st1_ptr->exp - EXP_BIAS;
- convert_l2reg(&e, st1_ptr);
- return;
- }
- else if ( st0_tag == TW_Zero )
- {
- char sign = st0_ptr->sign;
- if ( divide_by_zero(SIGN_NEG, st0_ptr) )
- return;
- push();
- reg_move(&CONST_Z, st_new_ptr);
- st_new_ptr->sign = sign;
- return;
- }
- else if ( st0_tag == TW_Infinity )
- {
- char sign = st0_ptr->sign;
- st0_ptr->sign = SIGN_POS;
- push();
- reg_move(&CONST_INF, st_new_ptr);
- st_new_ptr->sign = sign;
- return;
- }
- else if ( st0_tag == TW_NaN )
- {
- if ( real_2op_NaN(st0_ptr, st0_ptr, st0_ptr) )
- return;
- push();
- reg_move(st1_ptr, st_new_ptr);
- return;
- }
- else if ( st0_tag == TW_Empty )
- {
- /* Is this the correct behaviour? */
- if ( control_word & EX_Invalid )
- {
- stack_underflow();
- push();
- stack_underflow();
- }
- else
- EXCEPTION(EX_StackUnder);
- }
-#ifdef PARANOID
- else
- EXCEPTION(EX_INTERNAL | 0x119);
-#endif PARANOID
-}
-
-
-static void fdecstp(FPU_REG *st0_ptr)
-{
- clear_C1();
- top--; /* st0_ptr will be fixed in math_emulate() before the next instr */
-}
-
-static void fincstp(FPU_REG *st0_ptr)
-{
- clear_C1();
- top++; /* st0_ptr will be fixed in math_emulate() before the next instr */
-}
-
-
-static void fsqrt_(FPU_REG *st0_ptr)
-{
- char st0_tag = st0_ptr->tag;
-
- clear_C1();
- if ( !(st0_tag ^ TW_Valid) )
- {
- int expon;
-
- if (st0_ptr->sign == SIGN_NEG)
- {
- arith_invalid(st0_ptr); /* sqrt(negative) is invalid */
- return;
- }
-
-#ifdef DENORM_OPERAND
- if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
-
- expon = st0_ptr->exp - EXP_BIAS;
- st0_ptr->exp = EXP_BIAS + (expon & 1); /* make st(0) in [1.0 .. 4.0) */
-
- wm_sqrt(st0_ptr, control_word); /* Do the computation */
-
- st0_ptr->exp += expon >> 1;
- st0_ptr->sign = SIGN_POS;
- }
- else if ( st0_tag == TW_Zero )
- return;
- else if ( st0_tag == TW_Infinity )
- {
- if ( st0_ptr->sign == SIGN_NEG )
- arith_invalid(st0_ptr); /* sqrt(-Infinity) is invalid */
- return;
- }
- else
- { single_arg_error(st0_ptr); return; }
-
-}
-
-
-static void frndint_(FPU_REG *st0_ptr)
-{
- char st0_tag = st0_ptr->tag;
- int flags;
-
- if ( !(st0_tag ^ TW_Valid) )
- {
- if (st0_ptr->exp > EXP_BIAS+63)
- return;
-
-#ifdef DENORM_OPERAND
- if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
-
- /* Fortunately, this can't overflow to 2^64 */
- if ( (flags = round_to_int(st0_ptr)) )
- set_precision_flag(flags);
-
- st0_ptr->exp = EXP_BIAS + 63;
- normalize(st0_ptr);
- return;
- }
- else if ( (st0_tag == TW_Zero) || (st0_tag == TW_Infinity) )
- return;
- else
- single_arg_error(st0_ptr);
-}
-
-
-static void fsin(FPU_REG *st0_ptr)
-{
- char st0_tag = st0_ptr->tag;
- char arg_sign = st0_ptr->sign;
-
- if ( st0_tag == TW_Valid )
- {
- FPU_REG rv;
- int q;
-
- if ( st0_ptr->exp > EXP_BIAS - 40 )
- {
- st0_ptr->sign = SIGN_POS;
- if ( (q = trig_arg(st0_ptr, 0)) != -1 )
- {
-
- poly_sine(st0_ptr, &rv);
-
- if (q & 2)
- rv.sign ^= SIGN_POS ^ SIGN_NEG;
- rv.sign ^= arg_sign;
- reg_move(&rv, st0_ptr);
-
- /* We do not really know if up or down */
- set_precision_flag_up();
- return;
- }
- else
- {
- /* Operand is out of range */
- st0_ptr->sign = arg_sign; /* restore st(0) */
- return;
- }
- }
- else
- {
- /* For a small arg, the result == the argument */
- /* Underflow may happen */
-
- if ( st0_ptr->exp <= EXP_UNDER )
- {
-#ifdef DENORM_OPERAND
- if ( denormal_operand() )
- return;
-#endif DENORM_OPERAND
- /* A denormal result has been produced.
- Precision must have been lost, this is always
- an underflow. */
- arith_underflow(st0_ptr);
- return;
- }
-
- set_precision_flag_up(); /* Must be up. */
- }
- }
- else if ( st0_tag == TW_Zero )
- {
- setcc(0);
- return;
- }
- else if ( st0_tag == TW_Infinity )
- {
- /* The 80486 treats infinity as an invalid operand */
- arith_invalid(st0_ptr);
- return;
- }
- else
- single_arg_error(st0_ptr);
-}
-
-
-static int f_cos(FPU_REG *arg)
-{
- char arg_sign = arg->sign;
-
- if ( arg->tag == TW_Valid )
- {
- FPU_REG rv;
- int q;
-
- if ( arg->exp > EXP_BIAS - 40 )
- {
- arg->sign = SIGN_POS;
- 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 )
- {
- poly_sine(arg, &rv);
-
- if ((q+1) & 2)
- rv.sign ^= SIGN_POS ^ SIGN_NEG;
- reg_move(&rv, arg);
-
- /* We do not really know if up or down */
- set_precision_flag_down();
-
- return 0;
- }
- else
- {
- /* Operand is out of range */
- arg->sign = arg_sign; /* restore st(0) */
- return 1;
- }
- }
- else
- {
-#ifdef DENORM_OPERAND
- if ( (arg->exp <= EXP_UNDER) && (denormal_operand()) )
- return 1;
-#endif DENORM_OPERAND
-
- setcc(0);
- reg_move(&CONST_1, arg);
-#ifdef PECULIAR_486
- set_precision_flag_down(); /* 80486 appears to do this. */
-#else
- set_precision_flag_up(); /* Must be up. */
-#endif PECULIAR_486
- return 0;
- }
- }
- else if ( arg->tag == TW_Zero )
- {
- reg_move(&CONST_1, arg);
- setcc(0);
- return 0;
- }
- else if ( arg->tag == TW_Infinity )
- {
- /* The 80486 treats infinity as an invalid operand */
- arith_invalid(arg);
- return 1;
- }
- else
- {
- single_arg_error(arg); /* requires arg == &st(0) */
- return 1;
- }
-}
-
-
-static void fcos(FPU_REG *st0_ptr)
-{
- f_cos(st0_ptr);
-}
-
-
-static void fsincos(FPU_REG *st0_ptr)
-{
- char st0_tag = st0_ptr->tag;
- FPU_REG *st_new_ptr;
- FPU_REG arg;
-
- /* Stack underflow has higher priority */
- if ( st0_tag == TW_Empty )
- {
- stack_underflow(); /* Puts a QNaN in st(0) */
- if ( control_word & CW_Invalid )
- {
- st_new_ptr = &st(-1);
- push();
- stack_underflow(); /* Puts a QNaN in the new st(0) */
- }
- return;
- }
-
- if ( STACK_OVERFLOW )
- { stack_overflow(); return; }
-
- if ( st0_tag == TW_NaN )
- {
- single_arg_2_error(st0_ptr);
- return;
- }
- else if ( st0_tag == TW_Infinity )
- {
- /* The 80486 treats infinity as an invalid operand */
- if ( !arith_invalid(st0_ptr) )
- {
- /* unmasked response */
- push();
- arith_invalid(st_new_ptr);
- }
- return;
- }
-
- reg_move(st0_ptr,&arg);
- if ( !f_cos(&arg) )
- {
- fsin(st0_ptr);
- push();
- reg_move(&arg,st_new_ptr);
- }
-
-}
-
-
-/*---------------------------------------------------------------------------*/
-/* The following all require two arguments: st(0) and st(1) */
-
-/* A lean, mean kernel for the fprem instructions. This relies upon
- the division and rounding to an integer in do_fprem giving an
- exact result. Because of this, rem_kernel() needs to deal only with
- the least significant 64 bits, the more significant bits of the
- result must be zero.
- */
-static void rem_kernel(unsigned long long st0, unsigned long long *y,
- unsigned long long st1,
- unsigned long long q, int n)
-{
- unsigned long long x;
-
- x = st0 << n;
-
- /* Do the required multiplication and subtraction in the one operation */
- asm volatile ("movl %2,%%eax; mull %4; subl %%eax,%0; sbbl %%edx,%1;
- movl %3,%%eax; mull %4; subl %%eax,%1;
- movl %2,%%eax; mull %5; subl %%eax,%1;"
- :"=m" (x), "=m" (((unsigned *)&x)[1])
- :"m" (st1),"m" (((unsigned *)&st1)[1]),
- "m" (q),"m" (((unsigned *)&q)[1])
- :"%ax","%dx");
-
- *y = x;
-}
-
-
-/* Remainder of st(0) / st(1) */
-/* This routine produces exact results, i.e. there is never any
- rounding or truncation, etc of the result. */
-static void do_fprem(FPU_REG *st0_ptr, int round)
-{
- FPU_REG *st1_ptr = &st(1);
- char st1_tag = st1_ptr->tag;
- char st0_tag = st0_ptr->tag;
- char sign = st0_ptr->sign;
-
- if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
- {
- FPU_REG tmp;
- int old_cw = control_word;
- int expdif = st0_ptr->exp - st1_ptr->exp;
- long long q;
- unsigned short saved_status;
- int cc = 0;
-
-#ifdef DENORM_OPERAND
- if ( ((st0_ptr->exp <= EXP_UNDER) ||
- (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
-
- /* We want the status following the denorm tests, but don't want
- the status changed by the arithmetic operations. */
- saved_status = partial_status;
- control_word &= ~CW_RC;
- control_word |= RC_CHOP;
-
- if (expdif < 64)
- {
- /* This should be the most common case */
-
- if ( expdif > -2 )
- {
- reg_div(st0_ptr, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
-
- if ( tmp.exp >= EXP_BIAS )
- {
- round_to_int(&tmp); /* Fortunately, this can't overflow
- to 2^64 */
- q = significand(&tmp);
-
- rem_kernel(significand(st0_ptr),
- &significand(&tmp),
- significand(st1_ptr),
- q, expdif);
-
- tmp.exp = st1_ptr->exp;
- }
- else
- {
- reg_move(st0_ptr, &tmp);
- q = 0;
- }
- tmp.sign = sign;
-
- if ( (round == RC_RND) && (tmp.sigh & 0xc0000000) )
- {
- /* We may need to subtract st(1) once more,
- to get a result <= 1/2 of st(1). */
- unsigned long long x;
- expdif = st1_ptr->exp - tmp.exp;
- if ( expdif <= 1 )
- {
- if ( expdif == 0 )
- x = significand(st1_ptr) - significand(&tmp);
- else /* expdif is 1 */
- x = (significand(st1_ptr) << 1) - significand(&tmp);
- if ( (x < significand(&tmp)) ||
- /* or equi-distant (from 0 & st(1)) and q is odd */
- ((x == significand(&tmp)) && (q & 1) ) )
- {
- tmp.sign ^= (SIGN_POS^SIGN_NEG);
- significand(&tmp) = x;
- q++;
- }
- }
- }
-
- if (q & 4) cc |= SW_C0;
- if (q & 2) cc |= SW_C3;
- if (q & 1) cc |= SW_C1;
- }
- else
- {
- control_word = old_cw;
- setcc(0);
- return;
- }
- }
- else
- {
- /* There is a large exponent difference ( >= 64 ) */
- /* To make much sense, the code in this section should
- be done at high precision. */
- int exp_1;
-
- /* prevent overflow here */
- /* N is 'a number between 32 and 63' (p26-113) */
- reg_move(st0_ptr, &tmp);
- tmp.exp = EXP_BIAS + 56;
- exp_1 = st1_ptr->exp; st1_ptr->exp = EXP_BIAS;
- expdif -= 56;
-
- reg_div(&tmp, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
- st1_ptr->exp = exp_1;
-
- round_to_int(&tmp); /* Fortunately, this can't overflow to 2^64 */
-
- rem_kernel(significand(st0_ptr),
- &significand(&tmp),
- significand(st1_ptr),
- significand(&tmp),
- tmp.exp - EXP_BIAS
- );
- tmp.exp = exp_1 + expdif;
- tmp.sign = sign;
-
- /* It is possible for the operation to be complete here.
- What does the IEEE standard say? The Intel 80486 manual
- implies that the operation will never be completed at this
- point, and the behaviour of a real 80486 confirms this.
- */
- if ( !(tmp.sigh | tmp.sigl) )
- {
- /* The result is zero */
- control_word = old_cw;
- partial_status = saved_status;
- reg_move(&CONST_Z, st0_ptr);
- st0_ptr->sign = sign;
-#ifdef PECULIAR_486
- setcc(SW_C2);
-#else
- setcc(0);
-#endif PECULIAR_486
- return;
- }
- cc = SW_C2;
- }
-
- control_word = old_cw;
- partial_status = saved_status;
- normalize_nuo(&tmp);
- reg_move(&tmp, st0_ptr);
- setcc(cc);
-
- /* The only condition to be looked for is underflow,
- and it can occur here only if underflow is unmasked. */
- if ( (st0_ptr->exp <= EXP_UNDER) && (st0_ptr->tag != TW_Zero)
- && !(control_word & CW_Underflow) )
- arith_underflow(st0_ptr);
-
- return;
- }
- else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) )
- {
- stack_underflow();
- return;
- }
- else if ( st0_tag == TW_Zero )
- {
- if ( st1_tag == TW_Valid )
- {
-#ifdef DENORM_OPERAND
- if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
-
- setcc(0); return;
- }
- else if ( st1_tag == TW_Zero )
- { arith_invalid(st0_ptr); return; } /* fprem(?,0) always invalid */
- else if ( st1_tag == TW_Infinity )
- { setcc(0); return; }
- }
- else if ( st0_tag == TW_Valid )
- {
- if ( st1_tag == TW_Zero )
- {
- arith_invalid(st0_ptr); /* fprem(Valid,Zero) is invalid */
- return;
- }
- else if ( st1_tag != TW_NaN )
- {
-#ifdef DENORM_OPERAND
- if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
-
- if ( st1_tag == TW_Infinity )
- {
- /* fprem(Valid,Infinity) is o.k. */
- setcc(0); return;
- }
- }
- }
- else if ( st0_tag == TW_Infinity )
- {
- if ( st1_tag != TW_NaN )
- {
- arith_invalid(st0_ptr); /* fprem(Infinity,?) is invalid */
- return;
- }
- }
-
- /* One of the registers must contain a NaN is we got here. */
-
-#ifdef PARANOID
- if ( (st0_tag != TW_NaN) && (st1_tag != TW_NaN) )
- EXCEPTION(EX_INTERNAL | 0x118);
-#endif PARANOID
-
- real_2op_NaN(st1_ptr, st0_ptr, st0_ptr);
-
-}
-
-
-/* ST(1) <- ST(1) * log ST; pop ST */
-static void fyl2x(FPU_REG *st0_ptr)
-{
- char st0_tag = st0_ptr->tag;
- 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 )
- {
-#ifdef DENORM_OPERAND
- if ( ((st0_ptr->exp <= EXP_UNDER) ||
- (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
-
- 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
- {
- /* negative */
- if ( !arith_invalid(st1_ptr) )
- pop();
- return;
- }
- }
- else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) )
- {
- stack_underflow_pop(1);
- return;
- }
- else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
- {
- if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
- pop();
- return;
- }
- else if ( (st0_tag <= TW_Zero) && (st1_tag <= TW_Zero) )
- {
- /* one of the args is zero, the other valid, or both zero */
- if ( st0_tag == TW_Zero )
- {
- if ( st1_tag == TW_Zero )
- {
- /* Both args zero is invalid */
- if ( !arith_invalid(st1_ptr) )
- pop();
- }
-#ifdef PECULIAR_486
- /* This case is not specifically covered in the manual,
- but divide-by-zero would seem to be the best response.
- However, a real 80486 does it this way... */
- else if ( st0_ptr->tag == TW_Infinity )
- {
- reg_move(&CONST_INF, st1_ptr);
- pop();
- }
-#endif PECULIAR_486
- else
- {
- if ( !divide_by_zero(st1_ptr->sign^SIGN_NEG^SIGN_POS, st1_ptr) )
- pop();
- }
- return;
- }
- else
- {
- /* st(1) contains zero, st(0) valid <> 0 */
- /* Zero is the valid answer */
- char sign = st1_ptr->sign;
-
- if ( st0_ptr->sign == SIGN_NEG )
- {
- /* log(negative) */
- if ( !arith_invalid(st1_ptr) )
- pop();
- return;
- }
-
-#ifdef DENORM_OPERAND
- if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
-
- if ( st0_ptr->exp < EXP_BIAS ) sign ^= SIGN_NEG^SIGN_POS;
- pop(); st0_ptr = &st(0);
- reg_move(&CONST_Z, st0_ptr);
- st0_ptr->sign = sign;
- return;
- }
- }
- /* One or both arg must be an infinity */
- else if ( st0_tag == TW_Infinity )
- {
- if ( (st0_ptr->sign == SIGN_NEG) || (st1_tag == TW_Zero) )
- {
- /* log(-infinity) or 0*log(infinity) */
- if ( !arith_invalid(st1_ptr) )
- pop();
- return;
- }
- else
- {
- char sign = st1_ptr->sign;
-
-#ifdef DENORM_OPERAND
- if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
-
- pop(); st0_ptr = &st(0);
- reg_move(&CONST_INF, st0_ptr);
- st0_ptr->sign = sign;
- return;
- }
- }
- /* st(1) must be infinity here */
- else if ( (st0_tag == TW_Valid) && (st0_ptr->sign == SIGN_POS) )
- {
- if ( st0_ptr->exp >= EXP_BIAS )
- {
- if ( (st0_ptr->exp == EXP_BIAS) &&
- (st0_ptr->sigh == 0x80000000) &&
- (st0_ptr->sigl == 0) )
- {
- /* st(0) holds 1.0 */
- /* infinity*log(1) */
- if ( !arith_invalid(st1_ptr) )
- pop();
- return;
- }
- /* st(0) is positive and > 1.0 */
- pop();
- }
- else
- {
- /* st(0) is positive and < 1.0 */
-
-#ifdef DENORM_OPERAND
- if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
-
- st1_ptr->sign ^= SIGN_NEG;
- pop();
- }
- return;
- }
- else
- {
- /* st(0) must be zero or negative */
- if ( st0_ptr->tag == TW_Zero )
- {
- /* This should be invalid, but a real 80486 is happy with it. */
-#ifndef PECULIAR_486
- if ( !divide_by_zero(st1_ptr->sign, st1_ptr) )
-#endif PECULIAR_486
- {
- st1_ptr->sign ^= SIGN_NEG^SIGN_POS;
- pop();
- }
- }
- else
- {
- /* log(negative) */
- if ( !arith_invalid(st1_ptr) )
- pop();
- }
- return;
- }
-}
-
-
-static void fpatan(FPU_REG *st0_ptr)
-{
- char st0_tag = st0_ptr->tag;
- FPU_REG *st1_ptr = &st(1);
- char st1_tag = st1_ptr->tag;
-
- clear_C1();
- if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
- {
-#ifdef DENORM_OPERAND
- if ( ((st0_ptr->exp <= EXP_UNDER) ||
- (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
-
- poly_atan(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 by definition an underflow. */
- arith_underflow(st1_ptr);
- pop();
- return;
- }
- }
- else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) )
- {
- stack_underflow_pop(1);
- return;
- }
- else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
- {
- if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
- pop();
- return;
- }
- else if ( (st0_tag == TW_Infinity) || (st1_tag == TW_Infinity) )
- {
- char sign = st1_ptr->sign;
- if ( st0_tag == TW_Infinity )
- {
- if ( st1_tag == TW_Infinity )
- {
- if ( st0_ptr->sign == SIGN_POS )
- { reg_move(&CONST_PI4, st1_ptr); }
- else
- reg_add(&CONST_PI4, &CONST_PI2, st1_ptr, FULL_PRECISION);
- }
- else
- {
-#ifdef DENORM_OPERAND
- if ( st1_tag != TW_Zero )
- {
- if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
- }
-#endif DENORM_OPERAND
-
- if ( st0_ptr->sign == SIGN_POS )
- {
- reg_move(&CONST_Z, st1_ptr);
- st1_ptr->sign = sign; /* An 80486 preserves the sign */
- pop();
- return;
- }
- else
- reg_move(&CONST_PI, st1_ptr);
- }
- }
- else
- {
- /* st(1) is infinity, st(0) not infinity */
-#ifdef DENORM_OPERAND
- if ( st0_tag != TW_Zero )
- {
- if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
- }
-#endif DENORM_OPERAND
-
- reg_move(&CONST_PI2, st1_ptr);
- }
- st1_ptr->sign = sign;
- }
- else if ( st1_tag == TW_Zero )
- {
- /* st(0) must be valid or zero */
- char sign = st1_ptr->sign;
-
-#ifdef DENORM_OPERAND
- if ( st0_tag != TW_Zero )
- {
- if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
- }
-#endif DENORM_OPERAND
-
- if ( st0_ptr->sign == SIGN_POS )
- { /* An 80486 preserves the sign */ pop(); return; }
- else
- reg_move(&CONST_PI, st1_ptr);
- st1_ptr->sign = sign;
- }
- else if ( st0_tag == TW_Zero )
- {
- /* st(1) must be TW_Valid here */
- char sign = st1_ptr->sign;
-
-#ifdef DENORM_OPERAND
- if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
-
- reg_move(&CONST_PI2, st1_ptr);
- st1_ptr->sign = sign;
- }
-#ifdef PARANOID
- else
- EXCEPTION(EX_INTERNAL | 0x125);
-#endif PARANOID
-
- pop();
- set_precision_flag_up(); /* We do not really know if up or down */
-}
-
-
-static void fprem(FPU_REG *st0_ptr)
-{
- do_fprem(st0_ptr, RC_CHOP);
-}
-
-
-static void fprem1(FPU_REG *st0_ptr)
-{
- do_fprem(st0_ptr, RC_RND);
-}
-
-
-static void fyl2xp1(FPU_REG *st0_ptr)
-{
- 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)) )
- {
-#ifdef DENORM_OPERAND
- if ( ((st0_ptr->exp <= EXP_UNDER) ||
- (st1_ptr->exp <= EXP_UNDER)) && denormal_operand() )
- return;
-#endif DENORM_OPERAND
-
- 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;
-#else
- if ( arith_invalid(st1_ptr) ) /* poly_l2p1() returned invalid */
- return;
-#endif PECULIAR_486
- }
- 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) )
- {
- stack_underflow_pop(1);
- return;
- }
- else if ( st0_tag == TW_Zero )
- {
- if ( st1_tag <= TW_Zero )
- {
-#ifdef DENORM_OPERAND
- if ( (st1_tag == TW_Valid) && (st1_ptr->exp <= EXP_UNDER) &&
- (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
-
- st0_ptr->sign ^= st1_ptr->sign;
- reg_move(st0_ptr, st1_ptr);
- }
- else if ( st1_tag == TW_Infinity )
- {
- /* Infinity*log(1) */
- if ( !arith_invalid(st1_ptr) )
- pop();
- return;
- }
- else if ( st1_tag == TW_NaN )
- {
- if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
- pop();
- return;
- }
-#ifdef PARANOID
- else
- {
- EXCEPTION(EX_INTERNAL | 0x116);
- return;
- }
-#endif PARANOID
- pop(); return;
- }
- else if ( st0_tag == TW_Valid )
- {
- if ( st1_tag == TW_Zero )
- {
- if ( st0_ptr->sign == SIGN_NEG )
- {
- if ( st0_ptr->exp >= EXP_BIAS )
- {
- /* st(0) holds <= -1.0 */
-#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */
- st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
-#else
- if ( arith_invalid(st1_ptr) ) return;
-#endif PECULIAR_486
- pop(); return;
- }
-#ifdef DENORM_OPERAND
- if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
- st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
- pop(); return;
- }
-#ifdef DENORM_OPERAND
- if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
- pop(); return;
- }
- if ( st1_tag == TW_Infinity )
- {
- if ( st0_ptr->sign == SIGN_NEG )
- {
- if ( (st0_ptr->exp >= EXP_BIAS) &&
- !((st0_ptr->sigh == 0x80000000) &&
- (st0_ptr->sigl == 0)) )
- {
- /* st(0) holds < -1.0 */
-#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */
- st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
-#else
- if ( arith_invalid(st1_ptr) ) return;
-#endif PECULIAR_486
- pop(); return;
- }
-#ifdef DENORM_OPERAND
- if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
- st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
- pop(); return;
- }
-#ifdef DENORM_OPERAND
- if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
- pop(); return;
- }
- if ( st1_tag == TW_NaN )
- {
- if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
- pop();
- return;
- }
- }
- else if ( st0_tag == TW_NaN )
- {
- if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
- pop();
- return;
- }
- else if ( st0_tag == TW_Infinity )
- {
- if ( st1_tag == TW_NaN )
- {
- if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
- pop();
- return;
- }
- else if ( st0_ptr->sign == SIGN_NEG )
- {
- int exponent = st1_ptr->exp;
-#ifndef PECULIAR_486
- /* This should have higher priority than denormals, but... */
- if ( arith_invalid(st1_ptr) ) /* log(-infinity) */
- return;
-#endif PECULIAR_486
-#ifdef DENORM_OPERAND
- if ( st1_tag != TW_Zero )
- {
- if ( (exponent <= EXP_UNDER) && (denormal_operand()) )
- return;
- }
-#endif DENORM_OPERAND
-#ifdef PECULIAR_486
- /* Denormal operands actually get higher priority */
- if ( arith_invalid(st1_ptr) ) /* log(-infinity) */
- return;
-#endif PECULIAR_486
- pop();
- return;
- }
- else if ( st1_tag == TW_Zero )
- {
- /* log(infinity) */
- if ( !arith_invalid(st1_ptr) )
- pop();
- return;
- }
-
- /* st(1) must be valid here. */
-
-#ifdef DENORM_OPERAND
- if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
-
- /* The Manual says that log(Infinity) is invalid, but a real
- 80486 sensibly says that it is o.k. */
- { char sign = st1_ptr->sign;
- reg_move(&CONST_INF, st1_ptr);
- st1_ptr->sign = sign;
- }
- pop();
- return;
- }
-#ifdef PARANOID
- else
- {
- EXCEPTION(EX_INTERNAL | 0x117);
- }
-#endif PARANOID
-}
-
-
-static void fscale(FPU_REG *st0_ptr)
-{
- char st0_tag = st0_ptr->tag;
- FPU_REG *st1_ptr = &st(1);
- char st1_tag = st1_ptr->tag;
- int old_cw = control_word;
- char sign = st0_ptr->sign;
-
- clear_C1();
- if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
- {
- long scale;
- FPU_REG tmp;
-
-#ifdef DENORM_OPERAND
- if ( ((st0_ptr->exp <= EXP_UNDER) ||
- (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
-
- if ( st1_ptr->exp > EXP_BIAS + 30 )
- {
- /* 2^31 is far too large, would require 2^(2^30) or 2^(-2^30) */
- char sign;
-
- if ( st1_ptr->sign == SIGN_POS )
- {
- EXCEPTION(EX_Overflow);
- sign = st0_ptr->sign;
- reg_move(&CONST_INF, st0_ptr);
- st0_ptr->sign = sign;
- }
- else
- {
- EXCEPTION(EX_Underflow);
- sign = st0_ptr->sign;
- reg_move(&CONST_Z, st0_ptr);
- st0_ptr->sign = sign;
- }
- return;
- }
-
- control_word &= ~CW_RC;
- control_word |= RC_CHOP;
- reg_move(st1_ptr, &tmp);
- round_to_int(&tmp); /* This can never overflow here */
- control_word = old_cw;
- scale = st1_ptr->sign ? -tmp.sigl : tmp.sigl;
- scale += st0_ptr->exp;
- st0_ptr->exp = scale;
-
- /* Use round_reg() to properly detect under/overflow etc */
- round_reg(st0_ptr, 0, control_word);
-
- return;
- }
- else if ( st0_tag == TW_Valid )
- {
- if ( st1_tag == TW_Zero )
- {
-
-#ifdef DENORM_OPERAND
- if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
-
- return;
- }
- if ( st1_tag == TW_Infinity )
- {
-#ifdef DENORM_OPERAND
- if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
-
- if ( st1_ptr->sign == SIGN_POS )
- { reg_move(&CONST_INF, st0_ptr); }
- else
- reg_move(&CONST_Z, st0_ptr);
- st0_ptr->sign = sign;
- return;
- }
- if ( st1_tag == TW_NaN )
- { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
- }
- else if ( st0_tag == TW_Zero )
- {
- if ( st1_tag == TW_Valid )
- {
-
-#ifdef DENORM_OPERAND
- if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
-
- return;
- }
- else if ( st1_tag == TW_Zero ) { return; }
- else if ( st1_tag == TW_Infinity )
- {
- if ( st1_ptr->sign == SIGN_NEG )
- return;
- else
- {
- arith_invalid(st0_ptr); /* Zero scaled by +Infinity */
- return;
- }
- }
- else if ( st1_tag == TW_NaN )
- { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
- }
- else if ( st0_tag == TW_Infinity )
- {
- if ( st1_tag == TW_Valid )
- {
-
-#ifdef DENORM_OPERAND
- if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
- return;
-#endif DENORM_OPERAND
-
- return;
- }
- if ( ((st1_tag == TW_Infinity) && (st1_ptr->sign == SIGN_POS))
- || (st1_tag == TW_Zero) )
- return;
- else if ( st1_tag == TW_Infinity )
- {
- arith_invalid(st0_ptr); /* Infinity scaled by -Infinity */
- return;
- }
- else if ( st1_tag == TW_NaN )
- { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
- }
- else if ( st0_tag == TW_NaN )
- {
- if ( st1_tag != TW_Empty )
- { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
- }
-
-#ifdef PARANOID
- if ( !((st0_tag == TW_Empty) || (st1_tag == TW_Empty)) )
- {
- EXCEPTION(EX_INTERNAL | 0x115);
- return;
- }
-#endif
-
- /* At least one of st(0), st(1) must be empty */
- stack_underflow();
-
-}
-
-
-/*---------------------------------------------------------------------------*/
-
-static FUNC_ST0 const trig_table_a[] = {
- f2xm1, fyl2x, fptan, fpatan, fxtract, fprem1, fdecstp, fincstp
-};
-
-void trig_a(void)
-{
- (trig_table_a[FPU_rm])(&st(0));
-}
-
-
-static FUNC_ST0 const trig_table_b[] =
- {
- fprem, fyl2xp1, fsqrt_, fsincos, frndint_, fscale, fsin, fcos
- };
-
-void trig_b(void)
-{
- (trig_table_b[FPU_rm])(&st(0));
-}
diff --git a/drivers/FPU-emu/get_address.c b/drivers/FPU-emu/get_address.c
deleted file mode 100644
index 6f3270ae3..000000000
--- a/drivers/FPU-emu/get_address.c
+++ /dev/null
@@ -1,423 +0,0 @@
-/*---------------------------------------------------------------------------+
- | get_address.c |
- | |
- | Get the effective address from an FPU instruction. |
- | |
- | Copyright (C) 1992,1993,1994 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
-
-/*---------------------------------------------------------------------------+
- | Note: |
- | The file contains code which accesses user memory. |
- | Emulator static data may change when user memory is accessed, due to |
- | other processes using the emulator while swapping is in progress. |
- +---------------------------------------------------------------------------*/
-
-
-#include <linux/stddef.h>
-#include <linux/head.h>
-
-#include <asm/segment.h>
-
-#include "fpu_system.h"
-#include "exception.h"
-#include "fpu_emu.h"
-
-
-#define FPU_WRITE_BIT 0x10
-
-static int reg_offset[] = {
- offsetof(struct info,___eax),
- offsetof(struct info,___ecx),
- offsetof(struct info,___edx),
- offsetof(struct info,___ebx),
- offsetof(struct info,___esp),
- offsetof(struct info,___ebp),
- offsetof(struct info,___esi),
- offsetof(struct info,___edi)
-};
-
-#define REG_(x) (*(long *)(reg_offset[(x)]+(char *) FPU_info))
-
-static int reg_offset_vm86[] = {
- offsetof(struct info,___cs),
- offsetof(struct info,___vm86_ds),
- offsetof(struct info,___vm86_es),
- offsetof(struct info,___vm86_fs),
- offsetof(struct info,___vm86_gs),
- offsetof(struct info,___ss),
- offsetof(struct info,___vm86_ds)
- };
-
-#define VM86_REG_(x) (*(unsigned short *) \
- (reg_offset_vm86[((unsigned)x)]+(char *) FPU_info))
-
-static int reg_offset_pm[] = {
- offsetof(struct info,___cs),
- offsetof(struct info,___ds),
- offsetof(struct info,___es),
- offsetof(struct info,___fs),
- offsetof(struct info,___gs),
- offsetof(struct info,___ss),
- offsetof(struct info,___ds)
- };
-
-#define PM_REG_(x) (*(unsigned short *) \
- (reg_offset_pm[((unsigned)x)]+(char *) FPU_info))
-
-
-/* Decode the SIB byte. This function assumes mod != 0 */
-static int sib(int mod, unsigned long *fpu_eip)
-{
- unsigned char ss,index,base;
- long offset;
-
- RE_ENTRANT_CHECK_OFF;
- FPU_code_verify_area(1);
- base = get_fs_byte((char *) (*fpu_eip)); /* The SIB byte */
- RE_ENTRANT_CHECK_ON;
- (*fpu_eip)++;
- ss = base >> 6;
- index = (base >> 3) & 7;
- base &= 7;
-
- if ((mod == 0) && (base == 5))
- offset = 0; /* No base register */
- else
- offset = REG_(base);
-
- if (index == 4)
- {
- /* No index register */
- /* A non-zero ss is illegal */
- if ( ss )
- EXCEPTION(EX_Invalid);
- }
- else
- {
- offset += (REG_(index)) << ss;
- }
-
- if (mod == 1)
- {
- /* 8 bit signed displacement */
- RE_ENTRANT_CHECK_OFF;
- FPU_code_verify_area(1);
- offset += (signed char) get_fs_byte((char *) (*fpu_eip));
- RE_ENTRANT_CHECK_ON;
- (*fpu_eip)++;
- }
- else if (mod == 2 || base == 5) /* The second condition also has mod==0 */
- {
- /* 32 bit displacement */
- RE_ENTRANT_CHECK_OFF;
- FPU_code_verify_area(4);
- offset += (signed) get_fs_long((unsigned long *) (*fpu_eip));
- RE_ENTRANT_CHECK_ON;
- (*fpu_eip) += 4;
- }
-
- return offset;
-}
-
-
-static unsigned long vm86_segment(unsigned char segment,
- unsigned short *selector)
-{
- segment--;
-#ifdef PARANOID
- if ( segment > PREFIX_SS_ )
- {
- EXCEPTION(EX_INTERNAL|0x130);
- math_abort(FPU_info,SIGSEGV);
- }
-#endif PARANOID
- *selector = VM86_REG_(segment);
- return (unsigned long)VM86_REG_(segment) << 4;
-}
-
-
-/* This should work for 16 and 32 bit protected mode. */
-static long pm_address(unsigned char FPU_modrm, unsigned char segment,
- unsigned short *selector, long offset)
-{
- struct desc_struct descriptor;
- unsigned long base_address, limit, address, seg_top;
-
- segment--;
-#ifdef PARANOID
- if ( segment > PREFIX_SS_ )
- {
- EXCEPTION(EX_INTERNAL|0x132);
- math_abort(FPU_info,SIGSEGV);
- }
-#endif PARANOID
-
- *selector = PM_REG_(segment);
-
- descriptor = LDT_DESCRIPTOR(PM_REG_(segment));
- base_address = SEG_BASE_ADDR(descriptor);
- address = base_address + offset;
- limit = base_address
- + (SEG_LIMIT(descriptor)+1) * SEG_GRANULARITY(descriptor) - 1;
- if ( limit < base_address ) limit = 0xffffffff;
-
- if ( SEG_EXPAND_DOWN(descriptor) )
- {
- if ( SEG_G_BIT(descriptor) )
- seg_top = 0xffffffff;
- else
- {
- seg_top = base_address + (1 << 20);
- if ( seg_top < base_address ) seg_top = 0xffffffff;
- }
- access_limit =
- (address <= limit) || (address >= seg_top) ? 0 :
- ((seg_top-address) >= 255 ? 255 : seg_top-address);
- }
- else
- {
- access_limit =
- (address > limit) || (address < base_address) ? 0 :
- ((limit-address) >= 254 ? 255 : limit-address+1);
- }
- if ( SEG_EXECUTE_ONLY(descriptor) ||
- (!SEG_WRITE_PERM(descriptor) && (FPU_modrm & FPU_WRITE_BIT)) )
- {
- access_limit = 0;
- }
- return address;
-}
-
-
-/*
- MOD R/M byte: MOD == 3 has a special use for the FPU
- SIB byte used iff R/M = 100b
-
- 7 6 5 4 3 2 1 0
- ..... ......... .........
- MOD OPCODE(2) R/M
-
-
- SIB byte
-
- 7 6 5 4 3 2 1 0
- ..... ......... .........
- SS INDEX BASE
-
-*/
-
-void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
- struct address *addr,
-/* unsigned short *selector, unsigned long *offset, */
- fpu_addr_modes addr_modes)
-{
- unsigned char mod;
- unsigned rm = FPU_modrm & 7;
- long *cpu_reg_ptr;
- int address = 0; /* Initialized just to stop compiler warnings. */
-
- /* Memory accessed via the cs selector is write protected
- in `non-segmented' 32 bit protected mode. */
- if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
- && (addr_modes.override.segment == PREFIX_CS_) )
- {
- math_abort(FPU_info,SIGSEGV);
- }
-
- addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */
-
- mod = (FPU_modrm >> 6) & 3;
-
- if (rm == 4 && mod != 3)
- {
- address = sib(mod, fpu_eip);
- }
- else
- {
- cpu_reg_ptr = & REG_(rm);
- switch (mod)
- {
- case 0:
- if (rm == 5)
- {
- /* Special case: disp32 */
- RE_ENTRANT_CHECK_OFF;
- FPU_code_verify_area(4);
- address = get_fs_long((unsigned long *) (*fpu_eip));
- (*fpu_eip) += 4;
- RE_ENTRANT_CHECK_ON;
- addr->offset = address;
- return (void *) address;
- }
- else
- {
- address = *cpu_reg_ptr; /* Just return the contents
- of the cpu register */
- addr->offset = address;
- return (void *) address;
- }
- case 1:
- /* 8 bit signed displacement */
- RE_ENTRANT_CHECK_OFF;
- FPU_code_verify_area(1);
- address = (signed char) get_fs_byte((char *) (*fpu_eip));
- RE_ENTRANT_CHECK_ON;
- (*fpu_eip)++;
- break;
- case 2:
- /* 32 bit displacement */
- RE_ENTRANT_CHECK_OFF;
- FPU_code_verify_area(4);
- address = (signed) get_fs_long((unsigned long *) (*fpu_eip));
- (*fpu_eip) += 4;
- RE_ENTRANT_CHECK_ON;
- break;
- case 3:
- /* Not legal for the FPU */
- EXCEPTION(EX_Invalid);
- }
- address += *cpu_reg_ptr;
- }
-
- addr->offset = address;
-
- switch ( addr_modes.default_mode )
- {
- case 0:
- break;
- case VM86:
- address += vm86_segment(addr_modes.override.segment,
- (unsigned short *)&(addr->selector));
- break;
- case PM16:
- case SEG32:
- address = pm_address(FPU_modrm, addr_modes.override.segment,
- (unsigned short *)&(addr->selector), address);
- break;
- default:
- EXCEPTION(EX_INTERNAL|0x133);
- }
-
- return (void *)address;
-}
-
-
-void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip,
- struct address *addr,
-/* unsigned short *selector, unsigned long *offset, */
- fpu_addr_modes addr_modes)
-{
- unsigned char mod;
- unsigned rm = FPU_modrm & 7;
- int address = 0; /* Default used for mod == 0 */
-
- /* Memory accessed via the cs selector is write protected
- in `non-segmented' 32 bit protected mode. */
- if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
- && (addr_modes.override.segment == PREFIX_CS_) )
- {
- math_abort(FPU_info,SIGSEGV);
- }
-
- addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */
-
- mod = (FPU_modrm >> 6) & 3;
-
- switch (mod)
- {
- case 0:
- if (rm == 6)
- {
- /* Special case: disp16 */
- RE_ENTRANT_CHECK_OFF;
- FPU_code_verify_area(2);
- address = (unsigned short)get_fs_word((unsigned short *) (*fpu_eip));
- (*fpu_eip) += 2;
- RE_ENTRANT_CHECK_ON;
- goto add_segment;
- }
- break;
- case 1:
- /* 8 bit signed displacement */
- RE_ENTRANT_CHECK_OFF;
- FPU_code_verify_area(1);
- address = (signed char) get_fs_byte((signed char *) (*fpu_eip));
- RE_ENTRANT_CHECK_ON;
- (*fpu_eip)++;
- break;
- case 2:
- /* 16 bit displacement */
- RE_ENTRANT_CHECK_OFF;
- FPU_code_verify_area(2);
- address = (unsigned) get_fs_word((unsigned short *) (*fpu_eip));
- (*fpu_eip) += 2;
- RE_ENTRANT_CHECK_ON;
- break;
- case 3:
- /* Not legal for the FPU */
- EXCEPTION(EX_Invalid);
- break;
- }
- switch ( rm )
- {
- case 0:
- address += FPU_info->___ebx + FPU_info->___esi;
- break;
- case 1:
- address += FPU_info->___ebx + FPU_info->___edi;
- break;
- case 2:
- address += FPU_info->___ebp + FPU_info->___esi;
- if ( addr_modes.override.segment == PREFIX_DEFAULT )
- addr_modes.override.segment = PREFIX_SS_;
- break;
- case 3:
- address += FPU_info->___ebp + FPU_info->___edi;
- if ( addr_modes.override.segment == PREFIX_DEFAULT )
- addr_modes.override.segment = PREFIX_SS_;
- break;
- case 4:
- address += FPU_info->___esi;
- break;
- case 5:
- address += FPU_info->___edi;
- break;
- case 6:
- address += FPU_info->___ebp;
- if ( addr_modes.override.segment == PREFIX_DEFAULT )
- addr_modes.override.segment = PREFIX_SS_;
- break;
- case 7:
- address += FPU_info->___ebx;
- break;
- }
-
- add_segment:
- address &= 0xffff;
-
- addr->offset = address;
-
- switch ( addr_modes.default_mode )
- {
- case 0:
- break;
- case VM86:
- address += vm86_segment(addr_modes.override.segment,
- (unsigned short *)&(addr->selector));
- break;
- case PM16:
- case SEG32:
- address = pm_address(FPU_modrm, addr_modes.override.segment,
- (unsigned short *)&(addr->selector), address);
- break;
- default:
- EXCEPTION(EX_INTERNAL|0x131);
- }
-
- return (void *)address ;
-}
diff --git a/drivers/FPU-emu/load_store.c b/drivers/FPU-emu/load_store.c
deleted file mode 100644
index 6f0e167d6..000000000
--- a/drivers/FPU-emu/load_store.c
+++ /dev/null
@@ -1,260 +0,0 @@
-/*---------------------------------------------------------------------------+
- | load_store.c |
- | |
- | This file contains most of the code to interpret the FPU instructions |
- | which load and store from user memory. |
- | |
- | Copyright (C) 1992,1993,1994 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
-
-/*---------------------------------------------------------------------------+
- | Note: |
- | The file contains code which accesses user memory. |
- | Emulator static data may change when user memory is accessed, due to |
- | other processes using the emulator while swapping is in progress. |
- +---------------------------------------------------------------------------*/
-
-#include <asm/segment.h>
-
-#include "fpu_system.h"
-#include "exception.h"
-#include "fpu_emu.h"
-#include "status_w.h"
-#include "control_w.h"
-
-
-#define _NONE_ 0 /* st0_ptr etc not needed */
-#define _REG0_ 1 /* Will be storing st(0) */
-#define _PUSH_ 3 /* Need to check for space to push onto stack */
-#define _null_ 4 /* Function illegal or not implemented */
-
-#define pop_0() { st0_ptr->tag = TW_Empty; top++; }
-
-
-static unsigned char const type_table[32] = {
- _PUSH_, _PUSH_, _PUSH_, _PUSH_,
- _null_, _null_, _null_, _null_,
- _REG0_, _REG0_, _REG0_, _REG0_,
- _REG0_, _REG0_, _REG0_, _REG0_,
- _NONE_, _null_, _NONE_, _PUSH_,
- _NONE_, _PUSH_, _null_, _PUSH_,
- _NONE_, _null_, _NONE_, _REG0_,
- _NONE_, _REG0_, _NONE_, _REG0_
- };
-
-unsigned char const data_sizes_16[32] = {
- 4, 4, 8, 2, 0, 0, 0, 0,
- 4, 4, 8, 2, 4, 4, 8, 2,
- 14, 0, 94, 10, 2, 10, 0, 8,
- 14, 0, 94, 10, 2, 10, 2, 8
-};
-
-unsigned char const data_sizes_32[32] = {
- 4, 4, 8, 2, 0, 0, 0, 0,
- 4, 4, 8, 2, 4, 4, 8, 2,
- 28, 0,108, 10, 2, 10, 0, 8,
- 28, 0,108, 10, 2, 10, 2, 8
-};
-
-int load_store_instr(unsigned char type, fpu_addr_modes addr_modes,
- void *data_address)
-{
- FPU_REG loaded_data;
- FPU_REG *st0_ptr;
-
- st0_ptr = NULL; /* Initialized just to stop compiler warnings. */
-
- if ( addr_modes.default_mode & PROTECTED )
- {
- if ( addr_modes.default_mode == SEG32 )
- {
- if ( access_limit < data_sizes_32[type] )
- math_abort(FPU_info,SIGSEGV);
- }
- else if ( addr_modes.default_mode == PM16 )
- {
- if ( access_limit < data_sizes_16[type] )
- math_abort(FPU_info,SIGSEGV);
- }
-#ifdef PARANOID
- else
- EXCEPTION(EX_INTERNAL|0x140);
-#endif PARANOID
- }
-
- switch ( type_table[type] )
- {
- case _NONE_:
- break;
- case _REG0_:
- st0_ptr = &st(0); /* Some of these instructions pop after
- storing */
- break;
- case _PUSH_:
- {
- st0_ptr = &st(-1);
- if ( st0_ptr->tag != TW_Empty )
- { stack_overflow(); return 0; }
- top--;
- }
- break;
- case _null_:
- FPU_illegal();
- return 0;
-#ifdef PARANOID
- default:
- EXCEPTION(EX_INTERNAL|0x141);
- return 0;
-#endif PARANOID
- }
-
- switch ( type )
- {
- case 000: /* fld m32real */
- clear_C1();
- reg_load_single((float *)data_address, &loaded_data);
- if ( (loaded_data.tag == TW_NaN) &&
- real_2op_NaN(&loaded_data, &loaded_data, &loaded_data) )
- {
- top++;
- break;
- }
- reg_move(&loaded_data, st0_ptr);
- break;
- case 001: /* fild m32int */
- clear_C1();
- reg_load_int32((long *)data_address, st0_ptr);
- break;
- case 002: /* fld m64real */
- clear_C1();
- reg_load_double((double *)data_address, &loaded_data);
- if ( (loaded_data.tag == TW_NaN) &&
- real_2op_NaN(&loaded_data, &loaded_data, &loaded_data) )
- {
- top++;
- break;
- }
- reg_move(&loaded_data, st0_ptr);
- break;
- case 003: /* fild m16int */
- clear_C1();
- reg_load_int16((short *)data_address, st0_ptr);
- break;
- case 010: /* fst m32real */
- clear_C1();
- reg_store_single((float *)data_address, st0_ptr);
- break;
- case 011: /* fist m32int */
- clear_C1();
- reg_store_int32((long *)data_address, st0_ptr);
- break;
- case 012: /* fst m64real */
- clear_C1();
- reg_store_double((double *)data_address, st0_ptr);
- break;
- case 013: /* fist m16int */
- clear_C1();
- reg_store_int16((short *)data_address, st0_ptr);
- break;
- case 014: /* fstp m32real */
- clear_C1();
- if ( reg_store_single((float *)data_address, st0_ptr) )
- pop_0(); /* pop only if the number was actually stored
- (see the 80486 manual p16-28) */
- break;
- case 015: /* fistp m32int */
- clear_C1();
- if ( reg_store_int32((long *)data_address, st0_ptr) )
- pop_0(); /* pop only if the number was actually stored
- (see the 80486 manual p16-28) */
- break;
- case 016: /* fstp m64real */
- clear_C1();
- if ( reg_store_double((double *)data_address, st0_ptr) )
- pop_0(); /* pop only if the number was actually stored
- (see the 80486 manual p16-28) */
- break;
- case 017: /* fistp m16int */
- clear_C1();
- if ( reg_store_int16((short *)data_address, st0_ptr) )
- pop_0(); /* pop only if the number was actually stored
- (see the 80486 manual p16-28) */
- break;
- case 020: /* fldenv m14/28byte */
- fldenv(addr_modes, (char *)data_address);
- /* Ensure that the values just loaded are not changed by
- fix-up operations. */
- return 1;
- case 022: /* frstor m94/108byte */
- frstor(addr_modes, (char *)data_address);
- /* Ensure that the values just loaded are not changed by
- fix-up operations. */
- return 1;
- case 023: /* fbld m80dec */
- clear_C1();
- reg_load_bcd((char *)data_address, st0_ptr);
- break;
- case 024: /* fldcw */
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_READ, data_address, 2);
- control_word = get_fs_word((unsigned short *) data_address);
- RE_ENTRANT_CHECK_ON;
- if ( partial_status & ~control_word & CW_Exceptions )
- partial_status |= (SW_Summary | SW_Backward);
- else
- partial_status &= ~(SW_Summary | SW_Backward);
-#ifdef PECULIAR_486
- control_word |= 0x40; /* An 80486 appears to always set this bit */
-#endif PECULIAR_486
- return 1;
- case 025: /* fld m80real */
- clear_C1();
- reg_load_extended((long double *)data_address, st0_ptr);
- break;
- case 027: /* fild m64int */
- clear_C1();
- reg_load_int64((long long *)data_address, st0_ptr);
- break;
- case 030: /* fstenv m14/28byte */
- fstenv(addr_modes, (char *)data_address);
- return 1;
- case 032: /* fsave */
- fsave(addr_modes, (char *)data_address);
- return 1;
- case 033: /* fbstp m80dec */
- clear_C1();
- if ( reg_store_bcd((char *)data_address, st0_ptr) )
- pop_0(); /* pop only if the number was actually stored
- (see the 80486 manual p16-28) */
- break;
- case 034: /* fstcw m16int */
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_WRITE,data_address,2);
- put_fs_word(control_word, (short *) data_address);
- RE_ENTRANT_CHECK_ON;
- return 1;
- case 035: /* fstp m80real */
- clear_C1();
- if ( reg_store_extended((long double *)data_address, st0_ptr) )
- pop_0(); /* pop only if the number was actually stored
- (see the 80486 manual p16-28) */
- break;
- case 036: /* fstsw m2byte */
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_WRITE,data_address,2);
- put_fs_word(status_word(),(short *) data_address);
- RE_ENTRANT_CHECK_ON;
- return 1;
- case 037: /* fistp m64int */
- clear_C1();
- if ( reg_store_int64((long long *)data_address, st0_ptr) )
- pop_0(); /* pop only if the number was actually stored
- (see the 80486 manual p16-28) */
- break;
- }
- return 0;
-}
diff --git a/drivers/FPU-emu/mul_Xsig.S b/drivers/FPU-emu/mul_Xsig.S
deleted file mode 100644
index 1d88d4466..000000000
--- a/drivers/FPU-emu/mul_Xsig.S
+++ /dev/null
@@ -1,182 +0,0 @@
-/*---------------------------------------------------------------------------+
- | mul_Xsig.S |
- | |
- | Multiply a 12 byte fixed point number by another fixed point number. |
- | |
- | Copyright (C) 1992,1994 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | Call from C as: |
- | void mul32_Xsig(Xsig *x, unsigned b) |
- | |
- | void mul64_Xsig(Xsig *x, unsigned long long *b) |
- | |
- | void mul_Xsig_Xsig(Xsig *x, unsigned *b) |
- | |
- | The result is neither rounded nor normalized, and the ls bit or so may |
- | be wrong. |
- | |
- +---------------------------------------------------------------------------*/
- .file "mul_Xsig.S"
-
-
-#include "fpu_asm.h"
-
-.text
- .align 2,144
-.globl _mul32_Xsig
-_mul32_Xsig:
- pushl %ebp
- movl %esp,%ebp
- subl $16,%esp
- pushl %esi
-
- movl PARAM1,%esi
- movl PARAM2,%ecx
-
- xor %eax,%eax
- movl %eax,-4(%ebp)
- movl %eax,-8(%ebp)
-
- movl (%esi),%eax /* lsl of Xsig */
- mull %ecx /* msl of b */
- movl %edx,-12(%ebp)
-
- movl 4(%esi),%eax /* midl of Xsig */
- mull %ecx /* msl of b */
- addl %eax,-12(%ebp)
- adcl %edx,-8(%ebp)
- adcl $0,-4(%ebp)
-
- movl 8(%esi),%eax /* msl of Xsig */
- mull %ecx /* msl of b */
- addl %eax,-8(%ebp)
- adcl %edx,-4(%ebp)
-
- movl -12(%ebp),%eax
- movl %eax,(%esi)
- movl -8(%ebp),%eax
- movl %eax,4(%esi)
- movl -4(%ebp),%eax
- movl %eax,8(%esi)
-
- popl %esi
- leave
- ret
-
-
- .align 2,144
-.globl _mul64_Xsig
-_mul64_Xsig:
- pushl %ebp
- movl %esp,%ebp
- subl $16,%esp
- pushl %esi
-
- movl PARAM1,%esi
- movl PARAM2,%ecx
-
- xor %eax,%eax
- movl %eax,-4(%ebp)
- movl %eax,-8(%ebp)
-
- movl (%esi),%eax /* lsl of Xsig */
- mull 4(%ecx) /* msl of b */
- movl %edx,-12(%ebp)
-
- movl 4(%esi),%eax /* midl of Xsig */
- mull (%ecx) /* lsl of b */
- addl %edx,-12(%ebp)
- adcl $0,-8(%ebp)
- adcl $0,-4(%ebp)
-
- movl 4(%esi),%eax /* midl of Xsig */
- mull 4(%ecx) /* msl of b */
- addl %eax,-12(%ebp)
- adcl %edx,-8(%ebp)
- adcl $0,-4(%ebp)
-
- movl 8(%esi),%eax /* msl of Xsig */
- mull (%ecx) /* lsl of b */
- addl %eax,-12(%ebp)
- adcl %edx,-8(%ebp)
- adcl $0,-4(%ebp)
-
- movl 8(%esi),%eax /* msl of Xsig */
- mull 4(%ecx) /* msl of b */
- addl %eax,-8(%ebp)
- adcl %edx,-4(%ebp)
-
- movl -12(%ebp),%eax
- movl %eax,(%esi)
- movl -8(%ebp),%eax
- movl %eax,4(%esi)
- movl -4(%ebp),%eax
- movl %eax,8(%esi)
-
- popl %esi
- leave
- ret
-
-
-
- .align 2,144
-.globl _mul_Xsig_Xsig
-_mul_Xsig_Xsig:
- pushl %ebp
- movl %esp,%ebp
- subl $16,%esp
- pushl %esi
-
- movl PARAM1,%esi
- movl PARAM2,%ecx
-
- xor %eax,%eax
- movl %eax,-4(%ebp)
- movl %eax,-8(%ebp)
-
- movl (%esi),%eax /* lsl of Xsig */
- mull 8(%ecx) /* msl of b */
- movl %edx,-12(%ebp)
-
- movl 4(%esi),%eax /* midl of Xsig */
- mull 4(%ecx) /* midl of b */
- addl %edx,-12(%ebp)
- adcl $0,-8(%ebp)
- adcl $0,-4(%ebp)
-
- movl 8(%esi),%eax /* msl of Xsig */
- mull (%ecx) /* lsl of b */
- addl %edx,-12(%ebp)
- adcl $0,-8(%ebp)
- adcl $0,-4(%ebp)
-
- movl 4(%esi),%eax /* midl of Xsig */
- mull 8(%ecx) /* msl of b */
- addl %eax,-12(%ebp)
- adcl %edx,-8(%ebp)
- adcl $0,-4(%ebp)
-
- movl 8(%esi),%eax /* msl of Xsig */
- mull 4(%ecx) /* midl of b */
- addl %eax,-12(%ebp)
- adcl %edx,-8(%ebp)
- adcl $0,-4(%ebp)
-
- movl 8(%esi),%eax /* msl of Xsig */
- mull 8(%ecx) /* msl of b */
- addl %eax,-8(%ebp)
- adcl %edx,-4(%ebp)
-
- movl -12(%ebp),%edx
- movl %edx,(%esi)
- movl -8(%ebp),%edx
- movl %edx,4(%esi)
- movl -4(%ebp),%edx
- movl %edx,8(%esi)
-
- popl %esi
- leave
- ret
-
diff --git a/drivers/FPU-emu/poly.h b/drivers/FPU-emu/poly.h
deleted file mode 100644
index 397cb9e3e..000000000
--- a/drivers/FPU-emu/poly.h
+++ /dev/null
@@ -1,116 +0,0 @@
-/*---------------------------------------------------------------------------+
- | 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 --git a/drivers/FPU-emu/poly_2xm1.c b/drivers/FPU-emu/poly_2xm1.c
deleted file mode 100644
index f7c585d60..000000000
--- a/drivers/FPU-emu/poly_2xm1.c
+++ /dev/null
@@ -1,152 +0,0 @@
-/*---------------------------------------------------------------------------+
- | poly_2xm1.c |
- | |
- | Function to compute 2^x-1 by a polynomial approximation. |
- | |
- | Copyright (C) 1992,1993,1994 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
-
-#include "exception.h"
-#include "reg_constant.h"
-#include "fpu_emu.h"
-#include "control_w.h"
-#include "poly.h"
-
-
-#define HIPOWER 11
-static const unsigned long long lterms[HIPOWER] =
-{
- 0x0000000000000000LL, /* This term done separately as 12 bytes */
- 0xf5fdeffc162c7543LL,
- 0x1c6b08d704a0bfa6LL,
- 0x0276556df749cc21LL,
- 0x002bb0ffcf14f6b8LL,
- 0x0002861225ef751cLL,
- 0x00001ffcbfcd5422LL,
- 0x00000162c005d5f1LL,
- 0x0000000da96ccb1bLL,
- 0x0000000078d1b897LL,
- 0x000000000422b029LL
-};
-
-static const Xsig hiterm = MK_XSIG(0xb17217f7, 0xd1cf79ab, 0xc8a39194);
-
-/* Four slices: 0.0 : 0.25 : 0.50 : 0.75 : 1.0,
- These numbers are 2^(1/4), 2^(1/2), and 2^(3/4)
- */
-static const Xsig shiftterm0 = MK_XSIG(0, 0, 0);
-static const Xsig shiftterm1 = MK_XSIG(0x9837f051, 0x8db8a96f, 0x46ad2318);
-static const Xsig shiftterm2 = MK_XSIG(0xb504f333, 0xf9de6484, 0x597d89b3);
-static const Xsig shiftterm3 = MK_XSIG(0xd744fcca, 0xd69d6af4, 0x39a68bb9);
-
-static const Xsig *shiftterm[] = { &shiftterm0, &shiftterm1,
- &shiftterm2, &shiftterm3 };
-
-
-/*--- poly_2xm1() -----------------------------------------------------------+
- | Requires an argument which is TW_Valid and < 1. |
- +---------------------------------------------------------------------------*/
-int poly_2xm1(FPU_REG const *arg, FPU_REG *result)
-{
- long int exponent, shift;
- unsigned long long Xll;
- Xsig accumulator, Denom, argSignif;
-
-
- exponent = arg->exp - EXP_BIAS;
-
-#ifdef PARANOID
- if ( (exponent >= 0) /* Don't want a |number| >= 1.0 */
- || (arg->tag != TW_Valid) )
- {
- /* Number negative, too large, or not Valid. */
- EXCEPTION(EX_INTERNAL|0x127);
- return 1;
- }
-#endif PARANOID
-
- 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, -2-exponent) >= 0x80000000U )
- Xll++; /* round up */
- }
-
- accumulator.lsw = accumulator.midw = accumulator.msw = 0;
- polynomial_Xsig(&accumulator, &Xll, lterms, HIPOWER-1);
- mul_Xsig_Xsig(&accumulator, &argSignif);
- shr_Xsig(&accumulator, 3);
-
- mul_Xsig_Xsig(&argSignif, &hiterm); /* The leading term */
- add_two_Xsig(&accumulator, &argSignif, &exponent);
-
- 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);
- }
-
- /* Convert to 64 bit signed-compatible */
- exponent += round_Xsig(&accumulator);
-
- significand(result) = XSIG_LL(accumulator);
- result->tag = TW_Valid;
- result->exp = exponent + EXP_BIAS;
- result->sign = arg->sign;
-
- return 0;
-
-}
diff --git a/drivers/FPU-emu/poly_atan.c b/drivers/FPU-emu/poly_atan.c
deleted file mode 100644
index 6edca625f..000000000
--- a/drivers/FPU-emu/poly_atan.c
+++ /dev/null
@@ -1,197 +0,0 @@
-/*---------------------------------------------------------------------------+
- | poly_atan.c |
- | |
- | Compute the arctan of a FPU_REG, using a polynomial approximation. |
- | |
- | Copyright (C) 1992,1993,1994 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
-
-#include "exception.h"
-#include "reg_constant.h"
-#include "fpu_emu.h"
-#include "status_w.h"
-#include "control_w.h"
-#include "poly.h"
-
-
-#define HIPOWERon 6 /* odd poly, negative terms */
-static const unsigned long long oddnegterms[HIPOWERon] =
-{
- 0x0000000000000000LL, /* Dummy (not for - 1.0) */
- 0x015328437f756467LL,
- 0x0005dda27b73dec6LL,
- 0x0000226bf2bfb91aLL,
- 0x000000ccc439c5f7LL,
- 0x0000000355438407LL
-} ;
-
-#define HIPOWERop 6 /* odd poly, positive terms */
-static const unsigned long long oddplterms[HIPOWERop] =
-{
-/* 0xaaaaaaaaaaaaaaabLL, transferred to fixedpterm[] */
- 0x0db55a71875c9ac2LL,
- 0x0029fce2d67880b0LL,
- 0x0000dfd3908b4596LL,
- 0x00000550fd61dab4LL,
- 0x0000001c9422b3f9LL,
- 0x000000003e3301e1LL
-};
-
-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 *arg1, FPU_REG *arg2, FPU_REG *result)
-{
- char transformed, inverted,
- sign1 = arg1->sign, sign2 = arg2->sign;
- long int exponent, dummy_exp;
- Xsig accumulator, Numer, Denom, accumulatore, argSignif,
- argSq, argSqSq;
-
-
- arg1->sign = arg2->sign = SIGN_POS;
- if ( (compare(arg2) & ~COMP_Denormal) == COMP_A_lt_B )
- {
- inverted = 1;
- exponent = arg1->exp - arg2->exp;
- Numer.lsw = Denom.lsw = 0;
- XSIG_LL(Numer) = significand(arg1);
- XSIG_LL(Denom) = significand(arg2);
- }
- 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)) )
- {
- /* 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) &&
- (argSignif.lsw == 0) && (argSignif.midw == 0) &&
- (argSignif.msw == 0x80000000) ) )
- {
- EXCEPTION(EX_INTERNAL|0x104); /* There must be a logic error */
- return;
- }
-#endif PARANOID
- argSignif.msw = 0; /* Make the transformed arg -> 0.0 */
- }
- else
- {
- Numer.lsw = Denom.lsw = argSignif.lsw;
- XSIG_LL(Numer) = XSIG_LL(Denom) = XSIG_LL(argSignif);
-
- if ( exponent < -1 )
- shr_Xsig(&Numer, -1-exponent);
- negate_Xsig(&Numer);
-
- shr_Xsig(&Denom, -exponent);
- Denom.msw |= 0x80000000;
-
- div_Xsig(&Numer, &Denom, &argSignif);
-
- exponent = -1 + norm_Xsig(&argSignif);
- }
- }
- else
- {
- transformed = 0;
- }
-
- argSq.lsw = argSignif.lsw; argSq.midw = argSignif.midw;
- argSq.msw = argSignif.msw;
- mul_Xsig_Xsig(&argSq, &argSq);
-
- argSqSq.lsw = argSq.lsw; argSqSq.midw = argSq.midw; argSqSq.msw = argSq.msw;
- mul_Xsig_Xsig(&argSqSq, &argSqSq);
-
- accumulatore.lsw = argSq.lsw;
- XSIG_LL(accumulatore) = XSIG_LL(argSq);
-
- shr_Xsig(&argSq, 2*(-1-exponent-1));
- shr_Xsig(&argSqSq, 4*(-1-exponent-1));
-
- /* 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);
-
- if ( transformed )
- {
- /* compute pi/4 - accumulator */
- shr_Xsig(&accumulator, -1-exponent);
- negate_Xsig(&accumulator);
- add_Xsig_Xsig(&accumulator, &pi_signif);
- exponent = -1;
- }
-
- if ( inverted )
- {
- /* compute pi/2 - accumulator */
- shr_Xsig(&accumulator, -exponent);
- negate_Xsig(&accumulator);
- add_Xsig_Xsig(&accumulator, &pi_signif);
- exponent = 0;
- }
-
- if ( sign1 )
- {
- /* compute pi - accumulator */
- shr_Xsig(&accumulator, 1 - exponent);
- negate_Xsig(&accumulator);
- add_Xsig_Xsig(&accumulator, &pi_signif);
- exponent = 1;
- }
-
- exponent += round_Xsig(&accumulator);
- significand(result) = XSIG_LL(accumulator);
- result->exp = exponent + EXP_BIAS;
- result->tag = TW_Valid;
- result->sign = sign2;
-
-}
diff --git a/drivers/FPU-emu/poly_l2.c b/drivers/FPU-emu/poly_l2.c
deleted file mode 100644
index 1677f4aff..000000000
--- a/drivers/FPU-emu/poly_l2.c
+++ /dev/null
@@ -1,255 +0,0 @@
-/*---------------------------------------------------------------------------+
- | poly_l2.c |
- | |
- | Compute the base 2 log of a FPU_REG, using a polynomial approximation. |
- | |
- | Copyright (C) 1992,1993,1994 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
-
-
-#include "exception.h"
-#include "reg_constant.h"
-#include "fpu_emu.h"
-#include "control_w.h"
-#include "poly.h"
-
-
-
-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 const *y, FPU_REG *result)
-{
- long int exponent, expon, expon_expon;
- Xsig accumulator, expon_accum, yaccum;
- char sign;
- FPU_REG x;
-
-
- exponent = arg->exp - EXP_BIAS;
-
- /* From arg, make a number > sqrt(2)/2 and < sqrt(2) */
- if ( arg->sigh > (unsigned)0xb504f334 )
- {
- /* 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++;
- 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
- {
- 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);
-
- if ( sign ^ (x.sign == SIGN_NEG) )
- negate_Xsig(&accumulator);
- add_Xsig_Xsig(&accumulator, &expon_accum);
- }
- else
- {
- expon_expon = expon;
- sign = x.sign;
- }
-
- yaccum.lsw = 0; XSIG_LL(yaccum) = significand(y);
- mul_Xsig_Xsig(&accumulator, &yaccum);
-
- expon_expon += round_Xsig(&accumulator);
-
- if ( accumulator.msw == 0 )
- {
- reg_move(&CONST_Z, y);
- }
- else
- {
- result->exp = expon_expon + y->exp + 1;
- significand(result) = XSIG_LL(accumulator);
- result->tag = TW_Valid; /* set the tags to Valid */
- result->sign = sign ^ y->sign;
- }
-
- 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;
-
-
- sign = arg->sign;
-
- if ( arg->exp < EXP_BIAS )
- {
- log2_kernel(arg, &accumulator, &exponent);
-
- yaccum.lsw = 0;
- XSIG_LL(yaccum) = significand(y);
- mul_Xsig_Xsig(&accumulator, &yaccum);
-
- exponent += round_Xsig(&accumulator);
-
- result->exp = exponent + y->exp + 1;
- significand(result) = XSIG_LL(accumulator);
- result->tag = TW_Valid; /* set the tags to Valid */
- result->sign = sign ^ y->sign;
-
- return 0;
- }
- else
- {
- /* The magnitude of arg is far too large. */
- reg_move(y, result);
- if ( sign != SIGN_POS )
- {
- /* Trying to get the log of a negative number. */
- return 1;
- }
- else
- {
- return 0;
- }
- }
-
-}
-
-
-
-
-#undef HIPOWER
-#define HIPOWER 10
-static const unsigned long long logterms[HIPOWER] =
-{
- 0x2a8eca5705fc2ef0LL,
- 0xf6384ee1d01febceLL,
- 0x093bb62877cdf642LL,
- 0x006985d8a9ec439bLL,
- 0x0005212c4f55a9c8LL,
- 0x00004326a16927f0LL,
- 0x0000038d1d80a0e7LL,
- 0x0000003141cc80c6LL,
- 0x00000002b1668c9fLL,
- 0x000000002c7a46aaLL
-};
-
-static const unsigned long leadterm = 0xb8000000;
-
-
-/*--- log2_kernel() ---------------------------------------------------------+
- | Base 2 logarithm by a polynomial approximation. |
- | log2(x+1) |
- +---------------------------------------------------------------------------*/
-static void log2_kernel(FPU_REG const *arg, Xsig *accum_result,
- long int *expon)
-{
- char sign;
- long int exponent, adj;
- unsigned long long Xsq;
- Xsig accumulator, Numer, Denom, argSignif, arg_signif;
-
- sign = arg->sign;
-
- 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);
- }
- else
- {
- shr_Xsig(&Denom, 1 - (1 + exponent));
- negate_Xsig(&Denom);
- if ( Denom.msw & 0x80000000 )
- {
- div_Xsig(&Numer, &Denom, &argSignif);
- exponent ++;
- }
- else
- {
- /* Denom must be 1.0 */
- argSignif.lsw = Numer.lsw; argSignif.midw = Numer.midw;
- argSignif.msw = Numer.msw;
- }
- }
-
-#ifndef PECULIAR_486
- /* Should check here that |local_arg| is within the valid range */
- if ( exponent >= -2 )
- {
- if ( (exponent > -2) ||
- (argSignif.msw > (unsigned)0xafb0ccc0) )
- {
- /* The argument is too large */
- }
- }
-#endif PECULIAR_486
-
- arg_signif.lsw = argSignif.lsw; XSIG_LL(arg_signif) = XSIG_LL(argSignif);
- adj = norm_Xsig(&argSignif);
- accumulator.lsw = argSignif.lsw; XSIG_LL(accumulator) = XSIG_LL(argSignif);
- mul_Xsig_Xsig(&accumulator, &accumulator);
- shr_Xsig(&accumulator, 2*(-1 - (1 + exponent + adj)));
- Xsq = XSIG_LL(accumulator);
- if ( accumulator.lsw & 0x80000000 )
- Xsq++;
-
- accumulator.msw = accumulator.midw = accumulator.lsw = 0;
- /* Do the basic fixed point polynomial evaluation */
- polynomial_Xsig(&accumulator, &Xsq, logterms, HIPOWER-1);
-
- mul_Xsig_Xsig(&accumulator, &argSignif);
- shr_Xsig(&accumulator, 6 - adj);
-
- mul32_Xsig(&arg_signif, leadterm);
- add_two_Xsig(&accumulator, &arg_signif, &exponent);
-
- *expon = exponent + 1;
- accum_result->lsw = accumulator.lsw;
- accum_result->midw = accumulator.midw;
- accum_result->msw = accumulator.msw;
-
-}
diff --git a/drivers/FPU-emu/poly_sin.c b/drivers/FPU-emu/poly_sin.c
deleted file mode 100644
index 03db5b6aa..000000000
--- a/drivers/FPU-emu/poly_sin.c
+++ /dev/null
@@ -1,408 +0,0 @@
-/*---------------------------------------------------------------------------+
- | poly_sin.c |
- | |
- | 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, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
-
-
-#include "exception.h"
-#include "reg_constant.h"
-#include "fpu_emu.h"
-#include "control_w.h"
-#include "poly.h"
-
-
-#define 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)
-{
- 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;
- }
-#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)) )
- {
- /* 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;
- }
- else
- {
- /* The argument is > 0.88309101259 */
- /* We use sin(arg) = cos(pi/2-arg) */
-
- 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;
-
- 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
-
- 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);
-
- /* 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);
-
- 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 --git a/drivers/FPU-emu/poly_tan.c b/drivers/FPU-emu/poly_tan.c
deleted file mode 100644
index d9b09e438..000000000
--- a/drivers/FPU-emu/poly_tan.c
+++ /dev/null
@@ -1,213 +0,0 @@
-/*---------------------------------------------------------------------------+
- | poly_tan.c |
- | |
- | Compute the tan of a FPU_REG, using a polynomial approximation. |
- | |
- | Copyright (C) 1992,1993,1994 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
-
-#include "exception.h"
-#include "reg_constant.h"
-#include "fpu_emu.h"
-#include "control_w.h"
-#include "poly.h"
-
-
-#define HiPOWERop 3 /* odd poly, positive terms */
-static const unsigned long long oddplterm[HiPOWERop] =
-{
- 0x0000000000000000LL,
- 0x0051a1cf08fca228LL,
- 0x0000000071284ff7LL
-};
-
-#define HiPOWERon 2 /* odd poly, negative terms */
-static const unsigned long long oddnegterm[HiPOWERon] =
-{
- 0x1291a9a184244e80LL,
- 0x0000583245819c21LL
-};
-
-#define HiPOWERep 2 /* even poly, positive terms */
-static const unsigned long long evenplterm[HiPOWERep] =
-{
- 0x0e848884b539e888LL,
- 0x00003c7f18b887daLL
-};
-
-#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)
-{
- long int exponent;
- int invert;
- Xsig argSq, argSqSq, accumulatoro, accumulatore, accum,
- argSignif, fix_up;
- unsigned long adj;
-
- exponent = arg->exp - EXP_BIAS;
-
-#ifdef PARANOID
- if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */
- { arith_invalid(result); return; } /* Need a positive number */
-#endif PARANOID
-
- /* Split the problem into two domains, smaller and larger than pi/4 */
- if ( (exponent == 0) || ((exponent == -1) && (arg->sigh > 0xc90fdaa2)) )
- {
- /* The argument is greater than (approx) pi/4 */
- invert = 1;
- accum.lsw = 0;
- XSIG_LL(accum) = significand(arg);
-
- if ( exponent == 0 )
- {
- /* The argument is >= 1.0 */
- /* Put the binary point at the left. */
- XSIG_LL(accum) <<= 1;
- }
- /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */
- XSIG_LL(accum) = 0x921fb54442d18469LL - XSIG_LL(accum);
-
- argSignif.lsw = accum.lsw;
- XSIG_LL(argSignif) = XSIG_LL(accum);
- exponent = -1 + norm_Xsig(&argSignif);
- }
- else
- {
- invert = 0;
- argSignif.lsw = 0;
- XSIG_LL(accum) = XSIG_LL(argSignif) = significand(arg);
-
- if ( exponent < -1 )
- {
- /* shift the argument right by the required places */
- if ( shrx(&XSIG_LL(accum), -1-exponent) >= 0x80000000U )
- XSIG_LL(accum) ++; /* round up */
- }
- }
-
- 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);
-
-
- /* Compute the positive terms for the denominator polynomial */
- accumulatore.msw = accumulatore.midw = accumulatore.lsw = 0;
- polynomial_Xsig(&accumulatore, &XSIG_LL(argSqSq), evenplterm, HiPOWERep-1);
- mul_Xsig_Xsig(&accumulatore, &argSq);
- negate_Xsig(&accumulatore);
- /* Add the negative terms */
- polynomial_Xsig(&accumulatore, &XSIG_LL(argSqSq), evennegterm, HiPOWERen-1);
- /* Multiply by arg^2 */
- mul64_Xsig(&accumulatore, &XSIG_LL(argSignif));
- mul64_Xsig(&accumulatore, &XSIG_LL(argSignif));
- /* de-normalize and divide by 2 */
- shr_Xsig(&accumulatore, -2*(1+exponent) + 1);
- negate_Xsig(&accumulatore); /* This does 1 - accumulator */
-
- /* Now find the ratio. */
- if ( accumulatore.msw == 0 )
- {
- /* accumulatoro must contain 1.0 here, (actually, 0) but it
- really doesn't matter what value we use because it will
- have negligible effect in later calculations
- */
- XSIG_LL(accum) = 0x8000000000000000LL;
- accum.lsw = 0;
- }
- else
- {
- div_Xsig(&accumulatoro, &accumulatore, &accum);
- }
-
- /* 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));
-
- /* tan(arg) = arg + accum */
- add_two_Xsig(&accum, &argSignif, &exponent);
-
- 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 */
-
- fix_up.msw += adj;
- if ( !(fix_up.msw & 0x80000000) ) /* did fix_up overflow ? */
- {
- /* Yes, we need to add an msb */
- shr_Xsig(&fix_up, 1);
- fix_up.msw |= 0x80000000;
- shr_Xsig(&fix_up, 64 + exponent);
- }
- else
- shr_Xsig(&fix_up, 65 + exponent);
-
- add_two_Xsig(&accum, &fix_up, &exponent);
-
- /* accum now contains tan(pi/2 - arg).
- Use tan(arg) = 1.0 / tan(pi/2 - arg)
- */
- accumulatoro.lsw = accumulatoro.midw = 0;
- accumulatoro.msw = 0x80000000;
- div_Xsig(&accumulatoro, &accum, &accum);
- exponent = - exponent - 1;
- }
-
- /* Transfer the result */
- round_Xsig(&accum);
- *(short *)&(result->sign) = 0;
- significand(result) = XSIG_LL(accum);
- result->exp = EXP_BIAS + exponent;
-
-}
diff --git a/drivers/FPU-emu/polynom_Xsig.S b/drivers/FPU-emu/polynom_Xsig.S
deleted file mode 100644
index 585221f96..000000000
--- a/drivers/FPU-emu/polynom_Xsig.S
+++ /dev/null
@@ -1,137 +0,0 @@
-/*---------------------------------------------------------------------------+
- | 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
diff --git a/drivers/FPU-emu/reg_add_sub.c b/drivers/FPU-emu/reg_add_sub.c
deleted file mode 100644
index d70889b40..000000000
--- a/drivers/FPU-emu/reg_add_sub.c
+++ /dev/null
@@ -1,318 +0,0 @@
-/*---------------------------------------------------------------------------+
- | reg_add_sub.c |
- | |
- | Functions to add or subtract two registers and put the result in a third. |
- | |
- | Copyright (C) 1992,1993 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
-
-/*---------------------------------------------------------------------------+
- | For each function, the destination may be any FPU_REG, including one of |
- | the source FPU_REGs. |
- +---------------------------------------------------------------------------*/
-
-#include "exception.h"
-#include "reg_constant.h"
-#include "fpu_emu.h"
-#include "control_w.h"
-#include "fpu_system.h"
-
-
-int reg_add(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w)
-{
- char saved_sign = dest->sign;
- int diff;
-
- if ( !(a->tag | b->tag) )
- {
- /* Both registers are valid */
- if (!(a->sign ^ b->sign))
- {
- /* signs are the same */
- dest->sign = a->sign;
- if ( reg_u_add(a, b, dest, control_w) )
- {
- dest->sign = saved_sign;
- return 1;
- }
- return 0;
- }
-
- /* The signs are different, so do a subtraction */
- diff = a->exp - b->exp;
- if (!diff)
- {
- diff = a->sigh - b->sigh; /* Works only if ms bits are identical */
- if (!diff)
- {
- diff = a->sigl > b->sigl;
- if (!diff)
- diff = -(a->sigl < b->sigl);
- }
- }
-
- if (diff > 0)
- {
- dest->sign = a->sign;
- if ( reg_u_sub(a, b, dest, control_w) )
- {
- dest->sign = saved_sign;
- return 1;
- }
- }
- else if ( diff == 0 )
- {
-#ifdef DENORM_OPERAND
- if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
- denormal_operand() )
- return 1;
-#endif DENORM_OPERAND
- reg_move(&CONST_Z, dest);
- /* sign depends upon rounding mode */
- dest->sign = ((control_w & CW_RC) != RC_DOWN)
- ? SIGN_POS : SIGN_NEG;
- }
- else
- {
- dest->sign = b->sign;
- if ( reg_u_sub(b, a, dest, control_w) )
- {
- dest->sign = saved_sign;
- return 1;
- }
- }
- return 0;
- }
- else
- {
- if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
- { return real_2op_NaN(a, b, dest); }
- else if (a->tag == TW_Zero)
- {
- if (b->tag == TW_Zero)
- {
- char different_signs = a->sign ^ b->sign;
- /* Both are zero, result will be zero. */
- reg_move(a, dest);
- if (different_signs)
- {
- /* Signs are different. */
- /* Sign of answer depends upon rounding mode. */
- dest->sign = ((control_w & CW_RC) != RC_DOWN)
- ? SIGN_POS : SIGN_NEG;
- }
- }
- else
- {
-#ifdef DENORM_OPERAND
- if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
- denormal_operand() )
- return 1;
-#endif DENORM_OPERAND
- reg_move(b, dest);
- }
- return 0;
- }
- else if (b->tag == TW_Zero)
- {
-#ifdef DENORM_OPERAND
- if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
- denormal_operand() )
- return 1;
-#endif DENORM_OPERAND
- reg_move(a, dest); return 0;
- }
- else if (a->tag == TW_Infinity)
- {
- if (b->tag != TW_Infinity)
- {
-#ifdef DENORM_OPERAND
- if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
- denormal_operand() )
- return 1;
-#endif DENORM_OPERAND
- reg_move(a, dest); return 0;
- }
- if (a->sign == b->sign)
- {
- /* They are both + or - infinity */
- reg_move(a, dest); return 0;
- }
- return arith_invalid(dest); /* Infinity-Infinity is undefined. */
- }
- else if (b->tag == TW_Infinity)
- {
-#ifdef DENORM_OPERAND
- if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
- denormal_operand() )
- return 1;
-#endif DENORM_OPERAND
- reg_move(b, dest); return 0;
- }
- }
-#ifdef PARANOID
- EXCEPTION(EX_INTERNAL|0x101);
-#endif
- return 1;
-}
-
-
-/* Subtract b from a. (a-b) -> dest */
-int reg_sub(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w)
-{
- char saved_sign = dest->sign;
- int diff;
-
- if ( !(a->tag | b->tag) )
- {
- /* Both registers are valid */
- diff = a->exp - b->exp;
- if (!diff)
- {
- diff = a->sigh - b->sigh; /* Works only if ms bits are identical */
- if (!diff)
- {
- diff = a->sigl > b->sigl;
- if (!diff)
- diff = -(a->sigl < b->sigl);
- }
- }
-
- switch (a->sign*2 + b->sign)
- {
- case 0: /* P - P */
- case 3: /* N - N */
- if (diff > 0)
- {
- /* |a| > |b| */
- dest->sign = a->sign;
- if ( reg_u_sub(a, b, dest, control_w) )
- {
- dest->sign = saved_sign;
- return 1;
- }
- return 0;
- }
- else if ( diff == 0 )
- {
-#ifdef DENORM_OPERAND
- if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
- denormal_operand() )
- return 1;
-#endif DENORM_OPERAND
- reg_move(&CONST_Z, dest);
- /* sign depends upon rounding mode */
- dest->sign = ((control_w & CW_RC) != RC_DOWN)
- ? SIGN_POS : SIGN_NEG;
- }
- else
- {
- dest->sign = a->sign ^ SIGN_POS^SIGN_NEG;
- if ( reg_u_sub(b, a, dest, control_w) )
- {
- dest->sign = saved_sign;
- return 1;
- }
- }
- break;
- case 1: /* P - N */
- dest->sign = SIGN_POS;
- if ( reg_u_add(a, b, dest, control_w) )
- {
- dest->sign = saved_sign;
- return 1;
- }
- break;
- case 2: /* N - P */
- dest->sign = SIGN_NEG;
- if ( reg_u_add(a, b, dest, control_w) )
- {
- dest->sign = saved_sign;
- return 1;
- }
- break;
- }
- return 0;
- }
- else
- {
- if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
- { return real_2op_NaN(b, a, dest); }
- else if (b->tag == TW_Zero)
- {
- if (a->tag == TW_Zero)
- {
- char same_signs = !(a->sign ^ b->sign);
- /* Both are zero, result will be zero. */
- reg_move(a, dest); /* Answer for different signs. */
- if (same_signs)
- {
- /* Sign depends upon rounding mode */
- dest->sign = ((control_w & CW_RC) != RC_DOWN)
- ? SIGN_POS : SIGN_NEG;
- }
- }
- else
- {
-#ifdef DENORM_OPERAND
- if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
- denormal_operand() )
- return 1;
-#endif DENORM_OPERAND
- reg_move(a, dest);
- }
- return 0;
- }
- else if (a->tag == TW_Zero)
- {
-#ifdef DENORM_OPERAND
- if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
- denormal_operand() )
- return 1;
-#endif DENORM_OPERAND
- reg_move(b, dest);
- dest->sign ^= SIGN_POS^SIGN_NEG;
- return 0;
- }
- else if (a->tag == TW_Infinity)
- {
- if (b->tag != TW_Infinity)
- {
-#ifdef DENORM_OPERAND
- if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
- denormal_operand() )
- return 1;
-#endif DENORM_OPERAND
- reg_move(a, dest); return 0;
- }
- /* Both args are Infinity */
- if (a->sign == b->sign)
- {
- /* Infinity-Infinity is undefined. */
- return arith_invalid(dest);
- }
- reg_move(a, dest);
- return 0;
- }
- else if (b->tag == TW_Infinity)
- {
-#ifdef DENORM_OPERAND
- if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
- denormal_operand() )
- return 1;
-#endif DENORM_OPERAND
- reg_move(b, dest);
- dest->sign ^= SIGN_POS^SIGN_NEG;
- return 0;
- }
- }
-#ifdef PARANOID
- EXCEPTION(EX_INTERNAL|0x110);
-#endif
- return 1;
-}
-
diff --git a/drivers/FPU-emu/reg_compare.c b/drivers/FPU-emu/reg_compare.c
deleted file mode 100644
index eb4a1fa99..000000000
--- a/drivers/FPU-emu/reg_compare.c
+++ /dev/null
@@ -1,378 +0,0 @@
-/*---------------------------------------------------------------------------+
- | reg_compare.c |
- | |
- | Compare two floating point registers |
- | |
- | Copyright (C) 1992,1993,1994 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
-
-/*---------------------------------------------------------------------------+
- | compare() is the core FPU_REG comparison function |
- +---------------------------------------------------------------------------*/
-
-#include "fpu_system.h"
-#include "exception.h"
-#include "fpu_emu.h"
-#include "control_w.h"
-#include "status_w.h"
-
-
-int compare(FPU_REG const *b)
-{
- int diff;
- char st0_tag;
- FPU_REG *st0_ptr;
-
- st0_ptr = &st(0);
- st0_tag = st0_ptr->tag;
-
- if ( st0_tag | b->tag )
- {
- if ( st0_tag == TW_Zero )
- {
- if ( b->tag == TW_Zero ) return COMP_A_eq_B;
- if ( b->tag == TW_Valid )
- {
- return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
-#ifdef DENORM_OPERAND
- | ((b->exp <= EXP_UNDER) ?
- COMP_Denormal : 0)
-#endif DENORM_OPERAND
- ;
- }
- }
- else if ( b->tag == TW_Zero )
- {
- if ( st0_tag == TW_Valid )
- {
- return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B
- : COMP_A_lt_B)
-#ifdef DENORM_OPERAND
- | ((st0_ptr->exp <= EXP_UNDER )
- ? COMP_Denormal : 0 )
-#endif DENORM_OPERAND
- ;
- }
- }
-
- if ( st0_tag == TW_Infinity )
- {
- if ( (b->tag == TW_Valid) || (b->tag == TW_Zero) )
- {
- return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B
- : COMP_A_lt_B)
-#ifdef DENORM_OPERAND
- | (((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ?
- COMP_Denormal : 0 )
-#endif DENORM_OPERAND
-;
- }
- else if ( b->tag == TW_Infinity )
- {
- /* The 80486 book says that infinities can be equal! */
- return (st0_ptr->sign == b->sign) ? COMP_A_eq_B :
- ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
- }
- /* Fall through to the NaN code */
- }
- else if ( b->tag == TW_Infinity )
- {
- if ( (st0_tag == TW_Valid) || (st0_tag == TW_Zero) )
- {
- return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
-#ifdef DENORM_OPERAND
- | (((st0_tag == TW_Valid)
- && (st0_ptr->exp <= EXP_UNDER)) ?
- COMP_Denormal : 0)
-#endif DENORM_OPERAND
- ;
- }
- /* Fall through to the NaN code */
- }
-
- /* The only possibility now should be that one of the arguments
- is a NaN */
- if ( (st0_tag == TW_NaN) || (b->tag == TW_NaN) )
- {
- if ( ((st0_tag == TW_NaN) && !(st0_ptr->sigh & 0x40000000))
- || ((b->tag == TW_NaN) && !(b->sigh & 0x40000000)) )
- /* At least one arg is a signaling NaN */
- return COMP_No_Comp | COMP_SNaN | COMP_NaN;
- else
- /* Neither is a signaling NaN */
- return COMP_No_Comp | COMP_NaN;
- }
-
- EXCEPTION(EX_Invalid);
- }
-
-#ifdef PARANOID
- if (!(st0_ptr->sigh & 0x80000000)) EXCEPTION(EX_Invalid);
- if (!(b->sigh & 0x80000000)) EXCEPTION(EX_Invalid);
-#endif PARANOID
-
-
- if (st0_ptr->sign != b->sign)
- {
- return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
-#ifdef DENORM_OPERAND
- |
- ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
- COMP_Denormal : 0)
-#endif DENORM_OPERAND
- ;
- }
-
- diff = st0_ptr->exp - b->exp;
- if ( diff == 0 )
- {
- diff = st0_ptr->sigh - b->sigh; /* Works only if ms bits are
- identical */
- if ( diff == 0 )
- {
- diff = st0_ptr->sigl > b->sigl;
- if ( diff == 0 )
- diff = -(st0_ptr->sigl < b->sigl);
- }
- }
-
- if ( diff > 0 )
- {
- return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
-#ifdef DENORM_OPERAND
- |
- ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
- COMP_Denormal : 0)
-#endif DENORM_OPERAND
- ;
- }
- if ( diff < 0 )
- {
- return ((st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
-#ifdef DENORM_OPERAND
- |
- ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
- COMP_Denormal : 0)
-#endif DENORM_OPERAND
- ;
- }
-
- return COMP_A_eq_B
-#ifdef DENORM_OPERAND
- |
- ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
- COMP_Denormal : 0)
-#endif DENORM_OPERAND
- ;
-
-}
-
-
-/* This function requires that st(0) is not empty */
-int compare_st_data(FPU_REG const *loaded_data)
-{
- int f, c;
-
- c = compare(loaded_data);
-
- if (c & COMP_NaN)
- {
- EXCEPTION(EX_Invalid);
- f = SW_C3 | SW_C2 | SW_C0;
- }
- else
- switch (c & 7)
- {
- case COMP_A_lt_B:
- f = SW_C0;
- break;
- case COMP_A_eq_B:
- f = SW_C3;
- break;
- case COMP_A_gt_B:
- f = 0;
- break;
- case COMP_No_Comp:
- f = SW_C3 | SW_C2 | SW_C0;
- break;
-#ifdef PARANOID
- default:
- EXCEPTION(EX_INTERNAL|0x121);
- f = SW_C3 | SW_C2 | SW_C0;
- break;
-#endif PARANOID
- }
- setcc(f);
- if (c & COMP_Denormal)
- {
- return denormal_operand();
- }
- return 0;
-}
-
-
-static int compare_st_st(int nr)
-{
- int f, c;
-
- if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) )
- {
- setcc(SW_C3 | SW_C2 | SW_C0);
- /* Stack fault */
- EXCEPTION(EX_StackUnder);
- return !(control_word & CW_Invalid);
- }
-
- c = compare(&st(nr));
- if (c & COMP_NaN)
- {
- setcc(SW_C3 | SW_C2 | SW_C0);
- EXCEPTION(EX_Invalid);
- return !(control_word & CW_Invalid);
- }
- else
- switch (c & 7)
- {
- case COMP_A_lt_B:
- f = SW_C0;
- break;
- case COMP_A_eq_B:
- f = SW_C3;
- break;
- case COMP_A_gt_B:
- f = 0;
- break;
- case COMP_No_Comp:
- f = SW_C3 | SW_C2 | SW_C0;
- break;
-#ifdef PARANOID
- default:
- EXCEPTION(EX_INTERNAL|0x122);
- f = SW_C3 | SW_C2 | SW_C0;
- break;
-#endif PARANOID
- }
- setcc(f);
- if (c & COMP_Denormal)
- {
- return denormal_operand();
- }
- return 0;
-}
-
-
-static int compare_u_st_st(int nr)
-{
- int f, c;
-
- if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) )
- {
- setcc(SW_C3 | SW_C2 | SW_C0);
- /* Stack fault */
- EXCEPTION(EX_StackUnder);
- return !(control_word & CW_Invalid);
- }
-
- c = compare(&st(nr));
- if (c & COMP_NaN)
- {
- setcc(SW_C3 | SW_C2 | SW_C0);
- if (c & COMP_SNaN) /* This is the only difference between
- un-ordered and ordinary comparisons */
- {
- EXCEPTION(EX_Invalid);
- return !(control_word & CW_Invalid);
- }
- return 0;
- }
- else
- switch (c & 7)
- {
- case COMP_A_lt_B:
- f = SW_C0;
- break;
- case COMP_A_eq_B:
- f = SW_C3;
- break;
- case COMP_A_gt_B:
- f = 0;
- break;
- case COMP_No_Comp:
- f = SW_C3 | SW_C2 | SW_C0;
- break;
-#ifdef PARANOID
- default:
- EXCEPTION(EX_INTERNAL|0x123);
- f = SW_C3 | SW_C2 | SW_C0;
- break;
-#endif PARANOID
- }
- setcc(f);
- if (c & COMP_Denormal)
- {
- return denormal_operand();
- }
- return 0;
-}
-
-/*---------------------------------------------------------------------------*/
-
-void fcom_st()
-{
- /* fcom st(i) */
- compare_st_st(FPU_rm);
-}
-
-
-void fcompst()
-{
- /* fcomp st(i) */
- if ( !compare_st_st(FPU_rm) )
- pop();
-}
-
-
-void fcompp()
-{
- /* fcompp */
- if (FPU_rm != 1)
- {
- FPU_illegal();
- return;
- }
- if ( !compare_st_st(1) )
- poppop();
-}
-
-
-void fucom_()
-{
- /* fucom st(i) */
- compare_u_st_st(FPU_rm);
-
-}
-
-
-void fucomp()
-{
- /* fucomp st(i) */
- if ( !compare_u_st_st(FPU_rm) )
- pop();
-}
-
-
-void fucompp()
-{
- /* fucompp */
- if (FPU_rm == 1)
- {
- if ( !compare_u_st_st(1) )
- poppop();
- }
- else
- FPU_illegal();
-}
diff --git a/drivers/FPU-emu/reg_constant.c b/drivers/FPU-emu/reg_constant.c
deleted file mode 100644
index c1981ce24..000000000
--- a/drivers/FPU-emu/reg_constant.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/*---------------------------------------------------------------------------+
- | reg_constant.c |
- | |
- | All of the constant FPU_REGs |
- | |
- | Copyright (C) 1992,1993,1994 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
-
-#include "fpu_system.h"
-#include "fpu_emu.h"
-#include "status_w.h"
-#include "reg_constant.h"
-
-
-FPU_REG const CONST_1 = { SIGN_POS, TW_Valid, EXP_BIAS,
- 0x00000000, 0x80000000 };
-FPU_REG const CONST_2 = { SIGN_POS, TW_Valid, EXP_BIAS+1,
- 0x00000000, 0x80000000 };
-FPU_REG const CONST_HALF = { SIGN_POS, TW_Valid, EXP_BIAS-1,
- 0x00000000, 0x80000000 };
-FPU_REG const CONST_L2T = { SIGN_POS, TW_Valid, EXP_BIAS+1,
- 0xcd1b8afe, 0xd49a784b };
-FPU_REG const CONST_L2E = { SIGN_POS, TW_Valid, EXP_BIAS,
- 0x5c17f0bc, 0xb8aa3b29 };
-FPU_REG const CONST_PI = { SIGN_POS, TW_Valid, EXP_BIAS+1,
- 0x2168c235, 0xc90fdaa2 };
-FPU_REG const CONST_PI2 = { SIGN_POS, TW_Valid, EXP_BIAS,
- 0x2168c235, 0xc90fdaa2 };
-FPU_REG const CONST_PI4 = { SIGN_POS, TW_Valid, EXP_BIAS-1,
- 0x2168c235, 0xc90fdaa2 };
-FPU_REG const CONST_LG2 = { SIGN_POS, TW_Valid, EXP_BIAS-2,
- 0xfbcff799, 0x9a209a84 };
-FPU_REG const CONST_LN2 = { SIGN_POS, TW_Valid, EXP_BIAS-1,
- 0xd1cf79ac, 0xb17217f7 };
-
-/* Extra bits to take pi/2 to more than 128 bits precision. */
-FPU_REG const CONST_PI2extra = { SIGN_NEG, TW_Valid, EXP_BIAS-66,
- 0xfc8f8cbb, 0xece675d1 };
-
-/* Only the sign (and tag) is used in internal zeroes */
-FPU_REG const CONST_Z = { SIGN_POS, TW_Zero, EXP_UNDER, 0x0, 0x0 };
-
-/* Only the sign and significand (and tag) are used in internal NaNs */
-/* The 80486 never generates one of these
-FPU_REG const CONST_SNAN = { SIGN_POS, TW_NaN, EXP_OVER, 0x00000001, 0x80000000 };
- */
-/* This is the real indefinite QNaN */
-FPU_REG const CONST_QNaN = { SIGN_NEG, TW_NaN, EXP_OVER, 0x00000000, 0xC0000000 };
-
-/* Only the sign (and tag) is used in internal infinities */
-FPU_REG const CONST_INF = { SIGN_POS, TW_Infinity, EXP_OVER, 0x00000000, 0x80000000 };
-
-
-
-static void fld_const(FPU_REG const *c)
-{
- FPU_REG *st_new_ptr;
-
- if ( STACK_OVERFLOW )
- {
- stack_overflow();
- return;
- }
- push();
- reg_move(c, st_new_ptr);
- clear_C1();
-}
-
-
-static void fld1(void)
-{
- fld_const(&CONST_1);
-}
-
-static void fldl2t(void)
-{
- fld_const(&CONST_L2T);
-}
-
-static void fldl2e(void)
-{
- fld_const(&CONST_L2E);
-}
-
-static void fldpi(void)
-{
- fld_const(&CONST_PI);
-}
-
-static void fldlg2(void)
-{
- fld_const(&CONST_LG2);
-}
-
-static void fldln2(void)
-{
- fld_const(&CONST_LN2);
-}
-
-static void fldz(void)
-{
- fld_const(&CONST_Z);
-}
-
-static FUNC constants_table[] = {
- fld1, fldl2t, fldl2e, fldpi, fldlg2, fldln2, fldz, FPU_illegal
-};
-
-void fconst(void)
-{
- (constants_table[FPU_rm])();
-}
diff --git a/drivers/FPU-emu/reg_constant.h b/drivers/FPU-emu/reg_constant.h
deleted file mode 100644
index b7db97e34..000000000
--- a/drivers/FPU-emu/reg_constant.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*---------------------------------------------------------------------------+
- | reg_constant.h |
- | |
- | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- +---------------------------------------------------------------------------*/
-
-#ifndef _REG_CONSTANT_H_
-#define _REG_CONSTANT_H_
-
-#include "fpu_emu.h"
-
-extern FPU_REG const CONST_1;
-extern FPU_REG const CONST_2;
-extern FPU_REG const CONST_HALF;
-extern FPU_REG const CONST_L2T;
-extern FPU_REG const CONST_L2E;
-extern FPU_REG const CONST_PI;
-extern FPU_REG const CONST_PI2;
-extern FPU_REG const CONST_PI2extra;
-extern FPU_REG const CONST_PI4;
-extern FPU_REG const CONST_LG2;
-extern FPU_REG const CONST_LN2;
-extern FPU_REG const CONST_Z;
-extern FPU_REG const CONST_PINF;
-extern FPU_REG const CONST_INF;
-extern FPU_REG const CONST_MINF;
-extern FPU_REG const CONST_QNaN;
-
-#endif _REG_CONSTANT_H_
diff --git a/drivers/FPU-emu/reg_div.S b/drivers/FPU-emu/reg_div.S
deleted file mode 100644
index 2fbc5f7c4..000000000
--- a/drivers/FPU-emu/reg_div.S
+++ /dev/null
@@ -1,251 +0,0 @@
- .file "reg_div.S"
-/*---------------------------------------------------------------------------+
- | reg_div.S |
- | |
- | Divide one FPU_REG by another and put the result in a destination FPU_REG.|
- | |
- | 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 reg_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest, |
- | unsigned int control_word) |
- | |
- +---------------------------------------------------------------------------*/
-
-#include "exception.h"
-#include "fpu_asm.h"
-
-
-.text
- .align 2
-
-.globl _reg_div
-_reg_div:
- pushl %ebp
- movl %esp,%ebp
-#ifndef NON_REENTRANT_FPU
- subl $28,%esp /* Needed by divide_kernel */
-#endif NON_REENTRANT_FPU
-
- pushl %esi
- pushl %edi
- pushl %ebx
-
- movl PARAM1,%esi
- movl PARAM2,%ebx
- movl PARAM3,%edi
-
- movb TAG(%esi),%al
- orb TAG(%ebx),%al
-
- jne L_div_special /* Not (both numbers TW_Valid) */
-
-#ifdef DENORM_OPERAND
-/* Check for denormals */
- cmpl EXP_UNDER,EXP(%esi)
- jg xL_arg1_not_denormal
-
- call _denormal_operand
- orl %eax,%eax
- jnz fpu_Arith_exit
-
-xL_arg1_not_denormal:
- cmpl EXP_UNDER,EXP(%ebx)
- jg xL_arg2_not_denormal
-
- call _denormal_operand
- orl %eax,%eax
- jnz fpu_Arith_exit
-
-xL_arg2_not_denormal:
-#endif DENORM_OPERAND
-
-/* Both arguments are TW_Valid */
- movb TW_Valid,TAG(%edi)
-
- movb SIGN(%esi),%cl
- cmpb %cl,SIGN(%ebx)
- setne (%edi) /* Set the sign, requires SIGN_NEG=1, SIGN_POS=0 */
-
- movl EXP(%esi),%edx
- movl EXP(%ebx),%eax
- subl %eax,%edx
- addl EXP_BIAS,%edx
- movl %edx,EXP(%edi)
-
- jmp _divide_kernel
-
-
-/*-----------------------------------------------------------------------*/
-L_div_special:
- cmpb TW_NaN,TAG(%esi) /* A NaN with anything to give NaN */
- je L_arg1_NaN
-
- cmpb TW_NaN,TAG(%ebx) /* A NaN with anything to give NaN */
- jne L_no_NaN_arg
-
-/* Operations on NaNs */
-L_arg1_NaN:
-L_arg2_NaN:
- pushl %edi /* Destination */
- pushl %esi
- pushl %ebx /* Ordering is important here */
- call _real_2op_NaN
- jmp LDiv_exit
-
-/* Invalid operations */
-L_zero_zero:
-L_inf_inf:
- pushl %edi /* Destination */
- call _arith_invalid /* 0/0 or Infinity/Infinity */
- jmp LDiv_exit
-
-L_no_NaN_arg:
- cmpb TW_Infinity,TAG(%esi)
- jne L_arg1_not_inf
-
- cmpb TW_Infinity,TAG(%ebx)
- je L_inf_inf /* invalid operation */
-
- cmpb TW_Valid,TAG(%ebx)
- je L_inf_valid
-
-#ifdef PARANOID
- /* arg2 must be zero or valid */
- cmpb TW_Zero,TAG(%ebx)
- ja L_unknown_tags
-#endif PARANOID
-
- /* Note that p16-9 says that infinity/0 returns infinity */
- jmp L_copy_arg1 /* Answer is Inf */
-
-L_inf_valid:
-#ifdef DENORM_OPERAND
- cmpl EXP_UNDER,EXP(%ebx)
- jg L_copy_arg1 /* Answer is Inf */
-
- call _denormal_operand
- orl %eax,%eax
- jnz fpu_Arith_exit
-#endif DENORM_OPERAND
-
- jmp L_copy_arg1 /* Answer is Inf */
-
-L_arg1_not_inf:
- cmpb TW_Zero,TAG(%ebx) /* Priority to div-by-zero error */
- jne L_arg2_not_zero
-
- cmpb TW_Zero,TAG(%esi)
- je L_zero_zero /* invalid operation */
-
-#ifdef PARANOID
- /* arg1 must be valid */
- cmpb TW_Valid,TAG(%esi)
- ja L_unknown_tags
-#endif PARANOID
-
-/* Division by zero error */
- pushl %edi /* destination */
- movb SIGN(%esi),%al
- xorb SIGN(%ebx),%al
- pushl %eax /* lower 8 bits have the sign */
- call _divide_by_zero
- jmp LDiv_exit
-
-L_arg2_not_zero:
- cmpb TW_Infinity,TAG(%ebx)
- jne L_arg2_not_inf
-
-#ifdef DENORM_OPERAND
- cmpb TW_Valid,TAG(%esi)
- jne L_return_zero
-
- cmpl EXP_UNDER,EXP(%esi)
- jg L_return_zero /* Answer is zero */
-
- call _denormal_operand
- orl %eax,%eax
- jnz fpu_Arith_exit
-#endif DENORM_OPERAND
-
- jmp L_return_zero /* Answer is zero */
-
-L_arg2_not_inf:
-
-#ifdef PARANOID
- cmpb TW_Zero,TAG(%esi)
- jne L_unknown_tags
-#endif PARANOID
-
- /* arg1 is zero, arg2 is not Infinity or a NaN */
-
-#ifdef DENORM_OPERAND
- cmpl EXP_UNDER,EXP(%ebx)
- jg L_copy_arg1 /* Answer is zero */
-
- call _denormal_operand
- orl %eax,%eax
- jnz fpu_Arith_exit
-#endif DENORM_OPERAND
-
-L_copy_arg1:
- movb TAG(%esi),%ax
- movb %ax,TAG(%edi)
- movl EXP(%esi),%eax
- movl %eax,EXP(%edi)
- movl SIGL(%esi),%eax
- movl %eax,SIGL(%edi)
- movl SIGH(%esi),%eax
- movl %eax,SIGH(%edi)
-
-LDiv_set_result_sign:
- movb SIGN(%esi),%cl
- cmpb %cl,SIGN(%ebx)
- jne LDiv_negative_result
-
- movb SIGN_POS,SIGN(%edi)
- xorl %eax,%eax /* Valid result */
- jmp LDiv_exit
-
-LDiv_negative_result:
- movb SIGN_NEG,SIGN(%edi)
- xorl %eax,%eax /* Valid result */
-
-LDiv_exit:
-#ifndef NON_REENTRANT_FPU
- leal -40(%ebp),%esp
-#else
- leal -12(%ebp),%esp
-#endif NON_REENTRANT_FPU
-
- popl %ebx
- popl %edi
- popl %esi
- leave
- ret
-
-
-L_return_zero:
- xorl %eax,%eax
- movl %eax,SIGH(%edi)
- movl %eax,SIGL(%edi)
- movl EXP_UNDER,EXP(%edi)
- movb TW_Zero,TAG(%edi)
- jmp LDiv_set_result_sign
-
-#ifdef PARANOID
-L_unknown_tags:
- pushl EX_INTERNAL | 0x208
- call EXCEPTION
-
- /* Generate a NaN for unknown tags */
- movl _CONST_QNaN,%eax
- movl %eax,(%edi)
- movl _CONST_QNaN+4,%eax
- movl %eax,SIGL(%edi)
- movl _CONST_QNaN+8,%eax
- movl %eax,SIGH(%edi)
- jmp LDiv_exit /* %eax is nz */
-#endif PARANOID
diff --git a/drivers/FPU-emu/reg_ld_str.c b/drivers/FPU-emu/reg_ld_str.c
deleted file mode 100644
index efec9e010..000000000
--- a/drivers/FPU-emu/reg_ld_str.c
+++ /dev/null
@@ -1,1438 +0,0 @@
-/*---------------------------------------------------------------------------+
- | reg_ld_str.c |
- | |
- | All of the functions which transfer data between user memory and FPU_REGs.|
- | |
- | Copyright (C) 1992,1993,1994 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
-
-/*---------------------------------------------------------------------------+
- | Note: |
- | The file contains code which accesses user memory. |
- | Emulator static data may change when user memory is accessed, due to |
- | other processes using the emulator while swapping is in progress. |
- +---------------------------------------------------------------------------*/
-
-#include <asm/segment.h>
-
-#include "fpu_system.h"
-#include "exception.h"
-#include "reg_constant.h"
-#include "fpu_emu.h"
-#include "control_w.h"
-#include "status_w.h"
-
-
-#define EXTENDED_Ebias 0x3fff
-#define EXTENDED_Emin (-0x3ffe) /* smallest valid exponent */
-
-#define DOUBLE_Emax 1023 /* largest valid exponent */
-#define DOUBLE_Ebias 1023
-#define DOUBLE_Emin (-1022) /* smallest valid exponent */
-
-#define SINGLE_Emax 127 /* largest valid exponent */
-#define SINGLE_Ebias 127
-#define SINGLE_Emin (-126) /* smallest valid exponent */
-
-static void write_to_extended(FPU_REG *rp, char *d);
-
-
-/* Get a long double from user memory */
-int reg_load_extended(long double *s, FPU_REG *loaded_data)
-{
- unsigned long sigl, sigh, exp;
-
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_READ, s, 10);
- sigl = get_fs_long((unsigned long *) s);
- sigh = get_fs_long(1 + (unsigned long *) s);
- exp = get_fs_word(4 + (unsigned short *) s);
- RE_ENTRANT_CHECK_ON;
-
- loaded_data->tag = TW_Valid; /* Default */
- loaded_data->sigl = sigl;
- loaded_data->sigh = sigh;
- if (exp & 0x8000)
- loaded_data->sign = SIGN_NEG;
- else
- loaded_data->sign = SIGN_POS;
- exp &= 0x7fff;
- loaded_data->exp = exp - EXTENDED_Ebias + EXP_BIAS;
-
- if ( exp == 0 )
- {
- if ( !(sigh | sigl) )
- {
- loaded_data->tag = TW_Zero;
- return 0;
- }
- /* The number is a de-normal or pseudodenormal. */
- if (sigh & 0x80000000)
- {
- /* Is a pseudodenormal. */
- /* Convert it for internal use. */
- /* This is non-80486 behaviour because the number
- loses its 'denormal' identity. */
- loaded_data->exp++;
- return 1;
- }
- else
- {
- /* Is a denormal. */
- /* Convert it for internal use. */
- loaded_data->exp++;
- normalize_nuo(loaded_data);
- return 0;
- }
- }
- else if ( exp == 0x7fff )
- {
- if ( !((sigh ^ 0x80000000) | sigl) )
- {
- /* Matches the bit pattern for Infinity. */
- loaded_data->exp = EXP_Infinity;
- loaded_data->tag = TW_Infinity;
- return 0;
- }
-
- loaded_data->exp = EXP_NaN;
- loaded_data->tag = TW_NaN;
- if ( !(sigh & 0x80000000) )
- {
- /* NaNs have the ms bit set to 1. */
- /* This is therefore an Unsupported NaN data type. */
- /* This is non 80486 behaviour */
- /* This should generate an Invalid Operand exception
- later, so we convert it to a SNaN */
- loaded_data->sigh = 0x80000000;
- loaded_data->sigl = 0x00000001;
- loaded_data->sign = SIGN_NEG;
- return 1;
- }
- return 0;
- }
-
- if ( !(sigh & 0x80000000) )
- {
- /* Unsupported data type. */
- /* Valid numbers have the ms bit set to 1. */
- /* Unnormal. */
- /* Convert it for internal use. */
- /* This is non-80486 behaviour */
- /* This should generate an Invalid Operand exception
- later, so we convert it to a SNaN */
- loaded_data->sigh = 0x80000000;
- loaded_data->sigl = 0x00000001;
- loaded_data->sign = SIGN_NEG;
- loaded_data->exp = EXP_NaN;
- loaded_data->tag = TW_NaN;
- return 1;
- }
- return 0;
-}
-
-
-/* Get a double from user memory */
-int reg_load_double(double *dfloat, FPU_REG *loaded_data)
-{
- int exp;
- unsigned m64, l64;
-
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_READ, dfloat, 8);
- m64 = get_fs_long(1 + (unsigned long *) dfloat);
- l64 = get_fs_long((unsigned long *) dfloat);
- RE_ENTRANT_CHECK_ON;
-
- if (m64 & 0x80000000)
- loaded_data->sign = SIGN_NEG;
- else
- loaded_data->sign = SIGN_POS;
- exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias;
- m64 &= 0xfffff;
- if (exp > DOUBLE_Emax)
- {
- /* Infinity or NaN */
- if ((m64 == 0) && (l64 == 0))
- {
- /* +- infinity */
- loaded_data->sigh = 0x80000000;
- loaded_data->sigl = 0x00000000;
- loaded_data->exp = EXP_Infinity;
- loaded_data->tag = TW_Infinity;
- return 0;
- }
- else
- {
- /* Must be a signaling or quiet NaN */
- loaded_data->exp = EXP_NaN;
- loaded_data->tag = TW_NaN;
- loaded_data->sigh = (m64 << 11) | 0x80000000;
- loaded_data->sigh |= l64 >> 21;
- loaded_data->sigl = l64 << 11;
- return 0; /* The calling function must look for NaNs */
- }
- }
- else if ( exp < DOUBLE_Emin )
- {
- /* Zero or de-normal */
- if ((m64 == 0) && (l64 == 0))
- {
- /* Zero */
- int c = loaded_data->sign;
- reg_move(&CONST_Z, loaded_data);
- loaded_data->sign = c;
- return 0;
- }
- else
- {
- /* De-normal */
- loaded_data->exp = DOUBLE_Emin + EXP_BIAS;
- loaded_data->tag = TW_Valid;
- loaded_data->sigh = m64 << 11;
- loaded_data->sigh |= l64 >> 21;
- loaded_data->sigl = l64 << 11;
- normalize_nuo(loaded_data);
- return denormal_operand();
- }
- }
- else
- {
- loaded_data->exp = exp + EXP_BIAS;
- loaded_data->tag = TW_Valid;
- loaded_data->sigh = (m64 << 11) | 0x80000000;
- loaded_data->sigh |= l64 >> 21;
- loaded_data->sigl = l64 << 11;
-
- return 0;
- }
-}
-
-
-/* Get a float from user memory */
-int reg_load_single(float *single, FPU_REG *loaded_data)
-{
- unsigned m32;
- int exp;
-
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_READ, single, 4);
- m32 = get_fs_long((unsigned long *) single);
- RE_ENTRANT_CHECK_ON;
-
- if (m32 & 0x80000000)
- loaded_data->sign = SIGN_NEG;
- else
- loaded_data->sign = SIGN_POS;
- if (!(m32 & 0x7fffffff))
- {
- /* Zero */
- int c = loaded_data->sign;
- reg_move(&CONST_Z, loaded_data);
- loaded_data->sign = c;
- return 0;
- }
- exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias;
- m32 = (m32 & 0x7fffff) << 8;
- if ( exp < SINGLE_Emin )
- {
- /* De-normals */
- loaded_data->exp = SINGLE_Emin + EXP_BIAS;
- loaded_data->tag = TW_Valid;
- loaded_data->sigh = m32;
- loaded_data->sigl = 0;
- normalize_nuo(loaded_data);
- return denormal_operand();
- }
- else if ( exp > SINGLE_Emax )
- {
- /* Infinity or NaN */
- if ( m32 == 0 )
- {
- /* +- infinity */
- loaded_data->sigh = 0x80000000;
- loaded_data->sigl = 0x00000000;
- loaded_data->exp = EXP_Infinity;
- loaded_data->tag = TW_Infinity;
- return 0;
- }
- else
- {
- /* Must be a signaling or quiet NaN */
- loaded_data->exp = EXP_NaN;
- loaded_data->tag = TW_NaN;
- loaded_data->sigh = m32 | 0x80000000;
- loaded_data->sigl = 0;
- return 0; /* The calling function must look for NaNs */
- }
- }
- else
- {
- loaded_data->exp = exp + EXP_BIAS;
- loaded_data->sigh = m32 | 0x80000000;
- loaded_data->sigl = 0;
- loaded_data->tag = TW_Valid;
- return 0;
- }
-}
-
-
-/* Get a long long from user memory */
-void reg_load_int64(long long *_s, FPU_REG *loaded_data)
-{
- int e;
- long long s;
-
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_READ, _s, 8);
- ((unsigned long *)&s)[0] = get_fs_long((unsigned long *) _s);
- ((unsigned long *)&s)[1] = get_fs_long(1 + (unsigned long *) _s);
- RE_ENTRANT_CHECK_ON;
-
- if (s == 0)
- { reg_move(&CONST_Z, loaded_data); return; }
-
- if (s > 0)
- loaded_data->sign = SIGN_POS;
- else
- {
- s = -s;
- loaded_data->sign = SIGN_NEG;
- }
-
- e = EXP_BIAS + 63;
- significand(loaded_data) = s;
- loaded_data->exp = e;
- loaded_data->tag = TW_Valid;
- normalize_nuo(loaded_data);
-}
-
-
-/* Get a long from user memory */
-void reg_load_int32(long *_s, FPU_REG *loaded_data)
-{
- long s;
- int e;
-
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_READ, _s, 4);
- s = (long)get_fs_long((unsigned long *) _s);
- RE_ENTRANT_CHECK_ON;
-
- if (s == 0)
- { reg_move(&CONST_Z, loaded_data); return; }
-
- if (s > 0)
- loaded_data->sign = SIGN_POS;
- else
- {
- s = -s;
- loaded_data->sign = SIGN_NEG;
- }
-
- e = EXP_BIAS + 31;
- loaded_data->sigh = s;
- loaded_data->sigl = 0;
- loaded_data->exp = e;
- loaded_data->tag = TW_Valid;
- normalize_nuo(loaded_data);
-}
-
-
-/* Get a short from user memory */
-void reg_load_int16(short *_s, FPU_REG *loaded_data)
-{
- int s, e;
-
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_READ, _s, 2);
- /* Cast as short to get the sign extended. */
- s = (short)get_fs_word((unsigned short *) _s);
- RE_ENTRANT_CHECK_ON;
-
- if (s == 0)
- { reg_move(&CONST_Z, loaded_data); return; }
-
- if (s > 0)
- loaded_data->sign = SIGN_POS;
- else
- {
- s = -s;
- loaded_data->sign = SIGN_NEG;
- }
-
- e = EXP_BIAS + 15;
- loaded_data->sigh = s << 16;
-
- loaded_data->sigl = 0;
- loaded_data->exp = e;
- loaded_data->tag = TW_Valid;
- normalize_nuo(loaded_data);
-}
-
-
-/* Get a packed bcd array from user memory */
-void reg_load_bcd(char *s, FPU_REG *loaded_data)
-{
- int pos;
- unsigned char bcd;
- long long l=0;
-
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_READ, s, 10);
- RE_ENTRANT_CHECK_ON;
- for ( pos = 8; pos >= 0; pos--)
- {
- l *= 10;
- RE_ENTRANT_CHECK_OFF;
- bcd = (unsigned char)get_fs_byte((unsigned char *) s+pos);
- RE_ENTRANT_CHECK_ON;
- l += bcd >> 4;
- l *= 10;
- l += bcd & 0x0f;
- }
-
- RE_ENTRANT_CHECK_OFF;
- loaded_data->sign =
- ((unsigned char)get_fs_byte((unsigned char *) s+9)) & 0x80 ?
- SIGN_NEG : SIGN_POS;
- RE_ENTRANT_CHECK_ON;
-
- if (l == 0)
- {
- char sign = loaded_data->sign;
- reg_move(&CONST_Z, loaded_data);
- loaded_data->sign = sign;
- }
- else
- {
- significand(loaded_data) = l;
- loaded_data->exp = EXP_BIAS + 63;
- loaded_data->tag = TW_Valid;
- normalize_nuo(loaded_data);
- }
-}
-
-/*===========================================================================*/
-
-/* Put a long double into user memory */
-int reg_store_extended(long double *d, FPU_REG *st0_ptr)
-{
- /*
- The only exception raised by an attempt to store to an
- extended format is the Invalid Stack exception, i.e.
- attempting to store from an empty register.
- */
-
- if ( st0_ptr->tag != TW_Empty )
- {
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_WRITE, d, 10);
- RE_ENTRANT_CHECK_ON;
- write_to_extended(st0_ptr, (char *) d);
- return 1;
- }
-
- /* Empty register (stack underflow) */
- EXCEPTION(EX_StackUnder);
- if ( control_word & CW_Invalid )
- {
- /* The masked response */
- /* Put out the QNaN indefinite */
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_WRITE,d,10);
- put_fs_long(0, (unsigned long *) d);
- put_fs_long(0xc0000000, 1 + (unsigned long *) d);
- put_fs_word(0xffff, 4 + (short *) d);
- RE_ENTRANT_CHECK_ON;
- return 1;
- }
- else
- return 0;
-
-}
-
-
-/* Put a double into user memory */
-int reg_store_double(double *dfloat, FPU_REG *st0_ptr)
-{
- unsigned long l[2];
- unsigned long increment = 0; /* avoid gcc warnings */
- char st0_tag = st0_ptr->tag;
-
- if (st0_tag == TW_Valid)
- {
- int exp;
- FPU_REG tmp;
-
- reg_move(st0_ptr, &tmp);
- exp = tmp.exp - EXP_BIAS;
-
- if ( exp < DOUBLE_Emin ) /* It may be a denormal */
- {
- int precision_loss;
-
- /* A denormal will always underflow. */
-#ifndef PECULIAR_486
- /* An 80486 is supposed to be able to generate
- a denormal exception here, but... */
- if ( st0_ptr->exp <= EXP_UNDER )
- {
- /* Underflow has priority. */
- if ( control_word & CW_Underflow )
- denormal_operand();
- }
-#endif PECULIAR_486
-
- tmp.exp += -DOUBLE_Emin + 52; /* largest exp to be 51 */
-
- if ( (precision_loss = round_to_int(&tmp)) )
- {
-#ifdef PECULIAR_486
- /* Did it round to a non-denormal ? */
- /* This behaviour might be regarded as peculiar, it appears
- that the 80486 rounds to the dest precision, then
- converts to decide underflow. */
- if ( !((tmp.sigh == 0x00100000) && (tmp.sigl == 0) &&
- (st0_ptr->sigl & 0x000007ff)) )
-#endif PECULIAR_486
- {
- EXCEPTION(EX_Underflow);
- /* This is a special case: see sec 16.2.5.1 of
- the 80486 book */
- if ( !(control_word & CW_Underflow) )
- return 0;
- }
- EXCEPTION(precision_loss);
- if ( !(control_word & CW_Precision) )
- return 0;
- }
- l[0] = tmp.sigl;
- l[1] = tmp.sigh;
- }
- else
- {
- if ( tmp.sigl & 0x000007ff )
- {
- switch (control_word & CW_RC)
- {
- case RC_RND:
- /* Rounding can get a little messy.. */
- increment = ((tmp.sigl & 0x7ff) > 0x400) | /* nearest */
- ((tmp.sigl & 0xc00) == 0xc00); /* odd -> even */
- break;
- case RC_DOWN: /* towards -infinity */
- increment = (tmp.sign == SIGN_POS) ? 0 : tmp.sigl & 0x7ff;
- break;
- case RC_UP: /* towards +infinity */
- increment = (tmp.sign == SIGN_POS) ? tmp.sigl & 0x7ff : 0;
- break;
- case RC_CHOP:
- increment = 0;
- break;
- }
-
- /* Truncate the mantissa */
- tmp.sigl &= 0xfffff800;
-
- if ( increment )
- {
- set_precision_flag_up();
-
- if ( tmp.sigl >= 0xfffff800 )
- {
- /* the sigl part overflows */
- if ( tmp.sigh == 0xffffffff )
- {
- /* The sigh part overflows */
- tmp.sigh = 0x80000000;
- exp++;
- if (exp >= EXP_OVER)
- goto overflow;
- }
- else
- {
- tmp.sigh ++;
- }
- tmp.sigl = 0x00000000;
- }
- else
- {
- /* We only need to increment sigl */
- tmp.sigl += 0x00000800;
- }
- }
- else
- set_precision_flag_down();
- }
-
- l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21);
- l[1] = ((tmp.sigh >> 11) & 0xfffff);
-
- if ( exp > DOUBLE_Emax )
- {
- overflow:
- EXCEPTION(EX_Overflow);
- if ( !(control_word & CW_Overflow) )
- return 0;
- set_precision_flag_up();
- if ( !(control_word & CW_Precision) )
- return 0;
-
- /* This is a special case: see sec 16.2.5.1 of the 80486 book */
- /* Overflow to infinity */
- l[0] = 0x00000000; /* Set to */
- l[1] = 0x7ff00000; /* + INF */
- }
- else
- {
- /* Add the exponent */
- l[1] |= (((exp+DOUBLE_Ebias) & 0x7ff) << 20);
- }
- }
- }
- else if (st0_tag == TW_Zero)
- {
- /* Number is zero */
- l[0] = 0;
- l[1] = 0;
- }
- else if (st0_tag == TW_Infinity)
- {
- l[0] = 0;
- l[1] = 0x7ff00000;
- }
- else if (st0_tag == TW_NaN)
- {
- /* See if we can get a valid NaN from the FPU_REG */
- l[0] = (st0_ptr->sigl >> 11) | (st0_ptr->sigh << 21);
- l[1] = ((st0_ptr->sigh >> 11) & 0xfffff);
- if ( !(st0_ptr->sigh & 0x40000000) )
- {
- /* It is a signalling NaN */
- EXCEPTION(EX_Invalid);
- if ( !(control_word & CW_Invalid) )
- return 0;
- l[1] |= (0x40000000 >> 11);
- }
- l[1] |= 0x7ff00000;
- }
- else if ( st0_tag == TW_Empty )
- {
- /* Empty register (stack underflow) */
- EXCEPTION(EX_StackUnder);
- if ( control_word & CW_Invalid )
- {
- /* The masked response */
- /* Put out the QNaN indefinite */
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8);
- put_fs_long(0, (unsigned long *) dfloat);
- put_fs_long(0xfff80000, 1 + (unsigned long *) dfloat);
- RE_ENTRANT_CHECK_ON;
- return 1;
- }
- else
- return 0;
- }
- if ( st0_ptr->sign )
- l[1] |= 0x80000000;
-
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8);
- put_fs_long(l[0], (unsigned long *)dfloat);
- put_fs_long(l[1], 1 + (unsigned long *)dfloat);
- RE_ENTRANT_CHECK_ON;
-
- return 1;
-}
-
-
-/* Put a float into user memory */
-int reg_store_single(float *single, FPU_REG *st0_ptr)
-{
- long templ;
- unsigned long increment = 0; /* avoid gcc warnings */
- char st0_tag = st0_ptr->tag;
-
- if (st0_tag == TW_Valid)
- {
- int exp;
- FPU_REG tmp;
-
- reg_move(st0_ptr, &tmp);
- exp = tmp.exp - EXP_BIAS;
-
- if ( exp < SINGLE_Emin )
- {
- int precision_loss;
-
- /* A denormal will always underflow. */
-#ifndef PECULIAR_486
- /* An 80486 is supposed to be able to generate
- a denormal exception here, but... */
- if ( st0_ptr->exp <= EXP_UNDER )
- {
- /* Underflow has priority. */
- if ( control_word & CW_Underflow )
- denormal_operand();
- }
-#endif PECULIAR_486
-
- tmp.exp += -SINGLE_Emin + 23; /* largest exp to be 22 */
-
- if ( (precision_loss = round_to_int(&tmp)) )
- {
-#ifdef PECULIAR_486
- /* Did it round to a non-denormal ? */
- /* This behaviour might be regarded as peculiar, it appears
- that the 80486 rounds to the dest precision, then
- converts to decide underflow. */
- if ( !((tmp.sigl == 0x00800000) &&
- ((st0_ptr->sigh & 0x000000ff) || st0_ptr->sigl)) )
-#endif PECULIAR_486
- {
- EXCEPTION(EX_Underflow);
- /* This is a special case: see sec 16.2.5.1 of
- the 80486 book */
- if ( !(control_word & EX_Underflow) )
- return 0;
- }
- EXCEPTION(precision_loss);
- if ( !(control_word & EX_Precision) )
- return 0;
- }
- templ = tmp.sigl;
- }
- else
- {
- if ( tmp.sigl | (tmp.sigh & 0x000000ff) )
- {
- unsigned long sigh = tmp.sigh;
- unsigned long sigl = tmp.sigl;
-
- switch (control_word & CW_RC)
- {
- case RC_RND:
- increment = ((sigh & 0xff) > 0x80) /* more than half */
- || (((sigh & 0xff) == 0x80) && sigl) /* more than half */
- || ((sigh & 0x180) == 0x180); /* round to even */
- break;
- case RC_DOWN: /* towards -infinity */
- increment = (tmp.sign == SIGN_POS)
- ? 0 : (sigl | (sigh & 0xff));
- break;
- case RC_UP: /* towards +infinity */
- increment = (tmp.sign == SIGN_POS)
- ? (sigl | (sigh & 0xff)) : 0;
- break;
- case RC_CHOP:
- increment = 0;
- break;
- }
-
- /* Truncate part of the mantissa */
- tmp.sigl = 0;
-
- if (increment)
- {
- set_precision_flag_up();
-
- if ( sigh >= 0xffffff00 )
- {
- /* The sigh part overflows */
- tmp.sigh = 0x80000000;
- exp++;
- if ( exp >= EXP_OVER )
- goto overflow;
- }
- else
- {
- tmp.sigh &= 0xffffff00;
- tmp.sigh += 0x100;
- }
- }
- else
- {
- set_precision_flag_down();
- tmp.sigh &= 0xffffff00; /* Finish the truncation */
- }
- }
-
- templ = (tmp.sigh >> 8) & 0x007fffff;
-
- if ( exp > SINGLE_Emax )
- {
- overflow:
- EXCEPTION(EX_Overflow);
- if ( !(control_word & CW_Overflow) )
- return 0;
- set_precision_flag_up();
- if ( !(control_word & CW_Precision) )
- return 0;
-
- /* This is a special case: see sec 16.2.5.1 of the 80486 book. */
- /* Masked response is overflow to infinity. */
- templ = 0x7f800000;
- }
- else
- templ |= ((exp+SINGLE_Ebias) & 0xff) << 23;
- }
- }
- else if (st0_tag == TW_Zero)
- {
- templ = 0;
- }
- else if (st0_tag == TW_Infinity)
- {
- templ = 0x7f800000;
- }
- else if (st0_tag == TW_NaN)
- {
- /* See if we can get a valid NaN from the FPU_REG */
- templ = st0_ptr->sigh >> 8;
- if ( !(st0_ptr->sigh & 0x40000000) )
- {
- /* It is a signalling NaN */
- EXCEPTION(EX_Invalid);
- if ( !(control_word & CW_Invalid) )
- return 0;
- templ |= (0x40000000 >> 8);
- }
- templ |= 0x7f800000;
- }
- else if ( st0_tag == TW_Empty )
- {
- /* Empty register (stack underflow) */
- EXCEPTION(EX_StackUnder);
- if ( control_word & EX_Invalid )
- {
- /* The masked response */
- /* Put out the QNaN indefinite */
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_WRITE,(void *)single,4);
- put_fs_long(0xffc00000, (unsigned long *) single);
- RE_ENTRANT_CHECK_ON;
- return 1;
- }
- else
- return 0;
- }
-#ifdef PARANOID
- else
- {
- EXCEPTION(EX_INTERNAL|0x163);
- return 0;
- }
-#endif
- if (st0_ptr->sign)
- templ |= 0x80000000;
-
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_WRITE,(void *)single,4);
- put_fs_long(templ,(unsigned long *) single);
- RE_ENTRANT_CHECK_ON;
-
- return 1;
-}
-
-
-/* Put a long long into user memory */
-int reg_store_int64(long long *d, FPU_REG *st0_ptr)
-{
- FPU_REG t;
- long long tll;
- int precision_loss;
- char st0_tag = st0_ptr->tag;
-
- if ( st0_tag == TW_Empty )
- {
- /* Empty register (stack underflow) */
- EXCEPTION(EX_StackUnder);
- goto invalid_operand;
- }
- else if ( (st0_tag == TW_Infinity) ||
- (st0_tag == TW_NaN) )
- {
- EXCEPTION(EX_Invalid);
- goto invalid_operand;
- }
-
- reg_move(st0_ptr, &t);
- precision_loss = round_to_int(&t);
- ((long *)&tll)[0] = t.sigl;
- ((long *)&tll)[1] = t.sigh;
- if ( (precision_loss == 1) ||
- ((t.sigh & 0x80000000) &&
- !((t.sigh == 0x80000000) && (t.sigl == 0) &&
- (t.sign == SIGN_NEG))) )
- {
- EXCEPTION(EX_Invalid);
- /* This is a special case: see sec 16.2.5.1 of the 80486 book */
- invalid_operand:
- if ( control_word & EX_Invalid )
- {
- /* Produce something like QNaN "indefinite" */
- tll = 0x8000000000000000LL;
- }
- else
- return 0;
- }
- else
- {
- if ( precision_loss )
- set_precision_flag(precision_loss);
- if ( t.sign )
- tll = - tll;
- }
-
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_WRITE,(void *)d,8);
- put_fs_long(((long *)&tll)[0],(unsigned long *) d);
- put_fs_long(((long *)&tll)[1],1 + (unsigned long *) d);
- RE_ENTRANT_CHECK_ON;
-
- return 1;
-}
-
-
-/* Put a long into user memory */
-int reg_store_int32(long *d, FPU_REG *st0_ptr)
-{
- FPU_REG t;
- int precision_loss;
- char st0_tag = st0_ptr->tag;
-
- if ( st0_tag == TW_Empty )
- {
- /* Empty register (stack underflow) */
- EXCEPTION(EX_StackUnder);
- goto invalid_operand;
- }
- else if ( (st0_tag == TW_Infinity) ||
- (st0_tag == TW_NaN) )
- {
- EXCEPTION(EX_Invalid);
- goto invalid_operand;
- }
-
- reg_move(st0_ptr, &t);
- precision_loss = round_to_int(&t);
- if (t.sigh ||
- ((t.sigl & 0x80000000) &&
- !((t.sigl == 0x80000000) && (t.sign == SIGN_NEG))) )
- {
- EXCEPTION(EX_Invalid);
- /* This is a special case: see sec 16.2.5.1 of the 80486 book */
- invalid_operand:
- if ( control_word & EX_Invalid )
- {
- /* Produce something like QNaN "indefinite" */
- t.sigl = 0x80000000;
- }
- else
- return 0;
- }
- else
- {
- if ( precision_loss )
- set_precision_flag(precision_loss);
- if ( t.sign )
- t.sigl = -(long)t.sigl;
- }
-
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_WRITE,d,4);
- put_fs_long(t.sigl, (unsigned long *) d);
- RE_ENTRANT_CHECK_ON;
-
- return 1;
-}
-
-
-/* Put a short into user memory */
-int reg_store_int16(short *d, FPU_REG *st0_ptr)
-{
- FPU_REG t;
- int precision_loss;
- char st0_tag = st0_ptr->tag;
-
- if ( st0_tag == TW_Empty )
- {
- /* Empty register (stack underflow) */
- EXCEPTION(EX_StackUnder);
- goto invalid_operand;
- }
- else if ( (st0_tag == TW_Infinity) ||
- (st0_tag == TW_NaN) )
- {
- EXCEPTION(EX_Invalid);
- goto invalid_operand;
- }
-
- reg_move(st0_ptr, &t);
- precision_loss = round_to_int(&t);
- if (t.sigh ||
- ((t.sigl & 0xffff8000) &&
- !((t.sigl == 0x8000) && (t.sign == SIGN_NEG))) )
- {
- EXCEPTION(EX_Invalid);
- /* This is a special case: see sec 16.2.5.1 of the 80486 book */
- invalid_operand:
- if ( control_word & EX_Invalid )
- {
- /* Produce something like QNaN "indefinite" */
- t.sigl = 0x8000;
- }
- else
- return 0;
- }
- else
- {
- if ( precision_loss )
- set_precision_flag(precision_loss);
- if ( t.sign )
- t.sigl = -t.sigl;
- }
-
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_WRITE,d,2);
- put_fs_word((short)t.sigl,(short *) d);
- RE_ENTRANT_CHECK_ON;
-
- return 1;
-}
-
-
-/* Put a packed bcd array into user memory */
-int reg_store_bcd(char *d, FPU_REG *st0_ptr)
-{
- FPU_REG t;
- unsigned long long ll;
- unsigned char b;
- int i, precision_loss;
- unsigned char sign = (st0_ptr->sign == SIGN_NEG) ? 0x80 : 0;
- char st0_tag = st0_ptr->tag;
-
- if ( st0_tag == TW_Empty )
- {
- /* Empty register (stack underflow) */
- EXCEPTION(EX_StackUnder);
- goto invalid_operand;
- }
-
- reg_move(st0_ptr, &t);
- precision_loss = round_to_int(&t);
- ll = significand(&t);
-
- /* Check for overflow, by comparing with 999999999999999999 decimal. */
- if ( (t.sigh > 0x0de0b6b3) ||
- ((t.sigh == 0x0de0b6b3) && (t.sigl > 0xa763ffff)) )
- {
- EXCEPTION(EX_Invalid);
- /* This is a special case: see sec 16.2.5.1 of the 80486 book */
- invalid_operand:
- if ( control_word & CW_Invalid )
- {
- /* Produce the QNaN "indefinite" */
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_WRITE,d,10);
- for ( i = 0; i < 7; i++)
- put_fs_byte(0, (unsigned char *) d+i); /* These bytes "undefined" */
- put_fs_byte(0xc0, (unsigned char *) d+7); /* This byte "undefined" */
- put_fs_byte(0xff, (unsigned char *) d+8);
- put_fs_byte(0xff, (unsigned char *) d+9);
- RE_ENTRANT_CHECK_ON;
- return 1;
- }
- else
- return 0;
- }
- else if ( precision_loss )
- {
- /* Precision loss doesn't stop the data transfer */
- set_precision_flag(precision_loss);
- }
-
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_WRITE,d,10);
- RE_ENTRANT_CHECK_ON;
- for ( i = 0; i < 9; i++)
- {
- b = div_small(&ll, 10);
- b |= (div_small(&ll, 10)) << 4;
- RE_ENTRANT_CHECK_OFF;
- put_fs_byte(b,(unsigned char *) d+i);
- RE_ENTRANT_CHECK_ON;
- }
- RE_ENTRANT_CHECK_OFF;
- put_fs_byte(sign,(unsigned char *) d+9);
- RE_ENTRANT_CHECK_ON;
-
- return 1;
-}
-
-/*===========================================================================*/
-
-/* r gets mangled such that sig is int, sign:
- it is NOT normalized */
-/* The return value (in eax) is zero if the result is exact,
- if bits are changed due to rounding, truncation, etc, then
- a non-zero value is returned */
-/* Overflow is signalled by a non-zero return value (in eax).
- In the case of overflow, the returned significand always has the
- largest possible value */
-int round_to_int(FPU_REG *r)
-{
- char very_big;
- unsigned eax;
-
- if (r->tag == TW_Zero)
- {
- /* Make sure that zero is returned */
- significand(r) = 0;
- return 0; /* o.k. */
- }
-
- if (r->exp > EXP_BIAS + 63)
- {
- r->sigl = r->sigh = ~0; /* The largest representable number */
- return 1; /* overflow */
- }
-
- eax = shrxs(&r->sigl, EXP_BIAS + 63 - r->exp);
- very_big = !(~(r->sigh) | ~(r->sigl)); /* test for 0xfff...fff */
-#define half_or_more (eax & 0x80000000)
-#define frac_part (eax)
-#define more_than_half ((eax & 0x80000001) == 0x80000001)
- switch (control_word & CW_RC)
- {
- case RC_RND:
- if ( more_than_half /* nearest */
- || (half_or_more && (r->sigl & 1)) ) /* odd -> even */
- {
- if ( very_big ) return 1; /* overflow */
- significand(r) ++;
- return PRECISION_LOST_UP;
- }
- break;
- case RC_DOWN:
- if (frac_part && r->sign)
- {
- if ( very_big ) return 1; /* overflow */
- significand(r) ++;
- return PRECISION_LOST_UP;
- }
- break;
- case RC_UP:
- if (frac_part && !r->sign)
- {
- if ( very_big ) return 1; /* overflow */
- significand(r) ++;
- return PRECISION_LOST_UP;
- }
- break;
- case RC_CHOP:
- break;
- }
-
- return eax ? PRECISION_LOST_DOWN : 0;
-
-}
-
-/*===========================================================================*/
-
-char *fldenv(fpu_addr_modes addr_modes, char *s)
-{
- unsigned short tag_word = 0;
- unsigned char tag;
- int i;
-
- if ( (addr_modes.default_mode == VM86) ||
- ((addr_modes.default_mode == PM16)
- ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) )
- {
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_READ, s, 0x0e);
- control_word = get_fs_word((unsigned short *) s);
- partial_status = get_fs_word((unsigned short *) (s+2));
- tag_word = get_fs_word((unsigned short *) (s+4));
- instruction_address.offset = get_fs_word((unsigned short *) (s+6));
- instruction_address.selector = get_fs_word((unsigned short *) (s+8));
- operand_address.offset = get_fs_word((unsigned short *) (s+0x0a));
- operand_address.selector = get_fs_word((unsigned short *) (s+0x0c));
- RE_ENTRANT_CHECK_ON;
- s += 0x0e;
- if ( addr_modes.default_mode == VM86 )
- {
- instruction_address.offset
- += (instruction_address.selector & 0xf000) << 4;
- operand_address.offset += (operand_address.selector & 0xf000) << 4;
- }
- }
- else
- {
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_READ, s, 0x1c);
- control_word = get_fs_word((unsigned short *) s);
- partial_status = get_fs_word((unsigned short *) (s+4));
- tag_word = get_fs_word((unsigned short *) (s+8));
- instruction_address.offset = get_fs_long((unsigned long *) (s+0x0c));
- instruction_address.selector = get_fs_word((unsigned short *) (s+0x10));
- instruction_address.opcode = get_fs_word((unsigned short *) (s+0x12));
- operand_address.offset = get_fs_long((unsigned long *) (s+0x14));
- operand_address.selector = get_fs_long((unsigned long *) (s+0x18));
- RE_ENTRANT_CHECK_ON;
- s += 0x1c;
- }
-
-#ifdef PECULIAR_486
- control_word &= ~0xe080;
-#endif PECULIAR_486
-
- top = (partial_status >> SW_Top_Shift) & 7;
-
- if ( partial_status & ~control_word & CW_Exceptions )
- partial_status |= (SW_Summary | SW_Backward);
- else
- partial_status &= ~(SW_Summary | SW_Backward);
-
- for ( i = 0; i < 8; i++ )
- {
- tag = tag_word & 3;
- tag_word >>= 2;
-
- if ( tag == 3 )
- /* New tag is empty. Accept it */
- regs[i].tag = TW_Empty;
- else if ( regs[i].tag == TW_Empty )
- {
- /* Old tag is empty and new tag is not empty. New tag is determined
- by old reg contents */
- if ( regs[i].exp == EXP_BIAS - EXTENDED_Ebias )
- {
- if ( !(regs[i].sigl | regs[i].sigh) )
- regs[i].tag = TW_Zero;
- else
- regs[i].tag = TW_Valid;
- }
- else if ( regs[i].exp == 0x7fff + EXP_BIAS - EXTENDED_Ebias )
- {
- if ( !((regs[i].sigh & ~0x80000000) | regs[i].sigl) )
- regs[i].tag = TW_Infinity;
- else
- regs[i].tag = TW_NaN;
- }
- else
- regs[i].tag = TW_Valid;
- }
- /* Else old tag is not empty and new tag is not empty. Old tag
- remains correct */
- }
-
- return s;
-}
-
-
-void frstor(fpu_addr_modes addr_modes, char *data_address)
-{
- int i, stnr;
- unsigned char tag;
- char *s = fldenv(addr_modes, data_address);
-
- for ( i = 0; i < 8; i++ )
- {
- /* Load each register. */
- stnr = (i+top) & 7;
- tag = regs[stnr].tag; /* Derived from the fldenv() loaded tag word. */
- reg_load_extended((long double *)(s+i*10), &regs[stnr]);
- if ( tag == TW_Empty ) /* The loaded data over-rides all other cases. */
- regs[stnr].tag = tag;
- }
-
-}
-
-
-unsigned short tag_word(void)
-{
- unsigned short word = 0;
- unsigned char tag;
- int i;
-
- for ( i = 7; i >= 0; i-- )
- {
- switch ( tag = regs[i].tag )
- {
- case TW_Valid:
- if ( regs[i].exp <= (EXP_BIAS - EXTENDED_Ebias) )
- tag = 2;
- break;
- case TW_Infinity:
- case TW_NaN:
- tag = 2;
- break;
- case TW_Empty:
- tag = 3;
- break;
- /* TW_Zero already has the correct value */
- }
- word <<= 2;
- word |= tag;
- }
- return word;
-}
-
-
-char *fstenv(fpu_addr_modes addr_modes, char *d)
-{
- if ( (addr_modes.default_mode == VM86) ||
- ((addr_modes.default_mode == PM16)
- ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) )
- {
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_WRITE,d,14);
-#ifdef PECULIAR_486
- put_fs_long(control_word & ~0xe080, (unsigned short *) d);
-#else
- put_fs_word(control_word, (unsigned short *) d);
-#endif PECULIAR_486
- put_fs_word(status_word(), (unsigned short *) (d+2));
- put_fs_word(tag_word(), (unsigned short *) (d+4));
- put_fs_word(instruction_address.offset, (unsigned short *) (d+6));
- put_fs_word(operand_address.offset, (unsigned short *) (d+0x0a));
- if ( addr_modes.default_mode == VM86 )
- {
- put_fs_word((instruction_address.offset & 0xf0000) >> 4,
- (unsigned short *) (d+8));
- put_fs_word((operand_address.offset & 0xf0000) >> 4,
- (unsigned short *) (d+0x0c));
- }
- else
- {
- put_fs_word(instruction_address.selector, (unsigned short *) (d+8));
- put_fs_word(operand_address.selector, (unsigned short *) (d+0x0c));
- }
- RE_ENTRANT_CHECK_ON;
- d += 0x0e;
- }
- else
- {
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_WRITE,d,28);
-#ifdef PECULIAR_486
- /* An 80486 sets all the reserved bits to 1. */
- put_fs_long(0xffff0040 | (control_word & ~0xe080), (unsigned long *) d);
- put_fs_long(0xffff0000 | status_word(), (unsigned long *) (d+4));
- put_fs_long(0xffff0000 | tag_word(), (unsigned long *) (d+8));
-#else
- put_fs_word(control_word, (unsigned short *) d);
- put_fs_word(status_word(), (unsigned short *) (d+4));
- put_fs_word(tag_word(), (unsigned short *) (d+8));
-#endif PECULIAR_486
- put_fs_long(instruction_address.offset, (unsigned long *) (d+0x0c));
- put_fs_word(instruction_address.selector, (unsigned short *) (d+0x10));
- put_fs_word(instruction_address.opcode, (unsigned short *) (d+0x12));
- put_fs_long(operand_address.offset, (unsigned long *) (d+0x14));
-#ifdef PECULIAR_486
- /* An 80486 sets all the reserved bits to 1. */
- put_fs_word(operand_address.selector, (unsigned short *) (d+0x18));
- put_fs_word(0xffff, (unsigned short *) (d+0x1a));
-#else
- put_fs_long(operand_address.selector, (unsigned long *) (d+0x18));
-#endif PECULIAR_486
- RE_ENTRANT_CHECK_ON;
- d += 0x1c;
- }
-
- control_word |= CW_Exceptions;
- partial_status &= ~(SW_Summary | SW_Backward);
-
- return d;
-}
-
-
-void fsave(fpu_addr_modes addr_modes, char *data_address)
-{
- char *d;
- int i;
-
- d = fstenv(addr_modes, data_address);
- RE_ENTRANT_CHECK_OFF;
- FPU_verify_area(VERIFY_WRITE,d,80);
- RE_ENTRANT_CHECK_ON;
- for ( i = 0; i < 8; i++ )
- write_to_extended(&regs[(top + i) & 7], d + 10 * i);
-
- finit();
-
-}
-
-/*===========================================================================*/
-
-/*
- A call to this function must be preceded by a call to
- FPU_verify_area() to verify access to the 10 bytes at d
- */
-static void write_to_extended(FPU_REG *rp, char *d)
-{
- long e;
- FPU_REG tmp;
-
- e = rp->exp - EXP_BIAS + EXTENDED_Ebias;
-
-#ifdef PARANOID
- switch ( rp->tag )
- {
- case TW_Zero:
- if ( rp->sigh | rp->sigl | e )
- EXCEPTION(EX_INTERNAL | 0x160);
- break;
- case TW_Infinity:
- case TW_NaN:
- if ( (e ^ 0x7fff) | !(rp->sigh & 0x80000000) )
- EXCEPTION(EX_INTERNAL | 0x161);
- break;
- default:
- if (e > 0x7fff || e < -63)
- EXCEPTION(EX_INTERNAL | 0x162);
- }
-#endif PARANOID
-
- /*
- All numbers except denormals are stored internally in a
- format which is compatible with the extended real number
- format.
- */
- if ( e > 0 )
- {
- /* just copy the reg */
- RE_ENTRANT_CHECK_OFF;
- put_fs_long(rp->sigl, (unsigned long *) d);
- put_fs_long(rp->sigh, (unsigned long *) (d + 4));
- RE_ENTRANT_CHECK_ON;
- }
- else
- {
- /*
- The number is a de-normal stored as a normal using our
- extra exponent range, or is Zero.
- Convert it back to a de-normal, or leave it as Zero.
- */
- reg_move(rp, &tmp);
- tmp.exp += -EXTENDED_Emin + 63; /* largest exp to be 63 */
- round_to_int(&tmp);
- e = 0;
- RE_ENTRANT_CHECK_OFF;
- put_fs_long(tmp.sigl, (unsigned long *) d);
- put_fs_long(tmp.sigh, (unsigned long *) (d + 4));
- RE_ENTRANT_CHECK_ON;
- }
- e |= rp->sign == SIGN_POS ? 0 : 0x8000;
- RE_ENTRANT_CHECK_OFF;
- put_fs_word(e, (unsigned short *) (d + 8));
- RE_ENTRANT_CHECK_ON;
-}
diff --git a/drivers/FPU-emu/reg_mul.c b/drivers/FPU-emu/reg_mul.c
deleted file mode 100644
index 75246187b..000000000
--- a/drivers/FPU-emu/reg_mul.c
+++ /dev/null
@@ -1,105 +0,0 @@
-/*---------------------------------------------------------------------------+
- | reg_mul.c |
- | |
- | Multiply one FPU_REG by another, put the result in a destination FPU_REG. |
- | |
- | Copyright (C) 1992,1993 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
-
-/*---------------------------------------------------------------------------+
- | The destination may be any FPU_REG, including one of the source FPU_REGs. |
- +---------------------------------------------------------------------------*/
-
-#include "exception.h"
-#include "reg_constant.h"
-#include "fpu_emu.h"
-#include "fpu_system.h"
-
-
-/* This routine must be called with non-empty source registers */
-int reg_mul(FPU_REG const *a, FPU_REG const *b,
- FPU_REG *dest, unsigned int control_w)
-{
- char saved_sign = dest->sign;
- char sign = (a->sign ^ b->sign);
-
- if (!(a->tag | b->tag))
- {
- /* Both regs Valid, this should be the most common case. */
- dest->sign = sign;
- if ( reg_u_mul(a, b, dest, control_w) )
- {
- dest->sign = saved_sign;
- return 1;
- }
- return 0;
- }
- else if ((a->tag <= TW_Zero) && (b->tag <= TW_Zero))
- {
-#ifdef DENORM_OPERAND
- if ( ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ||
- ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER)) )
- {
- if ( denormal_operand() ) return 1;
- }
-#endif DENORM_OPERAND
- /* Must have either both arguments == zero, or
- one valid and the other zero.
- The result is therefore zero. */
- reg_move(&CONST_Z, dest);
- /* The 80486 book says that the answer is +0, but a real
- 80486 behaves this way.
- IEEE-754 apparently says it should be this way. */
- dest->sign = sign;
- return 0;
- }
- else
- {
- /* Must have infinities, NaNs, etc */
- if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
- { return real_2op_NaN(a, b, dest); }
- else if (a->tag == TW_Infinity)
- {
- if (b->tag == TW_Zero)
- { return arith_invalid(dest); } /* Zero*Infinity is invalid */
- else
- {
-#ifdef DENORM_OPERAND
- if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
- denormal_operand() )
- return 1;
-#endif DENORM_OPERAND
- reg_move(a, dest);
- dest->sign = sign;
- }
- return 0;
- }
- else if (b->tag == TW_Infinity)
- {
- if (a->tag == TW_Zero)
- { return arith_invalid(dest); } /* Zero*Infinity is invalid */
- else
- {
-#ifdef DENORM_OPERAND
- if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
- denormal_operand() )
- return 1;
-#endif DENORM_OPERAND
- reg_move(b, dest);
- dest->sign = sign;
- }
- return 0;
- }
-#ifdef PARANOID
- else
- {
- EXCEPTION(EX_INTERNAL|0x102);
- return 1;
- }
-#endif PARANOID
- }
-}
diff --git a/drivers/FPU-emu/reg_norm.S b/drivers/FPU-emu/reg_norm.S
deleted file mode 100644
index 9b7a9d77d..000000000
--- a/drivers/FPU-emu/reg_norm.S
+++ /dev/null
@@ -1,150 +0,0 @@
-/*---------------------------------------------------------------------------+
- | reg_norm.S |
- | |
- | Copyright (C) 1992,1993,1994 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | Normalize the value in a FPU_REG. |
- | |
- | Call from C as: |
- | void normalize(FPU_REG *n) |
- | |
- | void normalize_nuo(FPU_REG *n) |
- | |
- +---------------------------------------------------------------------------*/
-
-#include "fpu_asm.h"
-
-
-.text
-
- .align 2,144
-.globl _normalize
-
-_normalize:
- pushl %ebp
- movl %esp,%ebp
- pushl %ebx
-
- movl PARAM1,%ebx
-
-#ifdef PARANOID
- cmpb TW_Valid,TAG(%ebx)
- je L_ok
-
- pushl $0x220
- call _exception
- addl $4,%esp
-
-L_ok:
-#endif PARANOID
-
- movl SIGH(%ebx),%edx
- movl SIGL(%ebx),%eax
-
- orl %edx,%edx /* ms bits */
- js L_done /* Already normalized */
- jnz L_shift_1 /* Shift left 1 - 31 bits */
-
- orl %eax,%eax
- jz L_zero /* The contents are zero */
-
- movl %eax,%edx
- xorl %eax,%eax
- subl $32,EXP(%ebx) /* This can cause an underflow */
-
-/* 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
- shld %cl,%eax,%edx
- shl %cl,%eax
- subl %ecx,EXP(%ebx) /* This can cause an underflow */
-
- movl %edx,SIGH(%ebx)
- movl %eax,SIGL(%ebx)
-
-L_done:
- cmpl EXP_OVER,EXP(%ebx)
- jge L_overflow
-
- cmpl EXP_UNDER,EXP(%ebx)
- jle L_underflow
-
-L_exit:
- popl %ebx
- leave
- ret
-
-
-L_zero:
- movl EXP_UNDER,EXP(%ebx)
- movb TW_Zero,TAG(%ebx)
- jmp L_exit
-
-L_underflow:
- push %ebx
- call _arith_underflow
- pop %ebx
- jmp L_exit
-
-L_overflow:
- push %ebx
- call _arith_overflow
- pop %ebx
- jmp L_exit
-
-
-
-/* Normalise without reporting underflow or overflow */
- .align 2,144
-.globl _normalize_nuo
-
-_normalize_nuo:
- pushl %ebp
- movl %esp,%ebp
- pushl %ebx
-
- movl PARAM1,%ebx
-
-#ifdef PARANOID
- cmpb TW_Valid,TAG(%ebx)
- je L_ok_nuo
-
- pushl $0x221
- call _exception
- addl $4,%esp
-
-L_ok_nuo:
-#endif PARANOID
-
- movl SIGH(%ebx),%edx
- movl SIGL(%ebx),%eax
-
- orl %edx,%edx /* ms bits */
- js L_exit /* Already normalized */
- jnz L_nuo_shift_1 /* Shift left 1 - 31 bits */
-
- orl %eax,%eax
- jz L_zero /* The contents are zero */
-
- movl %eax,%edx
- xorl %eax,%eax
- subl $32,EXP(%ebx) /* This can cause an underflow */
-
-/* We need to shift left by 1 - 31 bits */
-L_nuo_shift_1:
- bsrl %edx,%ecx /* get the required shift in %ecx */
- subl $31,%ecx
- negl %ecx
- shld %cl,%eax,%edx
- shl %cl,%eax
- subl %ecx,EXP(%ebx) /* This can cause an underflow */
-
- movl %edx,SIGH(%ebx)
- movl %eax,SIGL(%ebx)
- jmp L_exit
-
-
diff --git a/drivers/FPU-emu/reg_round.S b/drivers/FPU-emu/reg_round.S
deleted file mode 100644
index bd8a40dc4..000000000
--- a/drivers/FPU-emu/reg_round.S
+++ /dev/null
@@ -1,701 +0,0 @@
- .file "reg_round.S"
-/*---------------------------------------------------------------------------+
- | reg_round.S |
- | |
- | Rounding/truncation/etc for FPU basic arithmetic functions. |
- | |
- | Copyright (C) 1993 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | This code has four possible entry points. |
- | The following must be entered by a jmp instruction: |
- | fpu_reg_round, fpu_reg_round_sqrt, and fpu_Arith_exit. |
- | |
- | The _round_reg entry point is intended to be used by C code. |
- | From C, call as: |
- | void round_reg(FPU_REG *arg, unsigned int extent, unsigned int control_w) |
- | |
- | For correct "up" and "down" rounding, the argument must have the correct |
- | sign. |
- | |
- +---------------------------------------------------------------------------*/
-
-/*---------------------------------------------------------------------------+
- | Four entry points. |
- | |
- | Needed by both the fpu_reg_round and fpu_reg_round_sqrt entry points: |
- | %eax:%ebx 64 bit significand |
- | %edx 32 bit extension of the significand |
- | %edi pointer to an FPU_REG for the result to be stored |
- | stack calling function must have set up a C stack frame and |
- | pushed %esi, %edi, and %ebx |
- | |
- | Needed just for the fpu_reg_round_sqrt entry point: |
- | %cx A control word in the same format as the FPU control word. |
- | Otherwise, PARAM4 must give such a value. |
- | |
- | |
- | The significand and its extension are assumed to be exact in the |
- | following sense: |
- | If the significand by itself is the exact result then the significand |
- | extension (%edx) must contain 0, otherwise the significand extension |
- | must be non-zero. |
- | If the significand extension is non-zero then the significand is |
- | smaller than the magnitude of the correct exact result by an amount |
- | greater than zero and less than one ls bit of the significand. |
- | The significand extension is only required to have three possible |
- | non-zero values: |
- | less than 0x80000000 <=> the significand is less than 1/2 an ls |
- | bit smaller than the magnitude of the |
- | true exact result. |
- | exactly 0x80000000 <=> the significand is exactly 1/2 an ls bit |
- | smaller than the magnitude of the true |
- | exact result. |
- | greater than 0x80000000 <=> the significand is more than 1/2 an ls |
- | bit smaller than the magnitude of the |
- | true exact result. |
- | |
- +---------------------------------------------------------------------------*/
-
-/*---------------------------------------------------------------------------+
- | The code in this module has become quite complex, but it should handle |
- | all of the FPU flags which are set at this stage of the basic arithmetic |
- | computations. |
- | There are a few rare cases where the results are not set identically to |
- | a real FPU. These require a bit more thought because at this stage the |
- | results of the code here appear to be more consistent... |
- | This may be changed in a future version. |
- +---------------------------------------------------------------------------*/
-
-
-#include "fpu_asm.h"
-#include "exception.h"
-#include "control_w.h"
-
-/* Flags for FPU_bits_lost */
-#define LOST_DOWN $1
-#define LOST_UP $2
-
-/* Flags for FPU_denormal */
-#define DENORMAL $1
-#define UNMASKED_UNDERFLOW $2
-
-
-#ifndef NON_REENTRANT_FPU
-/* Make the code re-entrant by putting
- local storage on the stack: */
-#define FPU_bits_lost (%esp)
-#define FPU_denormal 1(%esp)
-
-#else
-/* Not re-entrant, so we can gain speed by putting
- local storage in a static area: */
-.data
- .align 2,0
-FPU_bits_lost:
- .byte 0
-FPU_denormal:
- .byte 0
-#endif NON_REENTRANT_FPU
-
-
-.text
- .align 2,144
-.globl fpu_reg_round
-.globl fpu_reg_round_sqrt
-.globl fpu_Arith_exit
-.globl _round_reg
-
-/* Entry point when called from C */
-_round_reg:
- pushl %ebp
- movl %esp,%ebp
- pushl %esi
- pushl %edi
- pushl %ebx
-
- movl PARAM1,%edi
- movl SIGH(%edi),%eax
- movl SIGL(%edi),%ebx
- movl PARAM2,%edx
- movl PARAM3,%ecx
- jmp fpu_reg_round_sqrt
-
-fpu_reg_round: /* Normal entry point */
- movl PARAM4,%ecx
-
-fpu_reg_round_sqrt: /* Entry point from wm_sqrt.S */
-
-#ifndef NON_REENTRANT_FPU
- pushl %ebx /* adjust the stack pointer */
-#endif NON_REENTRANT_FPU
-
-#ifdef PARANOID
-/* Cannot use this here yet */
-/* orl %eax,%eax */
-/* jns L_entry_bugged */
-#endif PARANOID
-
- cmpl EXP_UNDER,EXP(%edi)
- jle xMake_denorm /* The number is a de-normal */
-
- movb $0,FPU_denormal /* 0 -> not a de-normal */
-
-xDenorm_done:
- movb $0,FPU_bits_lost /* No bits yet lost in rounding */
-
- movl %ecx,%esi
- andl CW_PC,%ecx
- cmpl PR_64_BITS,%ecx
- je LRound_To_64
-
- cmpl PR_53_BITS,%ecx
- je LRound_To_53
-
- cmpl PR_24_BITS,%ecx
- je LRound_To_24
-
-#ifdef PECULIAR_486
-/* With the precision control bits set to 01 "(reserved)", a real 80486
- behaves as if the precision control bits were set to 11 "64 bits" */
- cmpl PR_RESERVED_BITS,%ecx
- je LRound_To_64
-#ifdef PARANOID
- jmp L_bugged_denorm_486
-#endif PARANOID
-#else
-#ifdef PARANOID
- jmp L_bugged_denorm /* There is no bug, just a bad control word */
-#endif PARANOID
-#endif PECULIAR_486
-
-
-/* Round etc to 24 bit precision */
-LRound_To_24:
- movl %esi,%ecx
- andl CW_RC,%ecx
- cmpl RC_RND,%ecx
- je LRound_nearest_24
-
- cmpl RC_CHOP,%ecx
- je LCheck_truncate_24
-
- cmpl RC_UP,%ecx /* Towards +infinity */
- je LUp_24
-
- cmpl RC_DOWN,%ecx /* Towards -infinity */
- je LDown_24
-
-#ifdef PARANOID
- jmp L_bugged_round24
-#endif PARANOID
-
-LUp_24:
- cmpb SIGN_POS,SIGN(%edi)
- jne LCheck_truncate_24 /* If negative then up==truncate */
-
- jmp LCheck_24_round_up
-
-LDown_24:
- cmpb SIGN_POS,SIGN(%edi)
- je LCheck_truncate_24 /* If positive then down==truncate */
-
-LCheck_24_round_up:
- movl %eax,%ecx
- andl $0x000000ff,%ecx
- orl %ebx,%ecx
- orl %edx,%ecx
- jnz LDo_24_round_up
- jmp LRe_normalise
-
-LRound_nearest_24:
- /* Do rounding of the 24th bit if needed (nearest or even) */
- movl %eax,%ecx
- andl $0x000000ff,%ecx
- cmpl $0x00000080,%ecx
- jc LCheck_truncate_24 /* less than half, no increment needed */
-
- jne LGreater_Half_24 /* greater than half, increment needed */
-
- /* Possibly half, we need to check the ls bits */
- orl %ebx,%ebx
- jnz LGreater_Half_24 /* greater than half, increment needed */
-
- orl %edx,%edx
- jnz LGreater_Half_24 /* greater than half, increment needed */
-
- /* Exactly half, increment only if 24th bit is 1 (round to even) */
- testl $0x00000100,%eax
- jz LDo_truncate_24
-
-LGreater_Half_24: /* Rounding: increment at the 24th bit */
-LDo_24_round_up:
- andl $0xffffff00,%eax /* Truncate to 24 bits */
- xorl %ebx,%ebx
- movb LOST_UP,FPU_bits_lost
- addl $0x00000100,%eax
- jmp LCheck_Round_Overflow
-
-LCheck_truncate_24:
- movl %eax,%ecx
- andl $0x000000ff,%ecx
- orl %ebx,%ecx
- orl %edx,%ecx
- jz LRe_normalise /* No truncation needed */
-
-LDo_truncate_24:
- andl $0xffffff00,%eax /* Truncate to 24 bits */
- xorl %ebx,%ebx
- movb LOST_DOWN,FPU_bits_lost
- jmp LRe_normalise
-
-
-/* Round etc to 53 bit precision */
-LRound_To_53:
- movl %esi,%ecx
- andl CW_RC,%ecx
- cmpl RC_RND,%ecx
- je LRound_nearest_53
-
- cmpl RC_CHOP,%ecx
- je LCheck_truncate_53
-
- cmpl RC_UP,%ecx /* Towards +infinity */
- je LUp_53
-
- cmpl RC_DOWN,%ecx /* Towards -infinity */
- je LDown_53
-
-#ifdef PARANOID
- jmp L_bugged_round53
-#endif PARANOID
-
-LUp_53:
- cmpb SIGN_POS,SIGN(%edi)
- jne LCheck_truncate_53 /* If negative then up==truncate */
-
- jmp LCheck_53_round_up
-
-LDown_53:
- cmpb SIGN_POS,SIGN(%edi)
- je LCheck_truncate_53 /* If positive then down==truncate */
-
-LCheck_53_round_up:
- movl %ebx,%ecx
- andl $0x000007ff,%ecx
- orl %edx,%ecx
- jnz LDo_53_round_up
- jmp LRe_normalise
-
-LRound_nearest_53:
- /* Do rounding of the 53rd bit if needed (nearest or even) */
- movl %ebx,%ecx
- andl $0x000007ff,%ecx
- cmpl $0x00000400,%ecx
- jc LCheck_truncate_53 /* less than half, no increment needed */
-
- jnz LGreater_Half_53 /* greater than half, increment needed */
-
- /* Possibly half, we need to check the ls bits */
- orl %edx,%edx
- jnz LGreater_Half_53 /* greater than half, increment needed */
-
- /* Exactly half, increment only if 53rd bit is 1 (round to even) */
- testl $0x00000800,%ebx
- jz LTruncate_53
-
-LGreater_Half_53: /* Rounding: increment at the 53rd bit */
-LDo_53_round_up:
- movb LOST_UP,FPU_bits_lost
- andl $0xfffff800,%ebx /* Truncate to 53 bits */
- addl $0x00000800,%ebx
- adcl $0,%eax
- jmp LCheck_Round_Overflow
-
-LCheck_truncate_53:
- movl %ebx,%ecx
- andl $0x000007ff,%ecx
- orl %edx,%ecx
- jz LRe_normalise
-
-LTruncate_53:
- movb LOST_DOWN,FPU_bits_lost
- andl $0xfffff800,%ebx /* Truncate to 53 bits */
- jmp LRe_normalise
-
-
-/* Round etc to 64 bit precision */
-LRound_To_64:
- movl %esi,%ecx
- andl CW_RC,%ecx
- cmpl RC_RND,%ecx
- je LRound_nearest_64
-
- cmpl RC_CHOP,%ecx
- je LCheck_truncate_64
-
- cmpl RC_UP,%ecx /* Towards +infinity */
- je LUp_64
-
- cmpl RC_DOWN,%ecx /* Towards -infinity */
- je LDown_64
-
-#ifdef PARANOID
- jmp L_bugged_round64
-#endif PARANOID
-
-LUp_64:
- cmpb SIGN_POS,SIGN(%edi)
- jne LCheck_truncate_64 /* If negative then up==truncate */
-
- orl %edx,%edx
- jnz LDo_64_round_up
- jmp LRe_normalise
-
-LDown_64:
- cmpb SIGN_POS,SIGN(%edi)
- je LCheck_truncate_64 /* If positive then down==truncate */
-
- orl %edx,%edx
- jnz LDo_64_round_up
- jmp LRe_normalise
-
-LRound_nearest_64:
- cmpl $0x80000000,%edx
- jc LCheck_truncate_64
-
- jne LDo_64_round_up
-
- /* Now test for round-to-even */
- testb $1,%ebx
- jz LCheck_truncate_64
-
-LDo_64_round_up:
- movb LOST_UP,FPU_bits_lost
- addl $1,%ebx
- adcl $0,%eax
-
-LCheck_Round_Overflow:
- jnc LRe_normalise
-
- /* Overflow, adjust the result (significand to 1.0) */
- rcrl $1,%eax
- rcrl $1,%ebx
- incl EXP(%edi)
- jmp LRe_normalise
-
-LCheck_truncate_64:
- orl %edx,%edx
- jz LRe_normalise
-
-LTruncate_64:
- movb LOST_DOWN,FPU_bits_lost
-
-LRe_normalise:
- testb $0xff,FPU_denormal
- jnz xNormalise_result
-
-xL_Normalised:
- cmpb LOST_UP,FPU_bits_lost
- je xL_precision_lost_up
-
- cmpb LOST_DOWN,FPU_bits_lost
- je xL_precision_lost_down
-
-xL_no_precision_loss:
- /* store the result */
- movb TW_Valid,TAG(%edi)
-
-xL_Store_significand:
- movl %eax,SIGH(%edi)
- movl %ebx,SIGL(%edi)
-
- xorl %eax,%eax /* No errors detected. */
-
- cmpl EXP_OVER,EXP(%edi)
- jge L_overflow
-
-fpu_reg_round_exit:
-#ifndef NON_REENTRANT_FPU
- popl %ebx /* adjust the stack pointer */
-#endif NON_REENTRANT_FPU
-
-fpu_Arith_exit:
- popl %ebx
- popl %edi
- popl %esi
- leave
- ret
-
-
-/*
- * Set the FPU status flags to represent precision loss due to
- * round-up.
- */
-xL_precision_lost_up:
- push %eax
- call _set_precision_flag_up
- popl %eax
- jmp xL_no_precision_loss
-
-/*
- * Set the FPU status flags to represent precision loss due to
- * truncation.
- */
-xL_precision_lost_down:
- push %eax
- call _set_precision_flag_down
- popl %eax
- jmp xL_no_precision_loss
-
-
-/*
- * The number is a denormal (which might get rounded up to a normal)
- * Shift the number right the required number of bits, which will
- * have to be undone later...
- */
-xMake_denorm:
- /* The action to be taken depends upon whether the underflow
- exception is masked */
- testb CW_Underflow,%cl /* Underflow mask. */
- jz xUnmasked_underflow /* Do not make a denormal. */
-
- movb DENORMAL,FPU_denormal
-
- pushl %ecx /* Save */
- movl EXP_UNDER+1,%ecx
- subl EXP(%edi),%ecx
-
- cmpl $64,%ecx /* shrd only works for 0..31 bits */
- jnc xDenorm_shift_more_than_63
-
- cmpl $32,%ecx /* shrd only works for 0..31 bits */
- jnc xDenorm_shift_more_than_32
-
-/*
- * We got here without jumps by assuming that the most common requirement
- * is for a small de-normalising shift.
- * Shift by [1..31] bits
- */
- addl %ecx,EXP(%edi)
- orl %edx,%edx /* extension */
- setne %ch /* Save whether %edx is non-zero */
- xorl %edx,%edx
- shrd %cl,%ebx,%edx
- shrd %cl,%eax,%ebx
- shr %cl,%eax
- orb %ch,%dl
- popl %ecx
- jmp xDenorm_done
-
-/* Shift by [32..63] bits */
-xDenorm_shift_more_than_32:
- addl %ecx,EXP(%edi)
- subb $32,%cl
- orl %edx,%edx
- setne %ch
- orb %ch,%bl
- xorl %edx,%edx
- shrd %cl,%ebx,%edx
- shrd %cl,%eax,%ebx
- shr %cl,%eax
- orl %edx,%edx /* test these 32 bits */
- setne %cl
- orb %ch,%bl
- orb %cl,%bl
- movl %ebx,%edx
- movl %eax,%ebx
- xorl %eax,%eax
- popl %ecx
- jmp xDenorm_done
-
-/* Shift by [64..) bits */
-xDenorm_shift_more_than_63:
- cmpl $64,%ecx
- jne xDenorm_shift_more_than_64
-
-/* Exactly 64 bit shift */
- addl %ecx,EXP(%edi)
- xorl %ecx,%ecx
- orl %edx,%edx
- setne %cl
- orl %ebx,%ebx
- setne %ch
- orb %ch,%cl
- orb %cl,%al
- movl %eax,%edx
- xorl %eax,%eax
- xorl %ebx,%ebx
- popl %ecx
- jmp xDenorm_done
-
-xDenorm_shift_more_than_64:
- movl EXP_UNDER+1,EXP(%edi)
-/* This is easy, %eax must be non-zero, so.. */
- movl $1,%edx
- xorl %eax,%eax
- xorl %ebx,%ebx
- popl %ecx
- jmp xDenorm_done
-
-
-xUnmasked_underflow:
- movb UNMASKED_UNDERFLOW,FPU_denormal
- jmp xDenorm_done
-
-
-/* Undo the de-normalisation. */
-xNormalise_result:
- cmpb UNMASKED_UNDERFLOW,FPU_denormal
- je xSignal_underflow
-
-/* The number must be a denormal if we got here. */
-#ifdef PARANOID
- /* But check it... just in case. */
- cmpl EXP_UNDER+1,EXP(%edi)
- jne L_norm_bugged
-#endif PARANOID
-
-#ifdef PECULIAR_486
- /*
- * This implements a special feature of 80486 behaviour.
- * Underflow will be signalled even if the number is
- * not a denormal after rounding.
- * This difference occurs only for masked underflow, and not
- * in the unmasked case.
- * Actual 80486 behaviour differs from this in some circumstances.
- */
- orl %eax,%eax /* ms bits */
- js LNormalise_shift_done /* Will be masked underflow */
-#endif PECULIAR_486
-
- orl %eax,%eax /* ms bits */
- js xL_Normalised /* No longer a denormal */
-
- jnz LNormalise_shift_up_to_31 /* Shift left 0 - 31 bits */
-
- orl %ebx,%ebx
- jz L_underflow_to_zero /* The contents are zero */
-
-/* Shift left 32 - 63 bits */
- movl %ebx,%eax
- xorl %ebx,%ebx
- subl $32,EXP(%edi)
-
-LNormalise_shift_up_to_31:
- bsrl %eax,%ecx /* get the required shift in %ecx */
- subl $31,%ecx
- negl %ecx
- shld %cl,%ebx,%eax
- shl %cl,%ebx
- subl %ecx,EXP(%edi)
-
-LNormalise_shift_done:
- testb $0xff,FPU_bits_lost /* bits lost == underflow */
- jz xL_Normalised
-
- /* There must be a masked underflow */
- push %eax
- pushl EX_Underflow
- call _exception
- popl %eax
- popl %eax
- jmp xL_Normalised
-
-
-/*
- * The operations resulted in a number too small to represent.
- * Masked response.
- */
-L_underflow_to_zero:
- push %eax
- call _set_precision_flag_down
- popl %eax
-
- push %eax
- pushl EX_Underflow
- call _exception
- popl %eax
- popl %eax
-
-/* Reduce the exponent to EXP_UNDER */
- movl EXP_UNDER,EXP(%edi)
- movb TW_Zero,TAG(%edi)
- jmp xL_Store_significand
-
-
-/* The operations resulted in a number too large to represent. */
-L_overflow:
- push %edi
- call _arith_overflow
- pop %edi
- jmp fpu_reg_round_exit
-
-
-xSignal_underflow:
- /* The number may have been changed to a non-denormal */
- /* by the rounding operations. */
- cmpl EXP_UNDER,EXP(%edi)
- jle xDo_unmasked_underflow
-
- jmp xL_Normalised
-
-xDo_unmasked_underflow:
- /* Increase the exponent by the magic number */
- addl $(3*(1<<13)),EXP(%edi)
- push %eax
- pushl EX_Underflow
- call EXCEPTION
- popl %eax
- popl %eax
- jmp xL_Normalised
-
-
-#ifdef PARANOID
-#ifdef PECULIAR_486
-L_bugged_denorm_486:
- pushl EX_INTERNAL|0x236
- call EXCEPTION
- popl %ebx
- jmp L_exception_exit
-#else
-L_bugged_denorm:
- pushl EX_INTERNAL|0x230
- call EXCEPTION
- popl %ebx
- jmp L_exception_exit
-#endif PECULIAR_486
-
-L_bugged_round24:
- pushl EX_INTERNAL|0x231
- call EXCEPTION
- popl %ebx
- jmp L_exception_exit
-
-L_bugged_round53:
- pushl EX_INTERNAL|0x232
- call EXCEPTION
- popl %ebx
- jmp L_exception_exit
-
-L_bugged_round64:
- pushl EX_INTERNAL|0x233
- call EXCEPTION
- popl %ebx
- jmp L_exception_exit
-
-L_norm_bugged:
- pushl EX_INTERNAL|0x234
- call EXCEPTION
- popl %ebx
- jmp L_exception_exit
-
-L_entry_bugged:
- pushl EX_INTERNAL|0x235
- call EXCEPTION
- popl %ebx
-L_exception_exit:
- mov $1,%eax
- jmp fpu_reg_round_exit
-#endif PARANOID
diff --git a/drivers/FPU-emu/reg_u_add.S b/drivers/FPU-emu/reg_u_add.S
deleted file mode 100644
index 4410f8fd4..000000000
--- a/drivers/FPU-emu/reg_u_add.S
+++ /dev/null
@@ -1,189 +0,0 @@
- .file "reg_u_add.S"
-/*---------------------------------------------------------------------------+
- | reg_u_add.S |
- | |
- | Add two valid (TW_Valid) FPU_REG numbers, of the same sign, and put the |
- | result in a destination FPU_REG. |
- | |
- | Copyright (C) 1992,1993 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | Call from C as: |
- | void reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, |
- | int control_w) |
- | |
- +---------------------------------------------------------------------------*/
-
-/*
- | Kernel addition routine reg_u_add(reg *arg1, reg *arg2, reg *answ).
- | Takes two valid reg f.p. numbers (TW_Valid), which are
- | treated as unsigned numbers,
- | and returns their sum as a TW_Valid or TW_S f.p. number.
- | The returned number is normalized.
- | Basic checks are performed if PARANOID is defined.
- */
-
-#include "exception.h"
-#include "fpu_asm.h"
-#include "control_w.h"
-
-.text
- .align 2,144
-.globl _reg_u_add
-_reg_u_add:
- pushl %ebp
- movl %esp,%ebp
- pushl %esi
- pushl %edi
- pushl %ebx
-
- movl PARAM1,%esi /* source 1 */
- movl PARAM2,%edi /* source 2 */
-
-#ifdef DENORM_OPERAND
- cmpl EXP_UNDER,EXP(%esi)
- jg xOp1_not_denorm
-
- call _denormal_operand
- orl %eax,%eax
- jnz fpu_Arith_exit
-
-xOp1_not_denorm:
- cmpl EXP_UNDER,EXP(%edi)
- jg xOp2_not_denorm
-
- call _denormal_operand
- orl %eax,%eax
- jnz fpu_Arith_exit
-
-xOp2_not_denorm:
-#endif DENORM_OPERAND
-
- movl EXP(%esi),%ecx
- subl EXP(%edi),%ecx /* exp1 - exp2 */
- jge L_arg1_larger
-
- /* num1 is smaller */
- movl SIGL(%esi),%ebx
- movl SIGH(%esi),%eax
-
- movl %edi,%esi
- negw %cx
- jmp L_accum_loaded
-
-L_arg1_larger:
- /* num1 has larger or equal exponent */
- movl SIGL(%edi),%ebx
- movl SIGH(%edi),%eax
-
-L_accum_loaded:
- movl PARAM3,%edi /* destination */
-/* movb SIGN(%esi),%dl
- movb %dl,SIGN(%edi) */ /* Copy the sign from the first arg */
-
-
- movl EXP(%esi),%edx
- movl %edx,EXP(%edi) /* Copy exponent to destination */
-
- xorl %edx,%edx /* clear the extension */
-
-#ifdef PARANOID
- testl $0x80000000,%eax
- je L_bugged
-
- testl $0x80000000,SIGH(%esi)
- je L_bugged
-#endif PARANOID
-
-/* The number to be shifted is in %eax:%ebx:%edx */
- cmpw $32,%cx /* shrd only works for 0..31 bits */
- jnc L_more_than_31
-
-/* less than 32 bits */
- shrd %cl,%ebx,%edx
- shrd %cl,%eax,%ebx
- shr %cl,%eax
- jmp L_shift_done
-
-L_more_than_31:
- cmpw $64,%cx
- jnc L_more_than_63
-
- subb $32,%cl
- jz L_exactly_32
-
- shrd %cl,%eax,%edx
- shr %cl,%eax
- orl %ebx,%ebx
- jz L_more_31_no_low /* none of the lowest bits is set */
-
- orl $1,%edx /* record the fact in the extension */
-
-L_more_31_no_low:
- movl %eax,%ebx
- xorl %eax,%eax
- jmp L_shift_done
-
-L_exactly_32:
- movl %ebx,%edx
- movl %eax,%ebx
- xorl %eax,%eax
- jmp L_shift_done
-
-L_more_than_63:
- cmpw $65,%cx
- jnc L_more_than_64
-
- movl %eax,%edx
- orl %ebx,%ebx
- jz L_more_63_no_low
-
- orl $1,%edx
- jmp L_more_63_no_low
-
-L_more_than_64:
- movl $1,%edx /* The shifted nr always at least one '1' */
-
-L_more_63_no_low:
- xorl %ebx,%ebx
- xorl %eax,%eax
-
-L_shift_done:
- /* Now do the addition */
- addl SIGL(%esi),%ebx
- adcl SIGH(%esi),%eax
- jnc L_round_the_result
-
- /* Overflow, adjust the result */
- rcrl $1,%eax
- rcrl $1,%ebx
- rcrl $1,%edx
- jnc L_no_bit_lost
-
- orl $1,%edx
-
-L_no_bit_lost:
- incl EXP(%edi)
-
-L_round_the_result:
- jmp fpu_reg_round /* Round the result */
-
-
-
-#ifdef PARANOID
-/* If we ever get here then we have problems! */
-L_bugged:
- pushl EX_INTERNAL|0x201
- call EXCEPTION
- pop %ebx
- jmp L_exit
-#endif PARANOID
-
-
-L_exit:
- popl %ebx
- popl %edi
- popl %esi
- leave
- ret
diff --git a/drivers/FPU-emu/reg_u_div.S b/drivers/FPU-emu/reg_u_div.S
deleted file mode 100644
index 328e9116e..000000000
--- a/drivers/FPU-emu/reg_u_div.S
+++ /dev/null
@@ -1,477 +0,0 @@
- .file "reg_u_div.S"
-/*---------------------------------------------------------------------------+
- | reg_u_div.S |
- | |
- | Core division routines |
- | |
- | Copyright (C) 1992,1993 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
-
-/*---------------------------------------------------------------------------+
- | Kernel for the division routines. |
- | |
- | void reg_u_div(FPU_REG *a, FPU_REG *a, |
- | FPU_REG *dest, unsigned int control_word) |
- | |
- | Does not compute the destination exponent, but does adjust it. |
- +---------------------------------------------------------------------------*/
-
-#include "exception.h"
-#include "fpu_asm.h"
-#include "control_w.h"
-
-
-/* #define dSIGL(x) (x) */
-/* #define dSIGH(x) 4(x) */
-
-
-#ifndef NON_REENTRANT_FPU
-/*
- Local storage on the stack:
- Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
- Overflow flag: ovfl_flag
- */
-#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_1 -20(%ebp)
-#define FPU_result_2 -24(%ebp)
-#define FPU_ovfl_flag -28(%ebp)
-
-#else
-.data
-/*
- Local storage in a static area:
- Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
- Overflow flag: ovfl_flag
- */
- .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_1:
- .long 0
-FPU_result_2:
- .long 0
-FPU_ovfl_flag:
- .byte 0
-#endif NON_REENTRANT_FPU
-
-
-.text
- .align 2,144
-
-.globl _reg_u_div
-
-.globl _divide_kernel
-
-_reg_u_div:
- 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 */
- movl PARAM3,%edi /* pointer to answer */
-
-#ifdef DENORM_OPERAND
- movl EXP(%esi),%eax
- cmpl EXP_UNDER,%eax
- jg xOp1_not_denorm
-
- call _denormal_operand
- orl %eax,%eax
- jnz fpu_Arith_exit
-
-xOp1_not_denorm:
- movl EXP(%ebx),%eax
- cmpl EXP_UNDER,%eax
- jg xOp2_not_denorm
-
- call _denormal_operand
- orl %eax,%eax
- jnz fpu_Arith_exit
-
-xOp2_not_denorm:
-#endif DENORM_OPERAND
-
-_divide_kernel:
-#ifdef PARANOID
-/* testl $0x80000000, SIGH(%esi) // Dividend */
-/* je L_bugged */
- testl $0x80000000, SIGH(%ebx) /* Divisor */
- je L_bugged
-#endif PARANOID
-
-/* Check if the divisor can be treated as having just 32 bits */
- cmpl $0,SIGL(%ebx)
- jnz L_Full_Division /* Can't do a quick divide */
-
-/* We should be able to zip through the division here */
- movl SIGH(%ebx),%ecx /* The divisor */
- movl SIGH(%esi),%edx /* Dividend */
- movl SIGL(%esi),%eax /* Dividend */
-
- cmpl %ecx,%edx
- setaeb FPU_ovfl_flag /* Keep a record */
- jb L_no_adjust
-
- subl %ecx,%edx /* Prevent the overflow */
-
-L_no_adjust:
- /* Divide the 64 bit number by the 32 bit denominator */
- divl %ecx
- movl %eax,FPU_result_2
-
- /* Work on the remainder of the first division */
- xorl %eax,%eax
- divl %ecx
- movl %eax,FPU_result_1
-
- /* Work on the remainder of the 64 bit division */
- xorl %eax,%eax
- divl %ecx
-
- testb $255,FPU_ovfl_flag /* was the num > denom ? */
- je L_no_overflow
-
- /* Do the shifting here */
- /* increase the exponent */
- incl EXP(%edi)
-
- /* shift the mantissa right one bit */
- stc /* To set the ms bit */
- rcrl FPU_result_2
- rcrl FPU_result_1
- rcrl %eax
-
-L_no_overflow:
- jmp LRound_precision /* Do the rounding as required */
-
-
-/*---------------------------------------------------------------------------+
- | Divide: Return arg1/arg2 to arg3. |
- | |
- | This routine does not use the exponents of arg1 and arg2, but does |
- | adjust the exponent of 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 |
- | |
- +---------------------------------------------------------------------------*/
-
-
-L_Full_Division:
- /* Save extended dividend in local register */
- movl SIGL(%esi),%eax
- movl %eax,FPU_accum_2
- movl SIGH(%esi),%eax
- movl %eax,FPU_accum_3
- xorl %eax,%eax
- movl %eax,FPU_accum_1 /* zero the extension */
- movl %eax,FPU_accum_0 /* zero the extension */
-
- movl SIGL(%esi),%eax /* Get the current num */
- movl SIGH(%esi),%edx
-
-/*----------------------------------------------------------------------*/
-/* Initialization done.
- Do the first 32 bits. */
-
- movb $0,FPU_ovfl_flag
- cmpl SIGH(%ebx),%edx /* Test for imminent overflow */
- jb LLess_than_1
- ja LGreater_than_1
-
- cmpl SIGL(%ebx),%eax
- jb LLess_than_1
-
-LGreater_than_1:
-/* The dividend is greater or equal, would cause overflow */
- setaeb FPU_ovfl_flag /* Keep a record */
-
- subl SIGL(%ebx),%eax
- sbbl SIGH(%ebx),%edx /* Prevent the overflow */
- movl %eax,FPU_accum_2
- movl %edx,FPU_accum_3
-
-LLess_than_1:
-/* At this point, we have a dividend < divisor, with a record of
- adjustment in FPU_ovfl_flag */
-
- /* We will divide by a number which is too large */
- movl SIGH(%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_2 /* Put the result in the answer */
-
- mull SIGH(%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_2,%eax /* Get the result back */
- mull SIGL(%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_2 /* Correct the answer */
-
- movl SIGL(%ebx),%eax
- movl SIGH(%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 SIGH(%ebx),%edx
- jb LDo_2nd_div
- ja LPrevent_2nd_overflow
-
- cmpl SIGL(%ebx),%eax
- jb LDo_2nd_div
-
-LPrevent_2nd_overflow:
-/* The numerator is greater or equal, would cause overflow */
- /* prevent overflow */
- subl SIGL(%ebx),%eax
- sbbl SIGH(%ebx),%edx
- movl %edx,FPU_accum_2
- movl %eax,FPU_accum_1
-
- incl FPU_result_2 /* 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_1 /* Put the result in the answer */
-
- mull SIGH(%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_1,%eax /* Get the result back */
- mull SIGL(%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 SIGL(%ebx),%eax
- movl SIGH(%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_1 /* Correct the answer */
- adcl $0,FPU_result_2
-
-#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:
- movl FPU_accum_1,%edx /* get the reduced num */
- movl FPU_accum_0,%eax
-
- /* need to check for possible subsequent overflow */
- cmpl SIGH(%ebx),%edx /* denom */
- jb LRound_prep
- ja LPrevent_3rd_overflow
-
- cmpl SIGL(%ebx),%eax /* denom */
- jb LRound_prep
-
-LPrevent_3rd_overflow:
- /* prevent overflow */
- subl SIGL(%ebx),%eax
- sbbl SIGH(%ebx),%edx
- movl %edx,FPU_accum_1
- movl %eax,FPU_accum_0
-
- addl $1,FPU_result_1 /* Reflect the subtraction in the answer */
- adcl $0,FPU_result_2
- jne LRound_prep
- jnc LRound_prep
-
- /* This is a tricky spot, there is an overflow of the answer */
- movb $255,FPU_ovfl_flag /* Overflow -> 1.000 */
-
-LRound_prep:
-/*
- * Prepare for rounding.
- * To test for rounding, we just need to compare 2*accum with the
- * denom.
- */
- movl FPU_accum_0,%ecx
- movl FPU_accum_1,%edx
- movl %ecx,%eax
- orl %edx,%eax
- jz LRound_ovfl /* The accumulator contains zero. */
-
- /* Multiply by 2 */
- clc
- rcll $1,%ecx
- rcll $1,%edx
- jc LRound_large /* No need to compare, denom smaller */
-
- subl SIGL(%ebx),%ecx
- sbbl SIGH(%ebx),%edx
- jnc LRound_not_small
-
- movl $0x70000000,%eax /* Denom was larger */
- jmp LRound_ovfl
-
-LRound_not_small:
- jnz LRound_large
-
- movl $0x80000000,%eax /* Remainder was exactly 1/2 denom */
- jmp LRound_ovfl
-
-LRound_large:
- movl $0xff000000,%eax /* Denom was smaller */
-
-LRound_ovfl:
-/* We are now ready to deal with rounding, but first we must get
- the bits properly aligned */
- testb $255,FPU_ovfl_flag /* was the num > denom ? */
- je LRound_precision
-
- incl EXP(%edi)
-
- /* shift the mantissa right one bit */
- stc /* Will set the ms bit */
- rcrl FPU_result_2
- rcrl FPU_result_1
- rcrl %eax
-
-/* Round the result as required */
-LRound_precision:
- decl EXP(%edi) /* binary point between 1st & 2nd bits */
-
- movl %eax,%edx
- movl FPU_result_1,%ebx
- movl FPU_result_2,%eax
- jmp fpu_reg_round
-
-
-#ifdef PARANOID
-/* The logic is wrong if we got here */
-L_bugged:
- pushl EX_INTERNAL|0x202
- call EXCEPTION
- pop %ebx
- jmp L_exit
-
-L_bugged_1:
- pushl EX_INTERNAL|0x203
- call EXCEPTION
- pop %ebx
- jmp L_exit
-
-L_bugged_2:
- pushl EX_INTERNAL|0x204
- call EXCEPTION
- pop %ebx
- jmp L_exit
-
-L_exit:
- popl %ebx
- popl %edi
- popl %esi
-
- leave
- ret
-#endif PARANOID
diff --git a/drivers/FPU-emu/reg_u_mul.S b/drivers/FPU-emu/reg_u_mul.S
deleted file mode 100644
index 8250666bd..000000000
--- a/drivers/FPU-emu/reg_u_mul.S
+++ /dev/null
@@ -1,163 +0,0 @@
- .file "reg_u_mul.S"
-/*---------------------------------------------------------------------------+
- | reg_u_mul.S |
- | |
- | Core multiplication routine |
- | |
- | Copyright (C) 1992,1993 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
-
-/*---------------------------------------------------------------------------+
- | Basic multiplication routine. |
- | Does not check the resulting exponent for overflow/underflow |
- | |
- | reg_u_mul(FPU_REG *a, FPU_REG *b, FPU_REG *c, unsigned int cw); |
- | |
- | Internal working is at approx 128 bits. |
- | Result is rounded to nearest 53 or 64 bits, using "nearest or even". |
- +---------------------------------------------------------------------------*/
-
-#include "exception.h"
-#include "fpu_asm.h"
-#include "control_w.h"
-
-
-
-#ifndef NON_REENTRANT_FPU
-/* Local storage on the stack: */
-#define FPU_accum_0 -4(%ebp) /* ms word */
-#define FPU_accum_1 -8(%ebp)
-
-#else
-/* Local storage in a static area: */
-.data
- .align 4,0
-FPU_accum_0:
- .long 0
-FPU_accum_1:
- .long 0
-#endif NON_REENTRANT_FPU
-
-
-.text
- .align 2,144
-
-.globl _reg_u_mul
-_reg_u_mul:
- pushl %ebp
- movl %esp,%ebp
-#ifndef NON_REENTRANT_FPU
- subl $8,%esp
-#endif NON_REENTRANT_FPU
-
- pushl %esi
- pushl %edi
- pushl %ebx
-
- movl PARAM1,%esi
- movl PARAM2,%edi
-
-#ifdef PARANOID
- testl $0x80000000,SIGH(%esi)
- jz L_bugged
- testl $0x80000000,SIGH(%edi)
- jz L_bugged
-#endif PARANOID
-
-#ifdef DENORM_OPERAND
- movl EXP(%esi),%eax
- cmpl EXP_UNDER,%eax
- jg xOp1_not_denorm
-
- call _denormal_operand
- orl %eax,%eax
- jnz fpu_Arith_exit
-
-xOp1_not_denorm:
- movl EXP(%edi),%eax
- cmpl EXP_UNDER,%eax
- jg xOp2_not_denorm
-
- call _denormal_operand
- orl %eax,%eax
- jnz fpu_Arith_exit
-
-xOp2_not_denorm:
-#endif DENORM_OPERAND
-
- xorl %ecx,%ecx
- xorl %ebx,%ebx
-
- movl SIGL(%esi),%eax
- mull SIGL(%edi)
- movl %eax,FPU_accum_0
- movl %edx,FPU_accum_1
-
- movl SIGL(%esi),%eax
- mull SIGH(%edi)
- addl %eax,FPU_accum_1
- adcl %edx,%ebx
-/* adcl $0,%ecx // overflow here is not possible */
-
- movl SIGH(%esi),%eax
- mull SIGL(%edi)
- addl %eax,FPU_accum_1
- adcl %edx,%ebx
- adcl $0,%ecx
-
- movl SIGH(%esi),%eax
- mull SIGH(%edi)
- addl %eax,%ebx
- adcl %edx,%ecx
-
- movl EXP(%esi),%eax /* Compute the exponent */
- addl EXP(%edi),%eax
- subl EXP_BIAS-1,%eax
-
-/* Have now finished with the sources */
- movl PARAM3,%edi /* Point to the destination */
- movl %eax,EXP(%edi)
-
-/* Now make sure that the result is normalized */
- testl $0x80000000,%ecx
- jnz LResult_Normalised
-
- /* Normalize by shifting left one bit */
- shll $1,FPU_accum_0
- rcll $1,FPU_accum_1
- rcll $1,%ebx
- rcll $1,%ecx
- decl EXP(%edi)
-
-LResult_Normalised:
- movl FPU_accum_0,%eax
- movl FPU_accum_1,%edx
- orl %eax,%eax
- jz L_extent_zero
-
- orl $1,%edx
-
-L_extent_zero:
- movl %ecx,%eax
- jmp fpu_reg_round
-
-
-#ifdef PARANOID
-L_bugged:
- pushl EX_INTERNAL|0x205
- call EXCEPTION
- pop %ebx
- jmp L_exit
-
-L_exit:
- popl %ebx
- popl %edi
- popl %esi
- leave
- ret
-#endif PARANOID
-
diff --git a/drivers/FPU-emu/reg_u_sub.S b/drivers/FPU-emu/reg_u_sub.S
deleted file mode 100644
index fbec17dfb..000000000
--- a/drivers/FPU-emu/reg_u_sub.S
+++ /dev/null
@@ -1,292 +0,0 @@
- .file "reg_u_sub.S"
-/*---------------------------------------------------------------------------+
- | reg_u_sub.S |
- | |
- | Core floating point subtraction routine. |
- | |
- | Copyright (C) 1992,1993 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | Call from C as: |
- | void reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, |
- | int control_w) |
- | |
- +---------------------------------------------------------------------------*/
-
-/*
- | Kernel subtraction routine reg_u_sub(reg *arg1, reg *arg2, reg *answ).
- | Takes two valid reg f.p. numbers (TW_Valid), which are
- | treated as unsigned numbers,
- | and returns their difference as a TW_Valid or TW_Zero f.p.
- | number.
- | The first number (arg1) must be the larger.
- | The returned number is normalized.
- | Basic checks are performed if PARANOID is defined.
- */
-
-#include "exception.h"
-#include "fpu_asm.h"
-#include "control_w.h"
-
-.text
- .align 2,144
-.globl _reg_u_sub
-_reg_u_sub:
- pushl %ebp
- movl %esp,%ebp
- pushl %esi
- pushl %edi
- pushl %ebx
-
- movl PARAM1,%esi /* source 1 */
- movl PARAM2,%edi /* source 2 */
-
-#ifdef DENORM_OPERAND
- cmpl EXP_UNDER,EXP(%esi)
- jg xOp1_not_denorm
-
- call _denormal_operand
- orl %eax,%eax
- jnz fpu_Arith_exit
-
-xOp1_not_denorm:
- cmpl EXP_UNDER,EXP(%edi)
- jg xOp2_not_denorm
-
- call _denormal_operand
- orl %eax,%eax
- jnz fpu_Arith_exit
-
-xOp2_not_denorm:
-#endif DENORM_OPERAND
-
- movl EXP(%esi),%ecx
- subl EXP(%edi),%ecx /* exp1 - exp2 */
-
-#ifdef PARANOID
- /* source 2 is always smaller than source 1 */
- js L_bugged_1
-
- testl $0x80000000,SIGH(%edi) /* The args are assumed to be be normalized */
- je L_bugged_2
-
- testl $0x80000000,SIGH(%esi)
- je L_bugged_2
-#endif PARANOID
-
-/*--------------------------------------+
- | Form a register holding the |
- | smaller number |
- +--------------------------------------*/
- movl SIGH(%edi),%eax /* register ms word */
- movl SIGL(%edi),%ebx /* register ls word */
-
- movl PARAM3,%edi /* destination */
- movl EXP(%esi),%edx
- movl %edx,EXP(%edi) /* Copy exponent to destination */
-/* movb SIGN(%esi),%dl
- movb %dl,SIGN(%edi) */ /* Copy the sign from the first arg */
-
- xorl %edx,%edx /* register extension */
-
-/*--------------------------------------+
- | Shift the temporary register |
- | right the required number of |
- | places. |
- +--------------------------------------*/
-L_shift_r:
- cmpl $32,%ecx /* shrd only works for 0..31 bits */
- jnc L_more_than_31
-
-/* less than 32 bits */
- shrd %cl,%ebx,%edx
- shrd %cl,%eax,%ebx
- shr %cl,%eax
- jmp L_shift_done
-
-L_more_than_31:
- cmpl $64,%ecx
- jnc L_more_than_63
-
- subb $32,%cl
- jz L_exactly_32
-
- shrd %cl,%eax,%edx
- shr %cl,%eax
- orl %ebx,%ebx
- jz L_more_31_no_low /* none of the lowest bits is set */
-
- orl $1,%edx /* record the fact in the extension */
-
-L_more_31_no_low:
- movl %eax,%ebx
- xorl %eax,%eax
- jmp L_shift_done
-
-L_exactly_32:
- movl %ebx,%edx
- movl %eax,%ebx
- xorl %eax,%eax
- jmp L_shift_done
-
-L_more_than_63:
- cmpw $65,%cx
- jnc L_more_than_64
-
- /* Shift right by 64 bits */
- movl %eax,%edx
- orl %ebx,%ebx
- jz L_more_63_no_low
-
- orl $1,%edx
- jmp L_more_63_no_low
-
-L_more_than_64:
- jne L_more_than_65
-
- /* Shift right by 65 bits */
- /* Carry is clear if we get here */
- movl %eax,%edx
- rcrl %edx
- jnc L_shift_65_nc
-
- orl $1,%edx
- jmp L_more_63_no_low
-
-L_shift_65_nc:
- orl %ebx,%ebx
- jz L_more_63_no_low
-
- orl $1,%edx
- jmp L_more_63_no_low
-
-L_more_than_65:
- movl $1,%edx /* The shifted nr always at least one '1' */
-
-L_more_63_no_low:
- xorl %ebx,%ebx
- xorl %eax,%eax
-
-L_shift_done:
-L_subtr:
-/*------------------------------+
- | Do the subtraction |
- +------------------------------*/
- xorl %ecx,%ecx
- subl %edx,%ecx
- movl %ecx,%edx
- movl SIGL(%esi),%ecx
- sbbl %ebx,%ecx
- movl %ecx,%ebx
- movl SIGH(%esi),%ecx
- sbbl %eax,%ecx
- movl %ecx,%eax
-
-#ifdef PARANOID
- /* We can never get a borrow */
- jc L_bugged
-#endif PARANOID
-
-/*--------------------------------------+
- | Normalize the result |
- +--------------------------------------*/
- testl $0x80000000,%eax
- jnz L_round /* no shifting needed */
-
- orl %eax,%eax
- jnz L_shift_1 /* shift left 1 - 31 bits */
-
- orl %ebx,%ebx
- jnz L_shift_32 /* shift left 32 - 63 bits */
-
-/*
- * A rare case, the only one which is non-zero if we got here
- * is: 1000000 .... 0000
- * -0111111 .... 1111 1
- * --------------------
- * 0000000 .... 0000 1
- */
-
- cmpl $0x80000000,%edx
- jnz L_must_be_zero
-
- /* Shift left 64 bits */
- subl $64,EXP(%edi)
- xchg %edx,%eax
- jmp fpu_reg_round
-
-L_must_be_zero:
-#ifdef PARANOID
- orl %edx,%edx
- jnz L_bugged_3
-#endif PARANOID
-
- /* The result is zero */
- movb TW_Zero,TAG(%edi)
- movl $0,EXP(%edi) /* exponent */
- movl $0,SIGL(%edi)
- movl $0,SIGH(%edi)
- jmp L_exit /* %eax contains zero */
-
-L_shift_32:
- movl %ebx,%eax
- movl %edx,%ebx
- movl $0,%edx
- subl $32,EXP(%edi) /* Can get underflow here */
-
-/* We need to shift left by 1 - 31 bits */
-L_shift_1:
- bsrl %eax,%ecx /* get the required shift in %ecx */
- subl $31,%ecx
- negl %ecx
- shld %cl,%ebx,%eax
- shld %cl,%edx,%ebx
- shl %cl,%edx
- subl %ecx,EXP(%edi) /* Can get underflow here */
-
-L_round:
- jmp fpu_reg_round /* Round the result */
-
-
-#ifdef PARANOID
-L_bugged_1:
- pushl EX_INTERNAL|0x206
- call EXCEPTION
- pop %ebx
- jmp L_error_exit
-
-L_bugged_2:
- pushl EX_INTERNAL|0x209
- call EXCEPTION
- pop %ebx
- jmp L_error_exit
-
-L_bugged_3:
- pushl EX_INTERNAL|0x210
- call EXCEPTION
- pop %ebx
- jmp L_error_exit
-
-L_bugged_4:
- pushl EX_INTERNAL|0x211
- call EXCEPTION
- pop %ebx
- jmp L_error_exit
-
-L_bugged:
- pushl EX_INTERNAL|0x212
- call EXCEPTION
- pop %ebx
- jmp L_error_exit
-#endif PARANOID
-
-
-L_error_exit:
- movl $1,%eax
-L_exit:
- popl %ebx
- popl %edi
- popl %esi
- leave
- ret
diff --git a/drivers/FPU-emu/round_Xsig.S b/drivers/FPU-emu/round_Xsig.S
deleted file mode 100644
index 163755878..000000000
--- a/drivers/FPU-emu/round_Xsig.S
+++ /dev/null
@@ -1,148 +0,0 @@
-/*---------------------------------------------------------------------------+
- | round_Xsig.S |
- | |
- | Copyright (C) 1992,1993,1994 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | Normalize and round a 12 byte quantity. |
- | Call from C as: |
- | int round_Xsig(Xsig *n) |
- | |
- | Normalize a 12 byte quantity. |
- | Call from C as: |
- | int norm_Xsig(Xsig *n) |
- | |
- | Each function returns the size of the shift (nr of bits). |
- | |
- +---------------------------------------------------------------------------*/
- .file "round_Xsig.S"
-
-#include "fpu_asm.h"
-
-
-.text
-
- .align 2,144
-.globl _round_Xsig
-
-_round_Xsig:
- pushl %ebp
- movl %esp,%ebp
- pushl %ebx /* Reserve some space */
- pushl %ebx
- pushl %esi
-
- movl PARAM1,%esi
-
- movl 8(%esi),%edx
- movl 4(%esi),%ebx
- movl (%esi),%eax
-
- movl $0,-4(%ebp)
-
- orl %edx,%edx /* ms bits */
- js L_round /* Already normalized */
- jnz L_shift_1 /* Shift left 1 - 31 bits */
-
- movl %ebx,%edx
- movl %eax,%ebx
- xorl %eax,%eax
- movl $-32,-4(%ebp)
-
-/* We need to shift left by 1 - 31 bits */
-L_shift_1:
- bsrl %edx,%ecx /* get the required shift in %ecx */
- subl $31,%ecx
- negl %ecx
- subl %ecx,-4(%ebp)
- shld %cl,%ebx,%edx
- shld %cl,%eax,%ebx
- shl %cl,%eax
-
-L_round:
- testl $0x80000000,%eax
- jz L_exit
-
- addl $1,%ebx
- adcl $0,%edx
- jnz L_exit
-
- movl $0x80000000,%edx
- incl -4(%ebp)
-
-L_exit:
- movl %edx,8(%esi)
- movl %ebx,4(%esi)
- movl %eax,(%esi)
-
- movl -4(%ebp),%eax
-
- popl %esi
- popl %ebx
- leave
- ret
-
-
-
-
- .align 2,144
-.globl _norm_Xsig
-
-_norm_Xsig:
- pushl %ebp
- movl %esp,%ebp
- pushl %ebx /* Reserve some space */
- pushl %ebx
- pushl %esi
-
- movl PARAM1,%esi
-
- movl 8(%esi),%edx
- movl 4(%esi),%ebx
- movl (%esi),%eax
-
- movl $0,-4(%ebp)
-
- orl %edx,%edx /* ms bits */
- js L_n_exit /* Already normalized */
- jnz L_n_shift_1 /* Shift left 1 - 31 bits */
-
- movl %ebx,%edx
- movl %eax,%ebx
- xorl %eax,%eax
- movl $-32,-4(%ebp)
-
- orl %edx,%edx /* ms bits */
- js L_n_exit /* Normalized now */
- jnz L_n_shift_1 /* Shift left 1 - 31 bits */
-
- movl %ebx,%edx
- movl %eax,%ebx
- xorl %eax,%eax
- addl $-32,-4(%ebp)
- jmp L_n_exit /* Might not be normalized,
- but shift no more. */
-
-/* We need to shift left by 1 - 31 bits */
-L_n_shift_1:
- bsrl %edx,%ecx /* get the required shift in %ecx */
- subl $31,%ecx
- negl %ecx
- subl %ecx,-4(%ebp)
- shld %cl,%ebx,%edx
- shld %cl,%eax,%ebx
- shl %cl,%eax
-
-L_n_exit:
- movl %edx,8(%esi)
- movl %ebx,4(%esi)
- movl %eax,(%esi)
-
- movl -4(%ebp),%eax
-
- popl %esi
- popl %ebx
- leave
- ret
-
diff --git a/drivers/FPU-emu/shr_Xsig.S b/drivers/FPU-emu/shr_Xsig.S
deleted file mode 100644
index d6724a204..000000000
--- a/drivers/FPU-emu/shr_Xsig.S
+++ /dev/null
@@ -1,90 +0,0 @@
- .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 --git a/drivers/FPU-emu/status_w.h b/drivers/FPU-emu/status_w.h
deleted file mode 100644
index 96607d0e1..000000000
--- a/drivers/FPU-emu/status_w.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*---------------------------------------------------------------------------+
- | status_w.h |
- | |
- | Copyright (C) 1992,1993 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- +---------------------------------------------------------------------------*/
-
-#ifndef _STATUS_H_
-#define _STATUS_H_
-
-#include "fpu_emu.h" /* for definition of PECULIAR_486 */
-
-#ifdef __ASSEMBLER__
-#define Const__(x) $##x
-#else
-#define Const__(x) x
-#endif
-
-#define SW_Backward Const__(0x8000) /* backward compatibility */
-#define SW_C3 Const__(0x4000) /* condition bit 3 */
-#define SW_Top Const__(0x3800) /* top of stack */
-#define SW_Top_Shift Const__(11) /* shift for top of stack bits */
-#define SW_C2 Const__(0x0400) /* condition bit 2 */
-#define SW_C1 Const__(0x0200) /* condition bit 1 */
-#define SW_C0 Const__(0x0100) /* condition bit 0 */
-#define SW_Summary Const__(0x0080) /* exception summary */
-#define SW_Stack_Fault Const__(0x0040) /* stack fault */
-#define SW_Precision Const__(0x0020) /* loss of precision */
-#define SW_Underflow Const__(0x0010) /* underflow */
-#define SW_Overflow Const__(0x0008) /* overflow */
-#define SW_Zero_Div Const__(0x0004) /* divide by zero */
-#define SW_Denorm_Op Const__(0x0002) /* denormalized operand */
-#define SW_Invalid Const__(0x0001) /* invalid operation */
-
-#define SW_Exc_Mask Const__(0x27f) /* Status word exception bit mask */
-
-#ifndef __ASSEMBLER__
-
-#define COMP_A_gt_B 1
-#define COMP_A_eq_B 2
-#define COMP_A_lt_B 3
-#define COMP_No_Comp 4
-#define COMP_Denormal 0x20
-#define COMP_NaN 0x40
-#define COMP_SNaN 0x80
-
-#define status_word() \
- ((partial_status & ~SW_Top & 0xffff) | ((top << SW_Top_Shift) & SW_Top))
-#define setcc(cc) ({ \
- partial_status &= ~(SW_C0|SW_C1|SW_C2|SW_C3); \
- partial_status |= (cc) & (SW_C0|SW_C1|SW_C2|SW_C3); })
-
-#ifdef PECULIAR_486
- /* Default, this conveys no information, but an 80486 does it. */
- /* Clear the SW_C1 bit, "other bits undefined". */
-# define clear_C1() { partial_status &= ~SW_C1; }
-# else
-# define clear_C1()
-#endif PECULIAR_486
-
-#endif __ASSEMBLER__
-
-#endif _STATUS_H_
diff --git a/drivers/FPU-emu/version.h b/drivers/FPU-emu/version.h
deleted file mode 100644
index 4c75a4792..000000000
--- a/drivers/FPU-emu/version.h
+++ /dev/null
@@ -1,12 +0,0 @@
-/*---------------------------------------------------------------------------+
- | version.h |
- | |
- | |
- | Copyright (C) 1992,1993,1994 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
-
-#define FPU_VERSION "wm-FPU-emu version 1.20"
diff --git a/drivers/FPU-emu/wm_shrx.S b/drivers/FPU-emu/wm_shrx.S
deleted file mode 100644
index bef0e1963..000000000
--- a/drivers/FPU-emu/wm_shrx.S
+++ /dev/null
@@ -1,208 +0,0 @@
- .file "wm_shrx.S"
-/*---------------------------------------------------------------------------+
- | wm_shrx.S |
- | |
- | 64 bit right shift functions |
- | |
- | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | Call from C as: |
- | unsigned shrx(void *arg1, unsigned arg2) |
- | and |
- | unsigned shrxs(void *arg1, unsigned arg2) |
- | |
- +---------------------------------------------------------------------------*/
-
-#include "fpu_asm.h"
-
-.text
- .align 2,144
-
-/*---------------------------------------------------------------------------+
- | unsigned shrx(void *arg1, unsigned arg2) |
- | |
- | Extended shift right function. |
- | Fastest for small shifts. |
- | Shifts the 64 bit quantity pointed to by the first arg (arg1) |
- | right by the number of bits specified by the second arg (arg2). |
- | Forms a 96 bit quantity from the 64 bit arg and eax: |
- | [ 64 bit arg ][ eax ] |
- | shift right ---------> |
- | The eax register is initialized to 0 before the shifting. |
- | Results returned in the 64 bit arg and eax. |
- +---------------------------------------------------------------------------*/
-
- .globl _shrx
-
-_shrx:
- 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),%ebx /* lsl */
- movl 4(%esi),%edx /* msl */
- xorl %eax,%eax /* extension */
- shrd %cl,%ebx,%eax
- shrd %cl,%edx,%ebx
- shr %cl,%edx
- movl %ebx,(%esi)
- movl %edx,4(%esi)
- popl %ebx
- popl %esi
- leave
- ret
-
-L_more_than_31:
- cmpl $64,%ecx
- jnc L_more_than_63
-
- subb $32,%cl
- movl (%esi),%eax /* lsl */
- movl 4(%esi),%edx /* msl */
- shrd %cl,%edx,%eax
- shr %cl,%edx
- movl %edx,(%esi)
- movl $0,4(%esi)
- popl %esi
- leave
- ret
-
-L_more_than_63:
- cmpl $96,%ecx
- jnc L_more_than_95
-
- subb $64,%cl
- movl 4(%esi),%eax /* msl */
- shr %cl,%eax
- xorl %edx,%edx
- movl %edx,(%esi)
- movl %edx,4(%esi)
- popl %esi
- leave
- ret
-
-L_more_than_95:
- xorl %eax,%eax
- movl %eax,(%esi)
- movl %eax,4(%esi)
- popl %esi
- leave
- ret
-
-
-/*---------------------------------------------------------------------------+
- | unsigned shrxs(void *arg1, unsigned arg2) |
- | |
- | Extended shift right function (optimized for small floating point |
- | integers). |
- | Shifts the 64 bit quantity pointed to by the first arg (arg1) |
- | right by the number of bits specified by the second arg (arg2). |
- | Forms a 96 bit quantity from the 64 bit arg and eax: |
- | [ 64 bit arg ][ eax ] |
- | shift right ---------> |
- | The eax register is initialized to 0 before the shifting. |
- | The lower 8 bits of eax are lost and replaced by a flag which is |
- | set (to 0x01) if any bit, apart from the first one, is set in the |
- | part which has been shifted out of the arg. |
- | Results returned in the 64 bit arg and eax. |
- +---------------------------------------------------------------------------*/
- .globl _shrxs
-_shrxs:
- push %ebp
- movl %esp,%ebp
- pushl %esi
- pushl %ebx
- movl PARAM2,%ecx
- movl PARAM1,%esi
- cmpl $64,%ecx /* shrd only works for 0..31 bits */
- jnc Ls_more_than_63
-
- cmpl $32,%ecx /* shrd only works for 0..31 bits */
- jc Ls_less_than_32
-
-/* We got here without jumps by assuming that the most common requirement
- is for small integers */
-/* Shift by [32..63] bits */
- subb $32,%cl
- movl (%esi),%eax /* lsl */
- movl 4(%esi),%edx /* msl */
- xorl %ebx,%ebx
- shrd %cl,%eax,%ebx
- shrd %cl,%edx,%eax
- shr %cl,%edx
- orl %ebx,%ebx /* test these 32 bits */
- setne %bl
- test $0x7fffffff,%eax /* and 31 bits here */
- setne %bh
- orw %bx,%bx /* Any of the 63 bit set ? */
- setne %al
- movl %edx,(%esi)
- movl $0,4(%esi)
- popl %ebx
- popl %esi
- leave
- ret
-
-/* Shift by [0..31] bits */
-Ls_less_than_32:
- movl (%esi),%ebx /* lsl */
- movl 4(%esi),%edx /* msl */
- xorl %eax,%eax /* extension */
- shrd %cl,%ebx,%eax
- shrd %cl,%edx,%ebx
- shr %cl,%edx
- test $0x7fffffff,%eax /* only need to look at eax here */
- setne %al
- movl %ebx,(%esi)
- movl %edx,4(%esi)
- popl %ebx
- popl %esi
- leave
- ret
-
-/* Shift by [64..95] bits */
-Ls_more_than_63:
- cmpl $96,%ecx
- jnc Ls_more_than_95
-
- subb $64,%cl
- movl (%esi),%ebx /* lsl */
- movl 4(%esi),%eax /* msl */
- xorl %edx,%edx /* extension */
- shrd %cl,%ebx,%edx
- shrd %cl,%eax,%ebx
- shr %cl,%eax
- orl %ebx,%edx
- setne %bl
- test $0x7fffffff,%eax /* only need to look at eax here */
- setne %bh
- orw %bx,%bx
- setne %al
- xorl %edx,%edx
- movl %edx,(%esi) /* set to zero */
- movl %edx,4(%esi) /* set to zero */
- popl %ebx
- popl %esi
- leave
- ret
-
-Ls_more_than_95:
-/* Shift by [96..inf) bits */
- xorl %eax,%eax
- movl (%esi),%ebx
- orl 4(%esi),%ebx
- setne %al
- xorl %ebx,%ebx
- movl %ebx,(%esi)
- movl %ebx,4(%esi)
- popl %ebx
- popl %esi
- leave
- ret
diff --git a/drivers/FPU-emu/wm_sqrt.S b/drivers/FPU-emu/wm_sqrt.S
deleted file mode 100644
index 4e028cb80..000000000
--- a/drivers/FPU-emu/wm_sqrt.S
+++ /dev/null
@@ -1,474 +0,0 @@
- .file "wm_sqrt.S"
-/*---------------------------------------------------------------------------+
- | wm_sqrt.S |
- | |
- | Fixed point arithmetic square root evaluation. |
- | |
- | Copyright (C) 1992,1993 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@vaxc.cc.monash.edu.au |
- | |
- | Call from C as: |
- | void wm_sqrt(FPU_REG *n, unsigned int control_word) |
- | |
- +---------------------------------------------------------------------------*/
-
-/*---------------------------------------------------------------------------+
- | wm_sqrt(FPU_REG *n, unsigned int control_word) |
- | returns the square root of n in n. |
- | |
- | Use Newton's method to compute the square root of a number, which must |
- | be in the range [1.0 .. 4.0), to 64 bits accuracy. |
- | Does not check the sign or tag of the argument. |
- | Sets the exponent, but not the sign or tag of the result. |
- | |
- | The guess is kept in %esi:%edi |
- +---------------------------------------------------------------------------*/
-
-#include "exception.h"
-#include "fpu_asm.h"
-
-
-#ifndef NON_REENTRANT_FPU
-/* Local storage on the stack: */
-#define FPU_accum_3 -4(%ebp) /* ms word */
-#define FPU_accum_2 -8(%ebp)
-#define FPU_accum_1 -12(%ebp)
-#define FPU_accum_0 -16(%ebp)
-
-/*
- * The de-normalised argument:
- * sq_2 sq_1 sq_0
- * b b b b b b b ... b b b b b b .... b b b b 0 0 0 ... 0
- * ^ binary point here
- */
-#define FPU_fsqrt_arg_2 -20(%ebp) /* ms word */
-#define FPU_fsqrt_arg_1 -24(%ebp)
-#define FPU_fsqrt_arg_0 -28(%ebp) /* ls word, at most the ms bit is set */
-
-#else
-/* Local storage in a static area: */
-.data
- .align 4,0
-FPU_accum_3:
- .long 0 /* ms word */
-FPU_accum_2:
- .long 0
-FPU_accum_1:
- .long 0
-FPU_accum_0:
- .long 0
-
-/* The de-normalised argument:
- sq_2 sq_1 sq_0
- b b b b b b b ... b b b b b b .... b b b b 0 0 0 ... 0
- ^ binary point here
- */
-FPU_fsqrt_arg_2:
- .long 0 /* ms word */
-FPU_fsqrt_arg_1:
- .long 0
-FPU_fsqrt_arg_0:
- .long 0 /* ls word, at most the ms bit is set */
-#endif NON_REENTRANT_FPU
-
-
-.text
- .align 2,144
-
-.globl _wm_sqrt
-_wm_sqrt:
- 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
-
- movl SIGH(%esi),%eax
- movl SIGL(%esi),%ecx
- xorl %edx,%edx
-
-/* We use a rough linear estimate for the first guess.. */
-
- cmpl EXP_BIAS,EXP(%esi)
- jnz sqrt_arg_ge_2
-
- shrl $1,%eax /* arg is in the range [1.0 .. 2.0) */
- rcrl $1,%ecx
- rcrl $1,%edx
-
-sqrt_arg_ge_2:
-/* From here on, n is never accessed directly again until it is
- replaced by the answer. */
-
- movl %eax,FPU_fsqrt_arg_2 /* ms word of n */
- movl %ecx,FPU_fsqrt_arg_1
- movl %edx,FPU_fsqrt_arg_0
-
-/* Make a linear first estimate */
- shrl $1,%eax
- addl $0x40000000,%eax
- movl $0xaaaaaaaa,%ecx
- mull %ecx
- shll %edx /* max result was 7fff... */
- testl $0x80000000,%edx /* but min was 3fff... */
- jnz sqrt_prelim_no_adjust
-
- movl $0x80000000,%edx /* round up */
-
-sqrt_prelim_no_adjust:
- movl %edx,%esi /* Our first guess */
-
-/* We have now computed (approx) (2 + x) / 3, which forms the basis
- for a few iterations of Newton's method */
-
- movl FPU_fsqrt_arg_2,%ecx /* ms word */
-
-/*
- * From our initial estimate, three iterations are enough to get us
- * to 30 bits or so. This will then allow two iterations at better
- * precision to complete the process.
- */
-
-/* Compute (g + n/g)/2 at each iteration (g is the guess). */
- shrl %ecx /* Doing this first will prevent a divide */
- /* overflow later. */
-
- movl %ecx,%edx /* msw of the arg / 2 */
- divl %esi /* current estimate */
- shrl %esi /* divide by 2 */
- addl %eax,%esi /* the new estimate */
-
- movl %ecx,%edx
- divl %esi
- shrl %esi
- addl %eax,%esi
-
- movl %ecx,%edx
- divl %esi
- shrl %esi
- addl %eax,%esi
-
-/*
- * Now that an estimate accurate to about 30 bits has been obtained (in %esi),
- * we improve it to 60 bits or so.
- *
- * The strategy from now on is to compute new estimates from
- * guess := guess + (n - guess^2) / (2 * guess)
- */
-
-/* First, find the square of the guess */
- movl %esi,%eax
- mull %esi
-/* guess^2 now in %edx:%eax */
-
- movl FPU_fsqrt_arg_1,%ecx
- subl %ecx,%eax
- movl FPU_fsqrt_arg_2,%ecx /* ms word of normalized n */
- sbbl %ecx,%edx
- jnc sqrt_stage_2_positive
-
-/* Subtraction gives a negative result,
- negate the result before division. */
- notl %edx
- notl %eax
- addl $1,%eax
- adcl $0,%edx
-
- divl %esi
- movl %eax,%ecx
-
- movl %edx,%eax
- divl %esi
- jmp sqrt_stage_2_finish
-
-sqrt_stage_2_positive:
- divl %esi
- movl %eax,%ecx
-
- movl %edx,%eax
- divl %esi
-
- notl %ecx
- notl %eax
- addl $1,%eax
- adcl $0,%ecx
-
-sqrt_stage_2_finish:
- sarl $1,%ecx /* divide by 2 */
- rcrl $1,%eax
-
- /* Form the new estimate in %esi:%edi */
- movl %eax,%edi
- addl %ecx,%esi
-
- jnz sqrt_stage_2_done /* result should be [1..2) */
-
-#ifdef PARANOID
-/* It should be possible to get here only if the arg is ffff....ffff */
- cmp $0xffffffff,FPU_fsqrt_arg_1
- jnz sqrt_stage_2_error
-#endif PARANOID
-
-/* The best rounded result. */
- xorl %eax,%eax
- decl %eax
- movl %eax,%edi
- movl %eax,%esi
- movl $0x7fffffff,%eax
- jmp sqrt_round_result
-
-#ifdef PARANOID
-sqrt_stage_2_error:
- pushl EX_INTERNAL|0x213
- call EXCEPTION
-#endif PARANOID
-
-sqrt_stage_2_done:
-
-/* Now the square root has been computed to better than 60 bits. */
-
-/* Find the square of the guess. */
- movl %edi,%eax /* ls word of guess */
- mull %edi
- movl %edx,FPU_accum_1
-
- movl %esi,%eax
- mull %esi
- movl %edx,FPU_accum_3
- movl %eax,FPU_accum_2
-
- movl %edi,%eax
- mull %esi
- addl %eax,FPU_accum_1
- adcl %edx,FPU_accum_2
- adcl $0,FPU_accum_3
-
-/* movl %esi,%eax */
-/* mull %edi */
- addl %eax,FPU_accum_1
- adcl %edx,FPU_accum_2
- adcl $0,FPU_accum_3
-
-/* guess^2 now in FPU_accum_3:FPU_accum_2:FPU_accum_1 */
-
- movl FPU_fsqrt_arg_0,%eax /* get normalized n */
- subl %eax,FPU_accum_1
- movl FPU_fsqrt_arg_1,%eax
- sbbl %eax,FPU_accum_2
- movl FPU_fsqrt_arg_2,%eax /* ms word of normalized n */
- sbbl %eax,FPU_accum_3
- jnc sqrt_stage_3_positive
-
-/* Subtraction gives a negative result,
- negate the result before division */
- notl FPU_accum_1
- notl FPU_accum_2
- notl FPU_accum_3
- addl $1,FPU_accum_1
- adcl $0,FPU_accum_2
-
-#ifdef PARANOID
- adcl $0,FPU_accum_3 /* This must be zero */
- jz sqrt_stage_3_no_error
-
-sqrt_stage_3_error:
- pushl EX_INTERNAL|0x207
- call EXCEPTION
-
-sqrt_stage_3_no_error:
-#endif PARANOID
-
- movl FPU_accum_2,%edx
- movl FPU_accum_1,%eax
- divl %esi
- movl %eax,%ecx
-
- movl %edx,%eax
- divl %esi
-
- sarl $1,%ecx /* divide by 2 */
- rcrl $1,%eax
-
- /* prepare to round the result */
-
- addl %ecx,%edi
- adcl $0,%esi
-
- jmp sqrt_stage_3_finished
-
-sqrt_stage_3_positive:
- movl FPU_accum_2,%edx
- movl FPU_accum_1,%eax
- divl %esi
- movl %eax,%ecx
-
- movl %edx,%eax
- divl %esi
-
- sarl $1,%ecx /* divide by 2 */
- rcrl $1,%eax
-
- /* prepare to round the result */
-
- notl %eax /* Negate the correction term */
- notl %ecx
- addl $1,%eax
- adcl $0,%ecx /* carry here ==> correction == 0 */
- adcl $0xffffffff,%esi
-
- addl %ecx,%edi
- adcl $0,%esi
-
-sqrt_stage_3_finished:
-
-/*
- * The result in %esi:%edi:%esi should be good to about 90 bits here,
- * and the rounding information here does not have sufficient accuracy
- * in a few rare cases.
- */
- cmpl $0xffffffe0,%eax
- ja sqrt_near_exact_x
-
- cmpl $0x00000020,%eax
- jb sqrt_near_exact
-
- cmpl $0x7fffffe0,%eax
- jb sqrt_round_result
-
- cmpl $0x80000020,%eax
- jb sqrt_get_more_precision
-
-sqrt_round_result:
-/* Set up for rounding operations */
- movl %eax,%edx
- movl %esi,%eax
- movl %edi,%ebx
- movl PARAM1,%edi
- movl EXP_BIAS,EXP(%edi) /* Result is in [1.0 .. 2.0) */
- movl PARAM2,%ecx
- jmp fpu_reg_round_sqrt
-
-
-sqrt_near_exact_x:
-/* First, the estimate must be rounded up. */
- addl $1,%edi
- adcl $0,%esi
-
-sqrt_near_exact:
-/*
- * This is an easy case because x^1/2 is monotonic.
- * We need just find the square of our estimate, compare it
- * with the argument, and deduce whether our estimate is
- * above, below, or exact. We use the fact that the estimate
- * is known to be accurate to about 90 bits.
- */
- movl %edi,%eax /* ls word of guess */
- mull %edi
- movl %edx,%ebx /* 2nd ls word of square */
- movl %eax,%ecx /* ls word of square */
-
- movl %edi,%eax
- mull %esi
- addl %eax,%ebx
- addl %eax,%ebx
-
-#ifdef PARANOID
- cmp $0xffffffb0,%ebx
- jb sqrt_near_exact_ok
-
- cmp $0x00000050,%ebx
- ja sqrt_near_exact_ok
-
- pushl EX_INTERNAL|0x214
- call EXCEPTION
-
-sqrt_near_exact_ok:
-#endif PARANOID
-
- or %ebx,%ebx
- js sqrt_near_exact_small
-
- jnz sqrt_near_exact_large
-
- or %ebx,%edx
- jnz sqrt_near_exact_large
-
-/* Our estimate is exactly the right answer */
- xorl %eax,%eax
- jmp sqrt_round_result
-
-sqrt_near_exact_small:
-/* Our estimate is too small */
- movl $0x000000ff,%eax
- jmp sqrt_round_result
-
-sqrt_near_exact_large:
-/* Our estimate is too large, we need to decrement it */
- subl $1,%edi
- sbbl $0,%esi
- movl $0xffffff00,%eax
- jmp sqrt_round_result
-
-
-sqrt_get_more_precision:
-/* This case is almost the same as the above, except we start
- with an extra bit of precision in the estimate. */
- stc /* The extra bit. */
- rcll $1,%edi /* Shift the estimate left one bit */
- rcll $1,%esi
-
- movl %edi,%eax /* ls word of guess */
- mull %edi
- movl %edx,%ebx /* 2nd ls word of square */
- movl %eax,%ecx /* ls word of square */
-
- movl %edi,%eax
- mull %esi
- addl %eax,%ebx
- addl %eax,%ebx
-
-/* Put our estimate back to its original value */
- stc /* The ms bit. */
- rcrl $1,%esi /* Shift the estimate left one bit */
- rcrl $1,%edi
-
-#ifdef PARANOID
- cmp $0xffffff60,%ebx
- jb sqrt_more_prec_ok
-
- cmp $0x000000a0,%ebx
- ja sqrt_more_prec_ok
-
- pushl EX_INTERNAL|0x215
- call EXCEPTION
-
-sqrt_more_prec_ok:
-#endif PARANOID
-
- or %ebx,%ebx
- js sqrt_more_prec_small
-
- jnz sqrt_more_prec_large
-
- or %ebx,%ecx
- jnz sqrt_more_prec_large
-
-/* Our estimate is exactly the right answer */
- movl $0x80000000,%eax
- jmp sqrt_round_result
-
-sqrt_more_prec_small:
-/* Our estimate is too small */
- movl $0x800000ff,%eax
- jmp sqrt_round_result
-
-sqrt_more_prec_large:
-/* Our estimate is too large */
- movl $0x7fffff00,%eax
- jmp sqrt_round_result
diff --git a/drivers/Makefile b/drivers/Makefile
index 80d4488e4..3781c0d5d 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -16,10 +16,10 @@
.c.o:
$(CC) $(CFLAGS) -c $<
-SUBDIRS = block char net
+SUBDIRS = block char net #streams
-ifdef CONFIG_MATH_EMULATION
-SUBDIRS := $(SUBDIRS) FPU-emu
+ifdef CONFIG_PCI
+SUBDIRS := $(SUBDIRS) pci
endif
ifdef CONFIG_SCSI
@@ -35,6 +35,9 @@ all: driversubdirs
driversubdirs: dummy
set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i; done
+modules: dummy
+ set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i modules; done
+
dep:
set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i dep; done
diff --git a/drivers/block/MAKEDEV.ide1 b/drivers/block/MAKEDEV.ide1
new file mode 100644
index 000000000..506115ca5
--- /dev/null
+++ b/drivers/block/MAKEDEV.ide1
@@ -0,0 +1,24 @@
+#!/bin/sh
+makedev () {
+ rm -f /dev/$1
+ echo mknod /dev/$1 $2 $3 $4 &&
+ mknod /dev/$1 $2 $3 $4 &&
+ chown $5 /dev/$1 &&
+ chmod $6 /dev/$1
+}
+
+# Create /dev/hdc*
+makedev hdc b 22 0 root:$disk 660
+for part in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+do
+ makedev hdc$part b 22 `expr 0 + $part` root:$disk 660
+done
+echo " "
+
+# Create /dev/hdd*
+makedev hdd b 22 64 root:$disk 660
+for part in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+do
+ makedev hdd$part b 22 `expr 64 + $part` root:$disk 660
+done
+
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index ea77020f6..ebf02d8a5 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -21,8 +21,14 @@
# In the future, some of these should be built conditionally.
#
-OBJS := ll_rw_blk.o floppy.o ramdisk.o genhd.o
-SRCS := ll_rw_blk.c floppy.c ramdisk.c genhd.c
+OBJS := ll_rw_blk.o ramdisk.o genhd.o
+SRCS := ll_rw_blk.c ramdisk.c genhd.c
+BLOCK_MODULE_OBJS =
+
+ifdef CONFIG_BLK_DEV_FD
+OBJS := $(OBJS) floppy.o
+SRCS := $(SRCS) floppy.c
+endif
ifdef CONFIG_CDU31A
OBJS := $(OBJS) cdu31a.o
@@ -34,9 +40,18 @@ OBJS := $(OBJS) mcd.o
SRCS := $(SRCS) mcd.c
endif
+ifdef CONFIG_AZTCD
+OBJS := $(OBJS) aztcd.o
+SRCS := $(SRCS) aztcd.c
+else
+BLOCK_MODULE_OBJS := $(BLOCK_MODULE_OBJS) aztcd.o
+endif
+
ifdef CONFIG_SBPCD
OBJS := $(OBJS) sbpcd.o
SRCS := $(SRCS) sbpcd.c
+else
+BLOCK_MODULE_OBJS := $(BLOCK_MODULE_OBJS) sbpcd.o
endif #CONFIG_SBPCD
ifdef CONFIG_SBPCD2
@@ -59,11 +74,23 @@ OBJS := $(OBJS) hd.o
SRCS := $(SRCS) hd.c
endif
+ifdef CONFIG_BLK_DEV_IDE
+OBJS := ide.o $(OBJS)
+SRCS := ide.c $(SRCS)
+endif
+
ifdef CONFIG_BLK_DEV_XD
OBJS := $(OBJS) xd.o
SRCS := $(SRCS) xd.c
endif
+ifdef CONFIG_CDU535
+OBJS := $(OBJS) sonycd535.o
+SRCS := $(SRCS) sonycd535.c
+else
+BLOCK_MODULE_OBJS := $(BLOCK_MODULE_OBJS) sonycd535.o
+endif
+
all: block.a
block.a: $(OBJS)
@@ -73,6 +100,13 @@ block.a: $(OBJS)
dep:
$(CPP) -M $(SRCS) > .depend
+ifdef BLOCK_MODULE_OBJS
+ $(CPP) -M -DMODULE $(BLOCK_MODULE_OBJS:.o=.c) >> .depend
+endif
+
+modules: $(BLOCK_MODULE_OBJS)
+ echo $(BLOCK_MODULE_OBJS) > ../../modules/BLOCK_MODULES
+ (cd ../../modules;for i in $(BLOCK_MODULE_OBJS); do ln -sf ../drivers/block/$$i .; done)
dummy:
diff --git a/drivers/block/README.aztcd b/drivers/block/README.aztcd
new file mode 100644
index 000000000..797c11222
--- /dev/null
+++ b/drivers/block/README.aztcd
@@ -0,0 +1,730 @@
+ Readme-File README.aztcd
+ for Aztech CD-ROM CDA268-01A, ORCHID CD-3110, OKANO/WEARNES CDD110
+ CD-ROM Driver
+ Version 1.0 and newer
+ (for other drives see 6.-8.)
+
+NOTE: THIS DRIVER WILL WORK WITH THE CD-ROM DRIVES LISTED, WHICH HAVE
+ A PROPRIETARY INTERFACE (implemented on a sound card or on a
+ ISA-AT-bus card).
+ IT WILL DEFINITELY NOT WORK WITH CD-ROM DRIVES WITH *IDE*-INTERFACE,
+ such as the Aztech CDA269-031SE !!! IF YOU'RE USING A CD-ROM DRIVE
+ WITH IDE-INTERFACE, SOMETIMES ALSO CALLED ATAPI-COMPATIBLE, PLEASE
+ USE THE ide-cd.c DRIVER, WRITTEN BY MARK LORD AND SCOTT SNYDER !!!
+----------------------------------------------------------------------------
+Contents of this file:
+ 1. NOTE
+ 2. INSTALLATION
+ 3. CONFIGURING YOUR KERNEL
+ 4. RECOMPILING YOUR KERNEL
+ 5. KNOWN PROBLEMS, FUTURE DEVELOPMENTS
+ 6. BUG REPORTS
+ 7. OTHER DRIVES
+ 8. IF YOU DON'T SUCCEED ... DEBUGGING
+ 9. TECHNICAL HISTORY OF THE DRIVER
+ 10. ACKNOWLEDGMENTS
+ 11. PROGRAMMING ADD ONS: CDPLAY.C
+ APPENDIX: Source code of cdplay.c
+----------------------------------------------------------------------------
+
+1. NOTE
+This software has been successfully in alpha and beta test for quite a long
+time with AZTECH CDA268-01A, ORCHID CDS-3110 and ORCHID/WEARNES CDD110
+and has proven to be stable with kernel versions 1.0.9 to 1.2.0. But with
+any software there still may be bugs in it. So if you encounter problems,
+you are invited to help us improve this software. Please send me a detailed
+bug report (see chapter BUG REPORTS). You are also invited in helping us to
+increase the number of drives, which are supported.
+
+Please read the README-files carefully and always keep a backup copy of your
+old kernel, in order to reboot if something goes wrong!
+
+
+2. INSTALLATION
+If you received this software as a standalone package AZTECH.CDROM.Vxx.tgz
+(xx=version number) and not included in the standard Linux kernel, read the
+file AZTECH.CDROM.README included in that package to install it. The
+standalone package's home is 'ftp.gwdg.de : pub/linux/cdrom/drivers/aztech'.
+The driver consists of a header file 'aztcd.h', which normally should reside
+in /usr/include/linux and the source code 'aztcd.c', which normally resides in
+/usr/src/linux/drivers/block. It uses /dev/aztcd0, which must be a valid block
+device with major number 29 and reside in directory /dev. To mount a CD-ROM,
+your kernel needs to have the ISO9660-filesystem support included.
+
+
+3. CONFIGURING YOUR KERNEL
+If your kernel is already configured for using the AZTECH driver you will
+see the following message while Linux boots:
+ Aztech CD-ROM Init: DriverVersion=<version number> BaseAddress=<baseaddress>
+ Aztech CD-ROM Init: FirmwareVersion=<firmware version id of your I/O-card>>>
+ Aztech CD-ROM Init: <drive type> detected
+ Aztech CD-ROM Init: End
+If the message looks different and you are sure to have a supported drive,
+it may have a different base address. The Aztech driver does look for the
+CD-ROM drive at the base address specified in aztcd.h at compile time. This
+address can be overwritten by boot parameter aztcd=....You should reboot and
+start Linux with boot parameter aztcd=<base address>, e.g. aztcd=0x320. If
+you do not know the base address, start your PC with DOS and look at the boot
+message of your CD-ROM's DOS driver.
+
+If the message looks correct, as user 'root' you should be able to mount the
+drive by
+ mount -t iso9660 -r /dev/aztcd0 /mnt
+and use it as any other filesystem. (If this does not work, check if
+ /dev/aztcd0 and /mnt do exist and create them, if necessary by doing
+ mknod /dev/aztcd0 b 29 0
+ mkdir /mnt
+
+If you still get a different message while Linux boots or when you get the
+message, that the ISO9660-filesystem is not supported by your kernel, when
+you try to mount the CD-ROM drive, you have to recompile your kernel.
+
+If you do *not* have an Aztech/Orchid/Okano/Wearnes drive and want to bypass
+drive detection during Linux boot up, start with boot parameter aztcd=0.
+
+Joe Nardone has compiled a boot disk with the Aztech driver for installing
+Slackware from CDROM. You can find the disk images at 'sunsite.unc.edu';
+see file 'aztech.gz.README' for instructions on how to use it.
+
+
+4. RECOMPILING YOUR KERNEL
+If your kernel is not yet configured for the AZTECH driver and the ISO9660-
+filesystem, you have to recompile your kernel:
+
+- Edit aztcd.h to set the I/O-address to your I/O-Base address (AZT_BASE_ADDR),
+ the driver does not use interrupts or DMA, so if you are using an AZTECH
+ CD268, an ORCHID CD-3110 or ORCHID/WEARNES CDD110 that's the only item you
+ have to set up.
+ Users of other drives should read chapter OTHER DRIVES of this file.
+ You also can configure that address by LILO boot parameter aztcd=...
+- Build a new kernel, configure it for 'Aztech/Orchid/Okano/Wearnes support'
+ (if you want aztcd to be part of the kernel). Do not configure it for
+ 'Aztech... support', if you want to use aztcd as a run time loadable module.
+ But in any case you must have the ISO9660-filesystem included in your
+ kernel.
+- Activate the new kernel, normally this is done by running lilo (don't for-
+ get to configure it before and to keep a copy of your old kernel in case
+ something goes wrong!).
+- Reboot
+- If you've included aztcd in your kernel, you now should see during boot
+ some messages like
+ Aztech CD-ROM Init: DriverVersion=<version number> BaseAddress=<baseaddress>
+ Aztech CD-ROM Init: FirmwareVersion=<firmware version id of your I/O-card>
+ Aztech CD-ROM Init: <drive type> detected
+ Aztech CD-ROM Init: End
+- If you have not included aztcd in your kernel, but want to load aztcd as a
+ run time loadable module see 4.1.
+- If the message looks correct, as user 'root' you should be able to mount
+ the drive by
+ mount -t iso9660 -r /dev/aztcd0 /mnt
+ and use it as any other filesystem. (If this does not work, check if
+ /dev/aztcd0 and /mnt do exist and create them, if necessary by doing
+ mknod /dev/aztcd0 b 29 0
+ mkdir /mnt
+- If this still does not help, see chapters OTHER DRIVES and DEBUGGING.
+
+4.1 AZTCD AS A RUN-TIME LOADABLE MODULE
+If you do not need aztcd permanently, you can also load and remove the driver
+during runtime via insmod and rmmod. To build aztcd as a loadable module you
+must *not* configure your kernel for AZTECH support. But you need to have
+the ISO9660-filesystem included! So rebuild your kernel, if necessary.
+
+Now edit the base address of your AZTECH interface card in
+/usr/src/linux/include/linux/aztcd.h to the appropriate value. Then change
+to /usr/src/linux and do a
+ make modules
+ make modules_install
+After that you can run-time load the driver via
+ insmod /lib/modules/X.X.X/misc/aztcd.o
+and remove it via rmmod aztcd.
+If you have not configured the correct base address, you can also supply the
+base address when loading the driver via
+ insmod /lib/modules/X.X.X/misc/aztcd.o aztcd=<base address>
+In all commands 'X.X.X' is the current linux kernel version number. For details
+see file README.modules in /usr/src/linux.
+
+
+5. KNOWN PROBLEMS, FUTURE DEVELOPMENTS
+The driver does not support applications such as photo CD, multi session CD
+etc.. I do not plan to include the support for that in the driver, because I
+do not use such applications. If you are interested in that stuff and would
+like to extend the drivers capability on your own, please contact me, I'll
+support you as far as I can.
+
+The drive status recognition does not work correctly in all cases. Changing
+a disk or having the door open, when a drive is already mounted, is detected
+by the Aztech driver itself, but nevertheless causes multiple read attempts
+by the different layers of the ISO9660-filesystem driver, which finally timeout,
+so you have to wait quite a little... But isn't it bad style to change a disk
+in a mounted drive, anyhow ?!
+
+The driver uses busy wait in most cases for the drive handshake (macros
+STEN_LOW and DTEN_LOW). I tested with a 486/DX2 at 66MHz and a Pentium at
+60MHz. Whenever you use a much faster machine you are likely to get timeout
+messages. In that case edit aztcd.h and increase the timeout value AZT_TIMEOUT.
+
+For some 'slow' drive commands I implemented waiting with a timer waitqueue
+(macro STEN_LOW_WAIT). If you get this timeout message, you may also edit
+aztcd.h and increase the timeout value AZT_STATUS_DELAY. The waitqueue has
+shown to be a little critical. If you get kernel panic messages, edit aztcd.c
+and substitute STEN_LOW_WAIT by STEN_LOW. Busy waiting with STEN_LOW is more
+stable, but also causes CPU overhead.
+
+
+6. BUG REPORTS
+Please send detailed bug reports and bug fixes via EMail to
+
+ zimmerma@rz.fht-esslingen.de
+
+Please include a description of your CD-ROM drive type and interface card,
+the exact firmware message during Linux bootup, the version number of the
+AZTECH-CDROM-driver and the Linux kernel version. Also a description of your
+system's other hardware could be of interest, especially microprocessor type,
+clock frequency, other interface cards such as soundcards, ethernet adapter,
+game cards etc..
+
+I will try to collect the reports and make the necessary modifications from
+time to time. I may also come back to you directly with some bug fixes and
+ask you to do further testing and debugging.
+
+Editors of CD-ROMs are invited to send a 'cooperation' copy of their
+CD-ROMs to the volunteers, who provided the CD-ROM support for Linux. My
+snail mail address for such 'stuff' is
+ Prof. Dr. W. Zimmermann
+ Fachhochschule fuer Technik Esslingen
+ Fachbereich NT
+ Flandernstrasse 101
+ D-73732 Esslingen
+ Germany
+
+
+7. OTHER DRIVES
+The following drives ORCHID CDS3110, OKANO CDD110 and WEARNES nearly look
+the same as AZTECH CDA268-01A, especially they seem to use the same command
+codes. So it was quite simple to make the AZTECH driver work with these drives.
+
+Unfortunately I do not have any of these drives available, so I couldn't test
+it myself. But I've got reports, that it works with ORCHID CDS3110 and Game-
+Wave32 sound cards and also with WEARNES CDD110 in some different combinations.
+In some installations, it seems necessary to initialize the drive with the DOS
+driver before (especially if combined with a sound card) and then do a warm
+boot (CTRL-ALT-RESET) or start Linux from DOS, e.g. with 'loadlin'.
+
+If you do not succeed, read chapter DEBUGGING. Thanks in advance!
+
+Sorry for the inconvenience, but it is difficult to develop for hardware,
+which you don't have available for testing. So if you like, please help us.
+
+
+8. DEBUGGING : IF YOU DON'T SUCCEED, TRY THE FOLLOWING
+-reread the complete README file
+-make sure, that your drive is hardware configured for
+ transfer mode: polled
+ IRQ: not used
+ DMA: not used
+ Base Address: something like 300, 320 ...
+ You can check this, when you start the DOS driver, which came with your
+ drive. By appropriately configuring the drive and the DOS driver you can
+ check, whether your drive does operate in this mode correctly under DOS. If
+ it does not operate under DOS, it won't under Linux.
+ Make sure the Base Address is configured correctly in aztcd.h, also make
+ sure, that /dev/aztcd0 exists with the correct major number (compare it with
+ the entry in file /usr/include/linux/major.h for the Aztech drive).
+-insert a CD-ROM and close the tray
+-cold boot your PC (i.e. via the power on switch or the reset button)
+-if you start Linux via DOS, e.g. using loadlin, make sure, that the DOS
+ driver for the CD-ROM drive is not loaded (comment out the calling lines
+ in DOS' config.sys!)
+-look for the aztcd: init message during Linux init and note them exactly
+-log in as root and do a mount -t iso9660 /dev/aztcd0 /mnt
+-if you don't succeed in the first time, try several times. Try also to open
+ and close the tray, then mount again. Please note carefully all commands
+ you typed in and the aztcd-messages, which you get.
+-if you get an 'Aztech CD-ROM init: aborted' message, read the remarks about
+ the version string below.
+
+If this does not help, do the same with the following differences
+-start DOS before; make now sure, that the DOS driver for the CD-ROM is
+ loaded under DOS (i.e. uncomment it again in config.sys)
+-warm boot your PC (i.e. via CTRL-ALT-DEL)
+ if you have it, you can also start via loadlin (try both).
+ ...
+ Again note all commands and the aztcd-messages.
+
+If you see STEN_LOW or STEN_LOW_WAIT error messages, increase the timeout
+values.
+
+If this still does not help,
+-look in aztcd.c for the lines #if 0
+ #define AZT_TEST1
+ ...
+ #endif
+ and substitute '#if 0' by '#if 1'.
+-recompile your kernel and repeat the above two procedures. You will now get
+ a bundle of debugging messages from the driver. Again note your commands
+ and the appropriate messages. If you have syslogd running, these messages
+ may also be found in syslogd's kernel log file. Nevertheless in some
+ installations syslogd does not yet run, when init() is called, thus look for
+ the aztcd-messages during init, before the login-prompt appears.
+ Then look in aztcd.c, to find out, what happened. The normal calling sequence
+ is: aztcd_init() during Linux bootup procedure init()
+ after doing a 'mount -t iso9660 /dev/aztcd0 /mnt' the normal calling sequence is
+ aztcd_open() -> Status 2c after cold reboot with CDROM or audio CD inserted
+ -> Status 8 after warm reboot with CDROM inserted
+ -> Status 2e after cold reboot with no disk, closed tray
+ -> Status 6e after cold reboot, mount with door open
+ aztUpdateToc()
+ aztGetDiskInfo()
+ aztGetQChannelInfo() repeated several times
+ aztGetToc()
+ aztGetQChannelInfo() repeated several times
+ a list of track informations
+ do_aztcd_request() }
+ azt_transfer() } repeated several times
+ azt_poll }
+ Check, if there is a difference in the calling sequence or the status flags!
+
+ There are a lot of other messages, eg. the ACMD-command code (defined in
+ aztcd.h), status info from the getAztStatus-command and the state sequence of
+ the finite state machine in azt_poll(). The most important are the status
+ messages, look how they are defined and try to understand, if they make
+ sense in the context where they appear. With a CD-ROM inserted the status
+ should always be 8, except in aztcd_open(). Try to open the tray, insert a
+ audio disk, insert no disk or reinsert the CD-ROM and check, if the status
+ bits change accordingly. The status bits are the most likely point, where
+ the drive manufacturers may implement changes.
+
+If you still don't succeed, a good point to start is to look in aztcd.c in
+function aztcd_init, where the drive should be detected during init. Do the
+following:
+-reboot the system with boot parameter 'aztcd=<your base address>,0x79'. With
+ parameter 0x79 most of the drive version detection is bypassed. After that
+ you should see the complete version string including leading and trailing
+ blanks during init.
+ Now adapt the statement
+ if ((result[1]=='A')&&(result[2]=='Z' ...)
+ in aztcd_init() to exactly match the first 3 or 4 letters you have seen.
+-Another point is the 'smart' card detection feature in aztcd_init(). Normally
+ the CD-ROM drive is ready, when aztcd_init is trying to read the version
+ string and a time consuming ACMD_SOFT_RESET command can be avoided. This is
+ detected by looking, if AFL_OP_OK can be read correctly. If the CD-ROM drive
+ hangs in some unknown state, e.g. because of an error before a warm start or
+ because you first operated under DOS, even the version string may be correct,
+ but the following commands will not. Then change the code in such a way,
+ that the ACMD_SOFT_RESET is issued in any case, by substituting the
+ if-statement 'if ( ...=AFL_OP_OK)' by 'if (1)'.
+
+If you succeed, please mail may the exact version string of your drive and
+the code modifications, you have made together with a short explanation.
+If you don't succeed, you may mail me the output of the debugging messages.
+But remember, they are only useful, if they are exact and complete and you
+describe in detail your hardware setup and what you did (cold/warm reboot,
+with/without DOS, DOS-driver started/not started, which Linux-commands etc.)
+
+
+9. TECHNICAL HISTORY OF THE DRIVER
+The AZTECH-Driver is a rework of the Mitsumi-Driver. Four major items had to
+be reworked:
+
+a) The Mitsumi drive does issue complete status information acknowledging
+each command, the Aztech drive does only signal that the command was
+processed. So whenever the complete status information is needed, an extra
+ACMD_GET_STATUS command is issued. The handshake procedure for the drive
+can be found in the functions aztSendCmd(), sendAztCmd() and getAztStatus().
+
+b) The Aztech Drive does not have a ACMD_GET_DISK_INFO command, so the
+necessary info about the number of tracks (firstTrack, lastTrack), disk
+length etc. has to be read from the TOC in the lead in track (see function
+aztGetDiskInfo()).
+
+c) Whenever data is read from the drive, the Mitsumi drive is started with a
+command to read an indefinite (0xffffff) number of sectors. When the appropriate
+number of sectors is read, the drive is stopped by a ACDM_STOP command. This
+does not work with the Aztech drive. I did not find a way to stop it. The
+stop and pause commands do only work in AUDIO mode but not in DATA mode.
+Therefore I had to modify the 'finite state machine' in function azt_poll to
+only read a certain number of sectors and then start a new read on demand. As I
+have not completely understood, how the buffer/caching scheme of the Mitsumi
+driver was implemented, I am not sure, if I have covered all cases correctly,
+whenever you get timeout messages, the bug is most likely to be in that
+function azt_poll() around switch(cmd) .... case ACD_S_DATA.
+
+d) I did not get information about changing drive mode. So I doubt, that the
+code around function azt_poll() case AZT_S_MODE does work. In my test I have
+not been able to switch to reading in raw mode. For reading raw mode, Aztech
+uses a different command than for cooked mode, which I only have implemen-
+ted in the ioctl-section but not in the section which is used by the ISO9660-
+
+The driver was developed on an AST PC with Intel 486/DX2, 8MB RAM, 340MB IDE
+hard disk and on an AST PC with Intel Pentium 60MHz, 16MB RAM, 520MB IDE
+running Linux kernel version 1.0.9 from the LST 1.8 Distribution. The kernel
+was compiled with gcc.2.5.8. My CD-ROM drive is an Aztech CDA268-01A. My
+drive says, that it has Firmware Version AZT26801A1.3. It came with a ISA-bus
+interface card and works with polled I/O without DMA and without interrupts.
+The code for all other drives was 'remote' tested and debugged by a number of
+volunteers on the Internet.
+
+Points, where I feel that possible problems might be and all points where I
+did not completely understand the drive's behaviour or trust my own code are
+marked with /*???*/ in the source code. There are also some parts in the
+Mitsumi driver, where I did not completely understand their code.
+
+
+10. ACKNOWLEDGMENTS
+Without the help of P.Bush, Aztech, who delivered technical information
+about the Aztech Drive and without the help of E.Moenkeberg, GWDG, who did a
+great job in analyzing the command structure of various CD-ROM drives, this
+work would not have been possible. E.Moenkeberg was also a great help in
+making the software 'kernel ready' and in answering many of the CDROM-related
+questions in the newsgroups. He really is *the* Linux CD-ROM guru. Thanks
+also to all the guys on the Internet, who collected valuable technical
+information about CDROMs.
+
+Joe Nardone (nardone@clark.net) was a patient tester even for my first
+trial, which was more than slow, and made suggestions for code improvement.
+Especially the 'finite state machine' azt_poll() was rewritten by Joe to get
+clean C code and avoid the ugly 'gotos', which I copied from mcd.c.
+
+Robby Schirmer (schirmer@fmi.uni-passau.de) tested the audio stuff (ioctls)
+and suggested a lot of patches for them.
+
+Joseph Piskor and Peter Nugent were the first users with the ORCHID CD3110
+and also were very patient with the problems which occurred.
+
+Anybody, who is interested in these items should have a look at 'ftp.gwdg.de',
+directory 'pub/linux/cdrom' and at 'ftp.cdrom.com', directory 'pub/cdrom'.
+
+11. PROGRAMMING ADD ONs: cdplay.c
+You can use the ioctl-functions included in aztcd.c in your own programs. As
+an example on how to do this, you will find a tiny CD Player for audio CDs
+named 'cdplay.c'. It allows you to play audio CDs. You can play a specified
+track, pause and resume or skip tracks forward and backwards. If you quit the
+program without stopping the drive, playing is continued. You can also
+(mis)use cdplay to read and hexdump data disks.
+'cdplay.c' can be found as a separate file in package AZTECH.CDROM.Vxx.tgz.
+If you don't have it, you can find the code in the APPENDIX of this file,
+which you should cut out with an editor and store in a separate file
+'cdplay.c'. To compile it and make it executable, do
+ gcc -s -Wall -O2 -L/usr/lib cdplay.c -o /usr/local/bin/cdplay # compiles it
+ chmod +755 /usr/local/bin/cdplay # makes it executable
+ ln -s /dev/aztcd0 /dev/cdrom # creates a link
+ (for /usr/lib substitute the top level directory, where your include files
+ reside, and for /usr/local/bin the directory, where you want the executable
+ binary to reside )
+
+You have to set the correct permissions for cdplay *and* for /dev/mcd0 or
+/dev/aztcd0 in order to use it. Remember, that you should not have /dev/cdrom
+mounted, when you're playing audio CDs.
+
+This program is just a hack for testing the ioctl-functions in aztcd.c, I will
+not maintain it, so if you run into problems, discard it or have a look into
+the source code 'cdplay.c'. The program does only contain a minimum of user
+protection and input error detection. If you use the commands in the wrong
+order or if you try to read a CD at wrong addresses, you may get error messages
+or even hang your machine. If you get STEN_LOW, STEN_LOW_WAIT or segment violation
+error messages when using cdplay, after that, the system might not be stable
+any more, so you'd better reboot. As the ioctl-functions run in kernel mode,
+most normal Linux-multitasking protection features do not work. By using
+uninitialized 'wild' pointers etc., it is easy to write to other users data and
+program areas, destroy kernel tables etc.. So if you experiment with ioctls
+as always when you are doing systems programming and kernel hacking, you
+should have a backup copy of your system in a safe place (and you also
+should try before, how to restore from a backup copy)!
+
+
+Werner Zimmermann
+Fachhochschule fuer Technik Esslingen
+(EMail: zimmerma@rz.fht-esslingen.de)
+Mar 24, 1995
+
+---------------------------------------------------------------------------
+APPENDIX: Source code of cdplay.c
+
+/* Tiny Audio CD Player
+
+ Copyright 1994, 1995 Werner Zimmermann (zimmerma@rz.fht-esslingen.de)
+
+This program originally was written to test the audio functions of the
+AZTECH.CDROM-driver, but it should work with every CD-ROM drive. Before
+using it, you should set a symlink from /dev/cdrom to your real CDROM
+device.
+
+The GNU General Public License applies to this program.
+
+History: V0.1 W.Zimmermann: First release. Nov. 8, 1994
+ V0.2 W.Zimmermann: Enhanced functionality. Nov. 9, 1994
+ V0.3 W.Zimmermann: Additional functions. Nov. 28, 1994
+ V0.4 W.Zimmermann: fixed some bugs. Dec. 17, 1994
+ V0.5 W.Zimmermann: clean 'scanf' commands without compiler warnings
+ Jan. 6, 1995
+ V0.6 W.Zimmermann: volume control (still experimental). Jan. 24, 1995
+*/
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/cdrom.h>
+#include <linux/aztcd.h>
+
+void help(void)
+{ printf("Available Commands: STOP s EJECT e QUIT q\n");
+ printf(" PLAY TRACK t PAUSE p RESUME r\n");
+ printf(" NEXT TRACK n REPEAT LAST l HELP h\n");
+ printf(" SUB CHANNEL c TRACK INFO i PLAY AT a\n");
+ printf(" READ d READ RAW w VOLUME v\n");
+}
+
+int main(void)
+{ int handle;
+ unsigned char command=' ', ini=0, first=1, last=1;
+ unsigned int cmd, i,j,k, arg1,arg2,arg3;
+ struct cdrom_ti ti;
+ struct cdrom_tochdr tocHdr;
+ struct cdrom_subchnl subchnl;
+ struct cdrom_tocentry entry;
+ struct cdrom_msf msf;
+ union { struct cdrom_msf msf;
+ unsigned char buf[2336];
+ } azt;
+ struct cdrom_volctrl volctrl;
+
+ printf("\nMini-Audio CD-Player V0.6 (C) 1994,1995 W.Zimmermann\n");
+ handle=open("/dev/cdrom",O_RDWR);
+ ioctl(handle,CDROMRESUME);
+
+ if (handle<=0)
+ { printf("Drive Error: already playing, no audio disk, door open\n");
+ printf(" or no permission (you must be ROOT in order to use this program)\n");
+ }
+ else
+ { help();
+ while (1)
+ { printf("Type command (h = help): ");
+ scanf("%s",&command);
+ switch (command)
+ { case 'e': cmd=CDROMEJECT;
+ ioctl(handle,cmd);
+ break;
+ case 'p': if (!ini)
+ { printf("Command not allowed - play track first\n");
+ }
+ else
+ { cmd=CDROMPAUSE;
+ if (ioctl(handle,cmd)) printf("Drive Error\n");
+ }
+ break;
+ case 'r': if (!ini)
+ { printf("Command not allowed - play track first\n");
+ }
+ else
+ { cmd=CDROMRESUME;
+ if (ioctl(handle,cmd)) printf("Drive Error\n");
+ }
+ break;
+ case 's': cmd=CDROMPAUSE;
+ if (ioctl(handle,cmd)) printf("Drive error or already stopped\n");
+ ini=0;
+ cmd=CDROMSTOP;
+ if (ioctl(handle,cmd)) printf("Drive error\n");
+ ini=0;
+ break;
+ case 't': cmd=CDROMREADTOCHDR;
+ if (ioctl(handle,cmd,&tocHdr)) printf("Drive Error\n");
+ first=tocHdr.cdth_trk0;
+ last= tocHdr.cdth_trk1;
+ if ((first==0)||(first>last))
+ { printf ("--could not read TOC\n");
+ }
+ else
+ { printf("--first track: %d --last track: %d --enter track number: ",first,last);
+ cmd=CDROMPLAYTRKIND;
+ scanf("%i",&arg1);
+ ti.cdti_trk0=arg1;
+ if (ti.cdti_trk0<first) ti.cdti_trk0=first;
+ if (ti.cdti_trk0>last) ti.cdti_trk0=last;
+ ti.cdti_ind0=0;
+ ti.cdti_trk1=last;
+ ti.cdti_ind1=0;
+ if (ioctl(handle,cmd,&ti)) printf("Drive Error\n");
+ ini=1;
+ }
+ break;
+ case 'n': if (!ini++)
+ { if (ioctl(handle,CDROMREADTOCHDR,&tocHdr)) printf("Drive Error\n");
+ first=tocHdr.cdth_trk0;
+ last= tocHdr.cdth_trk1;
+ ti.cdti_trk0=first-1;
+ }
+ if ((first==0)||(first>last))
+ { printf ("--could not read TOC\n");
+ }
+ else
+ { cmd=CDROMPLAYTRKIND;
+ if (++ti.cdti_trk0 > last) ti.cdti_trk0=last;
+ ti.cdti_ind0=0;
+ ti.cdti_trk1=last;
+ ti.cdti_ind1=0;
+ if (ioctl(handle,cmd,&ti)) printf("Drive Error\n");
+ ini=1;
+ }
+ break;
+ case 'l': if (!ini++)
+ { if (ioctl(handle,CDROMREADTOCHDR,&tocHdr)) printf("Drive Error\n");
+ first=tocHdr.cdth_trk0;
+ last= tocHdr.cdth_trk1;
+ ti.cdti_trk0=first+1;
+ }
+ if ((first==0)||(first>last))
+ { printf ("--could not read TOC\n");
+ }
+ else
+ { cmd=CDROMPLAYTRKIND;
+ if (--ti.cdti_trk0 < first) ti.cdti_trk0=first;
+ ti.cdti_ind0=0;
+ ti.cdti_trk1=last;
+ ti.cdti_ind1=0;
+ if (ioctl(handle,cmd,&ti)) printf("Drive Error\n");
+ ini=1;
+ }
+ break;
+ case 'c': subchnl.cdsc_format=CDROM_MSF;
+ if (ioctl(handle,CDROMSUBCHNL,&subchnl))
+ printf("Drive Error\n");
+ else
+ { printf("AudioStatus:%s Track:%d Mode:%d MSF=%d:%d:%d\n", \
+ subchnl.cdsc_audiostatus==CDROM_AUDIO_PLAY ? "PLAYING":"NOT PLAYING",\
+ subchnl.cdsc_trk,subchnl.cdsc_adr, \
+ subchnl.cdsc_absaddr.msf.minute, subchnl.cdsc_absaddr.msf.second, \
+ subchnl.cdsc_absaddr.msf.frame);
+ }
+ break;
+ case 'i': if (!ini)
+ { printf("Command not allowed - play track first\n");
+ }
+ else
+ { cmd=CDROMREADTOCENTRY;
+ printf("Track No.: ");
+ scanf("%d",&arg1);
+ entry.cdte_track=arg1;
+ if (entry.cdte_track<first) entry.cdte_track=first;
+ if (entry.cdte_track>last) entry.cdte_track=last;
+ entry.cdte_format=CDROM_MSF;
+ if (ioctl(handle,cmd,&entry))
+ { printf("Drive error or invalid track no.\n");
+ }
+ else
+ { printf("Mode %d Track, starts at %d:%d:%d\n", \
+ entry.cdte_adr,entry.cdte_addr.msf.minute, \
+ entry.cdte_addr.msf.second,entry.cdte_addr.msf.frame);
+ }
+ }
+ break;
+ case 'a': cmd=CDROMPLAYMSF;
+ printf("Address (min:sec:frame) ");
+ scanf("%d:%d:%d",&arg1,&arg2,&arg3);
+ msf.cdmsf_min0 =arg1;
+ msf.cdmsf_sec0 =arg2;
+ msf.cdmsf_frame0=arg3;
+ if (msf.cdmsf_sec0 > 59) msf.cdmsf_sec0 =59;
+ if (msf.cdmsf_frame0> 74) msf.cdmsf_frame0=74;
+ msf.cdmsf_min1=60;
+ msf.cdmsf_sec1=00;
+ msf.cdmsf_frame1=00;
+ if (ioctl(handle,cmd,&msf))
+ { printf("Drive error or invalid address\n");
+ }
+ break;
+#ifdef AZT_PRIVATE_IOCTLS /*not supported by every CDROM driver*/
+ case 'd': cmd=CDROMREADMODE1;
+ printf("Address (min:sec:frame) ");
+ scanf("%d:%d:%d",&arg1,&arg2,&arg3);
+ azt.msf.cdmsf_min0 =arg1;
+ azt.msf.cdmsf_sec0 =arg2;
+ azt.msf.cdmsf_frame0=arg3;
+ if (azt.msf.cdmsf_sec0 > 59) azt.msf.cdmsf_sec0 =59;
+ if (azt.msf.cdmsf_frame0> 74) azt.msf.cdmsf_frame0=74;
+ if (ioctl(handle,cmd,&azt.msf))
+ { printf("Drive error, invalid address or unsupported command\n");
+ }
+ k=0;
+ getchar();
+ for (i=0;i<128;i++)
+ { printf("%4d:",i*16);
+ for (j=0;j<16;j++)
+ { printf("%2x ",azt.buf[i*16+j]);
+ }
+ for (j=0;j<16;j++)
+ { if (isalnum(azt.buf[i*16+j]))
+ printf("%c",azt.buf[i*16+j]);
+ else
+ printf(".");
+ }
+ printf("\n");
+ k++;
+ if (k>=20)
+ { printf("press ENTER to continue\n");
+ getchar();
+ k=0;
+ }
+ }
+ break;
+ case 'w': cmd=CDROMREADMODE2;
+ printf("Address (min:sec:frame) ");
+ scanf("%d:%d:%d",&arg1,&arg2,&arg3);
+ azt.msf.cdmsf_min0 =arg1;
+ azt.msf.cdmsf_sec0 =arg2;
+ azt.msf.cdmsf_frame0=arg3;
+ if (azt.msf.cdmsf_sec0 > 59) azt.msf.cdmsf_sec0 =59;
+ if (azt.msf.cdmsf_frame0> 74) azt.msf.cdmsf_frame0=74;
+ if (ioctl(handle,cmd,&azt))
+ { printf("Drive error, invalid address or unsupported command\n");
+ }
+ k=0;
+ for (i=0;i<146;i++)
+ { printf("%4d:",i*16);
+ for (j=0;j<16;j++)
+ { printf("%2x ",azt.buf[i*16+j]);
+ }
+ for (j=0;j<16;j++)
+ { if (isalnum(azt.buf[i*16+j]))
+ printf("%c",azt.buf[i*16+j]);
+ else
+ printf(".");
+ }
+ printf("\n");
+ k++;
+ if (k>=20)
+ { getchar();
+ k=0;
+ }
+ }
+ break;
+#endif
+ case 'v': cmd=CDROMVOLCTRL;
+ printf("--Channel 0 Left (0-255): ");
+ scanf("%d",&arg1);
+ printf("--Channel 1 Right (0-255): ");
+ scanf("%d",&arg2);
+ volctrl.channel0=arg1;
+ volctrl.channel1=arg2;
+ volctrl.channel2=0;
+ volctrl.channel3=0;
+ if (ioctl(handle,cmd,&volctrl))
+ { printf("Drive error or unsupported command\n");
+ }
+ break;
+ case 'q': if (close(handle)) printf("Drive Error: CLOSE\n");
+ exit(0);
+ case 'h': help();
+ break;
+ default: printf("unknown command\n");
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
diff --git a/drivers/block/README.fd b/drivers/block/README.fd
new file mode 100644
index 000000000..2bd07338c
--- /dev/null
+++ b/drivers/block/README.fd
@@ -0,0 +1,168 @@
+This Readme file describes the floppy driver.
+
+FAQ list:
+=========
+
+ A FAQ list may be found in the fdutils package (see below), and also
+at ftp.imag.fr:pub/Linux/ZLIBC/floppy/FAQ
+
+
+Lilo config options (Thinkpad users, read this)
+===============================================
+
+ The floppy driver is configured using the 'floppy=' option in
+lilo. This option can be typed at the boot prompt, or entered in the
+lilo configuration file.
+ Example: If your kernel is called linux-72, type the following line
+at the lilo boot prompt (if you have a thinkpad):
+ linux-72 floppy=thinkpad
+You may also enter the following line in /etc/lilo.conf, in the description
+of linux-72:
+ append = "floppy=thinkpad"
+
+ Several floppy related options may be given, example:
+ linux-72 floppy=daring floppy=two_fdc
+ append = "floppy=daring floppy=two_fdc"
+
+ If you give options both in the lilo config file and on the boot
+prompt, the option strings of both places are concatenated, the boot
+prompt options coming last. That's why there are also options to
+restore the default behaviour.
+
+ The floppy related options include:
+
+ floppy=<mask>,allowed_drive_mask
+ Sets the bitmask of allowed drives to <mask>. By default, only units
+ 0 and 1 of each floppy controller are allowed. This is done because
+ certain non-standard hardware (ASUS PCI motherboards) mess up the
+ keyboard when accessing units 2 or 3. This option is somewhat
+ obsoleted by the cmos option.
+
+ floppy=all_drives
+ Sets the bitmask of allowed drives to all drives. Use this if you have
+ more than two drives connected to a floppy controller.
+
+ floppy=asus_pci
+ Sets the bitmask to allow only units 0 and 1. (The default)
+
+ floppy=daring
+ Tells the floppy driver that you have a well behaved floppy controller.
+ This allows more efficient and smoother operation, but may fail on
+ certain controllers. This may speed up certain operations.
+
+ floppy=0,daring
+ Tells the floppy driver that your floppy controller should be used
+ with caution.
+
+ floppy=one_fdc
+ Tells the floppy driver that you have only floppy controller (default)
+
+ floppy=two_fdc
+ floppy=<address>,two_fdc
+ Tells the floppy driver that you have two floppy controllers. The
+ second floppy controller is assumed to be at <address>. If <address>
+ is not given, 0x370 is assumed.
+
+ floppy=thinkpad
+ Tells the floppy driver that you have a Thinkpad. Thinkpads use an
+ inverted convention for the disk change line.
+
+ floppy=0,thinkpad
+ Tells the floppy driver that you don't have a Thinkpad.
+
+ floppy=<drive>,<type>,cmos
+ Sets the cmos type of <drive> to <type>. Additionally, this drive is
+ allowed in the bitmask. This is useful if you have more than two
+ floppy drives (only two can be described in the physical cmos), or if
+ your BIOS uses non-standard CMOS types. The CMOS types are:
+ 0 - unknown or not installed
+ 1 - 5 1/4 DD
+ 2 - 5 1/4 HD
+ 3 - 3 1/2 DD
+ 4 - 3 1/2 HD
+ 5 - 3 1/2 ED
+ 6 - 3 1/2 ED
+ (Note: there are two valid types for ED drives. This is because 5 was
+ initially chosen to represent floppy *tapes*, and 6 for ED drives.
+ AMI ignored this, and used 5 for ED drives. That's why the floppy
+ driver handles both)
+ Setting the CMOS to 0 for the first two drives (default) makes the
+ floppy driver read the physical cmos for those drives.
+
+ floppy=unexpected_interrupts
+ Print a warning message when an unexpected interrupt is received
+ (default behaviour)
+
+ floppy=no_unexpected_interrupts
+ floppy=L40SX
+ Don't print a message when an unexpected interrupt is received. This
+ is needed on IBM L40SX laptops in certain video modes. (There seems
+ to be an interaction between video and floppy. The unexpected interrupts
+ only affect performance, and can safely be ignored.)
+
+
+Supporting utilities and additional documentation:
+==================================================
+
+ Additional parameters of the floppy driver can be configured at run
+time. Utilities which do this can be found in the fdutils
+package. This package also contains a new version of mtools which
+allows to access high capacity disks (up to 1992K on a high density 3
+1/2 disk!). It also contains additional documentation about the floppy
+driver. It can be found at:
+ ftp.imag.fr:pub/Linux/ZLIBC/fdutils/fdutils-4.0.src.tar.gz
+ sunsite.unc.edu:/pub/Linux/system/Misc/fdutils-4.0.src.tar.gz
+ tsx-11.mit.edu:/pub/linux/sources/sbin/fdutils-4.0.src.tar.gz
+
+ Alpha patches to these utilities are at:
+ ftp.imag.fr:pub/Linux/ZLIBC/fdutils/ALPHA
+ All patches contained in this directory are directly against the base
+version, i.e. DON'T APPLY THEM ON TOP OF EACH OTHER. Only apply the
+most recent one.
+
+
+Alpha patches for the floppy driver:
+====================================
+
+ You may find ALPHA patches of the driver itself in
+ftp.imag.fr:pub/Linux/ZLIBC/floppy/ALPHA. These patches are named
+fdp<kernel-version>-<day><month>.diff.gz
+ WARNING: These _are_ ALPHA, and may introduce new problems! Some
+problems may only show up on certain hardware, or when trying weirdo
+things. So don't be misled by people claiming they are stable and
+should really be BETA. What works for one person, may not work for
+somebody else at all. This directory contains a RELEASES file
+describing the features of some of these patches.
+
+ If after some testing these patches prove to be sufficiently stable,
+they'll move into ftp.imag.fr:pub/Linux/ZLIBC/floppy/BETA.
+
+ You may find quick&dirty fixes to the driver in
+ftp.imag.fr:pub/Linux/ZLIBC/QDF. These patches are named
+fdp<kernel-version>-<day><month>.diff
+ These patches fix only the most obvious problems, or provide trivial
+enhancements. The main objective is to keep these patches small and
+local, in order to keep the probability of introducing new problems as
+small as possible. However, they may not attack the root of the
+problem but only cure the symptoms. This directory contains a RELEASES
+file describing the features of these patches.
+
+ The ALPHA, BETA and QDF directories are removed, and replaced by a
+README file when they get empty due to integration of the patches into
+the stock kernel. You may still find patches to old kernels in
+ftp.imag.fr:pub/Linux/ZLIBC/obsolete
+
+
+Reporting problems about the floppy driver
+==========================================
+
+ If you have a question or a bug report about the floppy driver, mail
+me at Alain.Knaff@imag.fr. If you post to the news, use preferably one
+of the groups comp.os.linux.help (for questions) or
+comp.os.linux.hardware (for bug reports). As the volume in these
+groups is rather high, be sure to include the word "floppy" (or
+"FLOPPY") in the subject line.
+
+ Be sure to read the FAQ before mailing/posting any bug reports!
+
+ Alain
diff --git a/drivers/block/README.ide b/drivers/block/README.ide
new file mode 100644
index 000000000..39ae4fa79
--- /dev/null
+++ b/drivers/block/README.ide
@@ -0,0 +1,278 @@
+README.ide -- Information regarding ide.c and ide-cd.c (IDE driver in 1.2.x)
+================================================================================
+Supported by: mlord@bnr.ca -- disks, interfaces, probing
+ snyder@fnald0.fnal.gov -- cdroms, ATAPI, audio
+
+(see description later on below for handling BIG IDE drives with >1024 cyls).
+
+Major features of ide.c & ide-cd.c:
+
+ - support for up to two IDE interfaces on one or two IRQs
+ - support for any mix of up to four disk and/or cdrom drives
+ - support for reading IDE ATAPI cdrom drives (NEC,MITSUMI,VERTOS,SONY)
+ - support for audio functions
+ - auto-detection of interfaces, drives, IRQs, and disk geometries
+ -- "single" drives should be jumpered as "master", not "slave"
+ - support for BIOSs which report "more than 16 heads" on disk drives
+ - uses LBA (slightly faster) on disk drives which support it
+ - support for lots of fancy (E)IDE drive functions with hdparm utility
+ - optional (compile time) support for 32-bit VLB data transfers
+ - support for IDE multiple (block) mode (same as hd.c)
+ - support for interrupt unmasking during I/O (better than hd.c)
+ - improved handshaking and error detection/recovery
+ - can co-exist with hd.c to control only the secondary interface
+NEW! - support for reliable operation of buggy CMD-640 interfaces
+ - use kernel command line option: hda=serialize
+NEW! - experimental support for DTC-2278D interfaces
+ - use kernel command line option: hda=dtc2278
+NEW! - run-time selectable 32bit interface support (using hdparm-2.3)
+
+Under construction:
+
+ - improved CMD support: tech info is supposedly "in the mail"
+ - support for interface speed selection on jumperless interfaces
+ - improved detection of non-standard IDE ATAPI cdrom drives
+ - support for non-standard 3rd/4th drive interface on Promise cards
+
+***
+
+IMPORTANT NOTICE: "CMD" EIDE Interfaces will not (by default) work *reliably*
+when drives are attached to the second interface. To "fix" this, supply the
+special kernel "command line" parameter to LILO: hda=serialize
+Failure to do so can cause severe data corruption!
+
+***
+
+To access devices on the second interface, device entries must first be
+created in /dev for them. To create such entries, simply run the included
+shell script: MAKEDEV.ide1
+
+Apparently the early releases of Slackware 2.2 have incorrect entries
+in /dev for hdc* and hdd* -- this can also be corrected by running MAKEDEV.ide1
+
+ide.c automatically probes for the primary and secondary interfaces,
+for the drives/geometries attached to those interfaces, and for the
+IRQ numbers being used by the interfaces (normally IRQ14 & IRQ15).
+
+The primary and secondary interfaces may share a single IRQ if necessary,
+at a slight performance penalty, whether on separate cards or a single VLB card.
+
+Drives are normally found by auto-probing and/or examining the CMOS/BIOS data.
+For really weird situations, the apparent (fdisk) geometry can also be specified
+on the kernel "command line" using LILO. The format of such lines is:
+
+ hdx=cyls,heads,sects,wpcom,irq
+or hdx=cdrom
+
+where hdx can be any of {hda,hdb,hdc,hdd}, or simply hd, for the "next" drive
+in sequence. Only the first three parameters are required (cyls,heads,sects),
+and wpcom is ignored for IDE drives. For example:
+
+ hdc=1050,32,64 hdd=cdrom
+
+If an irq number is given, it will apply to both drives on the same interface,
+either {hda,hdb} or {hdc,hdd}. The results of successful auto-probing may
+override the physical geometry/irq specified, though the "original" geometry
+is retained as the "logical" geometry for partitioning purposes (fdisk).
+
+If the auto-probing during boot time confuses a drive (ie. the drive works
+with hd.c but not with ide.c), then an command line option may be specified
+for each drive for which you'd like the drive to skip the hardware
+probe/identification sequence. For example:
+
+ hdb=noprobe
+or
+ hdc=768,16,32
+ hdc=noprobe
+
+Note that when only one IDE device is attached to an interface,
+it must be jumpered as "single" or "master", *not* "slave".
+Many folks have had "trouble" with cdroms because of this requirement
+of the ATA (IDE) standard.
+
+Courtesy of Scott Snyder, the driver now supports ATAPI cdrom drives
+such as the NEC-260 and the new MITSUMI triple/quad speed drives.
+Such drives will be identified at boot time, as hda,hdb,hdc or hdd,
+just like a harddisk.
+
+If for some reason your cdrom drive is *not* found at boot time, you can force
+the probe to look harder by supplying a kernel command line parameter
+via LILO, such as: hdc=cdrom
+
+For example, a GW2000 system might have a harddrive on the primary
+interface (/dev/hda) and an IDE cdrom drive on the secondary interface
+(/dev/hdc). To mount a CD in the cdrom drive, one would use something like:
+
+ ln -sf /dev/hdc /dev/cdrom
+ mkdir /cd
+ mount /dev/cdrom /cd -t iso9660 -o ro
+
+Please pass on any feedback on the cdrom stuff to the author & maintainer,
+Scott Snyder (snyder@fnald0.fnal.gov).
+
+The kernel is now be able to execute binaries directly off of the cdrom,
+provided it is mounted with the default block size of 1024.
+
+The hdparm.c program for controlling various IDE features is now packaged
+separately. Look for it on popular linux FTP sites.
+
+mlord@bnr.ca
+snyder@fnald0.fnal.gov
+================================================================================
+
+Some Terminology
+----------------
+IDE = Integrated Drive Electronics, meaning that each drive has a built-in
+controller, which is why an "IDE interface card" is not a "controller card".
+
+IDE drives are designed to attach almost directly to the ISA bus of an AT-style
+computer. The typical IDE interface card merely provides I/O port address
+decoding and tri-state buffers, although several newer localbus cards go much
+beyond the basics. When purchasing a localbus IDE interface, avoid cards with
+an onboard BIOS and those which require special drivers. Instead, look for a
+card which uses hardware switches/jumpers to select the interface timing speed,
+to allow much faster data transfers than the original 8Mhz ISA bus allows.
+
+ATA = AT (the old IBM 286 computer) Attachment Interface, a draft American
+National Standard for connecting hard drives to PCs. This is the official
+name for "IDE".
+
+The latest standards define some enhancements, known as the ATA-2 spec,
+which grew out of vendor-specific "Enhanced IDE" (EIDE) implementations.
+
+ATAPI = ATA Packet Interface, a new protocol for controlling the drives,
+similar to SCSI protocols, created at the same time as the ATA2 standard.
+ATAPI is currently used for controlling CDROM and TAPE devices, and will
+likely also soon be used for Floppy drives, removable R/W cartridges,
+and for high capacity hard disk drives.
+
+How To Use *Big* ATA/IDE drives with Linux
+------------------------------------------
+The ATA Interface spec for IDE disk drives allows a total of 28 bits
+(8 bits for sector, 16 bits for cylinder, and 4 bits for head) for addressing
+individual disk sectors of 512 bytes each (in "Linear Block Address" (LBA)
+mode, there is still only a total of 28 bits available in the hardware).
+This "limits" the capacity of an IDE drive to no more than 128GB (Giga-bytes).
+All current day IDE drives are somewhat smaller than this upper limit, and
+within a few years, ATAPI disk drives will raise the limit considerably.
+
+All IDE disk drives "suffer" from a "16-heads" limitation: the hardware has
+only a four bit field for head selection, restricting the number of "physical"
+heads to 16 or less. Since the BIOS usually has a 63 sectors/track limit,
+this means that all IDE drivers larger than 504MB (528Meg) must use a "physical"
+geometry with more than 1024 cylinders.
+
+ (1024cyls * 16heads * 63sects * 512bytes/sector) / (1024 * 1024) == 504MB
+
+(Some BIOSs (and controllers with onboard BIOS) pretend to allow "32" or "64"
+ heads per drive (discussed below), but can only do so by playing games with
+ the real (hidden) geometry, which is always limited to 16 or fewer heads).
+
+This presents two problems to most systems:
+
+ 1. The INT13 interface to the BIOS only allows 10-bits for cylinder
+ addresses, giving a limit of 1024cyls for programs which use it.
+
+ 2. The physical geometry fields of the disk partition table only
+ allow 10-bits for cylinder addresses, giving a similar limit of 1024
+ cyls for operating systems that do not use the "sector count" fields
+ instead of the physical Cyl/Head/Sect (CHS) geometry fields.
+
+Neither of these limitations affects Linux itself, as it (1) does not use the
+BIOS for disk access, and it (2) is clever enough to use the "sector count"
+fields of the partition table instead of the physical CHS geometry fields.
+
+ a) Most folks use LILO to load linux. LILO uses the INT13 interface
+ to the BIOS to load the kernel at boot time. Therefore, LILO can only
+ load linux if the files it needs (usually just the kernel images) are
+ located below the magic 1024 cylinder "boundary" (more on this later).
+
+ b) Many folks also like to have bootable DOS partitions on their
+ drive(s). DOS also uses the INT13 interface to the BIOS, not only
+ for booting, but also for operation after booting. Therefore, DOS
+ can normally only access partitions which are contained entirely below
+ the magic 1024 cylinder "boundary".
+
+There are at least seven commonly used schemes for kludging DOS to work
+around this "limitation". In the long term, the problem is being solved
+by introduction of an alternative BIOS interface that does not have the
+same limitations as the INT13 interface. New versions of DOS are expected
+to detect and use this interface in systems whose BIOS provides it.
+
+But in the present day, alternative solutions are necessary.
+
+The most popular solution in newer systems is to have the BIOS shift bits
+between the cylinder and head number fields. This is activated by entering
+a translated logical geometry into the BIOS/CMOS setup for the drive.
+Thus, if the drive has a geometry of 2100/16/63 (CHS), then the BIOS could
+present a "logical" geometry of 525/64/63 by "shifting" two bits from the
+cylinder number into the head number field for purposes of the partition table,
+CMOS setup, and INT13 interfaces. Linux kernels 1.1.39 and higher detect and
+"handle" this translation automatically, making this a rather painless solution
+for the 1024 cyls problem. If for some reason Linux gets confused (unlikely),
+then use the kernel command line parameters to pass the *logical* geometry,
+as in: hda=525,64,63
+
+If the BIOS does not support this form of drive translation, then several
+options remain, listed below in inverse order of popularity:
+
+ - boot from a floppy disk instead of the hard drive (takes 10 seconds).
+ - use a partition below the 1024 cyl boundary to hold the linux
+ boot files (kernel images and /boot directory), and place the rest
+ of linux anywhere else on the drive. These files can reside in a DOS
+ partition, or in a tailor-made linux boot partition.
+
+If you cannot use drive translation, *and* your BIOS also restricts you to
+entering no more than 1024 cylinders in the geometry field in the CMOS setup,
+then just set it to 1024. As of v3.5 of this driver, Linux automatically
+determines the *real* number of cylinders for fdisk to use, allowing easy
+access to the full disk capacity without having to fiddle around.
+
+Regardless of what you do, all DOS partitions *must* be contained entirely
+within the first 1024 logical cylinders. For a 1Gig WD disk drive, here's
+a good "half and half" partitioning scheme to start with:
+
+ geometry = 2100/16/63
+ /dev/hda1 from cyl 1 to 992 dos
+ /dev/hda2 from cyl 993 to 1023 swap
+ /dev/hda3 from cyl 1024 to 2100 linux
+
+To ensure that LILO can boot linux, the boot files (kernel and /boot/*)
+must reside within the first 1024 cylinders of the drive. If your linux
+root partition is *not* completely within the first 1024 cyls (quite common),
+then you can use LILO to boot linux from files on your DOS partition
+by doing the following after installing slackware (or whatever):
+
+ 0. Boot from the "boot floppy" created during the installation
+ 1. Mount your DOS partition as /dos (and stick it in /etc/fstab)
+ 2. Move your kernel (/vmlinuz) to /dos/vmlinuz with: mv /vmlinuz /dos
+ 3. Edit /etc/lilo.conf to change /vmlinuz to /dos/vmlinuz
+ 4. Move /boot to /dos/boot with: cp -a /boot /dos ; rm -r /boot
+ 5. Create a symlink for LILO to use with: ln -s /dos/boot /boot
+ 6. Re-run LILO with: lilo
+
+ A danger with this approach is that whenever an MS-DOS "defragmentation"
+ program is run (like Norton "speeddisk"), it may move the Linux boot
+ files around, confusing LILO and making the (Linux) system unbootable.
+ Be sure to keep a kernel "boot floppy" at hand for such circumstances.
+
+If you "don't do DOS", then partition as you please, but remember to create
+a small partition to hold the /boot directory (and vmlinuz) as described above
+such that they stay within the first 1024 cylinders.
+
+Note that when creating partitions that span beyond cylinder 1024,
+Linux fdisk will complain about "Partition X has different physical/logical
+endings" and emit messages such as "This is larger than 1024, and may cause
+problems with some software". Ignore them for linux partitions. The "some
+software" refers to DOS, the BIOS, and LILO, as described previously.
+
+Western Digital now ships a "DiskManager 6.03" diskette with all of their big
+hard drives. Burn it! That idiotic piece of garbage isn't even universally
+compatible with DOS, let alone other operating systems like Linux. Eventually
+some kind person will kludge Linux to work with it, but at present the two
+are completely incompatible. If you have this version of DiskManager on your
+hard disk already, it can be exterminated at the expense of all data on the
+drive (back it up elsewhere), by using the "DM" command from the diskette
+as follows: DM /Y-
+
+mlord@bnr.ca
diff --git a/drivers/block/README.sbpcd b/drivers/block/README.sbpcd
index 74f188c26..6a40abfb0 100644
--- a/drivers/block/README.sbpcd
+++ b/drivers/block/README.sbpcd
@@ -1,44 +1,60 @@
-This README belongs to release 2.8 or newer of the SoundBlaster Pro
-(Matsushita, Kotobuki, Panasonic, CreativeLabs) CD-ROM driver for Linux.
+This README belongs to release 3.7 or newer of the SoundBlaster Pro
+(Matsushita, Kotobuki, Panasonic, CreativeLabs, Longshine and TEAC)
+CD-ROM driver for Linux.
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,
+is NOT the new "Enhanced IDE" or "ATAPI" 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.
The Longshine LCS-7260 is a double-speed drive which uses the "old"
-Matsushita command set. It should be fully supported soon, too.
+Matsushita command set. It is supported now - with help by Serge Robyns.
-There exists an "IBM External ISA CD-ROM Drive" which in fact is a CR-562
+There exists an "IBM External ISA CD-ROM Drive" which in fact is a CR-563
with a special controller board. This drive is supported (the interface is
of the "LaserMate" type), and it is possibly the best buy today (cheaper than
an internal drive, and you can use it as an internal, too - f.e. plug it into
-a soundcard). If you own such a drive, please mail me the DOS driver (gzipped
-and uuencoded) and the specifications (exact name, address range etc.) I still
-have to confirm my opinion. ;-)
+a soundcard).
-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. :-)
+CreativeLabs has a new drive "CD-200". Support is under construction.
+Drive detection and playing audio should already work. I need qualified
+feedback about the bugs within the data functions or a drive (I never saw
+a CD200).
-It will work with the soundcard interfaces (SB Pro, SB 16, Galaxy, SoundFX,
-...) and/or with the "no-sound" cards (Panasonic CI-101P, LaserMate,
+The quad-speed TEAC CD-55A drive is supported. The routines may still be
+a little bit buggy, but the data rate already reaches 500 kB/sec if you
+set SBP_BUFFER_FRAMES to 64. The drive is able to deliver 600 kB/sec, so
+this has to get a point of work.
+
+This driver is NOT for Mitsumi or Sony or Aztech or Philips or XXX drives,
+and this driver is in no way usable for any new IDE ATAPI drive.
+
+This driver 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, Longshine LCS-6853, 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. I still need feedback about
-this.
+this, or such a card. Anyways, the procedure "boot DOS and wait until
+CONFIG.SYS is done, then use CTL-ALT-DEL to boot Linux" should make it
+work.
+
The interface type has to get configured in /usr/include/linux/sbpcd.h,
because the behavior of some sound card interfaces is different.
-The driver respects different drive firmware releases - my old drive is a 2.11,
-but it should work with "old" drives <2.01 ... >3.00 and with "new" drives
-(which count the releases around 0.75 or 1.00).
+With some TEAC drives I have seen interface cards which seem to lack the
+"drive select" lines; always drive 0 gets addressed. To avoid "mirror drives"
+with such interface cards, set MAX_DRIVES to 1 and jumper your drive to ID 0.
+
+The driver respects all known drive firmware releases - my old drive is a 2.11,
+but it should work with CR-52x drives <2.01 ... >3.00 and with CR-56x drives
+<0.75 .. 5.00.
Up to 4 drives per interface card, and up to 4 interface cards are supported.
-CR-52x ("old"), CR-56x ("new") and LCS 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.
+All supported drive families can be mixed, but the CR-521 drives 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 indirectly to
+its drive ID.
The drive IDs may be selected freely from 0 to 3 - they do not have to be in
consecutive order.
@@ -59,40 +75,38 @@ to change old drives to any ID, too. He writes in this sense:
did not work with other values. If the values are not good,
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 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 (at top
-of sbpcd2.c) accordingly.
+To use more than 4 drives, you simply need a second controller card at a
+different address and a second cable.
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
CDplayer and WorkBone - tell me if it is not compatible with other software.
-With the "new" drive family CR-562 and CR-563, the reading of audio frames is
-possible. This is currently implemented by an IOCTL function which reads only
-up to 4 frames of 2352 bytes at once. Reading more than 1 frame at once misses
-some chunks at each frame boundary. Reading the same frame a second time gives
-different data; the frame data start at a different position. But all read
-bytes are valid, and we always read 98 consecutive chunks (of 24 Bytes) as a
-frame. This lack has to get corrected by higher level software which reads the
-same frame again and tries to find and eliminate overlapping chunks
-(24-byte-pieces).
-
-The transfer rate with reading audio (1-frame-pieces) is as slow as 32 kB/sec.
-This could be better reading bigger chunks, but the "missing" chunks occur at
-the beginning of each single frame.
+With the CR-562 and CR-563 drives, the reading of audio frames is possible.
+This is implemented by an IOCTL function which reads READ_AUDIO frames of
+2352 bytes at once (configurable with the "READ_AUDIO" define, default is 0).
+Reading the same frame a second time gives different data; the frame data
+start at a different position, but all read bytes are valid, and we always
+read 98 consecutive chunks (of 24 Bytes) as a frame. Reading more than 1 frame
+at once possibly misses some chunks at each frame boundary. This lack has to
+get corrected by external, "higher level" software which reads the same frame
+again and tries to find and eliminate overlapping chunks (24-byte-pieces).
+
+The transfer rate with reading audio (1-frame-pieces) currently is very slow.
+This can be better reading bigger chunks, but the "missing" chunks possibly
+occur at the beginning of each single frame.
The software interface possibly may change a bit the day the SCSI driver
supports it too.
-With the CR-562 and CR-563 drives, MultiSession is supported, "ManySession"
-(not recommended, see below) alternatively.
-Photo CDs work, too (the "old" drives like CR-521 can access only the first
+With all but the CR-52x drives, MultiSession is supported.
+Photo CDs work (the "old" drives like CR-521 can access only the first
session of a photoCD).
-At ftp.gwdg.de:/pub/linux/hpcdtoppm/ is Hadmut Danisch's package to convert
-photo CD image files.
+At ftp.gwdg.de:/pub/linux/hpcdtoppm/ you will find Hadmut Danisch's package to
+convert photo CD image files and Gerd Knorr's viewing utility.
-The transfer rate will reach 150 kB/sec with "old" drives and 300 kB/sec with
-double-speed drives. XA (PhotoCD) disks with "old" drives give only 50 kB/sec.
+The transfer rate will reach 150 kB/sec with "old" drives, 300 kB/sec with
+double-speed drives, and about 500 kB/sec with quad speed drives.
+XA (PhotoCD) disks with "old" drives give only 50 kB/sec.
This release is part of the standard kernel and consists of
- this README file
@@ -113,11 +127,13 @@ To install:
Most "compatible" sound cards (for example "Highscreen", "SoundFX"
and "Galaxy") need "SBPRO 0".
The "no-sound" board from OmniCd needs the "SBPRO 1" setup.
+ All other "no-sound" boards need the "SBPRO 0" setup.
The Spea Media FX sound card needs "SBPRO 2".
- sbpcd.c holds some examples in it's auto-probe list.
+ sbpcd.c holds some examples in its auto-probe list.
If you configure "SBPRO" wrong, the playing of audio CDs will work,
but you will not be able to mount a data CD.
a2. Tell the address of your CDROM_PORT (not of the sound port).
+ a3. Set DISTRIBUTION to 0.
b. Additionally for 2.a1 and 2.a2, the setup may be done during
boot time (via the "kernel command line" or "LILO option"):
sbpcd=0x230,SoundBlaster
@@ -125,31 +141,33 @@ To install:
sbpcd=0x320,LaserMate
or
sbpcd=0x330,SPEA
- (these strings are case sensitive!).
This is especially useful if you install a fresh distribution.
2. "cd /usr/src/linux" and do a "make config" and select "y" for Matsushita
CD-ROM support and for ISO9660 FileSystem support. If you do not have a
- second, third, or fourth controller and/or did not prepare sbpcd2 etc.,
- do not say "y" to the secondary Matsushita CD-ROM questions.
+ second, third, or fourth controller installed, do not say "y" to the
+ secondary Matsushita CD-ROM questions.
SCSI and/or SCSI CD-ROM support is not needed.
3. Then do a "make dep", then make the kernel image ("make zlilo" or else).
4. Make the device file(s). The driver uses definitely and exclusive the
MAJOR 25, so do
- mknod /dev/sbpcd b 25 0 (if you have only drive #0)
+ mknod /dev/sbpcd b 25 0 (if you have only one drive)
and/or
mknod /dev/sbpcd0 b 25 0
mknod /dev/sbpcd1 b 25 1
mknod /dev/sbpcd2 b 25 2
mknod /dev/sbpcd3 b 25 3
to make the node(s).
- Take care that you create a node with the same MINOR as your drive ID is.
- So, if the DOS driver tells you have drive id #3, you have to
- mknod /dev/<any_name> b 25 3
+
+ The driver no longer uses the "AT bus style" device numbering; the SCSI
+ scheme is used now; that means, the "first found" drive gets MINOR 0
+ (regardless to its jumpered ID), the "next found" (at the same cable)
+ gets MINOR 1, ...
For a second interface board, you have to make nodes like
mknod /dev/sbpcd4 b 26 0
+ mknod /dev/sbpcd5 b 26 1
and so on. Use the MAJORs 26, 27, 28.
If you further make a link like
@@ -168,29 +186,34 @@ and see the contents of your CD in the /CD directory, and/or hear music with
"workman -c /dev/sbpcd &".
+Using sbpcd as a "loadable module":
+-----------------------------------
+
+If you do NOT select "Matsushita/Panasonic CDROM driver support" during the
+"make config" of your kernel, you can build the "loadable module" sbpcd.o.
+Read /usr/src/linux/README.modules on this.
+
+If sbpcd gets used as a module, the support of more than one interface
+card (i.e. drives 4...15) is disabled.
+
+You can specify interface address and type with the "insmod" command like:
+ # insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x340,0
+or
+ # insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x230,1
+where the last number represents the SBPRO setting (no strings allowed here).
+
+
Things of interest:
-------------------
-The driver is configured to try the SoundBlaster Pro type of interface at
-I/O port 0x0230 first. If this is not appropriate, sbpcd.h should get changed
+The driver is configured to try the LaserMate type of interface at I/O port
+0x0340 first. If this is not appropriate, sbpcd.h should get changed
(you will find the right place - just at the beginning).
-No DMA and no IRQ is used, so the IRQ line stays free for the SB Pro sound
-drivers.
-
-To reduce or increase the amount of kernel messages, edit sbpcd.c and change
-the initialization of the variable "sbpcd_debug". This is the way to get rid
-of the initial warning message block, too.
+No DMA and no IRQ is used.
-With "#define MANY_SESSION 1" (sbpcd.c), the driver can use "many-session" CDs.
-This will work only with "new" drives like CR-562 or CR-563. That is NOT
-multisession - it is a CD with multiple independent sessions, each containing
-block addresses as if it were the only session. With this feature enabled, the
-driver will read the LAST session. Without it, the FIRST session gets read.
-If you would like the support of reading "in-between" sessions, drop me a mail
-and some food for the soul. :-)
-Those "many-session" CDs can get made by CDROM writers like Philips CDD 521.
-If you enable this feature, it is impossible to read true multisession CDs.
+To reduce or increase the amount of kernel messages, edit sbpcd.c and play
+with the "DBG_xxx" switches (initialization of the variable "sbpcd_debug").
The driver uses the "variable BLOCK_SIZE" feature. To use it, you have to
specify "block=2048" as a mount option. Doing this will disable the direct
@@ -204,65 +227,28 @@ do not use block=2048 with those.
At the beginning of sbpcd.c, you will find some "#define"s (f.e. EJECT and
JUKEBOX). With that, you can configure the driver for some special things.
-The following program disables the auto-eject feature during runtime:
-
-/*=================== begin program ========================================*/
-/*
- * set the "eject" switch (enable/disable auto-ejecting)
- *
- * (c) 1994 Eberhard Moenkeberg <emoenke@gwdg.de>
- * may be used & enhanced freely
- *
- * Disables or enables the auto-eject feature at run time.
- * Works only if a CD is in the drive (just like the feature itself ;-)
- * Useful for a "quiet" shutdown or for weird audio player programs.
- */
-#define EJECT 0 /* 0: disable, 1: enable auto-ejecting */
-
-#include <stdio.h>
-#include <sys/ioctl.h>
-#include <linux/cdrom.h>
-
-static char arg=EJECT;
-static int drive;
-static int err;
-
-main(int argc, char *argv[])
-{
-/*
- * open /dev/cdrom
- */
- drive=open("/dev/cdrom", 0);
- if (drive<0)
- {
- fprintf(stderr, "can't open drive /dev/cdrom.\n");
- exit (-1);
- }
-/*
- * set EJECT_SW
- */
- err=ioctl(drive, CDROMEJECT_SW, arg);
- if (err!=0)
- {
- fprintf(stderr, "can't set EJECT_SW (error %d).\n", err);
- exit (-1);
- }
- else
- fprintf(stdout, "EJECT_SW set to %d\n", arg);
-}
-/*===================== end program ========================================*/
+You can use the appended program "cdtester" to set the auto-eject feature
+during runtime. Jeff Tranter's "eject" utility can do this, too (and more)
+for you.
+There is a new ioctl CDROMMULTISESSION to obtain with a user program if
+the CD is an XA disk and - if it is - where the last session starts. The
+"cdtester" program illustrates how to call it.
Auto-probing at boot time:
--------------------------
-The driver does auto-probing at many well-known interface card addresses. The
-idea to do that came from Adam J. Richter (YGGDRASIL). Some well-known
-addresses are excluded from auto-probing because they can cause a hang if an
-ethernet card gets touched.
+The driver does auto-probing at many well-known interface card addresses,
+but not all:
+Some probings can cause a hang if an NE2000 ethernet card gets touched, because
+SBPCD's auto-probing happens before the initialization of the net drivers.
+Those "hazardous" addresses are excluded from auto-probing; the "kernel
+command line" feature has to be used during installation if you have your
+drive at those addresses. The "module" version is allowed to probe at those
+addresses, too.
-This auto-probing looks first at the configured address resp. the address
+The auto-probing looks first at the configured address resp. the address
submitted by the kernel command line. With this, it is possible to use this
driver within installation boot floppies, and for any non-standard address,
too.
@@ -273,20 +259,30 @@ o.k., but you will get I/O errors during mount). In that case, use the "kernel
command line" feature and specify address & type at boot time to find out the
right setup.
-SBPCD's auto-probing happens before the initialization of the net drivers. That
-makes a hang possible if an ethernet card gets touched.
-
For every-day use, address and type should get configured within sbpcd.h. That
will stop the auto-probing due to success with the first try.
+The kernel command "sbpcd=0" suppresses each auto-probing and causes
+the driver not to find any drive; it is meant for people who love sbpcd
+so much that they do not want to miss it, even if they miss the drives. ;-)
+
+If you configure "#define CDROM_PORT 0" in sbpcd.h, the auto-probing is
+initially disabled and needs an explicit kernel command to get activated.
+Once activated, it does not stop before success or end-of-list. This may be
+useful within "universal" CDROM installation boot floppies (but using the
+loadable module would be better because it allows an "extended" auto-probing
+without fearing NE2000 cards).
+
+To shorten the auto-probing list to a single entry, set DISTRIBUTION 0 within
+sbpcd.h.
+
Setting up address and interface type:
--------------------------------------
-If your I/O port address is not 0x0230 or if you use a "no-sound" interface
-other than OmniCD, you have to look for the #defines near the beginning of
-sbpcd.h and configure them: set SBPRO to 0 or 1 or 2, and change CDROM_PORT to
-the address of your CDROM I/O port.
+If your I/O port address is not 0x340, you have to look for the #defines near
+the beginning of sbpcd.h and configure them: set SBPRO to 0 or 1 or 2, and
+change CDROM_PORT to the address of your CDROM I/O port.
Most of the "SoundBlaster compatible" cards behave like the no-sound
interfaces!
@@ -300,19 +296,19 @@ you can set SOUND_BASE (in sbpcd.h) to get it done with your card, too...
Using audio CDs:
----------------
-Workman, WorkBone, xcdplayer and cdplayer should work good now, even with the
-double-speed drives.
+Workman, WorkBone, xcdplayer, cdplayer and the nice little tool "cdplay" (see
+README.aztcd from the Aztech driver package) should work.
The program CDplayer likes to talk to "/dev/mcd" only, xcdplayer wants
-"/dev/rsr0", workman loves "/dev/sr0" - so, do the appropriate links for using
-them without the need of supplying parameters.
+"/dev/rsr0", workman loves "/dev/sr0" or "/dev/cdrom" - so, do the appropriate
+links for using them without the need of supplying parameters.
Copying audio tracks:
---------------------
-The following little program will copy track 2 of an audio CD into the file
-"track02":
+The following program will copy track 1 (or a piece of it) from an audio CD
+into the file "track01":
/*=================== begin program ========================================*/
/*
@@ -335,13 +331,14 @@ The following little program will copy track 2 of an audio CD into the file
* This is only an example of the low-level access routine. The read data are
* pure 16-bit CDDA values; they have to get converted to make sound out of
* them.
+ * It is no fun to listen to it without prior overlap/underlap correction!
*/
#include <stdio.h>
#include <sys/ioctl.h>
#include <linux/cdrom.h>
static struct cdrom_tochdr hdr;
-static struct cdrom_tocentry entry[100];
+static struct cdrom_tocentry entry[101];
static struct cdrom_read_audio arg;
static u_char buffer[CD_FRAMESIZE_RAW];
static int datafile, drive;
@@ -400,10 +397,9 @@ main(int argc, char *argv[])
/*
* ask for track number (not implemented here)
*/
-track=2;
-#if 0 /* just read a little piece */
-entry[track].cdte_addr.lba=170;
-entry[track+1].cdte_addr.lba=190;
+track=1;
+#if 0 /* just read a little piece (4 seconds) */
+entry[track+1].cdte_addr.lba=entry[track].cdte_addr.lba+300;
#endif
/*
* read track into file
@@ -420,20 +416,19 @@ entry[track+1].cdte_addr.lba=190;
arg.nframes=1;
arg.buf=&buffer[0];
limit=entry[track+1].cdte_addr.lba;
- for (i=arg.addr.lba;i<limit;i++)
+ for (;arg.addr.lba<limit;arg.addr.lba++)
{
err=ioctl(drive, CDROMREADAUDIO, &arg);
if (err!=0)
{
- fprintf(stderr, "can't read frame #%d (error %d).\n",
- i-entry[track].cdte_addr.lba+1, err);
- exit (-1);
+ fprintf(stderr, "can't read abs. frame #%d (error %d).\n",
+ arg.addr.lba, err);
}
j=write(datafile, &buffer[0], CD_FRAMESIZE_RAW);
if (j!=CD_FRAMESIZE_RAW)
{
- fprintf(stderr,"I/O error (datafile) at frame %d\n",
- i-entry[track].cdte_addr.lba+1);
+ fprintf(stderr,"I/O error (datafile) at rel. frame %d\n",
+ arg.addr.lba-entry[track].cdte_addr.lba);
}
arg.addr.lba++;
}
@@ -457,10 +452,8 @@ 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
emoenke@gwdg.de
- or to eberhard_moenkeberg@rollo.central.de
or to my FIDO address: Eberhard Moenkeberg, 2:2437/210.27
-
SnailMail address, preferable for CD editors if they want to submit a free
"cooperation" copy:
Eberhard Moenkeberg
@@ -468,3 +461,537 @@ SnailMail address, preferable for CD editors if they want to submit a free
D-37083 Goettingen
Germany
---
+
+
+Appendix -- the "cdtester" utility:
+
+/*
+ * cdtester.c -- test the audio functions of a CD driver
+ *
+ * (c) 1995 Eberhard Moenkeberg <emoenke@gwdg.de>
+ * published under the GPL
+ *
+ * made under heavy use of the "Tiny Audio CD Player"
+ * from Werner Zimmermann <zimmerma@rz.fht-esslingen.de>
+ * (see linux/drivers/block/README.aztcd)
+ */
+#undef AZT_PRIVATE_IOCTLS /* not supported by every CDROM driver */
+#define SBP_PRIVATE_IOCTLS /* not supported by every CDROM driver */
+
+#include <stdio.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <sys/ioctl.h>
+#include <linux/cdrom.h>
+
+#ifdef AZT_PRIVATE_IOCTLS
+#include <linux/aztcd.h>
+#endif AZT_PRIVATE_IOCTLS
+#ifdef SBP_PRIVATE_IOCTLS
+#include <linux/sbpcd.h>
+#include <linux/fs.h>
+#endif SBP_PRIVATE_IOCTLS
+
+struct cdrom_tochdr hdr;
+struct cdrom_tochdr tocHdr;
+struct cdrom_tocentry TocEntry[101];
+struct cdrom_tocentry entry;
+struct cdrom_multisession ms_info;
+struct cdrom_read_audio read_audio;
+struct cdrom_ti ti;
+struct cdrom_subchnl subchnl;
+struct cdrom_msf msf;
+struct cdrom_volctrl volctrl;
+#ifdef AZT_PRIVATE_IOCTLS
+union
+{
+ struct cdrom_msf msf;
+ unsigned char buf[CD_FRAMESIZE_RAW];
+} azt;
+#endif AZT_PRIVATE_IOCTLS
+int i, i1, i2, i3, j, k;
+unsigned char sequence=0;
+unsigned char command[80];
+unsigned char first=1, last=1;
+char *default_device="/dev/cdrom";
+char dev[20];
+char filename[20];
+int drive;
+int datafile;
+int rc;
+
+void help(void)
+{
+ printf("Available Commands:\n");
+ printf("STOP s EJECT e QUIT q\n");
+ printf("PLAY TRACK t PAUSE p RESUME r\n");
+ printf("NEXT TRACK n REPEAT LAST l HELP h\n");
+ printf("SUBCHANNEL_Q c TRACK INFO i PLAY AT a\n");
+ printf("READ d READ RAW w READ AUDIO A\n");
+ printf("MS-INFO M TOC T START S\n");
+ printf("SET EJECTSW X DEVICE D DEBUG Y\n");
+ printf("AUDIO_BUFSIZ Z RESET R BLKRASET B\n");
+ printf("SET VOLUME v GET VOLUME V\n");
+}
+
+/*
+ * convert MSF number (3 bytes only) to Logical_Block_Address
+ */
+int msf2lba(u_char *msf)
+{
+ int i;
+
+ i=(msf[0] * CD_SECS + msf[1]) * CD_FRAMES + msf[2] - CD_BLOCK_OFFSET;
+ if (i<0) return (0);
+ return (i);
+}
+/*
+ * convert logical_block_address to m-s-f_number (3 bytes only)
+ */
+void lba2msf(int lba, unsigned char *msf)
+{
+ lba += CD_BLOCK_OFFSET;
+ msf[0] = lba / (CD_SECS*CD_FRAMES);
+ lba %= CD_SECS*CD_FRAMES;
+ msf[1] = lba / CD_FRAMES;
+ msf[2] = lba % CD_FRAMES;
+}
+
+int init_drive(char *dev)
+{
+ unsigned char msf_ent[3];
+
+ /*
+ * open the device
+ */
+ drive=open(dev,0);
+ if (drive<0) return (-1);
+ /*
+ * get TocHeader
+ */
+ printf("getting TocHeader...\n");
+ rc=ioctl(drive,CDROMREADTOCHDR,&hdr);
+ if (rc!=0)
+ {
+ printf("can't get TocHeader (error %d).\n",rc);
+ return (-2);
+ }
+ else
+ first=hdr.cdth_trk0;
+ last=hdr.cdth_trk1;
+ printf("TocHeader: %d %d\n",hdr.cdth_trk0,hdr.cdth_trk1);
+ /*
+ * get and display all TocEntries
+ */
+ printf("getting TocEntries...\n");
+ for (i=1;i<=hdr.cdth_trk1+1;i++)
+ {
+ if (i!=hdr.cdth_trk1+1) TocEntry[i].cdte_track = i;
+ else TocEntry[i].cdte_track = CDROM_LEADOUT;
+ TocEntry[i].cdte_format = CDROM_LBA;
+ rc=ioctl(drive,CDROMREADTOCENTRY,&TocEntry[i]);
+ if (rc!=0)
+ {
+ printf("can't get TocEntry #%d (error %d).\n",i,rc);
+ }
+ else
+ {
+ lba2msf(TocEntry[i].cdte_addr.lba,&msf_ent[0]);
+ if (TocEntry[i].cdte_track==CDROM_LEADOUT)
+ {
+ printf("TocEntry #%02X: %1X %1X %02d:%02d:%02d (lba: 0x%06X) %02X\n",
+ TocEntry[i].cdte_track,
+ TocEntry[i].cdte_adr,
+ TocEntry[i].cdte_ctrl,
+ msf_ent[0],
+ msf_ent[1],
+ msf_ent[2],
+ TocEntry[i].cdte_addr.lba,
+ TocEntry[i].cdte_datamode);
+ }
+ else
+ {
+ printf("TocEntry #%02d: %1X %1X %02d:%02d:%02d (lba: 0x%06X) %02X\n",
+ TocEntry[i].cdte_track,
+ TocEntry[i].cdte_adr,
+ TocEntry[i].cdte_ctrl,
+ msf_ent[0],
+ msf_ent[1],
+ msf_ent[2],
+ TocEntry[i].cdte_addr.lba,
+ TocEntry[i].cdte_datamode);
+ }
+ }
+ }
+ return (hdr.cdth_trk1); /* number of tracks */
+}
+
+void display(int size,unsigned char *buffer)
+{
+ k=0;
+ getchar();
+ for (i=0;i<(size+1)/16;i++)
+ {
+ printf("%4d:",i*16);
+ for (j=0;j<16;j++)
+ {
+ printf(" %02X",buffer[i*16+j]);
+ }
+ printf(" ");
+ for (j=0;j<16;j++)
+ {
+ if (isalnum(buffer[i*16+j]))
+ printf("%c",buffer[i*16+j]);
+ else
+ printf(".");
+ }
+ printf("\n");
+ k++;
+ if (k>=20)
+ {
+ printf("press ENTER to continue\n");
+ getchar();
+ k=0;
+ }
+ }
+}
+
+main(int argc, char *argv[])
+{
+ printf("\nTesting tool for a CDROM driver's audio functions V0.1\n");
+ printf("(C) 1995 Eberhard Moenkeberg <emoenke@gwdg.de>\n");
+ printf("initializing...\n");
+
+ rc=init_drive(default_device);
+ if (rc<0) printf("could not open %s (rc=%d).\n",default_device,rc);
+ help();
+ while (1)
+ {
+ printf("Give a one-letter command (h = help): ");
+ scanf("%s",command);
+ command[1]=0;
+ switch (command[0])
+ {
+ case 'D':
+ printf("device name (f.e. /dev/sbpcd3): ? ");
+ scanf("%s",&dev);
+ close(drive);
+ rc=init_drive(dev);
+ if (rc<0) printf("could not open %s (rc %d).\n",dev,rc);
+ break;
+ case 'e':
+ rc=ioctl(drive,CDROMEJECT);
+ if (rc<0) printf("CDROMEJECT: rc=%d.\n",rc);
+ break;
+ case 'p':
+ rc=ioctl(drive,CDROMPAUSE);
+ if (rc<0) printf("CDROMPAUSE: rc=%d.\n",rc);
+ break;
+ case 'r':
+ rc=ioctl(drive,CDROMRESUME);
+ if (rc<0) printf("CDROMRESUME: rc=%d.\n",rc);
+ break;
+ case 's':
+ rc=ioctl(drive,CDROMSTOP);
+ if (rc<0) printf("CDROMSTOP: rc=%d.\n",rc);
+ break;
+ case 'S':
+ rc=ioctl(drive,CDROMSTART);
+ if (rc<0) printf("CDROMSTART: rc=%d.\n",rc);
+ break;
+ case 't':
+ rc=ioctl(drive,CDROMREADTOCHDR,&tocHdr);
+ if (rc<0)
+ {
+ printf("CDROMREADTOCHDR: rc=%d.\n",rc);
+ break;
+ }
+ first=tocHdr.cdth_trk0;
+ last= tocHdr.cdth_trk1;
+ if ((first==0)||(first>last))
+ {
+ printf ("--got invalid TOC data.\n");
+ }
+ else
+ {
+ printf("--enter track number(first=%d, last=%d): ",first,last);
+ scanf("%d",&i1);
+ ti.cdti_trk0=i1;
+ if (ti.cdti_trk0<first) ti.cdti_trk0=first;
+ if (ti.cdti_trk0>last) ti.cdti_trk0=last;
+ ti.cdti_ind0=0;
+ ti.cdti_trk1=last;
+ ti.cdti_ind1=0;
+ rc=ioctl(drive,CDROMSTOP);
+ rc=ioctl(drive,CDROMPLAYTRKIND,&ti);
+ if (rc<0) printf("CDROMPLAYTRKIND: rc=%d.\n",rc);
+ }
+ break;
+ case 'n':
+ rc=ioctl(drive,CDROMSTOP);
+ if (++ti.cdti_trk0>last) ti.cdti_trk0=last;
+ ti.cdti_ind0=0;
+ ti.cdti_trk1=last;
+ ti.cdti_ind1=0;
+ rc=ioctl(drive,CDROMPLAYTRKIND,&ti);
+ if (rc<0) printf("CDROMPLAYTRKIND: rc=%d.\n",rc);
+ break;
+ case 'l':
+ rc=ioctl(drive,CDROMSTOP);
+ if (--ti.cdti_trk0<first) ti.cdti_trk0=first;
+ ti.cdti_ind0=0;
+ ti.cdti_trk1=last;
+ ti.cdti_ind1=0;
+ rc=ioctl(drive,CDROMPLAYTRKIND,&ti);
+ if (rc<0) printf("CDROMPLAYTRKIND: rc=%d.\n",rc);
+ break;
+ case 'c':
+ subchnl.cdsc_format=CDROM_MSF;
+ rc=ioctl(drive,CDROMSUBCHNL,&subchnl);
+ if (rc<0) printf("CDROMSUBCHNL: rc=%d.\n",rc);
+ else
+ {
+ printf("AudioStatus:%s Track:%d Mode:%d MSF=%02d:%02d:%02d\n",
+ subchnl.cdsc_audiostatus==CDROM_AUDIO_PLAY ? "PLAYING":"NOT PLAYING",
+ subchnl.cdsc_trk,subchnl.cdsc_adr,
+ subchnl.cdsc_absaddr.msf.minute,
+ subchnl.cdsc_absaddr.msf.second,
+ subchnl.cdsc_absaddr.msf.frame);
+ }
+ break;
+ case 'i':
+ printf("Track No.: ");
+ scanf("%d",&i1);
+ entry.cdte_track=i1;
+ if (entry.cdte_track<first) entry.cdte_track=first;
+ if (entry.cdte_track>last) entry.cdte_track=last;
+ entry.cdte_format=CDROM_MSF;
+ rc=ioctl(drive,CDROMREADTOCENTRY,&entry);
+ if (rc<0) printf("CDROMREADTOCENTRY: rc=%d.\n",rc);
+ else
+ {
+ printf("Mode %d Track, starts at %02d:%02d:%02d\n",
+ entry.cdte_adr,
+ entry.cdte_addr.msf.minute,
+ entry.cdte_addr.msf.second,
+ entry.cdte_addr.msf.frame);
+ }
+ break;
+ case 'a':
+ printf("Address (min:sec:frm) ");
+ scanf("%d:%d:%d",&i1,&i2,&i3);
+ msf.cdmsf_min0=i1;
+ msf.cdmsf_sec0=i2;
+ msf.cdmsf_frame0=i3;
+ if (msf.cdmsf_sec0>59) msf.cdmsf_sec0=59;
+ if (msf.cdmsf_frame0>74) msf.cdmsf_frame0=74;
+ lba2msf(TocEntry[last+1].cdte_addr.lba-1,&msf.cdmsf_min1);
+ rc=ioctl(drive,CDROMSTOP);
+ rc=ioctl(drive,CDROMPLAYMSF,&msf);
+ if (rc<0) printf("CDROMPLAYMSF: rc=%d.\n",rc);
+ break;
+#ifdef AZT_PRIVATE_IOCTLS /*not supported by every CDROM driver*/
+ case 'd':
+ printf("Address (min:sec:frm) ");
+ scanf("%d:%d:%d",&i1,&i2,&i3);
+ azt.msf.cdmsf_min0=i1;
+ azt.msf.cdmsf_sec0=i2;
+ azt.msf.cdmsf_frame0=i3;
+ if (azt.msf.cdmsf_sec0>59) azt.msf.cdmsf_sec0=59;
+ if (azt.msf.cdmsf_frame0>74) azt.msf.cdmsf_frame0=74;
+ rc=ioctl(drive,CDROMREADMODE1,&azt.msf);
+ if (rc<0) printf("CDROMREADMODE1: rc=%d.\n",rc);
+ else display(CD_FRAMESIZE,azt.buf);
+ break;
+ case 'w':
+ printf("Address (min:sec:frame) ");
+ scanf("%d:%d:%d",&i1,&i2,&i3);
+ azt.msf.cdmsf_min0=i1;
+ azt.msf.cdmsf_sec0=i2;
+ azt.msf.cdmsf_frame0=i3;
+ if (azt.msf.cdmsf_sec0>59) azt.msf.cdmsf_sec0=59;
+ if (azt.msf.cdmsf_frame0>74) azt.msf.cdmsf_frame0=74;
+ rc=ioctl(drive,CDROMREADMODE2,&azt.msf);
+ if (rc<0) printf("CDROMREADMODE2: rc=%d.\n",rc);
+ else display(CD_FRAMESIZE_RAW,azt.buf); /* currently only 2336 */
+ break;
+#endif
+ case 'v':
+ printf("--Channel 0 (Left) (0-255): ");
+ scanf("%d",&i1);
+ volctrl.channel0=i1;
+ printf("--Channel 1 (Right) (0-255): ");
+ scanf("%d",&i1);
+ volctrl.channel1=i1;
+ volctrl.channel2=0;
+ volctrl.channel3=0;
+ rc=ioctl(drive,CDROMVOLCTRL,&volctrl);
+ if (rc<0) printf("CDROMVOLCTRL: rc=%d.\n",rc);
+ break;
+ case 'q':
+ close(drive);
+ exit(0);
+ case 'h':
+ help();
+ break;
+ case 'T': /* display TOC entry - without involving the driver */
+ scanf("%d",&i);
+ if ((i<hdr.cdth_trk0)||(i>hdr.cdth_trk1))
+ printf("invalid track number.\n");
+ else
+ printf("TocEntry %02d: adr=%01X ctrl=%01X msf=%02d:%02d:%02d mode=%02X\n",
+ TocEntry[i].cdte_track,
+ TocEntry[i].cdte_adr,
+ TocEntry[i].cdte_ctrl,
+ TocEntry[i].cdte_addr.msf.minute,
+ TocEntry[i].cdte_addr.msf.second,
+ TocEntry[i].cdte_addr.msf.frame,
+ TocEntry[i].cdte_datamode);
+ break;
+ case 'A': /* read audio data into file */
+ printf("Address (min:sec:frm) ? ");
+ scanf("%d:%d:%d",&i1,&i2,&i3);
+ read_audio.addr.msf.minute=i1;
+ read_audio.addr.msf.second=i2;
+ read_audio.addr.msf.frame=i3;
+ read_audio.addr_format=CDROM_MSF;
+ printf("# of frames ? ");
+ scanf("%d",&i1);
+ read_audio.nframes=i1;
+ k=read_audio.nframes*CD_FRAMESIZE_RAW;
+ read_audio.buf=malloc(k);
+ if (read_audio.buf==NULL)
+ {
+ printf("can't malloc %d bytes.\n",k);
+ break;
+ }
+ sprintf(filename,"audio_%02d%02d%02d_%02d.%02d\0",
+ read_audio.addr.msf.minute,
+ read_audio.addr.msf.second,
+ read_audio.addr.msf.frame,
+ read_audio.nframes,
+ ++sequence);
+ datafile=creat(filename, 0755);
+ if (datafile<0)
+ {
+ printf("can't open datafile %s.\n",filename);
+ break;
+ }
+ rc=ioctl(drive,CDROMREADAUDIO,&read_audio);
+ if (rc!=0)
+ {
+ printf("CDROMREADAUDIO: rc=%d.\n",rc);
+ }
+ else
+ {
+ rc=write(datafile,&read_audio.buf,k);
+ if (rc!=k) printf("datafile I/O error (%d).\n",rc);
+ }
+ close(datafile);
+ break;
+ case 'X': /* set EJECT_SW (0: disable, 1: enable auto-ejecting) */
+ scanf("%d",&i);
+ rc=ioctl(drive,CDROMEJECT_SW,i);
+ if (rc!=0)
+ printf("CDROMEJECT_SW: rc=%d.\n",rc);
+ else
+ printf("EJECT_SW set to %d\n",i);
+ break;
+ case 'M': /* get the multisession redirection info */
+ ms_info.addr_format=CDROM_LBA;
+ rc=ioctl(drive,CDROMMULTISESSION,&ms_info);
+ if (rc!=0)
+ {
+ printf("CDROMMULTISESSION(lba): rc=%d.\n",rc);
+ }
+ else
+ {
+ if (ms_info.xa_flag) printf("MultiSession offset (lba): %d (0x%06X)\n",ms_info.addr.lba,ms_info.addr.lba);
+ else
+ {
+ printf("this CD is not an XA disk.\n");
+ break;
+ }
+ }
+ ms_info.addr_format=CDROM_MSF;
+ rc=ioctl(drive,CDROMMULTISESSION,&ms_info);
+ if (rc!=0)
+ {
+ printf("CDROMMULTISESSION(msf): rc=%d.\n",rc);
+ }
+ else
+ {
+ if (ms_info.xa_flag)
+ printf("MultiSession offset (msf): %02d:%02d:%02d (0x%02X%02X%02X)\n",
+ ms_info.addr.msf.minute,
+ ms_info.addr.msf.second,
+ ms_info.addr.msf.frame,
+ ms_info.addr.msf.minute,
+ ms_info.addr.msf.second,
+ ms_info.addr.msf.frame);
+ else printf("this CD is not an XA disk.\n");
+ }
+ break;
+#ifdef SBP_PRIVATE_IOCTLS
+ case 'Y': /* set the driver's message level */
+#if 0 /* not implemented yet */
+ printf("enter switch name (f.e. DBG_CMD): ");
+ scanf("%s",&dbg_switch);
+ j=get_dbg_num(dbg_switch);
+#else
+ printf("enter DDIOCSDBG switch number: ");
+ scanf("%d",&j);
+#endif
+ printf("enter 0 for \"off\", 1 for \"on\": ");
+ scanf("%d",&i);
+ if (i==0) j|=0x80;
+ printf("calling \"ioctl(drive,DDIOCSDBG,%d)\"\n",j);
+ rc=ioctl(drive,DDIOCSDBG,j);
+ printf("DDIOCSDBG: rc=%d.\n",rc);
+ break;
+ case 'Z': /* set the audio buffer size */
+ printf("# frames wanted: ? ");
+ scanf("%d",&j);
+ rc=ioctl(drive,CDROMAUDIOBUFSIZ,j);
+ printf("%d frames granted.\n",rc);
+ break;
+ case 'V':
+ rc=ioctl(drive,CDROMVOLREAD,&volctrl);
+ if (rc<0) printf("CDROMVOLCTRL: rc=%d.\n",rc);
+ printf("Volume: channel 0 (left) %d, channel 1 (right) %d\n",volctrl.channel0,volctrl.channel1);
+ break;
+ case 'R':
+ rc=ioctl(drive,CDROMRESET);
+ if (rc<0) printf("CDROMRESET: rc=%d.\n",rc);
+ break;
+ case 'B': /* set the driver's (?) read ahead value */
+ printf("enter read-ahead size: ? ");
+ scanf("%d",&i);
+ rc=ioctl(drive,BLKRASET,i);
+ if (rc<0) printf("BLKRASET: rc=%d.\n",rc);
+ break;
+#endif SBP_PRIVATE_IOCTLS
+ default:
+ printf("unknown command: \"%s\".\n",command);
+ break;
+ }
+ }
+}
+/*==========================================================================*/
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
+
diff --git a/drivers/block/README.sonycd535 b/drivers/block/README.sonycd535
new file mode 100644
index 000000000..ca5c371fd
--- /dev/null
+++ b/drivers/block/README.sonycd535
@@ -0,0 +1,119 @@
+ README FOR LINUX SONY CDU-535/531 DRIVER
+ ========================================
+
+This is the the Sony CDU-535 (and 531) driver version 0.7 for Linux.
+I do not think I have the documentation to add features like DMA support
+so if anyone else wants to pursue it or help me with it, please do.
+(I need to see what was done for the CDU-31A driver -- perhaps I can
+steal some of that code.)
+
+This is a Linux device driver for the Sony CDU-535 CDROM drive. This is
+one of the older Sony drives with its own interface card (Sony bus).
+The DOS driver for this drive is named SONY_CDU.SYS - when you boot DOS
+your drive should be identified as a SONY CDU-535. The driver works
+with a CDU-531 also. One user reported that the driver worked on drives
+OEM'ed by Procomm, drive and interface board were labelled Procomm.
+
+The Linux driver is based on Corey Minyard's sonycd 0.3 driver for
+the CDU-31A. Ron Jeppesen just changed the commands that were sent
+to the drive to correspond to the CDU-535 commands and registers.
+There were enough changes to let bugs creep in but it seems to be stable.
+Ron was able to tar an entire CDROM (should read all blocks) and built
+ghostview and xfig off Walnut Creek's X11R5/GNU CDROM. xcdplayer and
+workman work with the driver. Others have used the driver without
+problems except those dealing with wait loops (fixed in third release).
+Like Minyard's original driver this one uses a polled interface (this
+is also the default setup for the DOS driver). It has not been tried
+with interrupts or DMA enabled on the board.
+
+REQUIREMENTS
+============
+
+ - Sony CDU-535 drive, preferably without interrupts and DMA
+ enabled on the card.
+
+ - Drive must be set up as unit 1. Only the first unit will be
+ recognized
+
+ - you must enter your interface address into
+ /usr/src/linux/include/linux/sonycd535.h and build the
+ appropriate kernel or use the "kernel command line" parameter
+ sonycd535=0x320
+ with the correct interface address.
+
+NOTES:
+======
+
+1) The drive MUST be turned on when booting or it will not be recognized!
+ (but see comments on modularized version below)
+
+2) when the cdrom device is opened the eject button is disabled to keep the
+ user from ejecting a mounted disk and replacing it with another.
+ Unfortunately xcdplayer and workman also open the cdrom device so you
+ have to use the eject button in the software. Keep this in mind if your
+ cdrom player refuses to give up its disk -- exit workman or xcdplayer, or
+ umount the drive if it has been mounted.
+
+THANKS
+======
+
+Many thanks to Ron Jeppesen (ronj.an@site007.saic.com) for getting
+this project off the ground. He wrote the initial release
+and the first two patches to this driver (0.1, 0.2, and 0.3).
+Thanks also to Eberhard Moenkeberg (emoenke@gwdg.de) for prodding
+me to place this code into the mainstream Linux source tree
+(as of Linux version 1.1.91), as well as some patches to make
+it a better device citizen. Further thanks to "S. Joel Katz"
+<stimpson@panix.com> for his MODULE patches (see details below),
+Porfiri Claudio <C.Porfiri@nisms.tei.ericsson.se> for patches
+to make the driver work with the older CDU-510/515 series, and
+Heiko Eissfeldt <heiko@colossus.escape.de> for pointing out that
+the verify_area() checks were ignoring the results of said checks.
+
+(Acknowledgments from Ron Jeppesen in the 0.3 release:)
+Thanks to Corey Minyard who wrote the original CDU-31A driver on which
+this driver is based. Thanks to Ken Pizzini and Bob Blair who provided
+patches and feedback on the first release of this driver.
+
+Ken Pizzini
+ken@halcyon.com
+
+------------------------------------------------------------------------------
+(The following is from Joel Katz <Stimpson@Panix.COM>.)
+
+ To build a version of sony535.o that can be installed as a module,
+use the following command:
+
+gcc -c -D__KERNEL__ -DMODULE -O2 sonycd535.c -o sonycd535.o
+
+ To install the module, simply type:
+
+insmod sony535.o
+
+ And to remove it:
+
+rmmod sony535
+
+ The code checks to see if MODULE is defined and behaves as it used
+to if MODULE is not defined. That means your patched file should behave
+exactly as it used to if compiled into the kernel.
+
+ I have an external drive, and I usually leave it powered off. I used
+to have to reboot if I needed to use the CDROM drive. Now I don't.
+
+ Even if you have an internal drive, why waste the 268K of memory
+(unswappable) that the driver uses if you use your CD-ROM drive infrequently?
+
+ This driver will not install (whether compiled in or loaded as a
+module) if the CDROM drive is not available during its initialization. This
+means that you can have the driver compiled into the kernel and still load
+the module later (assuming the driver doesn't install itself during
+power-on). This only wastes 12K when you boot with the CDROM drive off.
+
+ This is what I usually do; I leave the driver compiled into the
+kernel, but load it as a module if I powered the system up with the drive
+off and then later decided to use the CDROM drive.
+
+ Since the driver only uses a single page to point to the chunks,
+attempting to set the buffer cache to more than 2 Megabytes would be very
+bad; don't do that.
diff --git a/drivers/block/aztcd.c b/drivers/block/aztcd.c
new file mode 100644
index 000000000..66e289452
--- /dev/null
+++ b/drivers/block/aztcd.c
@@ -0,0 +1,1713 @@
+#define AZT_VERSION "V1.0"
+/* $Id: aztcd.c,v 1.0 1995/03/25 08:27:11 root Exp $
+ linux/drivers/block/aztcd.c - AztechCD268 CDROM driver
+
+ Copyright (C) 1994,1995 Werner Zimmermann (zimmerma@rz.fht-esslingen.de)
+
+ based on Mitsumi CDROM driver by Martin Hariss and preworks by
+ Eberhard Moenkeberg; contains contributions by Joe Nardone and Robby
+ Schirmer.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ HISTORY
+ V0.0 Adaption to Adaptec CD268-01A Version 1.3
+ Version is PRE_ALPHA, unresolved points:
+ 1. I use busy wait instead of timer wait in STEN_LOW,DTEN_LOW
+ thus driver causes CPU overhead and is very slow
+ 2. could not find a way to stop the drive, when it is
+ in data read mode, therefore I had to set
+ msf.end.min/sec/frame to 0:0:1 (in azt_poll); so only one
+ frame can be read in sequence, this is also the reason for
+ 3. getting 'timeout in state 4' messages, but nevertheless
+ it works
+ W.Zimmermann, Oct. 31, 1994
+ V0.1 Version is ALPHA, problems #2 and #3 resolved.
+ W.Zimmermann, Nov. 3, 1994
+ V0.2 Modification to some comments, debugging aids for partial test
+ with Borland C under DOS eliminated. Timer interrupt wait
+ STEN_LOW_WAIT additionally to busy wait for STEN_LOW implemented;
+ use it only for the 'slow' commands (ACMD_GET_Q_CHANNEL, ACMD_
+ SEEK_TO_LEAD_IN), all other commands are so 'fast', that busy
+ waiting seems better to me than interrupt rescheduling.
+ Besides that, when used in the wrong place, STEN_LOW_WAIT causes
+ kernel panic.
+ In function aztPlay command ACMD_PLAY_AUDIO added, should make
+ audio functions work. The Aztech drive needs different commands
+ to read data tracks and play audio tracks.
+ W.Zimmermann, Nov. 8, 1994
+ V0.3 Recognition of missing drive during boot up improved (speeded up).
+ W.Zimmermann, Nov. 13, 1994
+ V0.35 Rewrote the control mechanism in azt_poll (formerly mcd_poll)
+ including removal of all 'goto' commands. :-);
+ J. Nardone, Nov. 14, 1994
+ V0.4 Renamed variables and constants to 'azt' instead of 'mcd'; had
+ to make some "compatibility" defines in azt.h; please note,
+ that the source file was renamed to azt.c, the include file to
+ azt.h
+ Speeded up drive recognition during init (will be a little bit
+ slower than before if no drive is installed!); suggested by
+ Robby Schirmer.
+ read_count declared volatile and set to AZT_BUF_SIZ to make
+ drive faster (now 300kB/sec, was 60kB/sec before, measured
+ by 'time dd if=/dev/cdrom of=/dev/null bs=2048 count=4096';
+ different AZT_BUF_SIZes were test, above 16 no further im-
+ provement seems to be possible; suggested by E.Moenkeberg.
+ W.Zimmermann, Nov. 18, 1994
+ V0.42 Included getAztStatus command in GetQChannelInfo() to allow
+ reading Q-channel info on audio disks, if drive is stopped,
+ and some other bug fixes in the audio stuff, suggested by
+ Robby Schirmer.
+ Added more ioctls (reading data in mode 1 and mode 2).
+ Completely removed the old azt_poll() routine.
+ Detection of ORCHID CDS-3110 in aztcd_init implemented.
+ Additional debugging aids (see the readme file).
+ W.Zimmermann, Dec. 9, 1994
+ V0.50 Autodetection of drives implemented.
+ W.Zimmermann, Dec. 12, 1994
+ V0.52 Prepared for including in the standard kernel, renamed most
+ variables to contain 'azt', included autoconf.h
+ W.Zimmermann, Dec. 16, 1994
+ V0.6 Version for being included in the standard Linux kernel.
+ Renamed source and header file to aztcd.c and aztcd.h
+ W.Zimmermann, Dec. 24, 1994
+ V0.7 Changed VERIFY_READ to VERIFY_WRITE in aztcd_ioctl, case
+ CDROMREADMODE1 and CDROMREADMODE2; bug fix in the ioctl,
+ which causes kernel crashes when playing audio, changed
+ include-files (config.h instead of autoconf.h, removed
+ delay.h)
+ W.Zimmermann, Jan. 8, 1995
+ V0.72 Some more modifications for adaption to the standard kernel.
+ W.Zimmermann, Jan. 16, 1995
+ V0.80 aztcd is now part of the standard kernel since version 1.1.83.
+ Modified the SET_TIMER and CLEAR_TIMER macros to comply with
+ the new timer scheme.
+ W.Zimmermann, Jan. 21, 1995
+ V0.90 Included CDROMVOLCTRL, but with my Aztech drive I can only turn
+ the channels on and off. If it works better with your drive,
+ please mail me. Also implemented ACMD_CLOSE for CDROMSTART.
+ W.Zimmermann, Jan. 24, 1995
+ V1.00 Implemented close and lock tray commands. Patches supplied by
+ Frank Racis
+ Added support for loadable MODULEs, so aztcd can now also be
+ loaded by insmod and removed by rmmod during run time
+ Werner Zimmermann, Mar. 24, 95
+ NOTE:
+ Points marked with ??? are questionable !
+*/
+#include <linux/major.h>
+#include <linux/config.h>
+
+#ifdef MODULE
+# include <linux/module.h>
+# include <linux/version.h>
+# ifndef CONFIG_MODVERSIONS
+ char kernel_version[]= UTS_RELEASE;
+# endif
+#endif
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/cdrom.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#define MAJOR_NR AZTECH_CDROM_MAJOR
+
+#ifdef MODULE
+# include "/usr/src/linux/drivers/block/blk.h"
+#else
+# include "blk.h"
+# define MOD_INC_USE_COUNT
+# define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/aztcd.h>
+
+static int aztPresent = 0;
+
+#if 0
+#define AZT_TEST1 /* <int-..> */
+#define AZT_TEST2 /* do_aztcd_request */
+#define AZT_TEST3 /* AZT_S_state */
+#define AZT_TEST4 /* QUICK_LOOP-counter */
+#define AZT_TEST5 /* port(1) state */
+#define AZT_DEBUG
+#endif
+
+#define CURRENT_VALID \
+ (CURRENT && MAJOR(CURRENT -> dev) == MAJOR_NR && CURRENT -> cmd == READ \
+ && CURRENT -> sector != -1)
+
+#define AFL_STATUSorDATA (AFL_STATUS | AFL_DATA)
+#define AZT_BUF_SIZ 16
+
+static volatile int azt_transfer_is_active=0;
+
+static char azt_buf[2048*AZT_BUF_SIZ]; /*buffer for block size conversion*/
+#ifdef AZT_PRIVATE_IOCTLS
+static char buf[2336]; /*separate buffer for the ioctls*/
+#endif
+
+static volatile int azt_buf_bn[AZT_BUF_SIZ], azt_next_bn;
+static volatile int azt_buf_in, azt_buf_out = -1;
+static volatile int azt_error=0;
+static int azt_open_count=0;
+enum azt_state_e {
+ AZT_S_IDLE, /* 0 */
+ AZT_S_START, /* 1 */
+ AZT_S_MODE, /* 2 */
+ AZT_S_READ, /* 3 */
+ AZT_S_DATA, /* 4 */
+ AZT_S_STOP, /* 5 */
+ AZT_S_STOPPING /* 6 */
+};
+static volatile enum azt_state_e azt_state = AZT_S_IDLE;
+#ifdef AZT_TEST3
+static volatile enum azt_state_e azt_state_old = AZT_S_STOP;
+static volatile int azt_st_old = 0;
+#endif
+
+static int azt_mode = -1;
+static int ACMD_DATA_READ= ACMD_PLAY_READ;
+static volatile int azt_read_count = 1;
+
+#define READ_TIMEOUT 3000
+
+#define azt_port aztcd /*needed for the modutils*/
+static short azt_port = AZT_BASE_ADDR;
+
+static char azt_cont = 0;
+static char azt_init_end = 0;
+
+static int AztTimeout, AztTries;
+static struct wait_queue *azt_waitq = NULL;
+static struct timer_list delay_timer = { NULL, NULL, 0, 0, NULL };
+
+static struct azt_DiskInfo DiskInfo;
+static struct azt_Toc Toc[MAX_TRACKS];
+static struct azt_Play_msf azt_Play;
+
+static int aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+static char aztDiskChanged = 1;
+static char aztTocUpToDate = 0;
+
+
+static void azt_transfer(void);
+static void azt_poll(void);
+static void azt_invalidate_buffers(void);
+static void do_aztcd_request(void);
+static void azt_hsg2msf(long hsg, struct msf *msf);
+static void azt_bin2bcd(unsigned char *p);
+static int azt_bcd2bin(unsigned char bcd);
+static int aztStatus(void);
+static int getAztStatus(void);
+static int aztSendCmd(int cmd);
+static int sendAztCmd(int cmd, struct azt_Play_msf *params);
+static int aztGetQChannelInfo(struct azt_Toc *qp);
+static int aztUpdateToc(void);
+static int aztGetDiskInfo(void);
+static int aztGetToc(void);
+static int aztGetValue(unsigned char *result);
+static void aztStatTimer(void);
+static void aztCloseDoor(void);
+static void aztLockDoor(void);
+static void aztUnlockDoor(void);
+
+static unsigned char aztIndatum;
+static unsigned long aztTimeOutCount;
+
+/* Macros for the drive hardware interface handshake, these macros use
+ busy waiting */
+/* Wait for OP_OK = drive answers with AFL_OP_OK after receiving a command*/
+# define OP_OK op_ok()
+void op_ok(void)
+{ aztTimeOutCount=0;
+ do { aztIndatum=inb(DATA_PORT);
+ aztTimeOutCount++;
+ if (aztTimeOutCount>=AZT_TIMEOUT)
+ { printk("aztcd: Error Wait OP_OK\n");
+ break;
+ }
+ } while (aztIndatum!=AFL_OP_OK);
+}
+
+/* Wait for PA_OK = drive answers with AFL_PA_OK after receiving parameters*/
+# define PA_OK pa_ok()
+void pa_ok(void)
+{ aztTimeOutCount=0;
+ do { aztIndatum=inb(DATA_PORT);
+ aztTimeOutCount++;
+ if (aztTimeOutCount>=AZT_TIMEOUT)
+ { printk("aztcd: Error Wait PA_OK\n");
+ break;
+ }
+ } while (aztIndatum!=AFL_PA_OK);
+}
+
+/* Wait for STEN=Low = handshake signal 'AFL_.._OK available or command executed*/
+# define STEN_LOW sten_low()
+void sten_low(void)
+{ aztTimeOutCount=0;
+ do { aztIndatum=inb(STATUS_PORT);
+ aztTimeOutCount++;
+ if (aztTimeOutCount>=AZT_TIMEOUT)
+ { if (azt_init_end) printk("aztcd: Error Wait STEN_LOW\n");
+ break;
+ }
+ } while (aztIndatum&AFL_STATUS);
+}
+
+/* Wait for DTEN=Low = handshake signal 'Data available'*/
+# define DTEN_LOW dten_low()
+void dten_low(void)
+{ aztTimeOutCount=0;
+ do { aztIndatum=inb(STATUS_PORT);
+ aztTimeOutCount++;
+ if (aztTimeOutCount>=AZT_TIMEOUT)
+ { printk("aztcd: Error Wait DTEN_OK\n");
+ break;
+ }
+ } while (aztIndatum&AFL_DATA);
+}
+
+/*
+ * Macro for timer wait on STEN=Low, should only be used for 'slow' commands;
+ * may cause kernel panic when used in the wrong place
+*/
+#define STEN_LOW_WAIT statusAzt()
+void statusAzt(void)
+{ AztTimeout = AZT_STATUS_DELAY;
+ SET_TIMER(aztStatTimer, 1);
+ sleep_on(&azt_waitq);
+ if (AztTimeout <= 0) printk("aztcd: Error Wait STEN_LOW_WAIT\n");
+ return;
+}
+
+static void aztStatTimer(void)
+{ if (!(inb(STATUS_PORT) & AFL_STATUS))
+ { wake_up(&azt_waitq);
+ return;
+ }
+ AztTimeout--;
+ if (AztTimeout <= 0)
+ { wake_up(&azt_waitq);
+ return;
+ }
+ SET_TIMER(aztStatTimer, 1);
+}
+
+
+void aztcd_setup(char *str, int *ints)
+{ if (ints[0] > 0)
+ azt_port = ints[1];
+ if (ints[0] > 1)
+ azt_cont = ints[2];
+}
+
+/*
+ * Subroutines to automatically close the door (tray) and
+ * lock it closed when the cd is mounted. Leave the tray
+ * locking as an option
+ */
+static void aztCloseDoor(void)
+{
+ aztSendCmd(ACMD_CLOSE);
+ STEN_LOW;
+ return;
+}
+
+static void aztLockDoor(void)
+{
+#ifdef AZT_ALLOW_TRAY_LOCK
+ aztSendCmd(ACMD_LOCK);
+ STEN_LOW;
+#endif
+ return;
+}
+
+static void aztUnlockDoor(void)
+{
+#ifdef AZT_ALLOW_TRAY_LOCK
+ aztSendCmd(ACMD_UNLOCK);
+ STEN_LOW;
+#endif
+ return;
+}
+
+/*
+ * Send a single command, return -1 on error, else 0
+*/
+static int aztSendCmd(int cmd)
+{ unsigned char data;
+ int retry;
+
+#ifdef AZT_DEBUG
+ printk("aztcd: Executing command %x\n",cmd);
+#endif
+ outb(POLLED,MODE_PORT);
+ do { if (inb(STATUS_PORT)&AFL_STATUS) break;
+ inb(DATA_PORT); /* if status left from last command, read and */
+ } while (1); /* discard it */
+ do { if (inb(STATUS_PORT)&AFL_DATA) break;
+ inb(DATA_PORT); /* if data left from last command, read and */
+ } while (1); /* discard it */
+ for (retry=0;retry<AZT_RETRY_ATTEMPTS;retry++)
+ { outb((unsigned char) cmd,CMD_PORT);
+ STEN_LOW;
+ data=inb(DATA_PORT);
+ if (data==AFL_OP_OK)
+ { return 0;} /*OP_OK?*/
+ if (data==AFL_OP_ERR)
+ { STEN_LOW;
+ data=inb(DATA_PORT);
+ printk("### Error 1 aztcd: aztSendCmd %x Error Code %x\n",cmd,data);
+ }
+ }
+ if (retry>=AZT_RETRY_ATTEMPTS)
+ { printk("### Error 2 aztcd: aztSendCmd %x \n",cmd);
+ azt_error=0xA5;
+ }
+ return -1;
+}
+
+/*
+ * Send a play or read command to the drive, return -1 on error, else 0
+*/
+static int sendAztCmd(int cmd, struct azt_Play_msf *params)
+{ unsigned char data;
+ int retry;
+
+#ifdef AZT_DEBUG
+ printk("start=%02x:%02x:%02x end=%02x:%02x:%02x\n", \
+ params->start.min, params->start.sec, params->start.frame, \
+ params->end.min, params->end.sec, params->end.frame);
+#endif
+ for (retry=0;retry<AZT_RETRY_ATTEMPTS;retry++)
+ { aztSendCmd(cmd);
+ outb(params -> start.min,CMD_PORT);
+ outb(params -> start.sec,CMD_PORT);
+ outb(params -> start.frame,CMD_PORT);
+ outb(params -> end.min,CMD_PORT);
+ outb(params -> end.sec,CMD_PORT);
+ outb(params -> end.frame,CMD_PORT);
+ STEN_LOW;
+ data=inb(DATA_PORT);
+ if (data==AFL_PA_OK)
+ { return 0;} /*PA_OK ?*/
+ if (data==AFL_PA_ERR)
+ { STEN_LOW;
+ data=inb(DATA_PORT);
+ printk("### Error 1 aztcd: sendAztCmd %x Error Code %x\n",cmd,data);
+ }
+ }
+ if (retry>=AZT_RETRY_ATTEMPTS)
+ { printk("### Error 2 aztcd: sendAztCmd %x\n ",cmd);
+ azt_error=0xA5;
+ }
+ return -1;
+}
+
+
+/*
+ * Checking if the media has been changed not yet implemented
+*/
+static int check_aztcd_media_change(dev_t full_dev)
+{ return 0;
+}
+
+
+/* used in azt_poll to poll the status, expects another program to issue a
+ * ACMD_GET_STATUS directly before
+ */
+static int aztStatus(void)
+{ int st;
+ int i;
+
+ i = inb(STATUS_PORT) & AFL_STATUS; /* is STEN=0? ???*/
+ if (!i)
+ {
+ st = inb(DATA_PORT) & 0xFF;
+ return st;
+ }
+ else
+ return -1;
+}
+
+/*
+ * Get the drive status
+ */
+static int getAztStatus(void)
+{ int st;
+
+ if (aztSendCmd(ACMD_GET_STATUS)) return -1;
+ STEN_LOW;
+ st = inb(DATA_PORT) & 0xFF;
+#ifdef AZT_DEBUG
+ printk("aztcd: Status = %x\n",st);
+#endif
+ if ((st == 0xFF)||(st&AST_CMD_CHECK))
+ { printk("aztcd: AST_CMD_CHECK error or no status available\n");
+ return -1;
+ }
+
+ if (((st&AST_MODE_BITS)!=AST_BUSY) && (aztAudioStatus == CDROM_AUDIO_PLAY))
+ /* XXX might be an error? look at q-channel? */
+ aztAudioStatus = CDROM_AUDIO_COMPLETED;
+
+ if (st & AST_DSK_CHG)
+ {
+ aztDiskChanged = 1;
+ aztTocUpToDate = 0;
+ aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+ }
+ return st;
+}
+
+
+/*
+ * Send a 'Play' command and get the status. Use only from the top half.
+ */
+static int aztPlay(struct azt_Play_msf *arg)
+{ if (sendAztCmd(ACMD_PLAY_AUDIO, arg) < 0) return -1;
+ return 0;
+}
+
+
+long azt_msf2hsg(struct msf *mp)
+{
+#ifdef AZT_DEBUG
+ if (mp->min >=70) printk("aztcd: Error msf2hsg address Minutes\n");
+ if (mp->sec >=60) printk("aztcd: Error msf2hsg address Seconds\n");
+ if (mp->frame>=75) printk("aztcd: Error msf2hsg address Frames\n");
+#endif
+ return azt_bcd2bin(mp -> frame)
+ + azt_bcd2bin(mp -> sec) * 75
+ + azt_bcd2bin(mp -> min) * 4500
+ - 150;
+}
+
+static int aztcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
+{ int i, st;
+ struct azt_Toc qInfo;
+ struct cdrom_ti ti;
+ struct cdrom_tochdr tocHdr;
+ struct cdrom_msf msf;
+ struct cdrom_tocentry entry;
+ struct azt_Toc *tocPtr;
+ struct cdrom_subchnl subchnl;
+ struct cdrom_volctrl volctrl;
+
+#ifdef AZT_DEBUG
+ printk("aztcd: starting aztcd_ioctl - Command:%x\n",cmd);
+#endif
+ if (!ip) return -EINVAL;
+ if (getAztStatus()<0) return -EIO;
+ if (!aztTocUpToDate)
+ { if ((i=aztUpdateToc())<0) return i; /* error reading TOC */
+ }
+
+ switch (cmd)
+ {
+ case CDROMSTART: /* Spin up the drive. Don't know, what to do,
+ at least close the tray */
+#ifdef AZT_PRIVATE_IOCTLS
+ if (aztSendCmd(ACMD_CLOSE)) return -1;
+ STEN_LOW_WAIT;
+#endif
+ break;
+ case CDROMSTOP: /* Spin down the drive */
+ if (aztSendCmd(ACMD_STOP)) return -1;
+ STEN_LOW_WAIT;
+ /* should we do anything if it fails? */
+ aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+ break;
+ case CDROMPAUSE: /* Pause the drive */
+ if (aztAudioStatus != CDROM_AUDIO_PLAY) return -EINVAL;
+
+ if (aztGetQChannelInfo(&qInfo) < 0)
+ { /* didn't get q channel info */
+ aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+ return 0;
+ }
+ azt_Play.start = qInfo.diskTime; /* remember restart point */
+ if (aztSendCmd(ACMD_PAUSE)) return -1;
+ STEN_LOW_WAIT;
+ aztAudioStatus = CDROM_AUDIO_PAUSED;
+ break;
+ case CDROMRESUME: /* Play it again, Sam */
+ if (aztAudioStatus != CDROM_AUDIO_PAUSED) return -EINVAL;
+ /* restart the drive at the saved position. */
+ i = aztPlay(&azt_Play);
+ if (i < 0)
+ { aztAudioStatus = CDROM_AUDIO_ERROR;
+ return -EIO;
+ }
+ aztAudioStatus = CDROM_AUDIO_PLAY;
+ break;
+ case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */
+ st = verify_area(VERIFY_READ, (void *) arg, sizeof ti);
+ if (st) return st;
+ memcpy_fromfs(&ti, (void *) arg, sizeof ti);
+ if (ti.cdti_trk0 < DiskInfo.first
+ || ti.cdti_trk0 > DiskInfo.last
+ || ti.cdti_trk1 < ti.cdti_trk0)
+ { return -EINVAL;
+ }
+ if (ti.cdti_trk1 > DiskInfo.last)
+ ti. cdti_trk1 = DiskInfo.last;
+ azt_Play.start = Toc[ti.cdti_trk0].diskTime;
+ azt_Play.end = Toc[ti.cdti_trk1 + 1].diskTime;
+#ifdef AZT_DEBUG
+printk("aztcd play: %02x:%02x.%02x to %02x:%02x.%02x\n",
+ azt_Play.start.min, azt_Play.start.sec, azt_Play.start.frame,
+ azt_Play.end.min, azt_Play.end.sec, azt_Play.end.frame);
+#endif
+ i = aztPlay(&azt_Play);
+ if (i < 0)
+ { aztAudioStatus = CDROM_AUDIO_ERROR;
+ return -EIO;
+ }
+ aztAudioStatus = CDROM_AUDIO_PLAY;
+ break;
+ case CDROMPLAYMSF: /* Play starting at the given MSF address. */
+/* if (aztAudioStatus == CDROM_AUDIO_PLAY)
+ { if (aztSendCmd(ACMD_STOP)) return -1;
+ STEN_LOW;
+ aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+ }
+*/
+ st = verify_area(VERIFY_READ, (void *) arg, sizeof msf);
+ if (st) return st;
+ memcpy_fromfs(&msf, (void *) arg, sizeof msf);
+ /* convert to bcd */
+ azt_bin2bcd(&msf.cdmsf_min0);
+ azt_bin2bcd(&msf.cdmsf_sec0);
+ azt_bin2bcd(&msf.cdmsf_frame0);
+ azt_bin2bcd(&msf.cdmsf_min1);
+ azt_bin2bcd(&msf.cdmsf_sec1);
+ azt_bin2bcd(&msf.cdmsf_frame1);
+ azt_Play.start.min = msf.cdmsf_min0;
+ azt_Play.start.sec = msf.cdmsf_sec0;
+ azt_Play.start.frame = msf.cdmsf_frame0;
+ azt_Play.end.min = msf.cdmsf_min1;
+ azt_Play.end.sec = msf.cdmsf_sec1;
+ azt_Play.end.frame = msf.cdmsf_frame1;
+#ifdef AZT_DEBUG
+printk("aztcd play: %02x:%02x.%02x to %02x:%02x.%02x\n",
+azt_Play.start.min, azt_Play.start.sec, azt_Play.start.frame,
+azt_Play.end.min, azt_Play.end.sec, azt_Play.end.frame);
+#endif
+ i = aztPlay(&azt_Play);
+ if (i < 0)
+ { aztAudioStatus = CDROM_AUDIO_ERROR;
+ return -EIO;
+ }
+ aztAudioStatus = CDROM_AUDIO_PLAY;
+ break;
+
+ case CDROMREADTOCHDR: /* Read the table of contents header */
+ st = verify_area(VERIFY_WRITE, (void *) arg, sizeof tocHdr);
+ if (st) return st;
+ tocHdr.cdth_trk0 = DiskInfo.first;
+ tocHdr.cdth_trk1 = DiskInfo.last;
+ memcpy_tofs((void *) arg, &tocHdr, sizeof tocHdr);
+ break;
+ case CDROMREADTOCENTRY: /* Read an entry in the table of contents */
+ st = verify_area(VERIFY_READ, (void *) arg, sizeof entry);
+ if (st) return st;
+ st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry);
+ if (st) return st;
+ memcpy_fromfs(&entry, (void *) arg, sizeof entry);
+ if (!aztTocUpToDate) aztGetDiskInfo();
+ if (entry.cdte_track == CDROM_LEADOUT)
+ tocPtr = &Toc[DiskInfo.last + 1]; /* ??? */
+ else if (entry.cdte_track > DiskInfo.last
+ || entry.cdte_track < DiskInfo.first)
+ { return -EINVAL;
+ }
+ else
+ tocPtr = &Toc[entry.cdte_track];
+ entry.cdte_adr = tocPtr -> ctrl_addr;
+ entry.cdte_ctrl = tocPtr -> ctrl_addr >> 4;
+ if (entry.cdte_format == CDROM_LBA)
+ entry.cdte_addr.lba = azt_msf2hsg(&tocPtr -> diskTime);
+ else if (entry.cdte_format == CDROM_MSF)
+ { entry.cdte_addr.msf.minute = azt_bcd2bin(tocPtr -> diskTime.min);
+ entry.cdte_addr.msf.second = azt_bcd2bin(tocPtr -> diskTime.sec);
+ entry.cdte_addr.msf.frame = azt_bcd2bin(tocPtr -> diskTime.frame);
+ }
+ else
+ { return -EINVAL;
+ }
+ memcpy_tofs((void *) arg, &entry, sizeof entry);
+ break;
+ case CDROMSUBCHNL: /* Get subchannel info */
+ st = verify_area(VERIFY_READ, (void *) arg, sizeof subchnl);
+ if (st) return st;
+ st = verify_area(VERIFY_WRITE, (void *) arg, sizeof subchnl);
+ if (st) return st;
+ memcpy_fromfs(&subchnl, (void *) arg, sizeof subchnl);
+ if (aztGetQChannelInfo(&qInfo) < 0)
+ return -EIO;
+ subchnl.cdsc_audiostatus = aztAudioStatus;
+ subchnl.cdsc_adr = qInfo.ctrl_addr;
+ subchnl.cdsc_ctrl = qInfo.ctrl_addr >> 4;
+ subchnl.cdsc_trk = azt_bcd2bin(qInfo.track);
+ subchnl.cdsc_ind = azt_bcd2bin(qInfo.pointIndex);
+ if (subchnl.cdsc_format == CDROM_LBA)
+ { subchnl.cdsc_absaddr.lba = azt_msf2hsg(&qInfo.diskTime);
+ subchnl.cdsc_reladdr.lba = azt_msf2hsg(&qInfo.trackTime);
+ }
+ else if (subchnl.cdsc_format == CDROM_MSF)
+ { subchnl.cdsc_absaddr.msf.minute = azt_bcd2bin(qInfo.diskTime.min);
+ subchnl.cdsc_absaddr.msf.second = azt_bcd2bin(qInfo.diskTime.sec);
+ subchnl.cdsc_absaddr.msf.frame = azt_bcd2bin(qInfo.diskTime.frame);
+ subchnl.cdsc_reladdr.msf.minute = azt_bcd2bin(qInfo.trackTime.min);
+ subchnl.cdsc_reladdr.msf.second = azt_bcd2bin(qInfo.trackTime.sec);
+ subchnl.cdsc_reladdr.msf.frame = azt_bcd2bin(qInfo.trackTime.frame);
+ }
+ else
+ return -EINVAL;
+ memcpy_tofs((void *) arg, &subchnl, sizeof subchnl);
+ break;
+ case CDROMVOLCTRL: /* Volume control
+ * With my Aztech CD268-01A volume control does not work, I can only
+ turn the channels on (any value !=0) or off (value==0). Maybe it
+ works better with your drive */
+ st=verify_area(VERIFY_READ,(void *) arg, sizeof(volctrl));
+ if (st) return (st);
+ memcpy_fromfs(&volctrl,(char *) arg,sizeof(volctrl));
+ azt_Play.start.min = 0x21;
+ azt_Play.start.sec = 0x84;
+ azt_Play.start.frame = volctrl.channel0;
+ azt_Play.end.min = volctrl.channel1;
+ azt_Play.end.sec = volctrl.channel2;
+ azt_Play.end.frame = volctrl.channel3;
+ sendAztCmd(ACMD_SET_VOLUME, &azt_Play);
+ STEN_LOW_WAIT;
+ break;
+ case CDROMEJECT:
+ aztUnlockDoor(); /* Assume user knows what they're doing */
+ /* all drives can at least stop! */
+ if (aztAudioStatus == CDROM_AUDIO_PLAY)
+ { if (aztSendCmd(ACMD_STOP)) return -1;
+ STEN_LOW_WAIT;
+ }
+ if (aztSendCmd(ACMD_EJECT)) return -1;
+ aztAudioStatus = CDROM_AUDIO_NO_STATUS;
+ break;
+ case CDROMREADMODE1: /*read data in mode 1 (2048 Bytes)*/
+ case CDROMREADMODE2: /*read data in mode 2 (2336 Bytes)*/
+/*Take care, the following code is not compatible with other CD-ROM drivers,
+ use it at your own risk with cdplay.c. Normally it is not activated, as
+ AZT_PRIVATE_IOCTLS is not defined
+*/
+#ifdef AZT_PRIVATE_IOCTLS
+ { st = verify_area(VERIFY_READ, (void *) arg, sizeof msf);
+ if (st) return st;
+ st = verify_area(VERIFY_WRITE, (void *) arg, sizeof buf);
+ if (st) return st;
+ memcpy_fromfs(&msf, (void *) arg, sizeof msf);
+ /* convert to bcd */
+ azt_bin2bcd(&msf.cdmsf_min0);
+ azt_bin2bcd(&msf.cdmsf_sec0);
+ azt_bin2bcd(&msf.cdmsf_frame0);
+ msf.cdmsf_min1=0;
+ msf.cdmsf_sec1=0;
+ msf.cdmsf_frame1=1; /*read only one frame*/
+ azt_Play.start.min = msf.cdmsf_min0;
+ azt_Play.start.sec = msf.cdmsf_sec0;
+ azt_Play.start.frame = msf.cdmsf_frame0;
+ azt_Play.end.min = msf.cdmsf_min1;
+ azt_Play.end.sec = msf.cdmsf_sec1;
+ azt_Play.end.frame = msf.cdmsf_frame1;
+ if (cmd==CDROMREADMODE1)
+ { sendAztCmd(ACMD_DATA_READ, &azt_Play);
+ DTEN_LOW;
+ insb(DATA_PORT,buf,2048);
+ memcpy_tofs((void *) arg, &buf, 2048);
+ }
+ else /*CDROMREADMODE2*/
+ { sendAztCmd(ACMD_DATA_READ_RAW, &azt_Play);
+ DTEN_LOW;
+ insb(DATA_PORT,buf,2336);
+ memcpy_tofs((void *) arg, &buf, 2336);
+ }
+ }
+#endif /*end of incompatible code*/
+ break;
+ default:
+ return -EINVAL;
+ }
+#ifdef AZT_DEBUG
+ printk("aztcd: exiting aztcd_ioctl\n");
+#endif
+ return 0;
+}
+
+
+/*
+ * Take care of the different block sizes between cdrom and Linux.
+ * When Linux gets variable block sizes this will probably go away.
+ */
+static void azt_transfer(void)
+{
+#ifdef AZT_TEST
+ printk("aztcd: executing azt_transfer\n");
+#endif
+ if (CURRENT_VALID) {
+ while (CURRENT -> nr_sectors) {
+ int bn = CURRENT -> sector / 4;
+ int i;
+ for (i = 0; i < AZT_BUF_SIZ && azt_buf_bn[i] != bn; ++i)
+ ;
+ if (i < AZT_BUF_SIZ) {
+ int offs = (i * 4 + (CURRENT -> sector & 3)) * 512;
+ int nr_sectors = 4 - (CURRENT -> sector & 3);
+ if (azt_buf_out != i) {
+ azt_buf_out = i;
+ if (azt_buf_bn[i] != bn) {
+ azt_buf_out = -1;
+ continue;
+ }
+ }
+ if (nr_sectors > CURRENT -> nr_sectors)
+ nr_sectors = CURRENT -> nr_sectors;
+ memcpy(CURRENT -> buffer, azt_buf + offs, nr_sectors * 512);
+ CURRENT -> nr_sectors -= nr_sectors;
+ CURRENT -> sector += nr_sectors;
+ CURRENT -> buffer += nr_sectors * 512;
+ } else {
+ azt_buf_out = -1;
+ break;
+ }
+ }
+ }
+}
+
+
+static void do_aztcd_request(void)
+{
+#ifdef AZT_TEST
+ printk(" do_aztcd_request(%ld+%ld)\n", CURRENT -> sector, CURRENT -> nr_sectors);
+#endif
+ azt_transfer_is_active = 1;
+ while (CURRENT_VALID) {
+ if (CURRENT->bh) {
+ if (!CURRENT->bh->b_lock)
+ panic(DEVICE_NAME ": block not locked");
+ }
+ azt_transfer();
+ if (CURRENT -> nr_sectors == 0) {
+ end_request(1);
+ } else {
+ azt_buf_out = -1; /* Want to read a block not in buffer */
+ if (azt_state == AZT_S_IDLE) {
+ if (!aztTocUpToDate) {
+ if (aztUpdateToc() < 0) {
+ while (CURRENT_VALID)
+ end_request(0);
+ break;
+ }
+ }
+ azt_state = AZT_S_START;
+ AztTries = 5;
+ SET_TIMER(azt_poll, 1);
+ }
+ break;
+ }
+ }
+ azt_transfer_is_active = 0;
+#ifdef AZT_TEST2
+ printk("azt_next_bn:%x azt_buf_in:%x azt_buf_out:%x azt_buf_bn:%x\n", \
+ azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]);
+ printk(" do_aztcd_request ends\n");
+#endif
+
+}
+
+static void azt_poll(void)
+{
+ int st = 0;
+ int loop_ctl = 1;
+ int skip = 0;
+
+ if (azt_error) { /* ???*/
+ if (aztSendCmd(ACMD_GET_ERROR)) return;
+ STEN_LOW;
+ azt_error=inb(DATA_PORT)&0xFF;
+ printk("aztcd: I/O error 0x%02x\n", azt_error);
+ azt_invalidate_buffers();
+#ifdef WARN_IF_READ_FAILURE
+ if (AztTries == 5)
+ printk("aztcd: read of block %d failed - maybe audio disk?\n", azt_next_bn);
+#endif
+ if (!AztTries--) {
+ printk("aztcd: read of block %d failed, maybe audio disk? Giving up\n", azt_next_bn);
+ if (azt_transfer_is_active) {
+ AztTries = 0;
+ loop_ctl = 0;
+ }
+ if (CURRENT_VALID)
+ end_request(0);
+ AztTries = 5;
+ }
+ azt_error = 0;
+ azt_state = AZT_S_STOP;
+ }
+
+ while (loop_ctl)
+ {
+ loop_ctl = 0; /* each case must flip this back to 1 if we want
+ to come back up here */
+ switch (azt_state) {
+
+ case AZT_S_IDLE:
+#ifdef AZT_TEST3
+ if (azt_state!=azt_state_old) {
+ azt_state_old=azt_state;
+ printk("AZT_S_IDLE\n");
+ }
+#endif
+ return;
+
+ case AZT_S_START:
+#ifdef AZT_TEST3
+ if (azt_state!=azt_state_old) {
+ azt_state_old=azt_state;
+ printk("AZT_S_START\n");
+ }
+#endif
+
+ if(aztSendCmd(ACMD_GET_STATUS)) return; /*result will be checked by aztStatus() */
+ azt_state = azt_mode == 1 ? AZT_S_READ : AZT_S_MODE;
+ AztTimeout = 3000;
+ break;
+
+ case AZT_S_MODE:
+#ifdef AZT_TEST3
+ if (azt_state!=azt_state_old) {
+ azt_state_old=azt_state;
+ printk("AZT_S_MODE\n");
+ }
+#endif
+ if (!skip) {
+ if ((st = aztStatus()) != -1) {
+ if (st & AST_DSK_CHG) {
+ aztDiskChanged = 1;
+ aztTocUpToDate = 0;
+ azt_invalidate_buffers();
+ }
+ } else break;
+ }
+ skip = 0;
+
+ if ((st & AST_DOOR_OPEN)||(st & AST_NOT_READY)) {
+ aztDiskChanged = 1;
+ aztTocUpToDate = 0;
+ printk((st & AST_DOOR_OPEN) ? "aztcd: door open\n" : "aztcd: disk removed\n");
+ if (azt_transfer_is_active) {
+ azt_state = AZT_S_START;
+ loop_ctl = 1; /* goto immediately */
+ break;
+ }
+ azt_state = AZT_S_IDLE;
+ while (CURRENT_VALID)
+ end_request(0);
+ return;
+ }
+ /*???*/
+ if (aztSendCmd(ACMD_SET_MODE)) return;
+ outb(0x01, DATA_PORT); /*Mode 1*/
+ PA_OK;
+ STEN_LOW;
+ if (aztSendCmd(ACMD_GET_STATUS)) return;
+ azt_mode = 1;
+ azt_state = AZT_S_READ;
+ AztTimeout = 3000;
+
+ break;
+
+
+ case AZT_S_READ:
+#ifdef AZT_TEST3
+ if (azt_state!=azt_state_old) {
+ azt_state_old=azt_state;
+ printk("AZT_S_READ\n");
+ }
+#endif
+ if (!skip) {
+ if ((st = aztStatus()) != -1) {
+ if (st & AST_DSK_CHG) {
+ aztDiskChanged = 1;
+ aztTocUpToDate = 0;
+ azt_invalidate_buffers();
+ }
+ } else break;
+ }
+
+ skip = 0;
+ if ((st & AST_DOOR_OPEN)||(st & AST_NOT_READY)) {
+ aztDiskChanged = 1;
+ aztTocUpToDate = 0;
+ printk((st & AST_DOOR_OPEN) ? "aztcd: door open\n" : "aztcd: disk removed\n");
+ if (azt_transfer_is_active) {
+ azt_state = AZT_S_START;
+ loop_ctl = 1;
+ break;
+ }
+ azt_state = AZT_S_IDLE;
+ while (CURRENT_VALID)
+ end_request(0);
+ return;
+ }
+
+ if (CURRENT_VALID) {
+ struct azt_Play_msf msf;
+ azt_next_bn = CURRENT -> sector / 4;
+ azt_hsg2msf(azt_next_bn, &msf.start);
+ azt_read_count=AZT_BUF_SIZ; /*??? fast, because we read ahead*/
+/* azt_read_count= CURRENT->nr_sectors; slow
+*/
+ msf.end.min = 0;
+ msf.end.sec = 0;
+ msf.end.frame = azt_read_count ;/*Mitsumi here reads 0xffffff sectors*/
+#ifdef AZT_TEST3
+ printk("---reading msf-address %x:%x:%x %x:%x:%x\n",msf.start.min,msf.start.sec,msf.start.frame,msf.end.min,msf.end.sec,msf.end.frame);
+ printk("azt_next_bn:%x azt_buf_in:%x azt_buf_out:%x azt_buf_bn:%x\n", \
+ azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]);
+#endif
+ sendAztCmd(ACMD_DATA_READ, &msf);
+ azt_state = AZT_S_DATA;
+ AztTimeout = READ_TIMEOUT;
+ } else {
+ azt_state = AZT_S_STOP;
+ loop_ctl = 1;
+ break;
+ }
+
+ break;
+
+
+ case AZT_S_DATA:
+#ifdef AZT_TEST3
+ if (azt_state!=azt_state_old) {
+ azt_state_old=azt_state;
+ printk("AZT_S_DATA\n");
+ }
+#endif
+
+ st = inb(STATUS_PORT) & AFL_STATUSorDATA; /*???*/
+
+ switch (st) {
+
+ case AFL_DATA:
+#ifdef AZT_TEST3
+ if (st!=azt_st_old) {
+ azt_st_old=st;
+ printk("---AFL_DATA st:%x\n",st);
+ }
+#endif
+#ifdef WARN_IF_READ_FAILURE
+ if (AztTries == 5)
+ printk("aztcd: read of block %d failed - maybe audio disk?\n", azt_next_bn);
+#endif
+ if (!AztTries--) {
+ printk("aztcd: read of block %d failed, maybe audio disk ? Giving up\n", azt_next_bn);
+ if (azt_transfer_is_active) {
+ AztTries = 0;
+ break;
+ }
+ if (CURRENT_VALID)
+ end_request(0);
+ AztTries = 5;
+ }
+ azt_state = AZT_S_START;
+ AztTimeout = READ_TIMEOUT;
+ loop_ctl = 1;
+ break;
+
+ case AFL_STATUSorDATA:
+#ifdef AZT_TEST3
+ if (st!=azt_st_old) {
+ azt_st_old=st;
+ printk("---AFL_STATUSorDATA st:%x\n",st);
+ }
+#endif
+ break;
+
+ default:
+#ifdef AZT_TEST3
+ if (st!=azt_st_old) {
+ azt_st_old=st;
+ printk("---default: st:%x\n",st);
+ }
+#endif
+ AztTries = 5;
+ if (!CURRENT_VALID && azt_buf_in == azt_buf_out) {
+ azt_state = AZT_S_STOP;
+ loop_ctl = 1;
+ break;
+ }
+ if (azt_read_count<=0)
+ printk("aztcd: warning - try to read 0 frames\n");
+ while (azt_read_count) /*??? fast read ahead loop*/
+ { azt_buf_bn[azt_buf_in] = -1;
+ DTEN_LOW; /*??? unsolved problem, very
+ seldom we get timeouts
+ here, don't now the real
+ reason. With my drive this
+ sometimes also happens with
+ Aztech's original driver under
+ DOS. Is it a hardware bug?
+ I tried to recover from such
+ situations here. Zimmermann*/
+ if (aztTimeOutCount>=AZT_TIMEOUT)
+ { printk("read_count:%d CURRENT->nr_sectors:%ld azt_buf_in:%d\n", azt_read_count,CURRENT->nr_sectors,azt_buf_in);
+ printk("azt_transfer_is_active:%x\n",azt_transfer_is_active);
+ azt_read_count=0;
+ azt_state = AZT_S_STOP;
+ loop_ctl = 1;
+ end_request(1); /*should we have here (1) or (0)? */
+ }
+ else
+ { insb(DATA_PORT, azt_buf + 2048 * azt_buf_in, 2048);
+ azt_read_count--;
+#ifdef AZT_TEST3
+ printk("AZT_S_DATA; ---I've read data- read_count: %d\n",azt_read_count);
+ printk("azt_next_bn:%d azt_buf_in:%d azt_buf_out:%d azt_buf_bn:%d\n", \
+ azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]);
+#endif
+ azt_buf_bn[azt_buf_in] = azt_next_bn++;
+ if (azt_buf_out == -1)
+ azt_buf_out = azt_buf_in;
+ azt_buf_in = azt_buf_in + 1 == AZT_BUF_SIZ ? 0 : azt_buf_in + 1;
+ }
+ }
+ if (!azt_transfer_is_active) {
+ while (CURRENT_VALID) {
+ azt_transfer();
+ if (CURRENT -> nr_sectors == 0)
+ end_request(1);
+ else
+ break;
+ }
+ }
+
+ if (CURRENT_VALID
+ && (CURRENT -> sector / 4 < azt_next_bn ||
+ CURRENT -> sector / 4 > azt_next_bn + AZT_BUF_SIZ)) {
+ azt_state = AZT_S_STOP;
+ loop_ctl = 1;
+ break;
+ }
+ AztTimeout = READ_TIMEOUT;
+ if (azt_read_count==0) {
+ azt_state = AZT_S_STOP; /*???*/
+ loop_ctl = 1;
+ break;
+ }
+ break;
+ }
+ break;
+
+
+ case AZT_S_STOP:
+#ifdef AZT_TEST3
+ if (azt_state!=azt_state_old) {
+ azt_state_old=azt_state;
+ printk("AZT_S_STOP\n");
+ }
+#endif
+ if (azt_read_count!=0) printk("aztcd: discard data=%x frames\n",azt_read_count); /*???*/
+ while (azt_read_count!=0) {
+ int i;
+ if ( !(inb(STATUS_PORT) & AFL_DATA) ) {
+ for (i=0; i<2048; i++) {
+ inb(DATA_PORT);
+ }
+ }
+ azt_read_count--;
+ }
+ if (aztSendCmd(ACMD_GET_STATUS)) return;
+ azt_state = AZT_S_STOPPING;
+ AztTimeout = 1000;
+ break;
+
+ case AZT_S_STOPPING:
+#ifdef AZT_TEST3
+ if (azt_state!=azt_state_old) {
+ azt_state_old=azt_state;
+ printk("AZT_S_STOPPING\n");
+ }
+#endif
+
+ if ((st = aztStatus()) == -1 && AztTimeout)
+ break;
+
+ if ((st != -1) && (st & AST_DSK_CHG)) {
+ aztDiskChanged = 1;
+ aztTocUpToDate = 0;
+ azt_invalidate_buffers();
+ }
+
+
+#ifdef AZT_TEST3
+ printk("CURRENT_VALID %d azt_mode %d\n",
+ CURRENT_VALID, azt_mode);
+#endif
+
+ if (CURRENT_VALID) {
+ if (st != -1) {
+ if (azt_mode == 1) {
+ azt_state = AZT_S_READ;
+ loop_ctl = 1;
+ skip = 1;
+ break;
+ } else {
+ azt_state = AZT_S_MODE;
+ loop_ctl = 1;
+ skip = 1;
+ break;
+ }
+ } else {
+ azt_state = AZT_S_START;
+ AztTimeout = 1;
+ }
+ } else {
+ azt_state = AZT_S_IDLE;
+ return;
+ }
+ break;
+
+ default:
+ printk("aztcd: invalid state %d\n", azt_state);
+ return;
+ } /* case */
+ } /* while */
+
+
+ if (!AztTimeout--)
+ { printk("aztcd: timeout in state %d\n", azt_state);
+ azt_state = AZT_S_STOP;
+ if (aztSendCmd(ACMD_STOP)) return;
+ STEN_LOW_WAIT;
+ };
+
+ SET_TIMER(azt_poll, 1);
+}
+
+static void azt_invalidate_buffers(void)
+{ int i;
+
+#ifdef AZT_DEBUG
+ printk("aztcd: executing azt_invalidate_buffers\n");
+#endif
+ for (i = 0; i < AZT_BUF_SIZ; ++i)
+ azt_buf_bn[i] = -1;
+ azt_buf_out = -1;
+}
+
+/*
+ * Open the device special file. Check that a disk is in.
+ */
+int aztcd_open(struct inode *ip, struct file *fp)
+{ int st;
+
+#ifdef AZT_DEBUG
+ printk("aztcd: starting aztcd_open\n");
+#endif
+ if (aztPresent == 0)
+ return -ENXIO; /* no hardware */
+
+ if (!azt_open_count && azt_state == AZT_S_IDLE) {
+
+ azt_invalidate_buffers();
+
+ st = getAztStatus(); /* check drive status */
+ if (st == -1)
+ return -EIO; /* drive doesn't respond */
+
+ if (st&AST_DOOR_OPEN)
+ {
+ /* close door, then get the status again. */
+ aztCloseDoor();
+ st = getAztStatus();
+ }
+
+ if ((st&AST_DOOR_OPEN)||(st&AST_NOT_READY)) /* no disk in drive or door open*/
+ { /* Door should be closed, probably no disk in drive */
+ printk("aztcd: no disk in drive or door open\n");
+ return -EIO;
+ }
+
+ if (aztUpdateToc() < 0)
+ return -EIO;
+
+ }
+ ++azt_open_count;
+ MOD_INC_USE_COUNT;
+ aztLockDoor();
+#ifdef AZT_DEBUG
+ printk("aztcd: exiting aztcd_open\n");
+#endif
+ return 0;
+}
+
+
+/*
+ * On close, we flush all azt blocks from the buffer cache.
+ */
+static void aztcd_release(struct inode * inode, struct file * file)
+{
+#ifdef AZT_DEBUG
+ printk("aztcd: executing aztcd_release\n");
+ printk("inode: %p, inode->i_rdev: %x file: %p\n",inode,inode->i_rdev,file);
+#endif
+ MOD_DEC_USE_COUNT;
+ if (!--azt_open_count) {
+ azt_invalidate_buffers();
+ sync_dev(inode->i_rdev); /*??? isn't it a read only dev?*/
+ invalidate_buffers(inode -> i_rdev);
+ aztUnlockDoor();
+ CLEAR_TIMER;
+ }
+ return;
+}
+
+
+static struct file_operations azt_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ aztcd_ioctl, /* ioctl */
+ NULL, /* mmap */
+ aztcd_open, /* open */
+ aztcd_release, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync*/
+ check_aztcd_media_change, /*media change*/
+ NULL /* revalidate*/
+};
+
+/*
+ * Test for presence of drive and initialize it. Called at boot time.
+ */
+#ifndef MODULE
+unsigned long aztcd_init(unsigned long mem_start, unsigned long mem_end)
+#else
+int init_module(void)
+#endif
+{ long int count, max_count;
+ unsigned char result[50];
+ int st;
+
+ if (azt_port <= 0) {
+ printk("aztcd: no Aztech CD-ROM Initialization");
+#ifndef MODULE
+ return (mem_start);
+#else
+ return -EIO;
+#endif
+ }
+ printk("Aztech CD-ROM Init: Aztech, Orchid, Okano, Wearnes CD-ROM Driver\n");
+ printk("Aztech CD-ROM Init: (C) 1994,1995 Werner Zimmermann\n");
+ printk("Aztech CD-ROM Init: DriverVersion=%s BaseAddress=0x%x \n",AZT_VERSION,azt_port);
+
+ if (check_region(azt_port, 4)) {
+ printk("aztcd: conflict, I/O port (%X) already used\n",
+ azt_port);
+#ifndef MODULE
+ return (mem_start);
+#else
+ return -EIO;
+#endif
+ }
+
+ /* check for card */
+ outb(POLLED,MODE_PORT); /*???*/
+ inb(CMD_PORT);
+ inb(CMD_PORT);
+ outb(ACMD_GET_VERSION,CMD_PORT); /*Try to get version info*/
+ STEN_LOW;
+ if (inb(DATA_PORT)!=AFL_OP_OK) /*OP_OK? If not, reset and try again*/
+ { printk("aztcd: drive reset - please wait\n");
+ for (count=0;count<50;count++)
+ { inb(STATUS_PORT); /*removing all data from earlier tries*/
+ inb(DATA_PORT);
+ }
+ outb(POLLED,MODE_PORT); /*???*/
+ inb(CMD_PORT);
+ inb(CMD_PORT);
+ outb(ACMD_SOFT_RESET,CMD_PORT); /*send reset*/
+ STEN_LOW;
+ if (inb(DATA_PORT)!=AFL_OP_OK) /*OP_OK?*/
+ { printk("aztcd: no AZTECH CD-ROM drive found\n");
+#ifndef MODULE
+ return (mem_start);
+#else
+ return -EIO;
+#endif
+ }
+ for (count = 0; count < AZT_TIMEOUT; count++); /* delay a bit */
+ if ((st=getAztStatus())==-1)
+ { printk("aztcd: Drive Status Error Status=%x\n",st);
+#ifndef MODULE
+ return (mem_start);
+#else
+ return -EIO;
+#endif
+ }
+#ifdef AZT_DEBUG
+ printk("aztcd: Status = %x\n",st);
+#endif
+ outb(POLLED,MODE_PORT); /*???*/
+ inb(CMD_PORT);
+ inb(CMD_PORT);
+ outb(ACMD_GET_VERSION,CMD_PORT); /*GetVersion*/
+ STEN_LOW;
+ OP_OK;
+ }
+ azt_init_end=1;
+ STEN_LOW;
+ result[0]=inb(DATA_PORT); /*reading in a null byte???*/
+ for (count=1;count<50;count++) /*Reading version string*/
+ { aztTimeOutCount=0; /*here we must implement STEN_LOW differently*/
+ do { aztIndatum=inb(STATUS_PORT);/*because we want to exit by timeout*/
+ aztTimeOutCount++;
+ if (aztTimeOutCount>=AZT_FAST_TIMEOUT) break;
+ } while (aztIndatum&AFL_STATUS);
+ if (aztTimeOutCount>=AZT_FAST_TIMEOUT) break; /*all chars read?*/
+ result[count]=inb(DATA_PORT);
+ }
+ if (count>30) max_count=30; /*print max.30 chars of the version string*/
+ else max_count=count;
+ printk("Aztech CD-ROM Init: FirmwareVersion=");
+ for (count=1;count<max_count;count++) printk("%c",result[count]);
+ printk("<<<\n");
+
+ if ((result[1]=='A')&&(result[2]=='Z')&&(result[3]=='T'))
+ { printk("Aztech CD-ROM Init: AZTECH drive detected\n"); /*AZTECH*/
+ }
+ else if ((result[2]=='C')&&(result[3]=='D')&&(result[4]=='D'))
+ { printk("Aztech CD-ROM Init: ORCHID or WEARNES drive detected\n"); /*ORCHID or WEARNES*/
+ }
+ else /*OTHERS or none*/
+ { printk("Aztech CD-ROM Init: : unknown drive or firmware version detected\n");
+ printk(" azt may not run stable, if you want to try anyhow,\n");
+ printk(" boot with: aztcd=base_address,0x79\n");
+ if ((azt_cont!=0x79))
+ { printk("Aztech CD-ROM Init: FirmwareVersion=");
+ for (count=1;count<5;count++) printk("%c",result[count]);
+ printk("\n");
+ printk("Aztech CD-ROM Init: Aborted\n");
+#ifndef MODULE
+ return (mem_start);
+#else
+ return -EIO;
+#endif
+ }
+ }
+ if (register_blkdev(MAJOR_NR, "aztcd", &azt_fops) != 0)
+ {
+ printk("aztcd: Unable to get major %d for Aztech CD-ROM\n",
+ MAJOR_NR);
+#ifndef MODULE
+ return (mem_start);
+#else
+ return -EIO;
+#endif
+ }
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ read_ahead[MAJOR_NR] = 4;
+
+ request_region(azt_port, 4, "aztcd");
+
+ azt_invalidate_buffers();
+ aztPresent = 1;
+ aztCloseDoor();
+ printk("Aztech CD-ROM Init: End\n");
+#ifndef MODULE
+ return (mem_start);
+#else
+ return (0);
+#endif
+}
+
+
+static void azt_hsg2msf(long hsg, struct msf *msf)
+{ hsg += 150;
+ msf -> min = hsg / 4500;
+ hsg %= 4500;
+ msf -> sec = hsg / 75;
+ msf -> frame = hsg % 75;
+#ifdef AZT_DEBUG
+ if (msf->min >=70) printk("aztcd: Error hsg2msf address Minutes\n");
+ if (msf->sec >=60) printk("aztcd: Error hsg2msf address Seconds\n");
+ if (msf->frame>=75) printk("aztcd: Error hsg2msf address Frames\n");
+#endif
+ azt_bin2bcd(&msf -> min); /* convert to BCD */
+ azt_bin2bcd(&msf -> sec);
+ azt_bin2bcd(&msf -> frame);
+}
+
+
+static void azt_bin2bcd(unsigned char *p)
+{ int u, t;
+
+ u = *p % 10;
+ t = *p / 10;
+ *p = u | (t << 4);
+}
+
+static int azt_bcd2bin(unsigned char bcd)
+{ return (bcd >> 4) * 10 + (bcd & 0xF);
+}
+
+
+
+/*
+ * Read a value from the drive. Should return quickly, so a busy wait
+ * is used to avoid excessive rescheduling. The read command itself must
+ * be issued with aztSendCmd() directly before
+ */
+static int aztGetValue(unsigned char *result)
+{ int s;
+
+ STEN_LOW;
+ if (aztTimeOutCount>=AZT_TIMEOUT)
+ { printk("aztcd: aztGetValue timeout\n");
+ return -1;
+ }
+ s = inb(DATA_PORT) & 0xFF;
+ *result = (unsigned char) s;
+ return 0;
+}
+
+
+/*
+ * Read the current Q-channel info. Also used for reading the
+ * table of contents.
+ */
+int aztGetQChannelInfo(struct azt_Toc *qp)
+{ unsigned char notUsed;
+ int st;
+
+#ifdef AZT_DEBUG
+ printk("aztcd: starting aztGetQChannelInfo\n");
+#endif
+ if ((st=getAztStatus())==-1) return -1;
+ if (aztSendCmd(ACMD_GET_Q_CHANNEL)) return -1;
+ STEN_LOW_WAIT;
+ if (aztGetValue(&notUsed) <0) return -1; /*Nullbyte ein-*/
+ /*lesen ???*/
+ if ((st&AST_MODE_BITS)==AST_INITIAL)
+ { qp->ctrl_addr=0; /* when audio stop ACMD_GET_Q_CHANNEL returns */
+ qp->track=0; /* only one byte with Aztech drives */
+ qp->pointIndex=0;
+ qp->trackTime.min=0;
+ qp->trackTime.sec=0;
+ qp->trackTime.frame=0;
+ qp->diskTime.min=0;
+ qp->diskTime.sec=0;
+ qp->diskTime.frame=0;
+ return 0;
+ }
+ else
+ { if (aztGetValue(&qp -> ctrl_addr) < 0) return -1;
+ if (aztGetValue(&qp -> track) < 0) return -1;
+ if (aztGetValue(&qp -> pointIndex) < 0) return -1;
+ if (aztGetValue(&qp -> trackTime.min) < 0) return -1;
+ if (aztGetValue(&qp -> trackTime.sec) < 0) return -1;
+ if (aztGetValue(&qp -> trackTime.frame) < 0) return -1;
+ if (aztGetValue(&notUsed) < 0) return -1;
+ if (aztGetValue(&qp -> diskTime.min) < 0) return -1;
+ if (aztGetValue(&qp -> diskTime.sec) < 0) return -1;
+ if (aztGetValue(&qp -> diskTime.frame) < 0) return -1;
+ }
+#ifdef AZT_DEBUG
+ printk("aztcd: exiting aztGetQChannelInfo\n");
+#endif
+ return 0;
+}
+
+/*
+ * Read the table of contents (TOC) and TOC header if necessary
+ */
+static int aztUpdateToc()
+{
+#ifdef AZT_DEBUG
+ printk("aztcd: starting aztUpdateToc\n");
+#endif
+ if (aztTocUpToDate)
+ return 0;
+
+ if (aztGetDiskInfo() < 0)
+ return -EIO;
+
+ if (aztGetToc() < 0)
+ return -EIO;
+
+ aztTocUpToDate = 1;
+#ifdef AZT_DEBUG
+ printk("aztcd: exiting aztUpdateToc\n");
+#endif
+ return 0;
+}
+
+
+/*
+ * Read the table of contents header
+ */
+static int aztGetDiskInfo()
+{ int limit;
+ unsigned char test;
+ struct azt_Toc qInfo;
+
+#ifdef AZT_DEBUG
+ printk("aztcd: starting aztGetDiskInfo\n");
+#endif
+ if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) return -1;
+ STEN_LOW_WAIT;
+ test=0;
+ for (limit=300;limit>0;limit--)
+ { if (aztGetQChannelInfo(&qInfo)<0) return -1;
+ if (qInfo.pointIndex==0xA0) /*Number of FirstTrack*/
+ { DiskInfo.first=qInfo.diskTime.min;
+ DiskInfo.first = azt_bcd2bin(DiskInfo.first);
+ test=test|0x01;
+ }
+ if (qInfo.pointIndex==0xA1) /*Number of LastTrack*/
+ { DiskInfo.last=qInfo.diskTime.min;
+ DiskInfo.last = azt_bcd2bin(DiskInfo.last);
+ test=test|0x02;
+ }
+ if (qInfo.pointIndex==0xA2) /*DiskLength*/
+ { DiskInfo.diskLength.min=qInfo.diskTime.min;
+ DiskInfo.diskLength.sec=qInfo.diskTime.sec-2;
+ DiskInfo.diskLength.frame=qInfo.diskTime.frame;
+ test=test|0x04;
+ }
+ if ((qInfo.pointIndex==DiskInfo.first)&&(test&0x01)) /*StartTime of First Track*/
+ { DiskInfo.firstTrack.min=qInfo.diskTime.min;
+ DiskInfo.firstTrack.sec=qInfo.diskTime.sec;
+ DiskInfo.firstTrack.frame=qInfo.diskTime.frame;
+ test=test|0x08;
+ }
+ if (test==0x0F) break;
+ }
+#ifdef AZT_DEBUG
+printk ("aztcd: exiting aztGetDiskInfo\n");
+printk("Disk Info: first %d last %d length %02x:%02x.%02x first %02x:%02x.%02x\n",
+ DiskInfo.first,
+ DiskInfo.last,
+ DiskInfo.diskLength.min,
+ DiskInfo.diskLength.sec,
+ DiskInfo.diskLength.frame,
+ DiskInfo.firstTrack.min,
+ DiskInfo.firstTrack.sec,
+ DiskInfo.firstTrack.frame);
+#endif
+ if (test!=0x0F) return -1;
+ return 0;
+}
+
+
+/*
+ * Read the table of contents (TOC)
+ */
+static int aztGetToc()
+{ int i, px;
+ int limit;
+ struct azt_Toc qInfo;
+
+#ifdef AZT_DEBUG
+ printk("aztcd: starting aztGetToc\n");
+#endif
+ for (i = 0; i < MAX_TRACKS; i++)
+ Toc[i].pointIndex = 0;
+
+ i = DiskInfo.last + 3;
+
+/* Is there a good reason to stop motor before TOC read?
+ if (aztSendCmd(ACMD_STOP)) return -1;
+ STEN_LOW_WAIT;
+*/
+
+ azt_mode = 0x05;
+ if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) return -1; /*???*/
+ STEN_LOW_WAIT;
+
+ for (limit = 300; limit > 0; limit--)
+ {
+ if (aztGetQChannelInfo(&qInfo) < 0)
+ break;
+
+ px = azt_bcd2bin(qInfo.pointIndex);
+ if (px > 0 && px < MAX_TRACKS && qInfo.track == 0)
+ if (Toc[px].pointIndex == 0)
+ {
+ Toc[px] = qInfo;
+ i--;
+ }
+
+ if (i <= 0)
+ break;
+ }
+
+ Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength;
+
+#ifdef AZT_DEBUG
+printk("aztcd: exiting aztGetToc\n");
+for (i = 1; i <= DiskInfo.last+1; i++)
+printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X %02X:%02X.%02X\n",
+i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
+Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
+Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
+for (i = 100; i < 103; i++)
+printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X %02X:%02X.%02X\n",
+i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
+Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
+Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
+#endif
+
+ return limit > 0 ? 0 : -1;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{ if (MOD_IN_USE)
+ { printk("aztcd module in use - can't remove it.\n");
+ return;
+ }
+ if ((unregister_blkdev(MAJOR_NR, "aztcd") == -EINVAL))
+ { printk("What's that: can't unregister aztcd\n");
+ return;
+ }
+ release_region(azt_port,4);
+ printk("aztcd module released.\n");
+}
+#endif MODULE
diff --git a/drivers/block/blk.h b/drivers/block/blk.h
index 53d55b17d..797c6a95e 100644
--- a/drivers/block/blk.h
+++ b/drivers/block/blk.h
@@ -3,16 +3,12 @@
#include <linux/blkdev.h>
#include <linux/locks.h>
+#include <linux/config.h>
/*
* NR_REQUEST is the number of entries in the request-queue.
* NOTE that writes may use only the low 2/3 of these: reads
* take precedence.
- *
- * 32 seems to be a reasonable number: enough to get some benefit
- * from the elevator-mechanism, but not so much as to lock a lot of
- * buffers when they are in the queue. 64 seems to be too many (easily
- * long pauses in reading when heavy writing/syncing is going on)
*/
#define NR_REQUEST 64
@@ -30,16 +26,31 @@
* These will have to be changed to be aware of different buffer
* sizes etc.. It actually needs a major cleanup.
*/
+#ifdef IDE_DRIVER
+#define SECTOR_MASK ((BLOCK_SIZE >> 9) - 1)
+#else
#define SECTOR_MASK (blksize_size[MAJOR_NR] && \
blksize_size[MAJOR_NR][MINOR(CURRENT->dev)] ? \
((blksize_size[MAJOR_NR][MINOR(CURRENT->dev)] >> 9) - 1) : \
((BLOCK_SIZE >> 9) - 1))
+#endif /* IDE_DRIVER */
#define SUBSECTOR(block) (CURRENT->current_nr_sectors > 0)
-extern unsigned long hd_init(unsigned long mem_start, unsigned long mem_end);
extern unsigned long cdu31a_init(unsigned long mem_start, unsigned long mem_end);
extern unsigned long mcd_init(unsigned long mem_start, unsigned long mem_end);
+#ifdef CONFIG_AZTCD
+extern unsigned long aztcd_init(unsigned long mem_start, unsigned long mem_end);
+#endif
+#ifdef CONFIG_CDU535
+extern unsigned long sony535_init(unsigned long mem_start, unsigned long mem_end);
+#endif
+#ifdef CONFIG_BLK_DEV_HD
+extern unsigned long hd_init(unsigned long mem_start, unsigned long mem_end);
+#endif
+#ifdef CONFIG_BLK_DEV_IDE
+extern unsigned long ide_init(unsigned long mem_start, unsigned long mem_end);
+#endif
#ifdef CONFIG_SBPCD
extern unsigned long sbpcd_init(unsigned long, unsigned long);
#endif CONFIG_SBPCD
@@ -64,14 +75,19 @@ extern unsigned long xd_init(unsigned long mem_start, unsigned long mem_end);
case BLKROGET: { int __err = verify_area(VERIFY_WRITE, (void *) (where), sizeof(long)); \
if (!__err) put_fs_long(0!=is_read_only(dev),(long *) (where)); return __err; }
-#ifdef MAJOR_NR
+#if defined(MAJOR_NR) || defined(IDE_DRIVER)
/*
- * Add entries as needed. Currently the only block devices
- * supported are hard-disks and floppies.
+ * Add entries as needed.
*/
-#if (MAJOR_NR == MEM_MAJOR)
+#ifdef IDE_DRIVER
+
+#define DEVICE_NR(device) (MINOR(device) >> PARTN_BITS)
+#define DEVICE_ON(device) /* nothing */
+#define DEVICE_OFF(device) /* nothing */
+
+#elif (MAJOR_NR == MEM_MAJOR)
/* ram disk */
#define DEVICE_NAME "ramdisk"
@@ -117,7 +133,7 @@ static void floppy_off(unsigned int nr);
#define DEVICE_NAME "scsitape"
#define DEVICE_INTR do_st
-#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_NR(device) (MINOR(device) & 0x7f)
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
@@ -155,6 +171,23 @@ static void floppy_off(unsigned int nr);
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
+#elif (MAJOR_NR == AZTECH_CDROM_MAJOR)
+
+#define DEVICE_NAME "Aztech CD-ROM"
+#define DEVICE_REQUEST do_aztcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == CDU535_CDROM_MAJOR)
+
+#define DEVICE_NAME "SONY-CDU535"
+#define DEVICE_INTR do_cdu535
+#define DEVICE_REQUEST do_cdu535_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
#elif (MAJOR_NR == MATSUSHITA_CDROM_MAJOR)
#define DEVICE_NAME "Matsushita CD-ROM controller #1"
@@ -187,9 +220,9 @@ static void floppy_off(unsigned int nr);
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
-#endif
+#endif /* MAJOR_NR == whatever */
-#if (MAJOR_NR != SCSI_TAPE_MAJOR)
+#if (MAJOR_NR != SCSI_TAPE_MAJOR) && !defined(IDE_DRIVER)
#ifndef CURRENT
#define CURRENT (blk_dev[MAJOR_NR].current_request)
@@ -215,27 +248,50 @@ if ((DEVICE_INTR = (x)) != NULL) \
else \
CLEAR_TIMER;
-#else
+#else /* !DEVICE_TIMEOUT */
#define SET_INTR(x) (DEVICE_INTR = (x))
-#endif
+#endif /* DEVICE_TIMEOUT */
+
static void (DEVICE_REQUEST)(void);
+#ifdef DEVICE_INTR
+#define CLEAR_INTR SET_INTR(NULL)
+#else
+#define CLEAR_INTR
+#endif
+
+#define INIT_REQUEST \
+ if (!CURRENT) {\
+ CLEAR_INTR; \
+ return; \
+ } \
+ if (MAJOR(CURRENT->dev) != MAJOR_NR) \
+ panic(DEVICE_NAME ": request list destroyed"); \
+ if (CURRENT->bh) { \
+ if (!CURRENT->bh->b_lock) \
+ panic(DEVICE_NAME ": block not locked"); \
+ }
+
+#endif /* (MAJOR_NR != SCSI_TAPE_MAJOR) && !defined(IDE_DRIVER) */
+
/* end_request() - SCSI devices have their own version */
#if ! SCSI_MAJOR(MAJOR_NR)
-static void end_request(int uptodate)
-{
- struct request * req;
+#ifdef IDE_DRIVER
+static void end_request(byte uptodate, byte hwif) {
+ struct request *req = ide_cur_rq[HWIF];
+#else
+static void end_request(int uptodate) {
+ struct request *req = CURRENT;
+#endif /* IDE_DRIVER */
struct buffer_head * bh;
- req = CURRENT;
req->errors = 0;
if (!uptodate) {
- printk(DEVICE_NAME " I/O error\n");
- printk("dev %04lX, sector %lu\n",
+ printk("end_request: I/O error, dev %04lX, sector %lu\n",
(unsigned long)req->dev, req->sector);
req->nr_sectors--;
req->nr_sectors &= ~SECTOR_MASK;
@@ -259,34 +315,19 @@ static void end_request(int uptodate)
return;
}
}
+#ifdef IDE_DRIVER
+ ide_cur_rq[HWIF] = NULL;
+#else
DEVICE_OFF(req->dev);
CURRENT = req->next;
+#endif /* IDE_DRIVER */
if (req->sem != NULL)
up(req->sem);
req->dev = -1;
wake_up(&wait_for_request);
}
-#endif
+#endif /* ! SCSI_MAJOR(MAJOR_NR) */
-#ifdef DEVICE_INTR
-#define CLEAR_INTR SET_INTR(NULL)
-#else
-#define CLEAR_INTR
-#endif
+#endif /* defined(MAJOR_NR) || defined(IDE_DRIVER) */
-#define INIT_REQUEST \
- if (!CURRENT) {\
- CLEAR_INTR; \
- return; \
- } \
- if (MAJOR(CURRENT->dev) != MAJOR_NR) \
- panic(DEVICE_NAME ": request list destroyed"); \
- if (CURRENT->bh) { \
- if (!CURRENT->bh->b_lock) \
- panic(DEVICE_NAME ": block not locked"); \
- }
-
-#endif
-
-#endif
-#endif
+#endif /* _BLK_H */
diff --git a/drivers/block/cdu31a.c b/drivers/block/cdu31a.c
index e4d789e4d..06847baf1 100644
--- a/drivers/block/cdu31a.c
+++ b/drivers/block/cdu31a.c
@@ -78,16 +78,9 @@
*
* Multi-Session
*
- * A multi-session disk is treated like a partitioned disk, each session
- * has it's own minor device number, starting with 0. The support is
- * pretty transparent, music, TOC operations, and read operations should
- * all work transparently on any session. Note that since the driver
- * writer doesn't have a multi-session disk, this is all theoretical.
- * Also, music operation will obviously only work on one session at a
- * time.
- *
- * NOTE: At the current time, multi-session still doesn't work. Maybe
- * I'll get a multi-session disk soon so I can play with it.
+ * A multi-session disk looks just like a normal disk to the user.
+ * Just mount one normally, and all the data should be there.
+ * A special thanks to Koen for help with this!
*
* Raw sector I/O
*
@@ -226,7 +219,7 @@ static struct
static int handle_sony_cd_attention(void);
static int read_subcode(void);
-static void sony_get_toc(int dev);
+static void sony_get_toc(void);
static int scd_open(struct inode *inode, struct file *filp);
static void do_sony_cd_cmd(unsigned char cmd,
unsigned char *params,
@@ -274,9 +267,12 @@ static unsigned int sony_usage = 0; /* How many processes have the
static int sony_pas_init = 0; /* Initialize the Pro-Audio
Spectrum card? */
-static struct s_sony_session_toc *(ses_tocs[MAX_TRACKS]); /* Points to the
- table of
- contents. */
+static struct s_sony_session_toc *sony_toc; /* Points to the
+ table of
+ contents. */
+
+static int sony_toc_read = 0; /* Has the TOC been read for
+ the drive? */
static struct s_sony_subcode * volatile last_sony_subcode; /* Points to the last
subcode address read */
@@ -317,11 +313,10 @@ static struct wait_queue *cdu31a_irq_wait = NULL;
static int curr_control_reg = 0; /* Current value of the control register */
-/* A disk changed variable for every possible session. When a disk change
- is detected, these will all be set to TRUE. As the upper layers ask
- for disk_changed status for individual minor numbers, they will be
- cleared. */
-static char disk_changed[MAX_TRACKS];
+/* A disk changed variable. When a disk change is detected, it will
+ all be set to TRUE. As the upper layers ask for disk_changed status
+ it will be cleared. */
+static char disk_changed;
/* Variable for using the readahead buffer. The readahead buffer
is used for raw sector reads and for blocksizes that are smaller
@@ -338,18 +333,10 @@ static int readahead_bad = 0;
static int
scd_disk_change(dev_t full_dev)
{
- int retval, target;
-
-
- target = MINOR(full_dev);
-
- if (target >= MAX_TRACKS) {
- printk("Sony CD-ROM request error: invalid device.\n");
- return 0;
- }
+ int retval;
- retval = disk_changed[target];
- disk_changed[target] = 0;
+ retval = disk_changed;
+ disk_changed = 0;
return retval;
}
@@ -373,7 +360,7 @@ disable_interrupts(void)
}
static void
-cdu31a_interrupt(int unused)
+cdu31a_interrupt(int irq, struct pt_regs *regs)
{
disable_interrupts();
if (cdu31a_irq_wait != NULL)
@@ -859,7 +846,6 @@ handle_sony_cd_attention(void)
{
unsigned char atten_code;
static int num_consecutive_attentions = 0;
- int i;
if (is_attention())
@@ -879,15 +865,8 @@ handle_sony_cd_attention(void)
{
/* Someone changed the CD. Mark it as changed */
case SONY_MECH_LOADED_ATTN:
- for (i=0; i<MAX_TRACKS; i++)
- {
- disk_changed[i] = 1;
- if (ses_tocs[i] != NULL)
- {
- kfree_s(ses_tocs[i], sizeof(struct s_sony_session_toc));
- ses_tocs[i] = NULL;
- }
- }
+ disk_changed = 1;
+ sony_toc_read = 0;
sony_audio_status = CDROM_AUDIO_NO_STATUS;
sony_blocks_left = 0;
break;
@@ -1000,8 +979,7 @@ size_to_buf(unsigned int size,
static int
start_request(unsigned int sector,
unsigned int nsect,
- int read_nsect_only,
- int dev)
+ int read_nsect_only)
{
unsigned char params[6];
unsigned int read_size;
@@ -1018,9 +996,9 @@ start_request(unsigned int sector,
* If the full read-ahead would go beyond the end of the media, trim
* it back to read just till the end of the media.
*/
- else if ((sector + nsect) >= ses_tocs[dev]->lead_out_start_lba)
+ else if ((sector + nsect) >= sony_toc->lead_out_start_lba)
{
- read_size = ses_tocs[dev]->lead_out_start_lba - sector;
+ read_size = sony_toc->lead_out_start_lba - sector;
}
/* Read the full readahead amount. */
else
@@ -1374,7 +1352,6 @@ static void
do_cdu31a_request(void)
{
int block;
- unsigned int dev;
int nblock;
unsigned char res_reg[12];
unsigned int res_size;
@@ -1407,7 +1384,7 @@ do_cdu31a_request(void)
cdu31a_request_startover:
/*
* The beginning here is stolen from the hard disk driver. I hope
- * its right.
+ * it's right.
*/
if (!(CURRENT) || CURRENT->dev < 0)
{
@@ -1440,36 +1417,24 @@ cdu31a_request_startover:
}
}
- dev = MINOR(CURRENT->dev);
block = CURRENT->sector;
nblock = CURRENT->nr_sectors;
- /* Check for multi-session disks. */
- if (dev > MAX_TRACKS)
- {
- printk("CDU31A: Invalid device request: %d\n", dev);
- end_request(0);
- goto cdu31a_request_startover;
- }
- else if (ses_tocs[dev] == NULL)
+ if (!sony_toc_read)
{
- printk("CDU31A: session TOC not read: %d\n", dev);
+ printk("CDU31A: TOC not read\n");
end_request(0);
goto cdu31a_request_startover;
}
- /* Check for base read of multi-session disk. */
- if ((dev != 0) && (block == 64))
+ /* Check for base read of multi-session disk. This will still work
+ for single session disks, so just do it. Blocks less than 80
+ are for the volume info, so offset them by the start track (which
+ should be zero for a single-session disk). */
+ if (block < 80)
{
- if (ses_tocs[dev]->first_track_num == ses_tocs[dev]->last_track_num)
- {
- printk("CDU31A: Not a multi-session disk: %d\n", dev);
- end_request(0);
- goto cdu31a_request_startover;
- }
-
/* Offset the request into the session. */
- block += (ses_tocs[dev]->start_track_lba * 4);
+ block += (sony_toc->start_track_lba * 4);
}
switch(CURRENT->cmd)
@@ -1479,19 +1444,21 @@ cdu31a_request_startover:
* If the block address is invalid or the request goes beyond the end of
* the media, return an error.
*/
- if ((block / 4) < ses_tocs[dev]->start_track_lba)
+#if 0
+ if ((block / 4) < sony_toc->start_track_lba)
{
printk("CDU31A: Request before beginning of media\n");
end_request(0);
goto cdu31a_request_startover;
}
- if ((block / 4) >= ses_tocs[dev]->lead_out_start_lba)
+#endif
+ if ((block / 4) >= sony_toc->lead_out_start_lba)
{
printk("CDU31A: Request past end of media\n");
end_request(0);
goto cdu31a_request_startover;
}
- if (((block + nblock) / 4) >= ses_tocs[dev]->lead_out_start_lba)
+ if (((block + nblock) / 4) >= sony_toc->lead_out_start_lba)
{
printk("CDU31A: Request past end of media\n");
end_request(0);
@@ -1504,9 +1471,9 @@ try_read_again:
while (handle_sony_cd_attention())
;
- if (ses_tocs[dev] == NULL)
+ if (!sony_toc_read)
{
- printk("CDU31A: session TOC not read: %d\n", dev);
+ printk("CDU31A: TOC not read\n");
end_request(0);
goto cdu31a_request_startover;
}
@@ -1515,7 +1482,7 @@ try_read_again:
next request. */
if (sony_blocks_left == 0)
{
- if (start_request(block / 4, CDU31A_READAHEAD / 4, 0, dev))
+ if (start_request(block / 4, CDU31A_READAHEAD / 4, 0))
{
end_request(0);
goto cdu31a_request_startover;
@@ -1532,13 +1499,13 @@ try_read_again:
sony_next_block);
#endif
abort_read();
- if (ses_tocs[dev] == NULL)
+ if (!sony_toc_read)
{
- printk("CDU31A: session TOC not read: %d\n", dev);
+ printk("CDU31A: TOC not read\n");
end_request(0);
goto cdu31a_request_startover;
}
- else if (start_request(block / 4, CDU31A_READAHEAD / 4, 0, dev))
+ if (start_request(block / 4, CDU31A_READAHEAD / 4, 0))
{
end_request(0);
goto cdu31a_request_startover;
@@ -1581,7 +1548,7 @@ try_read_again:
}
end_do_cdu31a_request:
-#if 0
+#if 1
/* After finished, cancel any pending operations. */
abort_read();
#endif
@@ -1614,93 +1581,137 @@ mcovlp(char *dst,
* successful.
*/
static void
-sony_get_toc(int dev)
+sony_get_toc(void)
{
+ unsigned char res_reg[2];
unsigned int res_size;
unsigned char parms[1];
+ int session;
- if (dev >= MAX_TRACKS)
- {
- printk("CDU31A: Request for invalid TOC track: %d\n", dev);
- }
- else if (ses_tocs[dev] == NULL)
+#if DEBUG
+ printk("Entering sony_get_toc\n");
+#endif
+
+ if (!sony_toc_read)
{
- ses_tocs[dev] = kmalloc(sizeof(struct s_sony_session_toc), 0);
- if (ses_tocs[dev] == NULL)
- {
- printk("CDU31A: Unable to alloc mem for TOC\n");
- }
- else
+ /* The idea here is we keep asking for sessions until the command
+ fails. Then we know what the last valid session on the disk is.
+ No need to check session 0, since session 0 is the same as session
+ 1; the command returns different information if you give it 0.
+ Don't check session 1 because that is the first session, it must
+ be there. */
+ session = 2;
+ while (1)
{
- parms[0] = dev+1;
- do_sony_cd_cmd(SONY_REQ_TOC_DATA_SPEC_CMD,
+#if DEBUG
+ printk("Trying session %d\n", session);
+#endif
+ parms[0] = session;
+ do_sony_cd_cmd(SONY_READ_TOC_SPEC_CMD,
parms,
1,
- (unsigned char *) ses_tocs[dev],
+ res_reg,
&res_size);
- if ((res_size < 2) || ((ses_tocs[dev]->exec_status[0] & 0xf0) == 0x20))
- {
- kfree_s(ses_tocs[dev], sizeof(struct s_sony_session_toc));
- ses_tocs[dev] = NULL;
- return;
- }
- /* For points that do not exist, move the data over them
- to the right location. */
- if (ses_tocs[dev]->pointb0 != 0xb0)
- {
- mcovlp(((char *) ses_tocs[dev]) + 27,
- ((char *) ses_tocs[dev]) + 18,
- res_size - 18);
- res_size += 9;
- }
- if (ses_tocs[dev]->pointb1 != 0xb1)
- {
- mcovlp(((char *) ses_tocs[dev]) + 36,
- ((char *) ses_tocs[dev]) + 27,
- res_size - 27);
- res_size += 9;
- }
- if (ses_tocs[dev]->pointb2 != 0xb2)
- {
- mcovlp(((char *) ses_tocs[dev]) + 45,
- ((char *) ses_tocs[dev]) + 36,
- res_size - 36);
- res_size += 9;
- }
- if (ses_tocs[dev]->pointb3 != 0xb3)
- {
- mcovlp(((char *) ses_tocs[dev]) + 54,
- ((char *) ses_tocs[dev]) + 45,
- res_size - 45);
- res_size += 9;
- }
- if (ses_tocs[dev]->pointb4 != 0xb4)
- {
- mcovlp(((char *) ses_tocs[dev]) + 63,
- ((char *) ses_tocs[dev]) + 54,
- res_size - 54);
- res_size += 9;
+#if DEBUG
+ printk("%2.2x %2.2x\n", res_reg[0], res_reg[1]);
+#endif
+
+ if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
+ {
+ /* An error reading the TOC, this must be past the last session. */
+ break;
}
- if (ses_tocs[dev]->pointc0 != 0xc0)
+
+ session++;
+
+ /* Let's not get carried away... */
+ if (session > 20)
{
- mcovlp(((char *) ses_tocs[dev]) + 72,
- ((char *) ses_tocs[dev]) + 63,
- res_size - 63);
- res_size += 9;
+ return;
}
+ }
+
+ session--;
- ses_tocs[dev]->start_track_lba = msf_to_log(ses_tocs[dev]->tracks[0].track_start_msf);
- ses_tocs[dev]->lead_out_start_lba = msf_to_log(ses_tocs[dev]->lead_out_start_msf);
#if DEBUG
- printk("Disk session %d, start track: %d, stop track: %d\n",
- dev,
- ses_tocs[dev]->start_track_lba,
- ses_tocs[dev]->lead_out_start_lba);
+ printk("Reading session %d\n", session);
#endif
+
+ parms[0] = session;
+ do_sony_cd_cmd(SONY_REQ_TOC_DATA_SPEC_CMD,
+ parms,
+ 1,
+ (unsigned char *) sony_toc,
+ &res_size);
+ if ((res_size < 2) || ((sony_toc->exec_status[0] & 0xf0) == 0x20))
+ {
+ /* An error reading the TOC. Return without sony_toc_read
+ set. */
+ return;
+ }
+
+ sony_toc_read = 1;
+
+ /* For points that do not exist, move the data over them
+ to the right location. */
+ if (sony_toc->pointb0 != 0xb0)
+ {
+ mcovlp(((char *) sony_toc) + 27,
+ ((char *) sony_toc) + 18,
+ res_size - 18);
+ res_size += 9;
+ }
+ if (sony_toc->pointb1 != 0xb1)
+ {
+ mcovlp(((char *) sony_toc) + 36,
+ ((char *) sony_toc) + 27,
+ res_size - 27);
+ res_size += 9;
+ }
+ if (sony_toc->pointb2 != 0xb2)
+ {
+ mcovlp(((char *) sony_toc) + 45,
+ ((char *) sony_toc) + 36,
+ res_size - 36);
+ res_size += 9;
+ }
+ if (sony_toc->pointb3 != 0xb3)
+ {
+ mcovlp(((char *) sony_toc) + 54,
+ ((char *) sony_toc) + 45,
+ res_size - 45);
+ res_size += 9;
}
+ if (sony_toc->pointb4 != 0xb4)
+ {
+ mcovlp(((char *) sony_toc) + 63,
+ ((char *) sony_toc) + 54,
+ res_size - 54);
+ res_size += 9;
+ }
+ if (sony_toc->pointc0 != 0xc0)
+ {
+ mcovlp(((char *) sony_toc) + 72,
+ ((char *) sony_toc) + 63,
+ res_size - 63);
+ res_size += 9;
+ }
+
+ sony_toc->start_track_lba = msf_to_log(sony_toc->tracks[0].track_start_msf);
+ sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf);
+
+#if DEBUG
+ printk("Disk session %d, start track: %d, stop track: %d\n",
+ session,
+ sony_toc->start_track_lba,
+ sony_toc->lead_out_start_lba);
+#endif
}
+#if DEBUG
+ printk("Leaving sony_get_toc\n");
+#endif
}
@@ -1708,17 +1719,16 @@ sony_get_toc(int dev)
* Search for a specific track in the table of contents.
*/
static int
-find_track(int track,
- int dev)
+find_track(int track)
{
int i;
int num_tracks;
- num_tracks = ses_tocs[dev]->last_track_num - ses_tocs[dev]->first_track_num + 1;
+ num_tracks = sony_toc->last_track_num - sony_toc->first_track_num + 1;
for (i = 0; i < num_tracks; i++)
{
- if (ses_tocs[dev]->tracks[i].track == track)
+ if (sony_toc->tracks[i].track == track)
{
return i;
}
@@ -1761,8 +1771,7 @@ read_subcode(void)
* (not BCD), so all the conversions are done.
*/
static int
-sony_get_subchnl_info(long arg,
- int dev)
+sony_get_subchnl_info(long arg)
{
struct cdrom_subchnl schi;
@@ -1771,8 +1780,8 @@ sony_get_subchnl_info(long arg,
while (handle_sony_cd_attention())
;
- sony_get_toc(dev);
- if (ses_tocs[dev] == NULL)
+ sony_get_toc();
+ if (!sony_toc_read)
{
return -EIO;
}
@@ -2025,7 +2034,7 @@ read_audio(struct cdrom_read_audio *ra,
retval = 0;
/* start_request clears out any readahead data, so it should be safe. */
- if (start_request(ra->addr.lba, ra->nframes, 1, 0))
+ if (start_request(ra->addr.lba, ra->nframes, 1))
{
retval = -EIO;
goto exit_read_audio;
@@ -2064,7 +2073,7 @@ read_audio(struct cdrom_read_audio *ra,
}
/* Restart the request on the current frame. */
- if (start_request(ra->addr.lba + cframe, ra->nframes - cframe, 1, 0))
+ if (start_request(ra->addr.lba + cframe, ra->nframes - cframe, 1))
{
retval = -EIO;
goto exit_read_audio;
@@ -2164,7 +2173,6 @@ scd_ioctl(struct inode *inode,
unsigned int cmd,
unsigned long arg)
{
- unsigned int dev;
unsigned char res_reg[12];
unsigned int res_size;
unsigned char params[7];
@@ -2175,11 +2183,6 @@ scd_ioctl(struct inode *inode,
{
return -EINVAL;
}
- dev = MINOR(inode->i_rdev);
- if (dev > MAX_TRACKS)
- {
- return -EINVAL;
- }
switch (cmd)
{
@@ -2289,16 +2292,16 @@ scd_ioctl(struct inode *inode,
struct cdrom_tochdr *hdr;
struct cdrom_tochdr loc_hdr;
- sony_get_toc(dev);
- if (ses_tocs[dev] == NULL)
+ sony_get_toc();
+ if (!sony_toc_read)
{
return -EIO;
}
hdr = (struct cdrom_tochdr *) arg;
verify_area(VERIFY_WRITE, hdr, sizeof(*hdr));
- loc_hdr.cdth_trk0 = bcd_to_int(ses_tocs[dev]->first_track_num);
- loc_hdr.cdth_trk1 = bcd_to_int(ses_tocs[dev]->last_track_num);
+ loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num);
+ loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num);
memcpy_tofs(hdr, &loc_hdr, sizeof(*hdr));
}
return 0;
@@ -2311,8 +2314,8 @@ scd_ioctl(struct inode *inode,
int track_idx;
unsigned char *msf_val = NULL;
- sony_get_toc(dev);
- if (ses_tocs[dev] == NULL)
+ sony_get_toc();
+ if (!sony_toc_read)
{
return -EIO;
}
@@ -2326,21 +2329,21 @@ scd_ioctl(struct inode *inode,
/* Lead out is handled separately since it is special. */
if (loc_entry.cdte_track == CDROM_LEADOUT)
{
- loc_entry.cdte_adr = ses_tocs[dev]->address2;
- loc_entry.cdte_ctrl = ses_tocs[dev]->control2;
- msf_val = ses_tocs[dev]->lead_out_start_msf;
+ loc_entry.cdte_adr = sony_toc->address2;
+ loc_entry.cdte_ctrl = sony_toc->control2;
+ msf_val = sony_toc->lead_out_start_msf;
}
else
{
- track_idx = find_track(int_to_bcd(loc_entry.cdte_track), dev);
+ track_idx = find_track(int_to_bcd(loc_entry.cdte_track));
if (track_idx < 0)
{
return -EINVAL;
}
- loc_entry.cdte_adr = ses_tocs[dev]->tracks[track_idx].address;
- loc_entry.cdte_ctrl = ses_tocs[dev]->tracks[track_idx].control;
- msf_val = ses_tocs[dev]->tracks[track_idx].track_start_msf;
+ loc_entry.cdte_adr = sony_toc->tracks[track_idx].address;
+ loc_entry.cdte_ctrl = sony_toc->tracks[track_idx].control;
+ msf_val = sony_toc->tracks[track_idx].track_start_msf;
}
/* Logical buffer address or MSF format requested? */
@@ -2364,8 +2367,8 @@ scd_ioctl(struct inode *inode,
struct cdrom_ti ti;
int track_idx;
- sony_get_toc(dev);
- if (ses_tocs[dev] == NULL)
+ sony_get_toc();
+ if (!sony_toc_read)
{
return -EIO;
}
@@ -2373,39 +2376,39 @@ scd_ioctl(struct inode *inode,
verify_area(VERIFY_READ, (char *) arg, sizeof(ti));
memcpy_fromfs(&ti, (char *) arg, sizeof(ti));
- if ( (ti.cdti_trk0 < ses_tocs[dev]->first_track_num)
- || (ti.cdti_trk0 > ses_tocs[dev]->last_track_num)
+ if ( (ti.cdti_trk0 < sony_toc->first_track_num)
+ || (ti.cdti_trk0 > sony_toc->last_track_num)
|| (ti.cdti_trk1 < ti.cdti_trk0))
{
return -EINVAL;
}
- track_idx = find_track(int_to_bcd(ti.cdti_trk0), dev);
+ track_idx = find_track(int_to_bcd(ti.cdti_trk0));
if (track_idx < 0)
{
return -EINVAL;
}
- params[1] = ses_tocs[dev]->tracks[track_idx].track_start_msf[0];
- params[2] = ses_tocs[dev]->tracks[track_idx].track_start_msf[1];
- params[3] = ses_tocs[dev]->tracks[track_idx].track_start_msf[2];
+ params[1] = sony_toc->tracks[track_idx].track_start_msf[0];
+ params[2] = sony_toc->tracks[track_idx].track_start_msf[1];
+ params[3] = sony_toc->tracks[track_idx].track_start_msf[2];
/*
* If we want to stop after the last track, use the lead-out
* MSF to do that.
*/
- if (ti.cdti_trk1 >= bcd_to_int(ses_tocs[dev]->last_track_num))
+ if (ti.cdti_trk1 >= bcd_to_int(sony_toc->last_track_num))
{
- log_to_msf(msf_to_log(ses_tocs[dev]->lead_out_start_msf)-1,
+ log_to_msf(msf_to_log(sony_toc->lead_out_start_msf)-1,
&(params[4]));
}
else
{
- track_idx = find_track(int_to_bcd(ti.cdti_trk1+1), dev);
+ track_idx = find_track(int_to_bcd(ti.cdti_trk1+1));
if (track_idx < 0)
{
return -EINVAL;
}
- log_to_msf(msf_to_log(ses_tocs[dev]->tracks[track_idx].track_start_msf)-1,
+ log_to_msf(msf_to_log(sony_toc->tracks[track_idx].track_start_msf)-1,
&(params[4]));
}
params[0] = 0x03;
@@ -2430,7 +2433,7 @@ scd_ioctl(struct inode *inode,
}
case CDROMSUBCHNL: /* Get subchannel info */
- return sony_get_subchnl_info(arg, dev);
+ return sony_get_subchnl_info(arg);
case CDROMVOLCTRL: /* Volume control. What volume does this change, anyway? */
{
@@ -2471,8 +2474,8 @@ scd_ioctl(struct inode *inode,
struct cdrom_read_audio ra;
- sony_get_toc(dev);
- if (ses_tocs[dev] == NULL)
+ sony_get_toc();
+ if (!sony_toc_read)
{
return -EIO;
}
@@ -2484,8 +2487,8 @@ scd_ioctl(struct inode *inode,
if (ra.addr_format == CDROM_LBA)
{
- if ( (ra.addr.lba >= ses_tocs[dev]->lead_out_start_lba)
- || (ra.addr.lba + ra.nframes >= ses_tocs[dev]->lead_out_start_lba))
+ if ( (ra.addr.lba >= sony_toc->lead_out_start_lba)
+ || (ra.addr.lba + ra.nframes >= sony_toc->lead_out_start_lba))
{
return -EINVAL;
}
@@ -2502,8 +2505,8 @@ scd_ioctl(struct inode *inode,
ra.addr.lba = ( (ra.addr.msf.minute * 4500)
+ (ra.addr.msf.second * 75)
+ ra.addr.msf.frame);
- if ( (ra.addr.lba >= ses_tocs[dev]->lead_out_start_lba)
- || (ra.addr.lba + ra.nframes >= ses_tocs[dev]->lead_out_start_lba))
+ if ( (ra.addr.lba >= sony_toc->lead_out_start_lba)
+ || (ra.addr.lba + ra.nframes >= sony_toc->lead_out_start_lba))
{
return -EINVAL;
}
@@ -2541,7 +2544,6 @@ scd_open(struct inode *inode,
unsigned int res_size;
int num_spin_ups;
unsigned char params[2];
- int dev;
if ((filp) && filp->f_mode & 2)
@@ -2568,7 +2570,7 @@ respinup_on_open:
it. It seems to mean the drive has already done the operation. */
if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0)))
{
- /* If the drive is already playing, its ok. */
+ /* If the drive is already playing, it's ok. */
if ((res_reg[1] == SONY_AUDIO_PLAYING_ERR) || (res_reg[1] == 0))
{
goto drive_spinning;
@@ -2589,9 +2591,8 @@ respinup_on_open:
return -EIO;
}
- dev = MINOR(inode->i_rdev);
- sony_get_toc(dev);
- if (ses_tocs[dev] == NULL)
+ sony_get_toc();
+ if (!sony_toc_read)
{
do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
return -EIO;
@@ -2599,7 +2600,7 @@ respinup_on_open:
/* For XA on the CDU31A only, we have to do special reads.
The CDU33A handles XA automagically. */
- if ( (ses_tocs[dev]->disk_type == SONY_XA_DISK_TYPE)
+ if ( (sony_toc->disk_type == SONY_XA_DISK_TYPE)
&& (!is_double_speed))
{
params[0] = SONY_SD_DECODE_PARAM;
@@ -2724,7 +2725,7 @@ get_drive_configuration(unsigned short base_io,
if (read_status_register() != 0xff)
{
/*
- * Reset the drive and wait for attention from it (to say its reset).
+ * Reset the drive and wait for attention from it (to say it's reset).
* If you don't wait, the next operation will probably fail.
*/
reset_drive();
@@ -2863,7 +2864,7 @@ cdu31a_init(unsigned long mem_start, unsigned long mem_end)
if (drive_found)
{
- snarf_region(sony_cd_base_io, 4);
+ request_region(sony_cd_base_io, 4,"cdu31a");
if (register_blkdev(MAJOR_NR,"cdu31a",&scd_fops))
{
@@ -2954,13 +2955,12 @@ cdu31a_init(unsigned long mem_start, unsigned long mem_end)
mem_start += sizeof(*last_sony_subcode);
readahead_buffer = (unsigned char *) mem_start;
mem_start += CD_FRAMESIZE_RAW;
+ sony_toc = (struct s_sony_session_toc *) mem_start;
+ mem_start += sizeof(struct s_sony_session_toc);
}
- for (i=0; i<MAX_TRACKS; i++)
- {
- disk_changed[i] = 1;
- }
+ disk_changed = 1;
return mem_start;
}
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index 7dc0ba4e1..e1501385f 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -81,9 +81,11 @@
* errors to allow safe writing by specialized programs.
*/
+/* 1995/8/26 -- Andreas Busse -- added Mips support.
+ * Needs some more cleanup, but seems to work so far.
+ */
+
#define CONFIG_FLOPPY_SANITY
-#undef CONFIG_FLOPPY_23
-#undef CONFIG_FLOPPY_2_FDC
#undef CONFIG_FLOPPY_SILENT_DCL_CLEAR
#define REALLY_SLOW_IO
@@ -93,27 +95,25 @@
#include <linux/config.h>
+/* do print messages for unexpected interrupts */
+static int print_unex=1;
+
#ifndef FD_MODULE
/* the following is the mask of allowed drives. By default units 2 and
* 3 of both floppy controllers are disabled, because switching on the
* motor of these drives causes system hangs on some PCI computers. drive
* 0 is the low bit (0x1), and drive 7 is the high bit (0x80). Bits are on if
* a drive is allowed. */
-#ifdef CONFIG_FLOPPY_23
-#define ALLOWED_DRIVE_MASK 0xff
-#else
-#define ALLOWED_DRIVE_MASK 0x33
-#endif
+static int ALLOWED_DRIVE_MASK=0x33;
#define FLOPPY_IRQ 6
#define FLOPPY_DMA 2
#define FDC1 0x3f0
-#define FDC2 0x370
+static int FDC2=-1;
#endif
#define MODULE_AWARE_DRIVER
-#ifdef CONFIG_BLK_DEV_FD
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/kernel.h>
@@ -127,6 +127,7 @@
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
+#include <linux/mc146818rtc.h> /* CMOS defines */
#include <asm/dma.h>
#include <asm/irq.h>
@@ -140,15 +141,44 @@
static unsigned int fake_change = 0;
static int initialising=1;
+#define FLOPPY0_TYPE ((CMOS_READ(0x10) >> 4) & 15)
+#define FLOPPY1_TYPE (CMOS_READ(0x10) & 15)
-#ifdef CONFIG_FLOPPY_2_FDC
#define N_FDC 2
#define N_DRIVE 8
-#else
-#define N_FDC 1
-#define N_DRIVE 4
+
+/*
+ * Again, the CMOS information doesn't work on the alpha..
+ */
+#ifdef __alpha__
+#undef FLOPPY0_TYPE
+#undef FLOPPY1_TYPE
+#define FLOPPY0_TYPE 6
+#define FLOPPY1_TYPE 0
#endif
+/*
+ * And on Mips's it doesn't work too.
+ */
+#ifdef __mips__
+#include <asm/bootinfo.h>
+#include <asm/mipsconfig.h>
+#include <asm/jazz.h>
+#include <asm/jazzdma.h>
+#include <asm/cachectl.h>
+#undef FLOPPY0_TYPE
+#undef FLOPPY1_TYPE
+#undef FDC1
+#define FLOPPY0_TYPE 4 /* this is wrong for the Olli M700, but who cares... */
+#define FLOPPY1_TYPE 0
+#define FDC1 ((boot_info.machtype == MACH_ACER_PICA_61 || \
+ boot_info.machtype == MACH_MIPS_MAGNUM_4000 || \
+ boot_info.machtype == MACH_OLIVETTI_M700) ? \
+ 0xe0003000 : PORT_BASE + 0x3f0)
+#undef N_FDC
+#define N_FDC 1 /* do you *really* want a second controller ? */
+#endif /* __mips__ */
+
#define TYPE(x) ( ((x)>>2) & 0x1f )
#define DRIVE(x) ( ((x)&0x03) | (((x)&0x80 ) >> 5))
#define UNIT(x) ( (x) & 0x03 ) /* drive on fdc */
@@ -171,16 +201,16 @@ static int initialising=1;
#define USETF(x) (set_bit(x##_BIT, &UDRS->flags))
#define UTESTF(x) (test_bit(x##_BIT, &UDRS->flags))
-#define DPRINT(x) printk(DEVICE_NAME "%d: " x,current_drive);
+#define DPRINT(x) printk(DEVICE_NAME "%d: " x,current_drive)
#define DPRINT1(x,x1) \
-printk(DEVICE_NAME "%d: " x,current_drive,(x1));
+printk(DEVICE_NAME "%d: " x,current_drive,(x1))
#define DPRINT2(x,x1,x2) \
-printk(DEVICE_NAME "%d: " x,current_drive,(x1),(x2));
+printk(DEVICE_NAME "%d: " x,current_drive,(x1),(x2))
#define DPRINT3(x,x1,x2,x3) \
-printk(DEVICE_NAME "%d: " x,current_drive,(x1),(x2),(x3));
+printk(DEVICE_NAME "%d: " x,current_drive,(x1),(x2),(x3))
/* read/write */
#define COMMAND raw_cmd.cmd[0]
@@ -217,6 +247,8 @@ printk(DEVICE_NAME "%d: " x,current_drive,(x1),(x2),(x3));
* Went back to the 1MB limit, as some people had problems with the floppy
* driver otherwise. It doesn't matter much for performance anyway, as most
* floppy accesses go through the track buffer.
+ * On MIPSes, this actually means that *all* transfers go thru the
+ * track buffer since 0x1000000 is always smaller than KSEG0/1.
*/
#define LAST_DMA_ADDR (0x1000000)
#define K_64 (0x10000) /* 64 k */
@@ -236,6 +268,9 @@ static int inr; /* size of reply buffer, when called from interrupt */
#define R_SECTOR (reply_buffer[5])
#define R_SIZECODE (reply_buffer[6])
+#define SEL_DLY (2*HZ/100)
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof( (x)[0] ))
/*
* this struct defines the different floppy drive types.
*/
@@ -249,38 +284,38 @@ static struct {
| | Head load time, msec
| | | Head unload time, msec (not used)
| | | | Step rate interval, usec
- | | | | | Time needed for spinup time (jiffies)
- | | | | | | Timeout for spinning down (jiffies)
- | | | | | | | Spindown offset (where disk stops)
- | | | | | | | | Select delay
- | | | | | | | | | RPS
- | | | | | | | | | | Max number of tracks
- | | | | | | | | | | | Interrupt timeout
- | | | | | | | | | | | | Max nonintlv. sectors
- | | | | | | | | | | | | | -Max Errors- flags */
-{{0, 500, 16, 16, 8000, 100, 300, 0, 2, 5, 80, 3*HZ, 20, {3,1,2,0,2}, 0,
- 0, { 7, 4, 8, 2, 1, 5, 3,10}, 150, 0 }, "unknown" },
-
-{{1, 300, 16, 16, 8000, 100, 300, 0, 2, 5, 40, 3*HZ, 17, {3,1,2,0,2}, 0,
- 0, { 1, 0, 0, 0, 0, 0, 0, 0}, 150, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/
-
-{{2, 500, 16, 16, 6000, 40, 300, 14, 2, 6, 83, 3*HZ, 17, {3,1,2,0,2}, 0,
- 0, { 2, 5, 6,23,10,20,11, 0}, 150, 2 }, "1.2M" }, /*5 1/4 HD AT*/
-
-{{3, 250, 16, 16, 3000, 100, 300, 0, 2, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
- 0, { 4,22,21,30, 3, 0, 0, 0}, 150, 4 }, "720k" }, /*3 1/2 DD*/
-
-{{4, 500, 16, 16, 4000, 40, 300, 10, 2, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
- 0, { 7, 4,25,22,31,21,29,11}, 150, 7 }, "1.44M" }, /*3 1/2 HD*/
-
-{{5, 1000, 15, 8, 3000, 40, 300, 10, 2, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
- 0, { 7, 8, 4,25,28,22,31,21}, 150, 8 }, "2.88M AMI BIOS" }, /*3 1/2 ED*/
-
-{{6, 1000, 15, 8, 3000, 40, 300, 10, 2, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
- 0, { 7, 8, 4,25,28,22,31,21}, 150, 8 }, "2.88M" } /*3 1/2 ED*/
-/* | ---autodetected formats-- | | |
- read_track | | Name printed when booting
- | Native format
+ | | | | | Time needed for spinup time (jiffies)
+ | | | | | | Timeout for spinning down (jiffies)
+ | | | | | | | Spindown offset (where disk stops)
+ | | | | | | | | Select delay
+ | | | | | | | | | RPS
+ | | | | | | | | | | Max number of tracks
+ | | | | | | | | | | | Interrupt timeout
+ | | | | | | | | | | | | Max nonintlv. sectors
+ | | | | | | | | | | | | | -Max Errors- flags */
+{{0, 500, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 80, 3*HZ, 20, {3,1,2,0,2}, 0,
+ 0, { 7, 4, 8, 2, 1, 5, 3,10}, 3*HZ/2, 0 }, "unknown" },
+
+{{1, 300, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 40, 3*HZ, 17, {3,1,2,0,2}, 0,
+ 0, { 1, 0, 0, 0, 0, 0, 0, 0}, 3*HZ/2, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/
+
+{{2, 500, 16, 16, 6000, 4*HZ/10, 3*HZ, 14, SEL_DLY, 6, 83, 3*HZ, 17, {3,1,2,0,2}, 0,
+ 0, { 2, 5, 6,23,10,20,11, 0}, 3*HZ/2, 2 }, "1.2M" }, /*5 1/4 HD AT*/
+
+{{3, 250, 16, 16, 3000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
+ 0, { 4,22,21,30, 3, 0, 0, 0}, 3*HZ/2, 4 }, "720k" }, /*3 1/2 DD*/
+
+{{4, 500, 16, 16, 4000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
+ 0, { 7, 4,25,22,31,21,29,11}, 3*HZ/2, 7 }, "1.44M" }, /*3 1/2 HD*/
+
+{{5, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
+ 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M AMI BIOS" }, /*3 1/2 ED*/
+
+{{6, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
+ 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M" } /*3 1/2 ED*/
+/* | ---autodetected formats-- | | |
+ read_track | | Name printed when booting
+ | Native format
Frequency of disk change checks */
};
@@ -339,11 +374,8 @@ static struct floppy_struct floppy_type[32] = {
/* Auto-detection: Disk type used until the next media change occurs. */
struct floppy_struct *current_type[N_DRIVE] = {
+ NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL
-#ifdef CONFIG_FLOPPY_2_FDC
- ,
- NULL, NULL, NULL, NULL
-#endif
};
/*
@@ -480,6 +512,107 @@ static inline void debugt(char *message)
}
/*
+ * Port access functions
+ */
+
+#ifdef CONFIG_MIPS_JAZZ
+static inline unsigned int fd_in(unsigned int port)
+{
+ if (port >= JAZZ_LOCAL_IO_SPACE) {
+ return (*(volatile unsigned char *)port);
+ udelay(1);
+ }
+ else
+ return inb_p(port);
+}
+
+static inline void fd_out(unsigned char value, unsigned int port)
+{
+ if (port >= JAZZ_LOCAL_IO_SPACE) {
+ *(volatile unsigned char *)port = value;
+ udelay(1);
+ }
+ else
+ outb_p(value, port);
+}
+#else /* !CONFIG_MIPS_JAZZ */
+#define fd_in(port) inb_p(port)
+#define fd_out(port,value) outb_p(port,value)
+#endif
+
+/*
+ * DMA control stuff. If MIPS_JAZZ support is compiled in, we have to
+ * check if we're running on such a board. If not, we just call the
+ * standard PC macros and functions.
+ */
+
+#ifdef CONFIG_MIPS_JAZZ
+#define fd_enable_dma() ((boot_info.machtype == MACH_ACER_PICA_61 || \
+ boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
+ vdma_enable(JAZZ_FLOPPY_DMA) : \
+ enable_dma(FLOPPY_DMA))
+#define fd_disable_dma() ((boot_info.machtype == MACH_ACER_PICA_61 || \
+ boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
+ vdma_disable(JAZZ_FLOPPY_DMA) : \
+ disable_dma(FLOPPY_DMA))
+#define fd_request_dma() ((boot_info.machtype == MACH_ACER_PICA_61 || \
+ boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
+ 0 : request_dma(FLOPPY_DMA,"floppy"))
+#define fd_free_dma() ((boot_info.machtype == MACH_ACER_PICA_61 || \
+ boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
+ : free_dma(FLOPPY_DMA))
+#define fd_clear_dma_ff() ((boot_info.machtype == MACH_ACER_PICA_61 || \
+ boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
+ : clear_dma_ff(FLOPPY_DMA))
+#define fd_set_dma_mode(mode) ((boot_info.machtype == MACH_ACER_PICA_61 || \
+ boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
+ vdma_set_mode(JAZZ_FLOPPY_DMA,mode) : \
+ set_dma_mode(FLOPPY_DMA,mode))
+#define fd_set_dma_addr(addr) ((boot_info.machtype == MACH_ACER_PICA_61 || \
+ boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
+ vdma_set_addr(JAZZ_FLOPPY_DMA, \
+ vdma_phys2log(PHYSADDR(addr))) : \
+ set_dma_addr(FLOPPY_DMA,addr))
+#define fd_set_dma_count(count) ((boot_info.machtype == MACH_ACER_PICA_61 || \
+ boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
+ vdma_set_count(JAZZ_FLOPPY_DMA,count) : \
+ set_dma_count(FLOPPY_DMA,count))
+#define fd_get_dma_residue() ((boot_info.machtype == MACH_ACER_PICA_61 || \
+ boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
+ vdma_get_residue(JAZZ_FLOPPY_DMA) : \
+ get_dma_residue(FLOPPY_DMA))
+#define fd_enable_irq() ((boot_info.machtype == MACH_ACER_PICA_61 || \
+ boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
+ : enable_irq(FLOPPY_IRQ))
+#define fd_disable_irq() ((boot_info.machtype == MACH_ACER_PICA_61 || \
+ boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
+ : disable_irq(FLOPPY_IRQ))
+#define fd_cacheflush(addr,size) \
+ ((boot_info.machtype == MACH_ACER_PICA_61 || \
+ boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
+ sys_cacheflush((char *)addr, size, DCACHE) : empty() )
+#else
+#define fd_enable_dma() enable_dma(FLOPPY_DMA)
+#define fd_disable_dma() disable_dma(FLOPPY_DMA)
+#define fd_request_dma() request_dma(FLOPPY_DMA,"floppy")
+#define fd_free_dma() free_dma(FLOPPY_DMA)
+#define fd_clear_dma_ff() clear_dma_ff(FLOPPY_DMA)
+#define fd_set_dma_mode(mode) set_dma_mode(FLOPPY_DMA,mode)
+#define fd_set_dma_addr(addr) set_dma_addr(FLOPPY_DMA,addr)
+#define fd_set_dma_count(count) set_dma_count(FLOPPY_DMA,count)
+#define fd_enable_irq() enable_irq(FLOPPY_IRQ)
+#define fd_disable_irq() disable_irq(FLOPPY_IRQ)
+#define fd_cacheflush(addr,size) /* nothing */
+#endif
+
+/* Just for coherency */
+
+#define fd_request_irq() request_irq(FLOPPY_IRQ, floppy_interrupt, \
+ SA_INTERRUPT, "floppy")
+#define fd_free_irq() free_irq(FLOPPY_IRQ);
+
+
+/*
* Bottom half floppy driver.
* ==========================
*
@@ -531,13 +664,13 @@ static int disk_change(int drive)
if (UDP->flags & FD_DEBUG){
DPRINT1("checking disk change line for drive %d\n",drive);
DPRINT1("jiffies=%ld\n", jiffies);
- DPRINT1("disk change line=%x\n",inb_p(FD_DIR)&0x80);
+ DPRINT1("disk change line=%x\n",fd_in(FD_DIR)&0x80);
DPRINT1("flags=%x\n",UDRS->flags);
}
#endif
if (UDP->flags & FD_BROKEN_DCL)
return UTESTF(FD_DISK_CHANGED);
- if( (inb_p(FD_DIR) ^ UDP->flags) & 0x80){
+ if( (fd_in(FD_DIR) ^ UDP->flags) & 0x80){
USETF(FD_VERIFY); /* verify write protection */
if(UDRS->maxblock){
/* mark it changed */
@@ -555,7 +688,7 @@ static int disk_change(int drive)
}
/*USETF(FD_DISK_NEWCHANGE);*/
return 1;
- } else if(jiffies >= DRS->select_date+DP->select_delay){
+ } else {
UDRS->last_checked=jiffies;
UCLEARF(FD_DISK_NEWCHANGE);
}
@@ -588,7 +721,8 @@ static int set_dor(int fdc, char mask, char data)
disk_change(drive);
}
FDCS->dor = newdor;
- outb_p(newdor, FD_DOR);
+/* printk("setting DOR to %02x\n",(unsigned)newdor); */
+ fd_out(newdor, FD_DOR);
unit = newdor & 0x3;
if(!is_selected(olddor, unit) && is_selected(newdor,unit)){
@@ -607,8 +741,8 @@ static void twaddle(void)
{
if (DP->select_delay)
return;
- outb_p(FDCS->dor & ~(0x10<<UNIT(current_drive)),FD_DOR);
- outb_p(FDCS->dor, FD_DOR);
+ fd_out(FDCS->dor & ~(0x10<<UNIT(current_drive)),FD_DOR);
+ fd_out(FDCS->dor, FD_DOR);
DRS->select_date = jiffies;
}
@@ -636,12 +770,10 @@ static void set_fdc(int drive)
current_drive = drive;
}
set_dor(fdc,~0,8);
-#ifdef CONFIG_FLOPPY_2_FDC
set_dor(1-fdc, ~8, 0);
-#endif
if ( FDCS->rawcmd == 2 )
reset_fdc_info(1);
- if( inb_p(FD_STATUS) != STATUS_READY )
+ if( fd_in(FD_STATUS) != STATUS_READY )
FDCS->reset = 1;
}
@@ -704,14 +836,11 @@ static struct timer_list motor_off_timer[N_DRIVE] = {
{ NULL, NULL, 0, 0, motor_off_callback },
{ NULL, NULL, 0, 1, motor_off_callback },
{ NULL, NULL, 0, 2, motor_off_callback },
- { NULL, NULL, 0, 3, motor_off_callback }
-#ifdef CONFIG_FLOPPY_2_FDC
- ,
+ { NULL, NULL, 0, 3, motor_off_callback },
{ NULL, NULL, 0, 4, motor_off_callback },
{ NULL, NULL, 0, 5, motor_off_callback },
{ NULL, NULL, 0, 6, motor_off_callback },
{ NULL, NULL, 0, 7, motor_off_callback }
-#endif
};
/* schedules motor off */
@@ -812,9 +941,50 @@ static int wait_for_completion(int delay, timeout_fn function)
return 0;
}
+static int hlt_disabled=0;
+static void floppy_disable_hlt(void)
+{
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ if(!hlt_disabled){
+ hlt_disabled=1;
+#ifdef HAVE_DISABLE_HLT
+ disable_hlt();
+#endif
+ }
+ restore_flags(flags);
+}
+
+static void floppy_enable_hlt(void)
+{
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ if(hlt_disabled){
+ hlt_disabled=0;
+#ifdef HAVE_DISABLE_HLT
+ enable_hlt();
+#endif
+ }
+ restore_flags(flags);
+}
+
+
static void setup_DMA(void)
{
#ifdef CONFIG_FLOPPY_SANITY
+ if (raw_cmd.length == 0){
+ int i;
+
+ printk("zero dma transfer size:");
+ for(i=0; i< raw_cmd.cmd_count; i++)
+ printk("%x,", raw_cmd.cmd[i]);
+ printk("\n");
+ cont->done(0);
+ FDCS->reset = 1;
+ return;
+ }
if ((!CURRENT ||
CURRENT->buffer != current_addr ||
raw_cmd.length > 512 * CURRENT->nr_sectors) &&
@@ -832,14 +1002,14 @@ static void setup_DMA(void)
FDCS->reset=1;
return;
}
- if ((long) current_addr % 512 ){
+ if ((unsigned long) current_addr % 512 ){
printk("non aligned address: %p\n", current_addr );
cont->done(0);
FDCS->reset=1;
return;
}
- if ( ( (long)current_addr & ~(64*1024-1) ) !=
- ((long)(current_addr + raw_cmd.length-1) & ~(64*1024-1))){
+ if ( ( (unsigned long)current_addr & ~(64*1024-1) ) !=
+ ((unsigned long)(current_addr + raw_cmd.length-1) & ~(64*1024-1))){
printk("DMA crossing 64-K boundary %p-%p\n",
current_addr, current_addr + raw_cmd.length);
cont->done(0);
@@ -849,15 +1019,15 @@ static void setup_DMA(void)
#endif
cli();
- disable_dma(FLOPPY_DMA);
- clear_dma_ff(FLOPPY_DMA);
- set_dma_mode(FLOPPY_DMA,
- (raw_cmd.flags & FD_RAW_READ)?
- DMA_MODE_READ : DMA_MODE_WRITE);
- set_dma_addr(FLOPPY_DMA, (long) current_addr);
- set_dma_count(FLOPPY_DMA, raw_cmd.length);
- enable_dma(FLOPPY_DMA);
+ fd_disable_dma();
+ fd_clear_dma_ff();
+ fd_set_dma_mode((raw_cmd.flags & FD_RAW_READ)?
+ DMA_MODE_READ : DMA_MODE_WRITE);
+ fd_set_dma_addr((long) current_addr);
+ fd_set_dma_count(raw_cmd.length);
+ fd_enable_dma();
sti();
+ floppy_disable_hlt();
}
/* sends a command byte to the fdc */
@@ -868,12 +1038,13 @@ static int output_byte(char byte)
if (FDCS->reset)
return -1;
- for(counter = 0 ; counter < 10000 && !FDCS->reset ; counter++) {
- status = inb_p(FD_STATUS) &(STATUS_READY|STATUS_DIR|STATUS_DMA);
+/* for(counter = 0 ; counter < 10000 && !FDCS->reset ; counter++) { */
+ for(counter = 0 ; counter < 100000 && !FDCS->reset ; counter++) {
+ status = fd_in(FD_STATUS) &(STATUS_READY|STATUS_DIR|STATUS_DMA);
if (!(status & STATUS_READY))
continue;
if (status == STATUS_READY){
- outb_p(byte,FD_DATA);
+ fd_out(byte,FD_DATA);
return 0;
} else
break;
@@ -894,7 +1065,7 @@ static int result(void)
if (FDCS->reset)
return -1;
for (counter = 0 ; counter < 10000 && !FDCS->reset ; counter++) {
- status = inb_p(FD_STATUS)&
+ status = fd_in(FD_STATUS)&
(STATUS_DIR|STATUS_READY|STATUS_BUSY|STATUS_DMA);
if (!(status & STATUS_READY))
continue;
@@ -907,7 +1078,7 @@ static int result(void)
DPRINT("floppy_stat reply overrun\n");
break;
}
- reply_buffer[i++] = inb_p(FD_DATA);
+ reply_buffer[i++] = fd_in(FD_DATA);
}
}
FDCS->reset = 1;
@@ -993,7 +1164,7 @@ static void fdc_specify(void)
/* TODO: lock this in via LOCK during initialization */
output_byte(FD_CONFIGURE);
output_byte(0);
- output_byte(0x1A); /* FIFO on, polling off, 10 byte threshold */
+ output_byte(0x2A); /* FIFO on, polling off, 10 byte threshold */
output_byte(0); /* precompensation from track 0 upwards */
if ( FDCS->reset ){
FDCS->has_fifo=0;
@@ -1063,7 +1234,7 @@ static int fdc_dtr(void)
return 0;
/* Set dtr */
- outb_p(raw_cmd.rate, FD_DCR);
+ fd_out(raw_cmd.rate, FD_DCR);
/* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB)
* need a stabilization period of several milliseconds to be
@@ -1398,18 +1569,22 @@ static void unexpected_floppy_interrupt(void)
int i;
if ( initialising )
return;
- DPRINT("unexpected interrupt\n");
- if ( inr >= 0 )
- for(i=0; i<inr; i++)
- printk("%d %x\n", i, reply_buffer[i] );
+ if(print_unex){
+ DPRINT("unexpected interrupt\n");
+ if ( inr >= 0 )
+ for(i=0; i<inr; i++)
+ printk("%d %x\n", i, reply_buffer[i] );
+ }
while(1){
output_byte(FD_SENSEI);
inr=result();
if ( inr != 2 )
break;
- printk("sensei\n");
- for(i=0; i<inr; i++)
- printk("%d %x\n", i, reply_buffer[i] );
+ if(print_unex){
+ printk("sensei\n");
+ for(i=0; i<inr; i++)
+ printk("%d %x\n", i, reply_buffer[i] );
+ }
}
FDCS->reset = 1;
}
@@ -1418,10 +1593,11 @@ struct tq_struct floppy_tq =
{ 0, 0, (void *) (void *) unexpected_floppy_interrupt, 0 };
/* interrupt handler */
-static void floppy_interrupt(int unused)
+static void floppy_interrupt(int irq, struct pt_regs * regs)
{
void (*handler)(void) = DEVICE_INTR;
+ floppy_enable_hlt();
CLEAR_INTR;
if ( fdc >= N_FDC || FDCS->address == -1){
/* we don't even know which FDC is the culprit */
@@ -1476,15 +1652,16 @@ static void reset_interrupt(void)
*/
static void reset_fdc(void)
{
+/* printk("in reset_fdc()\n"); */
SET_INTR(reset_interrupt);
FDCS->reset = 0;
reset_fdc_info(0);
if ( FDCS->version >= FDC_82077 )
- outb_p(0x80 | ( FDCS->dtr &3), FD_STATUS);
+ fd_out(0x80 | ( FDCS->dtr &3), FD_STATUS);
else {
- outb_p(FDCS->dor & ~0x04, FD_DOR);
+ fd_out(FDCS->dor & ~0x04, FD_DOR);
udelay(FD_RESET_DELAY);
- outb(FDCS->dor, FD_DOR);
+ fd_out(FDCS->dor, FD_DOR);
}
}
@@ -1500,11 +1677,13 @@ void show_floppy(void)
printk("floppy driver state\n");
printk("-------------------\n");
for(i=0; i<N_FDC; i++){
- printk("dor %d = %x\n", i, fdc_state[i].dor );
- outb_p(fdc_state[i].address+2, fdc_state[i].dor);
- udelay(1000); /* maybe we'll catch an interrupt... */
+ if(FDCS->address != -1){
+ printk("dor %d = %x\n", i, fdc_state[i].dor );
+ fd_out(fdc_state[i].address+2, fdc_state[i].dor);
+ udelay(1000); /* maybe we'll catch an interrupt... */
+ }
}
- printk("status=%x\n", inb_p(FD_STATUS));
+ printk("status=%x\n", fd_in(FD_STATUS));
printk("fdc_busy=%d\n", fdc_busy);
if( DEVICE_INTR)
printk("DEVICE_INTR=%p\n", DEVICE_INTR);
@@ -1529,7 +1708,8 @@ static void floppy_shutdown(void)
floppy_tq.routine = (void *)(void *) empty;
del_timer( &fd_timer);
- disable_dma(FLOPPY_DMA);
+ floppy_enable_hlt();
+ fd_disable_dma();
/* avoid dma going to a random drive after shutdown */
if(!initialising)
@@ -1710,9 +1890,10 @@ static void failure_and_wakeup(void)
static int next_valid_format(void)
{
int probed_format;
+
+ probed_format = DRS->probed_format;
while(1){
- probed_format = DRS->probed_format;
- if ( probed_format > N_DRIVE ||
+ if ( probed_format >= 8 ||
! DP->autodetect[probed_format] ){
DRS->probed_format = 0;
return 1;
@@ -2098,7 +2279,7 @@ static void copy_buffer(int ssize, int max_sector, int max_sector_2)
floppy_track_buffer + (max_buffer_sectors << 10) ||
dma_buffer < floppy_track_buffer ){
DPRINT1("buffer overrun in copy buffer %d\n",
- (floppy_track_buffer - dma_buffer) >>9);
+ (int) ((floppy_track_buffer - dma_buffer) >>9));
printk("sector_t=%d buffer_min=%d\n",
sector_t, buffer_min);
printk("current_count_sectors=%ld\n",
@@ -2109,13 +2290,17 @@ static void copy_buffer(int ssize, int max_sector, int max_sector_2)
printk("write\n");
break;
}
- if ( ((int)buffer) % 512 )
+ if ( ((unsigned long)buffer) % 512 )
DPRINT1("%p buffer not aligned\n", buffer);
#endif
- if ( CT(COMMAND) == FD_READ )
+ if ( CT(COMMAND) == FD_READ ) {
+ fd_cacheflush(dma_buffer, size);
memcpy( buffer, dma_buffer, size);
- else
+ }
+ else {
memcpy( dma_buffer, buffer, size);
+ fd_cacheflush(dma_buffer, size);
+ }
remaining -= size;
if ( !remaining)
break;
@@ -2246,19 +2431,19 @@ static int make_raw_rw_request(void)
raw_cmd.flags &= ~FD_RAW_WRITE;
raw_cmd.flags |= FD_RAW_READ;
COMMAND = FM_MODE(floppy,FD_READ);
- } else if ((long)CURRENT->buffer <= LAST_DMA_ADDR ) {
+ } else if ((unsigned long)CURRENT->buffer <= LAST_DMA_ADDR ) {
int direct, indirect;
indirect= transfer_size(ssize,max_sector,max_buffer_sectors*2) -
sector_t;
max_size = buffer_chain_size();
- if ( max_size > ( LAST_DMA_ADDR - ((long) CURRENT->buffer))>>9)
- max_size=(LAST_DMA_ADDR - ((long)CURRENT->buffer))>>9;
+ if ( max_size > ( LAST_DMA_ADDR - ((unsigned long) CURRENT->buffer))>>9)
+ max_size=(LAST_DMA_ADDR - ((unsigned long)CURRENT->buffer))>>9;
/* 64 kb boundaries */
- if ( ((max_size << 9) + ((long) CURRENT->buffer)) / K_64 !=
- ((long) CURRENT->buffer ) / K_64 )
- max_size = ( K_64 - ((long) CURRENT->buffer) % K_64)>>9;
+ if ( ((max_size << 9) + ((unsigned long) CURRENT->buffer)) / K_64 !=
+ ((unsigned long) CURRENT->buffer ) / K_64 )
+ max_size = ( K_64 - ((unsigned long) CURRENT->buffer) % K_64)>>9;
direct = transfer_size(ssize,max_sector,max_size) - sector_t;
/*
* We try to read tracks, but if we get too many errors, we
@@ -2267,15 +2452,21 @@ static int make_raw_rw_request(void)
* This means we should be able to read a sector even if there
* are other bad sectors on this track.
*/
- if ((indirect - sector_t) * 2 > (direct - sector_t) * 3 &&
- *errors < DP->max_errors.read_track &&
- /*!TESTF( FD_NEED_TWADDLE) &&*/
- ( ( !probing || (DP->read_track &
- (1 <<DRS->probed_format))))){
+ if (!direct ||
+ (indirect * 2 > direct * 3 &&
+ *errors < DP->max_errors.read_track &&
+ /*!TESTF( FD_NEED_TWADDLE) &&*/
+ ((!probing || (DP->read_track&(1<<DRS->probed_format)))))){
max_size = CURRENT->nr_sectors;
} else {
current_addr = CURRENT->buffer;
raw_cmd.length = current_count_sectors << 9;
+ if (raw_cmd.length == 0){
+ DPRINT("zero dma transfer attempted from make_raw_request\n");
+ DPRINT3("indirect=%d direct=%d sector_t=%d",
+ indirect, direct, sector_t);
+ return 0;
+ }
return 2;
}
}
@@ -2286,6 +2477,7 @@ static int make_raw_rw_request(void)
/* claim buffer track if needed */
if (buffer_track != raw_cmd.track || /* bad track */
buffer_drive !=current_drive || /* bad drive */
+ sector_t > buffer_max ||
sector_t < buffer_min ||
((CT(COMMAND) == FD_READ ||
(aligned_sector_t == sector_t && CURRENT->nr_sectors >= ssize ))&&
@@ -2330,7 +2522,7 @@ static int make_raw_rw_request(void)
raw_cmd.length, current_count_sectors);
if ( current_addr != CURRENT->buffer )
printk("addr=%d, length=%ld\n",
- (current_addr - floppy_track_buffer ) >> 9,
+ (int) ((current_addr - floppy_track_buffer ) >> 9),
current_count_sectors);
printk("st=%d ast=%d mse=%d msi=%d\n",
sector_t, aligned_sector_t, max_sector, max_size);
@@ -2371,6 +2563,10 @@ static int make_raw_rw_request(void)
printk("bytes=%ld\n", raw_cmd.length >> 9 );
printk("sectors=%ld\n", current_count_sectors);
}
+ if (raw_cmd.length == 0){
+ DPRINT("zero dma transfer attempted from make_raw_request\n");
+ return 0;
+ }
#endif
return 2;
}
@@ -2551,6 +2747,9 @@ static int fd_copyout(void *param, volatile void *address, int size)
i = verify_area(VERIFY_WRITE,param,size);
if (i)
return i;
+ fd_cacheflush(address, size); /* is this necessary ??? */
+ /* Ralf: Yes; only the l2 cache is completly chipset
+ controlled */
memcpy_tofs(param,(void *) address, size);
return 0;
}
@@ -2612,6 +2811,10 @@ static int raw_cmd_ioctl(void *param)
if (raw_cmd.flags & (FD_RAW_WRITE | FD_RAW_READ)){
if(count > max_buffer_sectors * 1024 )
return -ENOMEM;
+ if(count == 0){
+ printk("attempt to do a 0 byte dma transfer\n");
+ return -EINVAL;
+ }
buffer_track = -1;
}
if ( raw_cmd.flags & FD_RAW_WRITE ){
@@ -2619,6 +2822,7 @@ static int raw_cmd_ioctl(void *param)
if (i)
return i;
memcpy_fromfs(floppy_track_buffer, raw_cmd.data, count);
+ fd_cacheflush(floppy_track_buffer, count);
}
current_addr = floppy_track_buffer;
@@ -2649,6 +2853,7 @@ static int raw_cmd_ioctl(void *param)
return ret;
if ( raw_cmd.flags & FD_RAW_READ ){
+ fd_cacheflush(floppy_track_buffer, count);
i=fd_copyout( raw_cmd.data, floppy_track_buffer, count);
if (i)
return i;
@@ -2784,9 +2989,7 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
cnt < (type << 2 ) + 4 ;
cnt++)
floppy_sizes[cnt]=
-#ifdef CONFIG_FLOPPY_2_FDC
floppy_sizes[cnt+0x80]=
-#endif
floppy_type[type].size>>1;
process_fd_request();
for ( cnt = 0; cnt < N_DRIVE; cnt++){
@@ -2854,41 +3057,44 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
#undef IOCTL_ALLOWED
}
-#define CMOS_READ(addr) ({ \
-outb_p(addr,0x70); \
-inb_p(0x71); \
-})
-
-static void set_base_type(int drive,int code)
-{
- if (code > 0 && code <= NUMBER(default_drive_params)) {
- memcpy((char *) UDP,
- (char *) (&default_drive_params[code].params),
- sizeof( struct floppy_drive_params ));
- printk("fd%d is %s", drive, default_drive_params[code].name);
- return;
- } else if (!code)
- printk("fd%d is not installed", drive);
- else
- printk("fd%d is unknown type %d",drive,code);
-}
-
static void config_types(void)
{
+ int first=1;
int drive;
- for (drive=0; drive<N_DRIVE ; drive++){
- /* default type for unidentifiable drives */
- memcpy((char *) UDP, (char *) (&default_drive_params->params),
- sizeof( struct floppy_drive_params ));
- }
- printk("Floppy drive(s): ");
- set_base_type(0, (CMOS_READ(0x10) >> 4) & 15);
- if (CMOS_READ(0x10) & 15) {
- printk(", ");
- set_base_type(1, CMOS_READ(0x10) & 15);
+ /* read drive info out of physical cmos */
+ drive=0;
+ if (!UDP->cmos )
+ UDP->cmos= FLOPPY0_TYPE;
+ drive=1;
+ if (!UDP->cmos && FLOPPY1_TYPE)
+ UDP->cmos = FLOPPY1_TYPE;
+
+ /* XXX */
+ /* additional physical CMOS drive detection should go here */
+
+ for (drive=0; drive < N_DRIVE; drive++){
+ if (UDP->cmos >= 0 && UDP->cmos <= NUMBER(default_drive_params))
+ memcpy((char *) UDP,
+ (char *) (&default_drive_params[(int)UDP->cmos].params),
+ sizeof(struct floppy_drive_params));
+ if (UDP->cmos){
+ if (first)
+ printk("Floppy drive(s): ");
+ else
+ printk(", ");
+ first=0;
+ if (UDP->cmos > 0 ){
+ ALLOWED_DRIVE_MASK |= 1 << drive;
+ printk("fd%d is %s", drive,
+ default_drive_params[(int)UDP->cmos].name);
+ } else
+ printk("fd%d is unknown type %d",drive,
+ UDP->cmos);
+ }
}
- printk("\n");
+ if(!first)
+ printk("\n");
}
static int floppy_read(struct inode * inode, struct file * filp,
@@ -3000,7 +3206,7 @@ static int floppy_open(struct inode * inode, struct file * filp)
}
/* Allow ioctls if we have write-permissions even if read-only open */
- if ((filp->f_mode & 2) || permission(inode,2))
+ if ((filp->f_mode & 2) || (permission(inode,2) == 0))
filp->f_mode |= IOCTL_MODE_BIT;
if (filp->f_mode & 2)
filp->f_mode |= OPEN_WRITE_BIT;
@@ -3163,6 +3369,130 @@ static char get_fdc_version(void)
return FDC_82077; /* Revised 82077AA passes all the tests */
} /* get_fdc_version */
+/* lilo configuration */
+
+/* we make the invert_dcl function global. One day, somebody might
+want to centralize all thinkpad related options into one lilo option,
+there are just so many thinkpad related quirks! */
+void floppy_invert_dcl(int *ints,int param)
+{
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(default_drive_params); i++){
+ if (param)
+ default_drive_params[i].params.flags |= 0x80;
+ else
+ default_drive_params[i].params.flags &= ~0x80;
+ }
+ DPRINT("Configuring drives for inverted dcl\n");
+}
+
+static void daring(int *ints,int param)
+{
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(default_drive_params); i++){
+ if (param){
+ default_drive_params[i].params.select_delay = 0;
+ default_drive_params[i].params.flags |= FD_SILENT_DCL_CLEAR;
+ } else {
+ default_drive_params[i].params.select_delay = 2*HZ/100;
+ default_drive_params[i].params.flags &= ~FD_SILENT_DCL_CLEAR;
+ }
+ }
+ DPRINT1("Assuming %s floppy hardware\n", param ? "standard" : "broken");
+}
+
+static void allow_drives(int *ints, int param)
+{
+ ALLOWED_DRIVE_MASK=param;
+ DPRINT1("setting allowed_drive_mask to 0x%x\n", param);
+}
+
+static void fdc2_adr(int *ints, int param)
+{
+ FDC2 = param;
+ if(param)
+ DPRINT1("enabling second fdc at address 0x%3x\n", FDC2);
+ else
+ DPRINT("disabling second fdc\n");
+}
+
+static void unex(int *ints,int param)
+{
+ print_unex = param;
+ DPRINT1("%sprinting messages for unexpected interrupts\n",
+ param ? "" : "not ");
+}
+
+static void set_cmos(int *ints, int dummy)
+{
+ int current_drive=0;
+
+ if ( ints[0] != 2 ){
+ DPRINT("wrong number of parameter for cmos\n");
+ return;
+ }
+ current_drive = ints[1];
+ if (current_drive < 0 || current_drive >= 8 ){
+ DPRINT("bad drive for set_cmos\n");
+ return;
+ }
+ if(ints[2] <= 0 || ints[2] >= NUMBER(default_drive_params)){
+ DPRINT1("bad cmos code %d\n", ints[2]);
+ return;
+ }
+ DP->cmos = ints[2];
+ DPRINT1("setting cmos code to %d\n", ints[2]);
+}
+
+static struct param_table {
+ char *name;
+ void (*fn)(int *ints, int param);
+ int def_param;
+} config_params[]={
+{ "allowed_drive_mask", allow_drives, 0xff },
+{ "all_drives", allow_drives, 0xff },
+{ "asus_pci", allow_drives, 0x33 },
+
+{ "daring", daring, 1},
+
+{ "two_fdc", fdc2_adr, 0x370 },
+{ "one_fdc", fdc2_adr, 0 },
+
+{ "thinkpad", floppy_invert_dcl, 1 },
+
+{ "cmos", set_cmos, 0 },
+
+{ "unexpected_interrupts", unex, 1 },
+{ "no_unexpected_interrupts", unex, 0 },
+{ "L40SX", unex, 0 } };
+
+#define FLOPPY_SETUP
+void floppy_setup(char *str, int *ints)
+{
+ int i;
+ int param;
+ if(!str)
+ return;
+ for(i=0; i< ARRAY_SIZE(config_params); i++){
+ if (strcmp(str,config_params[i].name) == 0 ){
+ if (ints[0] )
+ param = ints[1];
+ else
+ param = config_params[i].def_param;
+ config_params[i].fn(ints,param);
+ return;
+ }
+ }
+ DPRINT1("unknown floppy option %s\n", str);
+ DPRINT("allowed options are:");
+ for(i=0; i< ARRAY_SIZE(config_params); i++)
+ printk(" %s",config_params[i].name);
+ printk("\n");
+ DPRINT("Read linux/drivers/block/README.fd\n");
+}
+
#ifdef FD_MODULE
static
#endif
@@ -3230,12 +3560,16 @@ int new_floppy_init(void)
if (FDCS->address == -1 )
continue;
FDCS->rawcmd = 2;
- if(user_reset_fdc(-1,FD_RESET_IF_NEEDED,0))
+ if(user_reset_fdc(-1,FD_RESET_IF_NEEDED,0)){
+ FDCS->address = -1;
continue;
+ }
/* Try to determine the floppy controller type */
FDCS->version = get_fdc_version();
- if (FDCS->version == FDC_NONE)
+ if (FDCS->version == FDC_NONE){
+ FDCS->address = -1;
continue;
+ }
have_no_fdc = 0;
/* Not all FDCs seem to be able to handle the version command
@@ -3251,6 +3585,14 @@ int new_floppy_init(void)
initialising=0;
if(have_no_fdc)
unregister_blkdev(MAJOR_NR,"fd");
+#ifdef CONFIG_MIPS_JAZZ
+ else {
+ if (boot_info.machtype == MACH_ACER_PICA_61 ||
+ boot_info.machtype == MACH_MIPS_MAGNUM_4000)
+ vdma_alloc(PHYSADDR(floppy_track_buffer),
+ 512*2*MAX_BUFFER_SECTORS);
+ }
+#endif
return have_no_fdc;
}
@@ -3272,29 +3614,32 @@ static int floppy_grab_irq_and_dma(void)
#ifdef FD_MODULE
MOD_INC_USE_COUNT;
#endif
- for(i=0; i< N_FDC; i++){
- fdc = i;
- reset_fdc_info(1);
- outb_p(FDCS->dor, FD_DOR);
+ for(i=0; i< N_FDC; i++){
+ if(FDCS->address != -1){
+ fdc = i;
+ reset_fdc_info(1);
+ fd_out(FDCS->dor, FD_DOR);
+ }
}
set_dor(0, ~0, 8); /* avoid immediate interrupt */
- if (request_irq(FLOPPY_IRQ, floppy_interrupt, SA_INTERRUPT, "floppy")) {
+ if (fd_request_irq()) {
DPRINT1("Unable to grab IRQ%d for the floppy driver\n",
FLOPPY_IRQ);
return -1;
}
- if (request_dma(FLOPPY_DMA,"floppy")) {
+ if (fd_request_dma()) {
DPRINT1("Unable to grab DMA%d for the floppy driver\n",
FLOPPY_DMA);
- free_irq(FLOPPY_IRQ);
+ fd_free_irq();
return -1;
}
for(fdc = 0; fdc < N_FDC ; fdc++)
if(FDCS->address != -1)
- outb_p(FDCS->dor, FD_DOR);
+ fd_out(FDCS->dor, FD_DOR);
fdc = 0;
- enable_irq(FLOPPY_IRQ);
+/* printk("enable irq %d\n",FLOPPY_IRQ); */
+ fd_enable_irq();
return 0;
}
@@ -3312,16 +3657,16 @@ static void floppy_release_irq_and_dma(void)
#ifdef FD_MODULE
MOD_DEC_USE_COUNT;
#endif
- disable_dma(FLOPPY_DMA);
- free_dma(FLOPPY_DMA);
- disable_irq(FLOPPY_IRQ);
- free_irq(FLOPPY_IRQ);
+ fd_disable_dma();
+ fd_free_dma();
+ fd_disable_irq();
+ fd_free_irq();
set_dor(0, ~0, 8);
#if N_FDC > 1
set_dor(1, ~8, 0);
#endif
-
+ floppy_enable_hlt();
#ifdef CONFIG_FLOPPY_SANITY
for(drive=0; drive < N_FDC * 4; drive++)
if( motor_off_timer[drive].next )
@@ -3336,4 +3681,3 @@ static void floppy_release_irq_and_dma(void)
#endif
}
-#endif
diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c
index f7e98e1ed..1108288fe 100644
--- a/drivers/block/genhd.c
+++ b/drivers/block/genhd.c
@@ -10,10 +10,10 @@
* in the early extended-partition checks and added DM partitions
*/
-#include <linux/config.h>
#include <linux/fs.h>
#include <linux/genhd.h>
#include <linux/kernel.h>
+#include <linux/major.h>
struct gendisk *gendisk_head = NULL;
@@ -22,6 +22,19 @@ extern int *blk_size[];
extern void rd_load(void);
extern int ramdisk_size;
+static char minor_name (struct gendisk *hd, int minor)
+{
+ char base_name = (hd->major == IDE1_MAJOR) ? 'c' : 'a';
+ return base_name + (minor >> hd->minor_shift);
+}
+
+static void add_partition (struct gendisk *hd, int minor, int start, int size)
+{
+ hd->part[minor].start_sect = start;
+ hd->part[minor].nr_sects = size;
+ printk(" %s%c%d", hd->major_name, minor_name(hd, minor),
+ minor & ((1 << hd->minor_shift) - 1));
+}
/*
* Create devices for each logical partition in an extended partition.
* The logical partitions form a linked list, with each entry being
@@ -61,13 +74,9 @@ static void extended_partition(struct gendisk *hd, int dev)
* Process the first entry, which should be the real
* data partition.
*/
- if (p->sys_ind == EXTENDED_PARTITION ||
- !(hd->part[current_minor].nr_sects = p->nr_sects))
+ if (p->sys_ind == EXTENDED_PARTITION || !p->nr_sects)
goto done; /* shouldn't happen */
- hd->part[current_minor].start_sect = this_sector + p->start_sect;
- printk(" %s%c%d", hd->major_name,
- 'a'+(current_minor >> hd->minor_shift),
- mask & current_minor);
+ add_partition(hd, current_minor, this_sector+p->start_sect, p->nr_sects);
current_minor++;
p++;
/*
@@ -81,6 +90,7 @@ static void extended_partition(struct gendisk *hd, int dev)
!(hd->part[current_minor].nr_sects = p->nr_sects))
goto done; /* no more logicals in this partition */
hd->part[current_minor].start_sect = first_sector + p->start_sect;
+ hd->sizes[current_minor] = p->nr_sects >> (BLOCK_SIZE_BITS - 9);
this_sector = first_sector + p->start_sect;
dev = ((hd->major) << 8) | current_minor;
brelse(bh);
@@ -104,19 +114,28 @@ static void check_partition(struct gendisk *hd, unsigned int dev)
printk("Partition check:\n");
first_time = 0;
first_sector = hd->part[MINOR(dev)].start_sect;
+
+ /*
+ * This is a kludge to allow the partition check to be
+ * skipped for specific drives (ie. IDE cd-rom drives)
+ */
+ if ((int)first_sector == -1) {
+ hd->part[MINOR(dev)].start_sect = 0;
+ return;
+ }
+
if (!(bh = bread(dev,0,1024))) {
printk(" unable to read partition table of device %04x\n",dev);
return;
}
- printk(" %s%c:", hd->major_name, 'a'+(minor >> hd->minor_shift));
- current_minor += 4; /* first "extra" minor */
+ printk(" %s%c:", hd->major_name, minor_name(hd, minor));
+ current_minor += 4; /* first "extra" minor (for extended partitions) */
if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
p = (struct partition *) (0x1BE + bh->b_data);
for (i=1 ; i<=4 ; minor++,i++,p++) {
- if (!(hd->part[minor].nr_sects = p->nr_sects))
+ if (!p->nr_sects)
continue;
- hd->part[minor].start_sect = first_sector + p->start_sect;
- printk(" %s%c%d", hd->major_name,'a'+(minor >> hd->minor_shift), i);
+ add_partition(hd, minor, first_sector+p->start_sect, p->nr_sects);
if ((current_minor & 0x3f) >= 60)
continue;
if (p->sys_ind == EXTENDED_PARTITION) {
@@ -136,11 +155,7 @@ static void check_partition(struct gendisk *hd, unsigned int dev)
break;
if (!(p->start_sect && p->nr_sects))
continue;
- hd->part[current_minor].start_sect = p->start_sect;
- hd->part[current_minor].nr_sects = p->nr_sects;
- printk(" %s%c%d", hd->major_name,
- 'a'+(current_minor >> hd->minor_shift),
- current_minor & mask);
+ add_partition(hd, current_minor, p->start_sect, p->nr_sects);
}
}
} else
diff --git a/drivers/block/hd.c b/drivers/block/hd.c
index f6af9eaa4..f17aa86d9 100644
--- a/drivers/block/hd.c
+++ b/drivers/block/hd.c
@@ -1,5 +1,5 @@
/*
- * linux/kernel/hd.c
+ * linux/drivers/block/hd.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
@@ -31,9 +31,10 @@
#include <linux/kernel.h>
#include <linux/hdreg.h>
#include <linux/genhd.h>
-#include <linux/config.h>
#include <linux/malloc.h>
#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/mc146818rtc.h> /* CMOS defines */
#define REALLY_SLOW_IO
#include <asm/system.h>
@@ -47,12 +48,6 @@
static int revalidate_hddisk(int, int);
-static inline unsigned char CMOS_READ(unsigned char addr)
-{
- outb_p(addr,0x70);
- return inb_p(0x71);
-}
-
#define HD_DELAY 0
#define MAX_ERRORS 16 /* Max read/write errors/sector */
@@ -836,14 +831,6 @@ static int hd_ioctl(struct inode * inode, struct file * file,
case BLKRRPART: /* Re-read partition tables */
return revalidate_hddisk(inode->i_rdev, 1);
- case HDIO_SETUNMASKINTR: /* obsolete */
- printk("hd: obsolete syscall: HDIO_SETUNMASKINTR\n");
- if (!arg) return -EINVAL;
- err = verify_area(VERIFY_READ, (long *) arg, sizeof(long));
- if (err)
- return err;
- arg = get_fs_long((long *) arg);
- /* drop into HDIO_SET_UNMASKINTR */
case HDIO_SET_UNMASKINTR:
if (!suser()) return -EACCES;
if ((arg > 1) || (MINOR(inode->i_rdev) & 0x3F))
@@ -867,14 +854,6 @@ static int hd_ioctl(struct inode * inode, struct file * file,
put_fs_long(mult_count[dev], (long *) arg);
return 0;
- case HDIO_SETMULTCOUNT: /* obsolete */
- printk("hd: obsolete syscall: HDIO_SETMULTCOUNT\n");
- if (!arg) return -EINVAL;
- err = verify_area(VERIFY_READ, (long *) arg, sizeof(long));
- if (err)
- return err;
- arg = get_fs_long((long *) arg);
- /* drop into HDIO_SET_MULTCOUNT */
case HDIO_SET_MULTCOUNT:
if (!suser()) return -EACCES;
if (MINOR(inode->i_rdev) & 0x3F) return -EINVAL;
@@ -914,6 +893,8 @@ static int hd_open(struct inode * inode, struct file * filp)
int target;
target = DEVICE_NR(inode->i_rdev);
+ if (target >= NR_HD)
+ return -ENODEV;
while (busy[target])
sleep_on(&busy_wait);
access_count[target]++;
@@ -950,7 +931,7 @@ static struct gendisk hd_gendisk = {
NULL /* next */
};
-static void hd_interrupt(int unused)
+static void hd_interrupt(int irq, struct pt_regs *regs)
{
void (*handler)(void) = DEVICE_INTR;
@@ -1040,6 +1021,9 @@ static void hd_geninit(void)
if (request_irq(HD_IRQ, hd_interrupt, SA_INTERRUPT, "hd")) {
printk("hd: unable to get IRQ%d for the harddisk driver\n",HD_IRQ);
NR_HD = 0;
+ } else {
+ request_region(HD_DATA, 8, "hd");
+ request_region(HD_CMD, 1, "hd(cmd)");
}
}
hd_gendisk.nr_real = NR_HD;
diff --git a/drivers/block/ide-cd.c b/drivers/block/ide-cd.c
new file mode 100644
index 000000000..1aef40dcf
--- /dev/null
+++ b/drivers/block/ide-cd.c
@@ -0,0 +1,2126 @@
+/*
+ * linux/drivers/block/ide-cd.c
+ *
+ * 1.00 Oct 31, 1994 -- Initial version.
+ * 1.01 Nov 2, 1994 -- Fixed problem with starting request in
+ * cdrom_check_status.
+ * 1.03 Nov 25, 1994 -- leaving unmask_intr[] as a user-setting (as for disks)
+ * (from mlord) -- minor changes to cdrom_setup()
+ * -- renamed ide_dev_s to ide_dev_t, enable irq on command
+ * 2.00 Nov 27, 1994 -- Generalize packet command interface;
+ * add audio ioctls.
+ * 2.01 Dec 3, 1994 -- Rework packet command interface to handle devices
+ * which send an interrupt when ready for a command.
+ * 2.02 Dec 11, 1994 -- Cache the TOC in the driver.
+ * Don't use SCMD_PLAYAUDIO_TI; it's not included
+ * in the current version of ATAPI.
+ * Try to use LBA instead of track or MSF addressing
+ * when possible.
+ * Don't wait for READY_STAT.
+ * 2.03 Jan 10, 1995 -- Rewrite block read routines to handle block sizes
+ * other than 2k and to move multiple sectors in a
+ * single transaction.
+ * 2.04 Apr 21, 1995 -- Add work-around for Creative Labs CD220E drives.
+ * Thanks to Nick Saw <cwsaw@pts7.pts.mot.com> for
+ * help in figuring this out. Ditto for Acer and
+ * Aztech drives, which seem to have the same problem.
+ * 2.04b May 30, 1995 -- Fix to match changes in ide.c version 3.16 -ml
+ * 2.05 Jun 8, 1995 -- Don't attempt to retry after an illegal request
+ * or data protect error.
+ * Use HWIF and DEV_HWIF macros as in ide.c.
+ * Always try to do a request_sense after
+ * a failed command.
+ * Include an option to give textual descriptions
+ * of ATAPI errors.
+ * Fix a bug in handling the sector cache which
+ * showed up if the drive returned data in 512 byte
+ * blocks (like Pioneer drives). Thanks to
+ * Richard Hirst <srh@gpt.co.uk> for diagnosing this.
+ * Properly supply the page number field in the
+ * MODE_SELECT command.
+ * PLAYAUDIO12 is broken on the Aztech; work around it.
+ *
+ *
+ * ATAPI cd-rom driver. To be used with ide.c.
+ *
+ * Copyright (C) 1994, 1995 scott snyder <snyder@fnald0.fnal.gov>
+ * May be copied or modified under the terms of the GNU General Public License
+ * (../../COPYING).
+ */
+
+
+/* Turn this on to have the driver print out the meanings of the
+ ATAPI error codes. This will use up additional kernel-space
+ memory, though. */
+
+#ifndef VERBOSE_IDE_CD_ERRORS
+#define VERBOSE_IDE_CD_ERRORS 0
+#endif
+
+/***************************************************************************/
+
+#include <linux/cdrom.h>
+
+#define SECTOR_SIZE 512
+#define SECTOR_BITS 9
+#define SECTORS_PER_FRAME (CD_FRAMESIZE / SECTOR_SIZE)
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+#if 1 /* "old" method */
+#define OUT_WORDS(b,n) outsw (IDE_PORT (HD_DATA, DEV_HWIF), (b), (n))
+#define IN_WORDS(b,n) insw (IDE_PORT (HD_DATA, DEV_HWIF), (b), (n))
+#else /* "new" method -- should really fix each instance instead of this */
+#define OUT_WORDS(b,n) output_ide_data(dev,b,(n)/2)
+#define IN_WORDS(b,n) input_ide_data(dev,b,(n)/2)
+#endif
+
+/* special command codes for strategy routine. */
+#define PACKET_COMMAND 4315
+#define REQUEST_SENSE_COMMAND 4316
+
+#define WIN_PACKETCMD 0xa0 /* Send a packet command. */
+
+/* Some ATAPI command opcodes (just like SCSI).
+ (Some other cdrom-specific codes are in cdrom.h.) */
+#define TEST_UNIT_READY 0x00
+#define REQUEST_SENSE 0x03
+#define START_STOP 0x1b
+#define ALLOW_MEDIUM_REMOVAL 0x1e
+#define READ_10 0x28
+#define MODE_SENSE_10 0x5a
+#define MODE_SELECT_10 0x55
+
+
+/* ATAPI sense keys (mostly copied from scsi.h). */
+
+#define NO_SENSE 0x00
+#define RECOVERED_ERROR 0x01
+#define NOT_READY 0x02
+#define MEDIUM_ERROR 0x03
+#define HARDWARE_ERROR 0x04
+#define ILLEGAL_REQUEST 0x05
+#define UNIT_ATTENTION 0x06
+#define DATA_PROTECT 0x07
+#define ABORTED_COMMAND 0x0b
+#define MISCOMPARE 0x0e
+
+
+struct packet_command {
+ char *buffer;
+ int buflen;
+ int stat;
+ unsigned char c[12];
+};
+
+
+struct atapi_request_sense {
+ unsigned char error_code : 7;
+ unsigned char valid : 1;
+ byte reserved1;
+ unsigned char sense_key : 4;
+ unsigned char reserved2 : 1;
+ unsigned char ili : 1;
+ unsigned char reserved3 : 2;
+ byte info[4];
+ byte sense_len;
+ byte command_info[4];
+ byte asc;
+ byte ascq;
+ byte fru;
+ byte sense_key_specific[3];
+};
+
+/* We want some additional flags for cd-rom drives.
+ To save space in the ide_dev_t struct, use one of the fields which
+ doesn't make sense for cd-roms -- `bios_sect'. */
+
+struct ide_cd_flags {
+ unsigned drq_interrupt : 1; /* Device sends an interrupt when ready
+ for a packet command. */
+ unsigned no_playaudio12: 1; /* The PLAYAUDIO12 command is not supported. */
+
+ unsigned media_changed : 1; /* Driver has noticed a media change. */
+ unsigned toc_valid : 1; /* Saved TOC information is current. */
+ unsigned no_lba_toc : 1; /* Drive cannot return TOC info in LBA format. */
+ unsigned msf_as_bcd : 1; /* Drive uses BCD in PLAYAUDIO_MSF. */
+ unsigned reserved : 2;
+};
+
+#define CDROM_FLAGS(dev) ((struct ide_cd_flags *)&((dev)->bios_sect))
+
+
+/* Space to hold the disk TOC. */
+
+#define MAX_TRACKS 99
+struct atapi_toc_header {
+ unsigned short toc_length;
+ byte first_track;
+ byte last_track;
+};
+
+struct atapi_toc_entry {
+ byte reserved1;
+ unsigned control : 4;
+ unsigned adr : 4;
+ byte track;
+ byte reserved2;
+ unsigned lba;
+};
+
+struct atapi_toc {
+ struct atapi_toc_header hdr;
+ struct atapi_toc_entry ent[MAX_TRACKS+1]; /* One extra for the leadout. */
+};
+
+
+#define SECTOR_BUFFER_SIZE CD_FRAMESIZE
+
+/* Extra per-device info for cdrom drives. */
+struct cdrom_info {
+
+ /* Buffer for table of contents. NULL if we haven't allocated
+ a TOC buffer for this device yet. */
+
+ struct atapi_toc *toc;
+
+ /* Sector buffer. If a read request wants only the first part of a cdrom
+ block, we cache the rest of the block here, in the expectation that that
+ data is going to be wanted soon. SECTOR_BUFFERED is the number of the
+ first buffered sector, and NSECTORS_BUFFERED is the number of sectors
+ in the buffer. Before the buffer is allocated, we should have
+ SECTOR_BUFFER == NULL and NSECTORS_BUFFERED == 0. */
+
+ unsigned long sector_buffered;
+ unsigned long nsectors_buffered;
+ char *sector_buffer;
+
+ /* The result of the last successful request sense command
+ on this device. */
+ struct atapi_request_sense sense_data;
+};
+
+
+static struct cdrom_info cdrom_info[2][MAX_DRIVES];
+
+/* Statically allocate one request packet and one packet command struct
+ for each interface for retrieving sense data during error recovery. */
+
+static struct request request_sense_request[2];
+static struct packet_command request_sense_pc[2];
+
+
+
+/****************************************************************************
+ * Descriptions of ATAPI error codes.
+ */
+
+#define ARY_LEN(a) ((sizeof(a) / sizeof(a[0])))
+
+#if VERBOSE_IDE_CD_ERRORS
+
+/* From Table 124 of the ATAPI 1.2 spec. */
+
+char *sense_key_texts[16] = {
+ "No sense data",
+ "Recovered error",
+ "Not ready",
+ "Medium error",
+ "Hardware error",
+ "Illegal request",
+ "Unit attention",
+ "Data protect",
+ "(reserved)",
+ "(reserved)",
+ "(reserved)",
+ "Aborted command",
+ "(reserved)",
+ "(reserved)",
+ "Miscompare",
+ "(reserved)",
+};
+
+
+/* From Table 125 of the ATAPI 1.2 spec. */
+
+struct {
+ short asc_ascq;
+ char *text;
+} sense_data_texts[] = {
+ { 0x0000, "No additional sense information" },
+ { 0x0011, "Audio play operation in progress" },
+ { 0x0012, "Audio play operation paused" },
+ { 0x0013, "Audio play operation successfully completed" },
+ { 0x0014, "Audio play operation stopped due to error" },
+ { 0x0015, "No current audio status to return" },
+
+ { 0x0200, "No seek complete" },
+
+ { 0x0400, "Logical unit not ready - cause not reportable" },
+ { 0x0401, "Logical unit not ready - in progress (sic) of becoming ready" },
+ { 0x0402, "Logical unit not ready - initializing command required" },
+ { 0x0403, "Logical unit not ready - manual intervention required" },
+
+ { 0x0600, "No reference position found" },
+
+ { 0x0900, "Track following error" },
+ { 0x0901, "Tracking servo failure" },
+ { 0x0902, "Focus servo failure" },
+ { 0x0903, "Spindle servo failure" },
+
+ { 0x1100, "Unrecovered read error" },
+ { 0x1106, "CIRC unrecovered error" },
+
+ { 0x1500, "Random positioning error" },
+ { 0x1501, "Mechanical positioning error" },
+ { 0x1502, "Positioning error detected by read of medium" },
+
+ { 0x1700, "Recovered data with no error correction applied" },
+ { 0x1701, "Recovered data with retries" },
+ { 0x1702, "Recovered data with positive head offset" },
+ { 0x1703, "Recovered data with negative head offset" },
+ { 0x1704, "Recovered data with retries and/or CIRC applied" },
+ { 0x1705, "Recovered data using previous sector ID" },
+
+ { 0x1800, "Recovered data with error correction applied" },
+ { 0x1801, "Recovered data with error correction and retries applied" },
+ { 0x1802, "Recovered data - the data was auto-reallocated" },
+ { 0x1803, "Recovered data with CIRC" },
+ { 0x1804, "Recovered data with L-EC" },
+ { 0x1805, "Recovered data - recommend reassignment" },
+ { 0x1806, "Recovered data - recommend rewrite" },
+
+ { 0x1a00, "Parameter list length error" },
+
+ { 0x2000, "Invalid command operation code" },
+
+ { 0x2100, "Logical block address out of range" },
+
+ { 0x2400, "Invalid field in command packet" },
+
+ { 0x2600, "Invalid field in parameter list" },
+ { 0x2601, "Parameter not supported" },
+ { 0x2602, "Parameter value invalid" },
+ { 0x2603, "Threshold parameters not supported" },
+
+ { 0x2800, "Not ready to ready transition, medium may have changed" },
+
+ { 0x2900, "Power on, reset or bus device reset occurred" },
+
+ { 0x2a00, "Parameters changed" },
+ { 0x2a01, "Mode parameters changed" },
+
+ { 0x3000, "Incompatible medium installed" },
+ { 0x3001, "Cannot read medium - unknown format" },
+ { 0x3002, "Cannot read medium - incompatible format" },
+
+ { 0x3700, "Rounded parameter" },
+
+ { 0x3900, "Saving parameters not supported" },
+
+ { 0x3a00, "Medium not present" },
+
+ { 0x3f00, "ATAPI CD-ROM drive operating conditions have changed" },
+ { 0x3f01, "Microcode has been changed" },
+ { 0x3f02, "Changed operating definition" },
+ { 0x3f03, "Inquiry data has changed" },
+
+ { 0x4000, "Diagnostic failure on component (ASCQ)" },
+
+ { 0x4400, "Internal ATAPI CD-ROM drive failure" },
+
+ { 0x4e00, "Overlapped commands attempted" },
+
+ { 0x5300, "Media load or eject failed" },
+ { 0x5302, "Medium removal prevented" },
+
+ { 0x5700, "Unable to recover table of contents" },
+
+ { 0x5a00, "Operator request or state change input (unspecified)" },
+ { 0x5a01, "Operator medium removal request" },
+
+ { 0x5b00, "Threshold condition met" },
+
+ { 0x5c00, "Status change" },
+
+ { 0x6300, "End of user area encountered on this track" },
+
+ { 0x6400, "Illegal mode for this track" },
+
+ { 0xbf00, "Loss of streaming" },
+};
+#endif
+
+
+
+/****************************************************************************
+ * Generic packet command support routines.
+ */
+
+
+static
+void cdrom_analyze_sense_data (ide_dev_t *dev,
+ struct atapi_request_sense *reqbuf,
+ struct packet_command *failed_command)
+{
+ /* Don't print not ready or unit attention errors for READ_SUBCHANNEL.
+ Workman (and probably other programs) uses this command to poll
+ the drive, and we don't want to fill the syslog with useless errors. */
+ if (failed_command &&
+ failed_command->c[0] == SCMD_READ_SUBCHANNEL &&
+ (reqbuf->sense_key == 2 || reqbuf->sense_key == 6))
+ return;
+
+#if VERBOSE_IDE_CD_ERRORS
+ {
+ int i;
+ char *s;
+ char buf[80];
+
+ printk ("ATAPI device %s:\n", dev->name);
+
+ printk (" Error code: %x\n", reqbuf->error_code);
+
+ if (reqbuf->sense_key >= 0 &&
+ reqbuf->sense_key < ARY_LEN (sense_key_texts))
+ s = sense_key_texts[reqbuf->sense_key];
+ else
+ s = "(bad sense key)";
+
+ printk (" Sense key: %x - %s\n", reqbuf->sense_key, s);
+
+ if (reqbuf->asc == 0x40) {
+ sprintf (buf, "Diagnostic failure on component %x", reqbuf->ascq);
+ s = buf;
+ }
+
+ else {
+ int lo, hi;
+ int key = (reqbuf->asc << 8);
+ if ( ! (reqbuf->ascq >= 0x80 && reqbuf->ascq <= 0xdd) )
+ key |= reqbuf->ascq;
+
+ lo = 0;
+ hi = ARY_LEN (sense_data_texts);
+ s = NULL;
+
+ while (hi > lo) {
+ int mid = (lo + hi) / 2;
+ if (sense_data_texts[mid].asc_ascq == key) {
+ s = sense_data_texts[mid].text;
+ break;
+ }
+ else if (sense_data_texts[mid].asc_ascq > key)
+ hi = mid;
+ else
+ lo = mid+1;
+ }
+ }
+
+ if (s == NULL) {
+ if (reqbuf->asc > 0x80)
+ s = "(vendor-specific error)";
+ else
+ s = "(reserved error code)";
+ }
+
+ printk (" Additional sense data: %x, %x - %s\n",
+ reqbuf->asc, reqbuf->ascq, s);
+
+ if (failed_command != NULL) {
+ printk (" Failed packet command: ");
+ for (i=0; i<sizeof (failed_command->c); i++)
+ printk ("%02x ", failed_command->c[i]);
+ printk ("\n");
+ }
+ }
+
+#else
+ printk ("%s: code: %x key: %x asc: %x ascq: %x\n",
+ dev->name,
+ reqbuf->error_code, reqbuf->sense_key, reqbuf->asc, reqbuf->ascq);
+#endif
+}
+
+
+/* Fix up a possibly partially-processed request so that we can
+ start it over entirely, or even put it back on the request queue. */
+static void restore_request (struct request *rq)
+{
+ if (rq->buffer != rq->bh->b_data)
+ {
+ int n = (rq->buffer - rq->bh->b_data) / SECTOR_SIZE;
+ rq->buffer = rq->bh->b_data;
+ rq->nr_sectors += n;
+ rq->sector -= n;
+ }
+ rq->current_nr_sectors = rq->bh->b_size >> SECTOR_BITS;
+}
+
+
+static void cdrom_queue_request_sense (ide_dev_t *dev)
+{
+ struct request *rq;
+ struct packet_command *pc;
+ struct atapi_request_sense *reqbuf;
+ unsigned long flags;
+
+ int major = ide_major[DEV_HWIF];
+
+ save_flags (flags);
+ cli (); /* safety */
+
+ rq = ide_cur_rq[DEV_HWIF];
+
+ /* If we're processing a request, put it back on the request queue. */
+ if (rq != NULL)
+ {
+ restore_request (rq);
+ rq->next = blk_dev[major].current_request;
+ blk_dev[major].current_request = rq;
+ ide_cur_rq[DEV_HWIF] = NULL;
+ }
+
+ restore_flags (flags);
+
+ /* Make up a new request to retrieve sense information. */
+ reqbuf = &cdrom_info[DEV_HWIF][dev->select.b.drive].sense_data;
+
+ pc = &request_sense_pc[DEV_HWIF];
+ memset (pc, 0, sizeof (*pc));
+
+ pc->c[0] = REQUEST_SENSE;
+ pc->c[4] = sizeof (*reqbuf);
+ pc->buffer = (char *)reqbuf;
+ pc->buflen = sizeof (*reqbuf);
+
+ rq = &request_sense_request[DEV_HWIF];
+ rq->dev = MKDEV (major, (dev->select.b.drive) << PARTN_BITS);
+ rq->cmd = REQUEST_SENSE_COMMAND;
+ rq->errors = 0;
+ rq->sector = 0;
+ rq->nr_sectors = 0;
+ rq->current_nr_sectors = 0;
+ rq->buffer = (char *)pc;
+ rq->sem = NULL;
+ rq->bh = NULL;
+ rq->bhtail = NULL;
+ rq->next = NULL;
+
+ save_flags (flags);
+ cli (); /* safety */
+
+ /* Stick it onto the front of the queue. */
+ rq->next = blk_dev[major].current_request;
+ blk_dev[major].current_request = rq;
+
+ restore_flags (flags);
+}
+
+
+static void cdrom_end_request (int uptodate, ide_dev_t *dev)
+{
+ struct request *rq = ide_cur_rq[DEV_HWIF];
+
+ /* The code in blk.h can screw us up on error recovery if the block
+ size is larger than 1k. Fix that up here. */
+ if (!uptodate && rq->bh != 0)
+ {
+ int adj = rq->current_nr_sectors - 1;
+ rq->current_nr_sectors -= adj;
+ rq->sector += adj;
+ }
+
+ if (rq->cmd == REQUEST_SENSE_COMMAND && uptodate)
+ {
+ struct atapi_request_sense *reqbuf;
+ reqbuf = &cdrom_info[DEV_HWIF][dev->select.b.drive].sense_data;
+ cdrom_analyze_sense_data (dev, reqbuf, NULL);
+ }
+
+ end_request (uptodate, DEV_HWIF);
+}
+
+
+/* Mark that we've seen a media change, and invalidate our internal
+ buffers. */
+static void cdrom_saw_media_change (ide_dev_t *dev)
+{
+ CDROM_FLAGS (dev)->media_changed = 1;
+ CDROM_FLAGS (dev)->toc_valid = 0;
+ cdrom_info[DEV_HWIF][dev->select.b.drive].nsectors_buffered = 0;
+}
+
+
+/* Returns 0 if the request should be continued.
+ Returns 1 if the request was ended. */
+static int cdrom_decode_status (ide_dev_t *dev, int good_stat, int *stat_ret)
+{
+ struct request *rq = ide_cur_rq[DEV_HWIF];
+ int stat, err, sense_key, cmd;
+
+ /* Check for errors. */
+ stat = GET_STAT (DEV_HWIF);
+ *stat_ret = stat;
+
+ if (OK_STAT (stat, good_stat, BAD_R_STAT))
+ return 0;
+
+ /* Got an error. */
+ err = IN_BYTE (HD_ERROR, DEV_HWIF);
+ sense_key = err >> 4;
+
+ if (rq == NULL)
+ printk ("%s : missing request in cdrom_decode_status\n", dev->name);
+ else
+ {
+ cmd = rq->cmd;
+
+ /* Check for tray open */
+ if (sense_key == NOT_READY)
+ {
+ struct packet_command *pc;
+ cdrom_saw_media_change (dev);
+
+ /* Fail the request if this is a read command. */
+ if (cmd == READ)
+ {
+ printk ("%s : tray open\n", dev->name);
+ cdrom_end_request (0, dev);
+ }
+
+ else
+ {
+ /* Otherwise, it's some other packet command.
+ Print an error message to the syslog.
+ Exception: don't print anything if this is a read subchannel
+ command. This is because workman constantly polls the drive
+ with this command, and we don't want to uselessly fill up
+ the syslog. */
+ pc = (struct packet_command *)rq->buffer;
+ if (pc->c[0] != SCMD_READ_SUBCHANNEL)
+ printk ("%s : tray open\n", dev->name);
+
+ /* Set the error flag and complete the request. */
+ pc->stat = 1;
+ cdrom_end_request (1, dev);
+ }
+ }
+
+ /* Check for media change. */
+ else if (sense_key == UNIT_ATTENTION)
+ {
+ cdrom_saw_media_change (dev);
+ printk ("%s: media changed\n", dev->name);
+
+ /* Return failure for a packet command, so that
+ cdrom_queue_packet_command can do a request sense before
+ the command gets retried. */
+
+ if (cmd == PACKET_COMMAND)
+ {
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+ pc->stat = 1;
+ cdrom_end_request (1, dev);
+ }
+
+ /* Otherwise, it's a block read. Arrange to retry it.
+ But be sure to give up if we've retried too many times. */
+ else if ((++rq->errors > ERROR_MAX))
+ {
+ cdrom_end_request (0, dev);
+ }
+ }
+
+ /* Don't attempt to retry if this was a packet command. */
+ else if (cmd == PACKET_COMMAND)
+ {
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+ dump_status (DEV_HWIF, "packet command error", stat);
+ pc->stat = 1; /* signal error */
+ cdrom_end_request (1, dev);
+ }
+
+ /* No point in retrying after an illegal request or data protect error.*/
+ else if (sense_key == ILLEGAL_REQUEST || sense_key == DATA_PROTECT)
+ {
+ dump_status (DEV_HWIF, "command error", stat);
+ cdrom_end_request (0, dev);
+ }
+
+ /* If there were other errors, go to the default handler. */
+ else if ((err & ~ABRT_ERR) != 0)
+ {
+ ide_error (dev, "cdrom_decode_status", stat);
+ }
+
+ /* Else, abort if we've racked up too many retries. */
+ else if ((++rq->errors > ERROR_MAX))
+ {
+ cdrom_end_request (0, dev);
+ }
+
+ /* If we got a CHECK_STATUS condition, and this was a READ request,
+ queue a request sense command to try to find out more about
+ what went wrong (and clear a unit attention)? For packet commands,
+ this is done separately in cdrom_queue_packet_command. */
+ if ((stat & ERR_STAT) != 0 && cmd == READ)
+ cdrom_queue_request_sense (dev);
+ }
+
+ /* Retry, or handle the next request. */
+ DO_REQUEST;
+ return 1;
+}
+
+
+/* Set up the device registers for transferring a packet command on DEV,
+ expecting to later transfer XFERLEN bytes. This should be followed
+ by a call to cdrom_transfer_packet_command; however, if this is a
+ drq_interrupt device, one must wait for an interrupt first. */
+static int cdrom_start_packet_command (ide_dev_t *dev, int xferlen)
+{
+ /* Wait for the controller to be idle. */
+ if (wait_stat (dev, 0, BUSY_STAT, WAIT_READY)) return 1;
+
+ /* Set up the controller registers. */
+ OUT_BYTE (0, HD_FEATURE);
+ OUT_BYTE (0, HD_NSECTOR);
+ OUT_BYTE (0, HD_SECTOR);
+
+ OUT_BYTE (xferlen & 0xff, HD_LCYL);
+ OUT_BYTE (xferlen >> 8 , HD_HCYL);
+ OUT_BYTE (dev->ctl, HD_CMD);
+ OUT_BYTE (WIN_PACKETCMD, HD_COMMAND); /* packet command */
+
+ return 0;
+}
+
+
+/* Send a packet command to DEV described by CMD_BUF and CMD_LEN.
+ The device registers must have already been prepared
+ by cdrom_start_packet_command. */
+static int cdrom_transfer_packet_command (ide_dev_t *dev,
+ char *cmd_buf, int cmd_len)
+{
+ if (CDROM_FLAGS (dev)->drq_interrupt)
+ {
+ /* Here we should have been called after receiving an interrupt
+ from the device. DRQ should how be set. */
+ int stat_dum;
+
+ /* Check for errors. */
+ if (cdrom_decode_status (dev, DRQ_STAT, &stat_dum)) return 1;
+ }
+ else
+ {
+ /* Otherwise, we must wait for DRQ to get set. */
+ if (wait_stat (dev, DRQ_STAT, BUSY_STAT, WAIT_READY)) return 1;
+ }
+
+ /* Send the command to the device. */
+ OUT_WORDS (cmd_buf, cmd_len/2);
+
+ return 0;
+}
+
+
+
+/****************************************************************************
+ * Block read functions.
+ */
+
+/*
+ * Buffer up to SECTORS_TO_TRANSFER sectors from the drive in our sector
+ * buffer. Once the first sector is added, any subsequent sectors are
+ * assumed to be continuous (until the buffer is cleared). For the first
+ * sector added, SECTOR is its sector number. (SECTOR is then ignored until
+ * the buffer is cleared.)
+ */
+static void cdrom_buffer_sectors (ide_dev_t *dev, unsigned long sector,
+ int sectors_to_transfer)
+{
+ struct cdrom_info *info = &cdrom_info[DEV_HWIF][dev->select.b.drive];
+
+ /* Number of sectors to read into the buffer. */
+ int sectors_to_buffer = MIN (sectors_to_transfer,
+ (SECTOR_BUFFER_SIZE >> SECTOR_BITS) -
+ info->nsectors_buffered);
+
+ char *dest;
+
+ /* If we don't yet have a sector buffer, try to allocate one.
+ If we can't get one atomically, it's not fatal -- we'll just throw
+ the data away rather than caching it. */
+ if (info->sector_buffer == NULL)
+ {
+ info->sector_buffer = (char *) kmalloc (SECTOR_BUFFER_SIZE, GFP_ATOMIC);
+
+ /* If we couldn't get a buffer, don't try to buffer anything... */
+ if (info->sector_buffer == NULL)
+ sectors_to_buffer = 0;
+ }
+
+ /* If this is the first sector in the buffer, remember its number. */
+ if (info->nsectors_buffered == 0)
+ info->sector_buffered = sector;
+
+ /* Read the data into the buffer. */
+ dest = info->sector_buffer + info->nsectors_buffered * SECTOR_SIZE;
+ while (sectors_to_buffer > 0)
+ {
+ IN_WORDS (dest, SECTOR_SIZE / 2);
+ --sectors_to_buffer;
+ --sectors_to_transfer;
+ ++info->nsectors_buffered;
+ dest += SECTOR_SIZE;
+ }
+
+ /* Throw away any remaining data. */
+ while (sectors_to_transfer > 0)
+ {
+ char dum[SECTOR_SIZE];
+ IN_WORDS (dum, sizeof (dum) / 2);
+ --sectors_to_transfer;
+ }
+}
+
+
+/*
+ * Check the contents of the interrupt reason register from the cdrom
+ * and attempt to recover if there are problems. Returns 0 if everything's
+ * ok; nonzero if the request has been terminated.
+ */
+static inline
+int cdrom_read_check_ireason (ide_dev_t *dev, int len, int ireason)
+{
+ ireason &= 3;
+ if (ireason == 2) return 0;
+
+ if (ireason == 0)
+ {
+ /* Whoops... The drive is expecting to receive data from us! */
+ printk ("%s: cdrom_read_intr: "
+ "Drive wants to transfer data the wrong way!\n",
+ dev->name);
+
+ /* Throw some data at the drive so it doesn't hang
+ and quit this request. */
+ while (len > 0)
+ {
+ short dum = 0;
+ OUT_WORDS (&dum, 1);
+ len -= 2;
+ }
+ }
+
+ else
+ {
+ /* Drive wants a command packet, or invalid ireason... */
+ printk ("%s: cdrom_read_intr: bad interrupt reason %d\n",
+ dev->name, ireason);
+ }
+
+ cdrom_end_request (0, dev);
+ DO_REQUEST;
+ return -1;
+}
+
+
+/*
+ * Interrupt routine. Called when a read request has completed.
+ */
+static void cdrom_read_intr (ide_dev_t *dev)
+{
+ int stat;
+ int ireason, len, sectors_to_transfer, nskip;
+
+ struct request *rq = ide_cur_rq[DEV_HWIF];
+
+ /* Check for errors. */
+ if (cdrom_decode_status (dev, 0, &stat)) return;
+
+ /* Read the interrupt reason and the transfer length. */
+ ireason = IN_BYTE (HD_NSECTOR, DEV_HWIF);
+ len = IN_BYTE (HD_LCYL, DEV_HWIF) + 256 * IN_BYTE (HD_HCYL, DEV_HWIF);
+
+ /* If DRQ is clear, the command has completed. */
+ if ((stat & DRQ_STAT) == 0)
+ {
+ /* If we're not done filling the current buffer, complain.
+ Otherwise, complete the command normally. */
+ if (rq->current_nr_sectors > 0)
+ {
+ printk ("%s: cdrom_read_intr: data underrun (%ld blocks)\n",
+ dev->name, rq->current_nr_sectors);
+ cdrom_end_request (0, dev);
+ }
+ else
+ cdrom_end_request (1, dev);
+
+ DO_REQUEST;
+ return;
+ }
+
+ /* Check that the drive is expecting to do the same thing that we are. */
+ if (cdrom_read_check_ireason (dev, len, ireason)) return;
+
+ /* Assume that the drive will always provide data in multiples of at least
+ SECTOR_SIZE, as it gets hairy to keep track of the transfers otherwise. */
+ if ((len % SECTOR_SIZE) != 0)
+ {
+ printk ("%s: cdrom_read_intr: Bad transfer size %d\n",
+ dev->name, len);
+ printk (" This drive is not supported by this version of the driver\n");
+ cdrom_end_request (0, dev);
+ DO_REQUEST;
+ return;
+ }
+
+ /* The number of sectors we need to read from the drive. */
+ sectors_to_transfer = len / SECTOR_SIZE;
+
+ /* First, figure out if we need to bit-bucket any of the leading sectors. */
+ nskip = MIN ((int)(rq->current_nr_sectors - (rq->bh->b_size >> SECTOR_BITS)),
+ sectors_to_transfer);
+
+ while (nskip > 0)
+ {
+ /* We need to throw away a sector. */
+ char dum[SECTOR_SIZE];
+ IN_WORDS (dum, sizeof (dum) / 2);
+
+ --rq->current_nr_sectors;
+ --nskip;
+ --sectors_to_transfer;
+ }
+
+ /* Now loop while we still have data to read from the drive. */
+ while (sectors_to_transfer > 0)
+ {
+ int this_transfer;
+
+ /* If we've filled the present buffer but there's another chained
+ buffer after it, move on. */
+ if (rq->current_nr_sectors == 0 &&
+ rq->nr_sectors > 0)
+ cdrom_end_request (1, dev);
+
+ /* If the buffers are full, cache the rest of the data in our
+ internal buffer. */
+ if (rq->current_nr_sectors == 0)
+ {
+ cdrom_buffer_sectors (dev, rq->sector, sectors_to_transfer);
+ sectors_to_transfer = 0;
+ }
+ else
+ {
+ /* Transfer data to the buffers.
+ Figure out how many sectors we can transfer
+ to the current buffer. */
+ this_transfer = MIN (sectors_to_transfer,
+ rq->current_nr_sectors);
+
+ /* Read this_transfer sectors into the current buffer. */
+ while (this_transfer > 0)
+ {
+ IN_WORDS (rq->buffer, SECTOR_SIZE / 2);
+ rq->buffer += SECTOR_SIZE;
+ --rq->nr_sectors;
+ --rq->current_nr_sectors;
+ ++rq->sector;
+ --this_transfer;
+ --sectors_to_transfer;
+ }
+ }
+ }
+
+ /* Done moving data!
+ Wait for another interrupt. */
+ ide_handler[DEV_HWIF] = cdrom_read_intr;
+}
+
+
+/*
+ * Try to satisfy some of the current read request from our cached data.
+ * Returns nonzero if the request has been completed, zero otherwise.
+ */
+static int cdrom_read_from_buffer (ide_dev_t *dev)
+{
+ struct cdrom_info *info = &cdrom_info[DEV_HWIF][dev->select.b.drive];
+ struct request *rq = ide_cur_rq[DEV_HWIF];
+
+ /* Can't do anything if there's no buffer. */
+ if (info->sector_buffer == NULL) return 0;
+
+ /* Loop while this request needs data and the next block is present
+ in our cache. */
+ while (rq->nr_sectors > 0 &&
+ rq->sector >= info->sector_buffered &&
+ rq->sector < info->sector_buffered + info->nsectors_buffered)
+ {
+ if (rq->current_nr_sectors == 0)
+ cdrom_end_request (1, dev);
+
+ memcpy (rq->buffer,
+ info->sector_buffer +
+ (rq->sector - info->sector_buffered) * SECTOR_SIZE,
+ SECTOR_SIZE);
+ rq->buffer += SECTOR_SIZE;
+ --rq->current_nr_sectors;
+ --rq->nr_sectors;
+ ++rq->sector;
+ }
+
+ /* If we've satisfied the current request, terminate it successfully. */
+ if (rq->nr_sectors == 0)
+ {
+ cdrom_end_request (1, dev);
+ return -1;
+ }
+
+ /* Move on to the next buffer if needed. */
+ if (rq->current_nr_sectors == 0)
+ cdrom_end_request (1, dev);
+
+ /* If this condition does not hold, then the kluge i use to
+ represent the number of sectors to skip at the start of a transfer
+ will fail. I think that this will never happen, but let's be
+ paranoid and check. */
+ if (rq->current_nr_sectors < (rq->bh->b_size >> SECTOR_BITS) &&
+ (rq->sector % SECTORS_PER_FRAME) != 0)
+ {
+ printk ("%s: cdrom_read_from_buffer: buffer botch (%ld)\n",
+ dev->name, rq->sector);
+ cdrom_end_request (0, dev);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * Routine to send a read packet command to the drive.
+ * This is usually called directly from cdrom_start_read.
+ * However, for drq_interrupt devices, it is called from an interrupt
+ * when the drive is ready to accept the command.
+ */
+static int cdrom_start_read_continuation (ide_dev_t *dev)
+{
+ struct packet_command pc;
+ struct request *rq = ide_cur_rq[DEV_HWIF];
+
+ int nsect, sector, nframes, frame, nskip;
+
+ /* Number of sectors to transfer. */
+ nsect = rq->nr_sectors;
+
+ /* Starting sector. */
+ sector = rq->sector;
+
+ /* If the requested sector doesn't start on a cdrom block boundary,
+ we must adjust the start of the transfer so that it does,
+ and remember to skip the first few sectors. If the CURRENT_NR_SECTORS
+ field is larger than the size of the buffer, it will mean that
+ we're to skip a number of sectors equal to the amount by which
+ CURRENT_NR_SECTORS is larger than the buffer size. */
+ nskip = (sector % SECTORS_PER_FRAME);
+ if (nskip > 0)
+ {
+ /* Sanity check... */
+ if (rq->current_nr_sectors != (rq->bh->b_size >> SECTOR_BITS))
+ {
+ printk ("%s: cdrom_start_read_continuation: buffer botch (%ld)\n",
+ dev->name, rq->current_nr_sectors);
+ cdrom_end_request (0, dev);
+ DO_REQUEST;
+ return 1;
+ }
+
+ sector -= nskip;
+ nsect += nskip;
+ rq->current_nr_sectors += nskip;
+ }
+
+ /* Convert from sectors to cdrom blocks, rounding up the transfer
+ length if needed. */
+ nframes = (nsect + SECTORS_PER_FRAME-1) / SECTORS_PER_FRAME;
+ frame = sector / SECTORS_PER_FRAME;
+
+ /* Largest number of frames was can transfer at once is 64k-1. */
+ nframes = MIN (nframes, 65535);
+
+ /* Set up the command */
+ memset (&pc.c, 0, sizeof (pc.c));
+ pc.c[0] = READ_10;
+ pc.c[7] = (nframes >> 8);
+ pc.c[8] = (nframes & 0xff);
+
+ /* Write the sector address into the command image. */
+ {
+ union {
+ struct {unsigned char b0, b1, b2, b3;} b;
+ struct {unsigned long l0;} l;
+ } conv;
+ conv.l.l0 = frame;
+ pc.c[2] = conv.b.b3;
+ pc.c[3] = conv.b.b2;
+ pc.c[4] = conv.b.b1;
+ pc.c[5] = conv.b.b0;
+ }
+
+ if (cdrom_transfer_packet_command (dev, pc.c, sizeof (pc.c)))
+ return 1;
+
+ /* Set up our interrupt handler and return. */
+ ide_handler[DEV_HWIF] = cdrom_read_intr;
+
+ return 0;
+}
+
+
+/*
+ * Start a read request from the CD-ROM.
+ * Returns 0 if the request was started successfully,
+ * 1 if there was an error and we should either retry or move on to the
+ * next request.
+ */
+static int cdrom_start_read (ide_dev_t *dev, unsigned int block)
+{
+ struct request *rq = ide_cur_rq[DEV_HWIF];
+
+ /* We may be retrying this request after an error.
+ Fix up any weirdness which might be present in the request packet. */
+ restore_request (rq);
+
+ /* Satisfy whatever we can of this request from our cached sector. */
+ if (cdrom_read_from_buffer (dev))
+ return 1;
+
+ /* Clear the local sector buffer. */
+ cdrom_info[DEV_HWIF][dev->select.b.drive].nsectors_buffered = 0;
+
+ if (cdrom_start_packet_command (dev, 32768))
+ return 1;
+
+ if (CDROM_FLAGS (dev)->drq_interrupt)
+ ide_handler[DEV_HWIF] = (void (*)(ide_dev_t *))cdrom_start_read_continuation;
+ else
+ {
+ if (cdrom_start_read_continuation (dev))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+
+
+/****************************************************************************
+ * Execute all other packet commands.
+ */
+
+/* Forward declaration */
+static int
+cdrom_request_sense (ide_dev_t *dev, struct atapi_request_sense *reqbuf);
+
+
+/* Interrupt routine for packet command completion. */
+static void cdrom_pc_intr (ide_dev_t *dev)
+{
+ int ireason, len, stat, thislen;
+ struct request *rq = ide_cur_rq[DEV_HWIF];
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+
+ /* Check for errors. */
+ if (cdrom_decode_status (dev, 0, &stat)) return;
+
+ /* Read the interrupt reason and the transfer length. */
+ ireason = IN_BYTE (HD_NSECTOR, DEV_HWIF);
+ len = IN_BYTE (HD_LCYL, DEV_HWIF) + 256 * IN_BYTE (HD_HCYL, DEV_HWIF);
+
+ /* If DRQ is clear, the command has completed.
+ Complain if we still have data left to transfer. */
+ if ((stat & DRQ_STAT) == 0)
+ {
+ /* Some of the trailing request sense fields are optional, and
+ some drives don't send them. Sigh. */
+ if (pc->c[0] == REQUEST_SENSE && pc->buflen > 0 && pc->buflen <= 5) {
+ while (pc->buflen > 0) {
+ *pc->buffer++ = 0;
+ --pc->buflen;
+ }
+ }
+
+ if (pc->buflen == 0)
+ cdrom_end_request (1, dev);
+ else
+ {
+ printk ("%s: cdrom_pc_intr: data underrun %d\n",
+ dev->name, pc->buflen);
+ pc->stat = 1;
+ cdrom_end_request (1, dev);
+ }
+ DO_REQUEST;
+ return;
+ }
+
+ /* Figure out how much data to transfer. */
+ thislen = pc->buflen;
+ if (thislen < 0) thislen = -thislen;
+ if (thislen > len) thislen = len;
+
+ /* The drive wants to be written to. */
+ if ((ireason & 3) == 0)
+ {
+ /* Check that we want to write. */
+ if (pc->buflen > 0)
+ {
+ printk ("%s: cdrom_pc_intr: Drive wants to transfer data the wrong way!\n",
+ dev->name);
+ pc->stat = 1;
+ thislen = 0;
+ }
+
+ /* Transfer the data. */
+ OUT_WORDS (pc->buffer, thislen / 2);
+
+ /* If we haven't moved enough data to satisfy the drive,
+ add some padding. */
+ while (len > thislen)
+ {
+ short dum = 0;
+ OUT_WORDS (&dum, 1);
+ len -= 2;
+ }
+
+ /* Keep count of how much data we've moved. */
+ pc->buffer += thislen;
+ pc->buflen += thislen;
+ }
+
+ /* Same drill for reading. */
+ else if ((ireason & 3) == 2)
+ {
+ /* Check that we want to read. */
+ if (pc->buflen < 0)
+ {
+ printk ("%s: cdrom_pc_intr: Drive wants to transfer data the wrong way!\n",
+ dev->name);
+ pc->stat = 1;
+ thislen = 0;
+ }
+
+ /* Transfer the data. */
+ IN_WORDS (pc->buffer, thislen / 2);
+
+ /* If we haven't moved enough data to satisfy the drive,
+ add some padding. */
+ while (len > thislen)
+ {
+ short dum = 0;
+ IN_WORDS (&dum, 1);
+ len -= 2;
+ }
+
+ /* Keep count of how much data we've moved. */
+ pc->buffer += thislen;
+ pc->buflen -= thislen;
+ }
+
+ else
+ {
+ printk ("%s: cdrom_pc_intr: The drive appears confused (ireason = 0x%2x)\n",
+ dev->name, ireason);
+ pc->stat = 1;
+ }
+
+ /* Now we wait for another interrupt. */
+ ide_handler[DEV_HWIF] = cdrom_pc_intr;
+}
+
+
+static int cdrom_do_pc_continuation (ide_dev_t *dev)
+{
+ struct request *rq = ide_cur_rq[DEV_HWIF];
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+
+ if (cdrom_transfer_packet_command (dev, pc->c, sizeof (pc->c)))
+ return 1;
+
+ /* Set up our interrupt handler and return. */
+ ide_handler[DEV_HWIF] = cdrom_pc_intr;
+
+ return 0;
+}
+
+
+static int cdrom_do_packet_command (ide_dev_t *dev)
+{
+ int len;
+ struct request *rq = ide_cur_rq[DEV_HWIF];
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+
+ len = pc->buflen;
+ if (len < 0) len = -len;
+
+ pc->stat = 0;
+
+ if (cdrom_start_packet_command (dev, len))
+ return 1;
+
+ if (CDROM_FLAGS (dev)->drq_interrupt)
+ ide_handler[DEV_HWIF] = (void (*)(ide_dev_t *))cdrom_do_pc_continuation;
+ else
+ {
+ if (cdrom_do_pc_continuation (dev))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static
+int cdrom_queue_packet_command (ide_dev_t *dev, struct packet_command *pc)
+{
+ int retries = 3;
+ unsigned long flags;
+ struct request req, **p, **pfirst;
+ struct semaphore sem = MUTEX_LOCKED;
+ int major = ide_major[DEV_HWIF];
+
+ retry:
+ req.dev = MKDEV (major, (dev->select.b.drive) << PARTN_BITS);
+ req.cmd = PACKET_COMMAND;
+ req.errors = 0;
+ req.sector = 0;
+ req.nr_sectors = 0;
+ req.current_nr_sectors = 0;
+ req.buffer = (char *)pc;
+ req.sem = &sem;
+ req.bh = NULL;
+ req.bhtail = NULL;
+ req.next = NULL;
+
+ save_flags (flags);
+ cli ();
+
+ p = &blk_dev[major].current_request;
+ pfirst = p;
+ while ((*p) != NULL)
+ {
+ p = &((*p)->next);
+ }
+ *p = &req;
+ if (p == pfirst)
+ blk_dev[major].request_fn ();
+
+ restore_flags (flags);
+
+ down (&sem);
+
+ if (pc->stat != 0)
+ {
+ /* The request failed. Try to do a request sense to get more information
+ about the error; store the result in the cdrom_info struct
+ for this drive. Check to be sure that it wasn't a request sense
+ request that failed, though, to prevent infinite loops. */
+
+ struct atapi_request_sense *reqbuf =
+ &cdrom_info[DEV_HWIF][dev->select.b.drive].sense_data;
+
+ if (pc->c[0] == REQUEST_SENSE || cdrom_request_sense (dev, reqbuf))
+ {
+ memset (reqbuf, 0, sizeof (*reqbuf));
+ reqbuf->asc = 0xff;
+ }
+ cdrom_analyze_sense_data (dev, reqbuf, pc);
+
+ /* If the error was a unit attention (usually means media was changed),
+ retry the command. */
+ if (reqbuf->sense_key == UNIT_ATTENTION && retries > 0)
+ {
+ --retries;
+ goto retry;
+ }
+
+ return -EIO;
+ }
+ else
+ return 0;
+}
+
+
+
+/****************************************************************************
+ * cdrom driver request routine.
+ */
+
+static int do_rw_cdrom (ide_dev_t *dev, unsigned long block)
+{
+ struct request *rq = ide_cur_rq[DEV_HWIF];
+
+ if (rq -> cmd == PACKET_COMMAND || rq -> cmd == REQUEST_SENSE_COMMAND)
+ return cdrom_do_packet_command (dev);
+
+ if (rq -> cmd != READ)
+ {
+ printk ("ide-cd: bad cmd %d\n", rq -> cmd);
+ cdrom_end_request (0, dev);
+ return 1;
+ }
+
+ return cdrom_start_read (dev, block);
+}
+
+
+
+/****************************************************************************
+ * ioctl handling.
+ */
+
+static inline
+void byte_swap_word (unsigned short *x)
+{
+ char *c = (char *)x;
+ char d = c[0];
+ c[0] = c[1];
+ c[1] = d;
+}
+
+
+static inline
+void byte_swap_long (unsigned *x)
+{
+ char *c = (char *)x;
+ char d = c[0];
+ c[0] = c[3];
+ c[3] = d;
+ d = c[1];
+ c[1] = c[2];
+ c[2] = d;
+}
+
+
+static
+int bin2bcd (int x)
+{
+ return (x%10) | ((x/10) << 4);
+}
+
+
+static inline
+void lba_to_msf (int lba, byte *m, byte *s, byte *f)
+{
+ lba += CD_BLOCK_OFFSET;
+ lba &= 0xffffff; /* negative lbas use only 24 bits */
+ *m = lba / (CD_SECS * CD_FRAMES);
+ lba %= (CD_SECS * CD_FRAMES);
+ *s = lba / CD_FRAMES;
+ *f = lba % CD_FRAMES;
+}
+
+
+static inline
+int msf_to_lba (byte m, byte s, byte f)
+{
+ return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_BLOCK_OFFSET;
+}
+
+
+static void
+cdrom_check_status (ide_dev_t *dev)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+
+ pc.c[0] = TEST_UNIT_READY;
+
+ (void) cdrom_queue_packet_command (dev, &pc);
+}
+
+
+static int
+cdrom_request_sense (ide_dev_t *dev, struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+
+ pc.c[0] = REQUEST_SENSE;
+ pc.c[4] = sizeof (*reqbuf);
+ pc.buffer = (char *)reqbuf;
+ pc.buflen = sizeof (*reqbuf);
+
+ return cdrom_queue_packet_command (dev, &pc);
+}
+
+
+#if 0
+/* Lock the door if LOCKFLAG is nonzero; unlock it otherwise. */
+static int
+cdrom_lockdoor (ide_dev_t *dev, int lockflag)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+
+ pc.c[0] = ALLOW_MEDIUM_REMOVAL;
+ pc.c[4] = (lockflag != 0);
+ return cdrom_queue_packet_command (dev, &pc);
+}
+#endif
+
+
+/* Eject the disk if EJECTFLAG is 0.
+ If EJECTFLAG is 1, try to reload the disk. */
+static int
+cdrom_eject (ide_dev_t *dev, int ejectflag)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+
+ pc.c[0] = START_STOP;
+ pc.c[4] = 2 + (ejectflag != 0);
+ return cdrom_queue_packet_command (dev, &pc);
+}
+
+
+static int
+cdrom_pause (ide_dev_t *dev, int pauseflag)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+
+ pc.c[0] = SCMD_PAUSE_RESUME;
+ pc.c[8] = !pauseflag;
+ return cdrom_queue_packet_command (dev, &pc);
+}
+
+
+static int
+cdrom_startstop (ide_dev_t *dev, int startflag)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+
+ pc.c[0] = START_STOP;
+ pc.c[1] = 1;
+ pc.c[4] = startflag;
+ return cdrom_queue_packet_command (dev, &pc);
+}
+
+
+static int
+cdrom_read_tocentry (ide_dev_t *dev, int trackno, int msf_flag,
+ char *buf, int buflen)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+
+ pc.buffer = buf;
+ pc.buflen = buflen;
+ pc.c[0] = SCMD_READ_TOC;
+ pc.c[6] = trackno;
+ pc.c[7] = (buflen >> 8);
+ pc.c[8] = (buflen & 0xff);
+ if (msf_flag) pc.c[1] = 2;
+ return cdrom_queue_packet_command (dev, &pc);
+}
+
+
+/* Try to read the entire TOC for the disk into our internal buffer. */
+static int
+cdrom_read_toc (ide_dev_t *dev)
+{
+ int msf_flag;
+ int stat, ntracks, i;
+ struct atapi_toc *toc = cdrom_info[DEV_HWIF][dev->select.b.drive].toc;
+
+ if (toc == NULL)
+ {
+ /* Try to allocate space. */
+ toc = (struct atapi_toc *) kmalloc (sizeof (struct atapi_toc),
+ GFP_KERNEL);
+ cdrom_info[DEV_HWIF][dev->select.b.drive].toc = toc;
+ }
+
+ if (toc == NULL)
+ {
+ printk ("%s: No cdrom TOC buffer!\n", dev->name);
+ return -EIO;
+ }
+
+ /* Check to see if the existing data is still valid.
+ If it is, just return. */
+ if (CDROM_FLAGS (dev)->toc_valid)
+ cdrom_check_status (dev);
+
+ if (CDROM_FLAGS (dev)->toc_valid) return 0;
+
+ /* Some drives can't return TOC data in LBA format. */
+ msf_flag = (CDROM_FLAGS (dev)->no_lba_toc);
+
+ /* First read just the header, so we know how long the TOC is. */
+ stat = cdrom_read_tocentry (dev, 0, msf_flag, (char *)toc,
+ sizeof (struct atapi_toc_header) +
+ sizeof (struct atapi_toc_entry));
+ if (stat) return stat;
+
+ ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
+ if (ntracks <= 0) return -EIO;
+ if (ntracks > MAX_TRACKS) ntracks = MAX_TRACKS;
+
+ /* Now read the whole schmeer. */
+ stat = cdrom_read_tocentry (dev, 0, msf_flag, (char *)toc,
+ sizeof (struct atapi_toc_header) +
+ (ntracks+1) * sizeof (struct atapi_toc_entry));
+ if (stat) return stat;
+ byte_swap_word (&toc->hdr.toc_length);
+ for (i=0; i<=ntracks; i++)
+ {
+ if (msf_flag)
+ {
+ byte *adr = (byte *)&(toc->ent[i].lba);
+ toc->ent[i].lba = msf_to_lba (adr[1], adr[2], adr[3]);
+ }
+ else
+ byte_swap_long (&toc->ent[i].lba);
+ }
+
+ /* Remember that we've read this stuff. */
+ CDROM_FLAGS (dev)->toc_valid = 1;
+
+ return 0;
+}
+
+
+static int
+cdrom_read_subchannel (ide_dev_t *dev,
+ char *buf, int buflen)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+
+ pc.buffer = buf;
+ pc.buflen = buflen;
+ pc.c[0] = SCMD_READ_SUBCHANNEL;
+ pc.c[2] = 0x40; /* request subQ data */
+ pc.c[3] = 0x01; /* Format 1: current position */
+ pc.c[7] = (buflen >> 8);
+ pc.c[8] = (buflen & 0xff);
+ return cdrom_queue_packet_command (dev, &pc);
+}
+
+
+/* modeflag: 0 = current, 1 = changeable mask, 2 = default, 3 = saved */
+static int
+cdrom_mode_sense (ide_dev_t *dev, int pageno, int modeflag,
+ char *buf, int buflen)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+
+ pc.buffer = buf;
+ pc.buflen = buflen;
+ pc.c[0] = MODE_SENSE_10;
+ pc.c[2] = pageno | (modeflag << 6);
+ pc.c[7] = (buflen >> 8);
+ pc.c[8] = (buflen & 0xff);
+ return cdrom_queue_packet_command (dev, &pc);
+}
+
+
+static int
+cdrom_mode_select (ide_dev_t *dev, int pageno, char *buf, int buflen)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+
+ pc.buffer = buf;
+ pc.buflen = - buflen;
+ pc.c[0] = MODE_SELECT_10;
+ pc.c[1] = 0x10;
+ pc.c[2] = pageno;
+ pc.c[7] = (buflen >> 8);
+ pc.c[8] = (buflen & 0xff);
+ return cdrom_queue_packet_command (dev, &pc);
+}
+
+
+static int
+cdrom_play_lba_range_play12 (ide_dev_t *dev, int lba_start, int lba_end)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+
+ pc.c[0] = SCMD_PLAYAUDIO12;
+ *(int *)(&pc.c[2]) = lba_start;
+ *(int *)(&pc.c[6]) = lba_end - lba_start;
+ byte_swap_long ((int *)(&pc.c[2]));
+ byte_swap_long ((int *)(&pc.c[6]));
+
+ return cdrom_queue_packet_command (dev, &pc);
+}
+
+
+static int
+cdrom_play_lba_range_msf (ide_dev_t *dev, int lba_start, int lba_end)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+
+ pc.c[0] = SCMD_PLAYAUDIO_MSF;
+ lba_to_msf (lba_start, &pc.c[3], &pc.c[4], &pc.c[5]);
+ lba_to_msf (lba_end-1, &pc.c[6], &pc.c[7], &pc.c[8]);
+
+ if (CDROM_FLAGS (dev)->msf_as_bcd)
+ {
+ pc.c[3] = bin2bcd (pc.c[3]);
+ pc.c[4] = bin2bcd (pc.c[4]);
+ pc.c[5] = bin2bcd (pc.c[5]);
+ pc.c[6] = bin2bcd (pc.c[6]);
+ pc.c[7] = bin2bcd (pc.c[7]);
+ pc.c[8] = bin2bcd (pc.c[8]);
+ }
+
+ return cdrom_queue_packet_command (dev, &pc);
+}
+
+
+/* Play audio starting at LBA LBA_START and finishing with the
+ LBA before LBA_END. */
+static int
+cdrom_play_lba_range (ide_dev_t *dev, int lba_start, int lba_end)
+{
+ /* This is rather annoying.
+ My NEC-260 won't recognize group 5 commands such as PLAYAUDIO12;
+ the only way to get it to play more than 64k of blocks at once
+ seems to be the PLAYAUDIO_MSF command. However, the parameters
+ the NEC 260 wants for the PLAYMSF command are incompatible with
+ the new version of the spec.
+
+ So what i'll try is this. First try for PLAYAUDIO12. If it works,
+ great. Otherwise, if the drive reports an illegal command code,
+ try PLAYAUDIO_MSF using the NEC 260-style bcd parameters. */
+
+ if (CDROM_FLAGS (dev)->no_playaudio12)
+ return cdrom_play_lba_range_msf (dev, lba_start, lba_end);
+ else
+ {
+ int stat;
+ struct atapi_request_sense *reqbuf;
+
+ stat = cdrom_play_lba_range_play12 (dev, lba_start, lba_end);
+ if (stat == 0) return 0;
+
+ /* It failed. Try to find out why. */
+ reqbuf = &cdrom_info[DEV_HWIF][dev->select.b.drive].sense_data;
+ if (reqbuf->sense_key == 0x05 && reqbuf->asc == 0x20)
+ {
+ /* The drive didn't recognize the command.
+ Retry with the MSF variant. */
+ printk ("%s: Drive does not support PLAYAUDIO12; "
+ "trying PLAYAUDIO_MSF\n", dev->name);
+ CDROM_FLAGS (dev)->no_playaudio12 = 1;
+ CDROM_FLAGS (dev)->msf_as_bcd = 1;
+ return cdrom_play_lba_range_msf (dev, lba_start, lba_end);
+ }
+
+ /* Failed for some other reason. Give up. */
+ return stat;
+ }
+}
+
+
+static
+int cdrom_get_toc_entry (ide_dev_t *dev, int track,
+ struct atapi_toc_entry **ent)
+{
+ int stat, ntracks;
+ struct atapi_toc *toc;
+
+ /* Make sure our saved TOC is valid. */
+ stat = cdrom_read_toc (dev);
+ if (stat) return stat;
+
+ toc = cdrom_info[DEV_HWIF][dev->select.b.drive].toc;
+
+ /* Check validity of requested track number. */
+ ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
+ if (track == CDROM_LEADOUT)
+ *ent = &toc->ent[ntracks];
+ else if (track < toc->hdr.first_track ||
+ track > toc->hdr.last_track)
+ return -EINVAL;
+ else
+ *ent = &toc->ent[track - toc->hdr.first_track];
+
+ return 0;
+}
+
+
+static int ide_cdrom_ioctl (ide_dev_t *dev, struct inode *inode,
+ struct file *file, unsigned int cmd, unsigned long arg)
+{
+ switch (cmd)
+ {
+ case CDROMEJECT:
+ return cdrom_eject (dev, 0);
+
+ case CDROMPAUSE:
+ return cdrom_pause (dev, 1);
+
+ case CDROMRESUME:
+ return cdrom_pause (dev, 0);
+
+ case CDROMSTART:
+ return cdrom_startstop (dev, 1);
+
+ case CDROMSTOP:
+ return cdrom_startstop (dev, 0);
+
+ case CDROMPLAYMSF:
+ {
+ struct cdrom_msf msf;
+ int stat, lba_start, lba_end;
+
+ stat = verify_area (VERIFY_READ, (void *)arg, sizeof (msf));
+ if (stat) return stat;
+
+ memcpy_fromfs (&msf, (void *) arg, sizeof(msf));
+
+ lba_start = msf_to_lba (msf.cdmsf_min0, msf.cdmsf_sec0,
+ msf.cdmsf_frame0);
+ lba_end = msf_to_lba (msf.cdmsf_min1, msf.cdmsf_sec1,
+ msf.cdmsf_frame1) + 1;
+
+ if (lba_end <= lba_start) return -EINVAL;
+
+ return cdrom_play_lba_range (dev, lba_start, lba_end);
+ }
+
+ /* Like just about every other Linux cdrom driver, we ignore the
+ index part of the request here. */
+ case CDROMPLAYTRKIND:
+ {
+ int stat, lba_start, lba_end;
+ struct cdrom_ti ti;
+ struct atapi_toc_entry *first_toc, *last_toc;
+
+ stat = verify_area (VERIFY_READ, (void *)arg, sizeof (ti));
+ if (stat) return stat;
+
+ memcpy_fromfs (&ti, (void *) arg, sizeof(ti));
+
+ stat = cdrom_get_toc_entry (dev, ti.cdti_trk0, &first_toc);
+ if (stat) return stat;
+ stat = cdrom_get_toc_entry (dev, ti.cdti_trk1, &last_toc);
+ if (stat) return stat;
+
+ if (ti.cdti_trk1 != CDROM_LEADOUT) ++last_toc;
+ lba_start = first_toc->lba;
+ lba_end = last_toc->lba;
+
+ if (lba_end <= lba_start) return -EINVAL;
+
+ return cdrom_play_lba_range (dev, lba_start, lba_end);
+ }
+
+ case CDROMREADTOCHDR:
+ {
+ int stat;
+ struct cdrom_tochdr tochdr;
+ struct atapi_toc *toc;
+
+ stat = verify_area (VERIFY_WRITE, (void *) arg, sizeof (tochdr));
+ if (stat) return stat;
+
+ /* Make sure our saved TOC is valid. */
+ stat = cdrom_read_toc (dev);
+ if (stat) return stat;
+
+ toc = cdrom_info[DEV_HWIF][dev->select.b.drive].toc;
+ tochdr.cdth_trk0 = toc->hdr.first_track;
+ tochdr.cdth_trk1 = toc->hdr.last_track;
+
+ memcpy_tofs ((void *) arg, &tochdr, sizeof (tochdr));
+
+ return stat;
+ }
+
+ case CDROMREADTOCENTRY:
+ {
+ int stat;
+ struct cdrom_tocentry tocentry;
+ struct atapi_toc_entry *toce;
+
+ stat = verify_area (VERIFY_READ, (void *) arg, sizeof (tocentry));
+ if (stat) return stat;
+ stat = verify_area (VERIFY_WRITE, (void *) arg, sizeof (tocentry));
+ if (stat) return stat;
+
+ memcpy_fromfs (&tocentry, (void *) arg, sizeof (tocentry));
+
+ stat = cdrom_get_toc_entry (dev, tocentry.cdte_track, &toce);
+ if (stat) return stat;
+
+ tocentry.cdte_ctrl = toce->control;
+ tocentry.cdte_adr = toce->adr;
+
+ if (tocentry.cdte_format == CDROM_MSF)
+ {
+ /* convert to MSF */
+ lba_to_msf (toce->lba,
+ &tocentry.cdte_addr.msf.minute,
+ &tocentry.cdte_addr.msf.second,
+ &tocentry.cdte_addr.msf.frame);
+ }
+ else
+ tocentry.cdte_addr.lba = toce->lba;
+
+ memcpy_tofs ((void *) arg, &tocentry, sizeof (tocentry));
+
+ return stat;
+ }
+
+ case CDROMSUBCHNL:
+ {
+ char buffer[16];
+ int stat, abs_lba, rel_lba;
+ struct cdrom_subchnl subchnl;
+
+ stat = verify_area (VERIFY_WRITE, (void *) arg, sizeof (subchnl));
+ if (stat) return stat;
+ stat = verify_area (VERIFY_READ, (void *) arg, sizeof (subchnl));
+ if (stat) return stat;
+
+ memcpy_fromfs (&subchnl, (void *) arg, sizeof (subchnl));
+
+ stat = cdrom_read_subchannel (dev, buffer, sizeof (buffer));
+ if (stat) return stat;
+
+ abs_lba = *(int *)&buffer[8];
+ rel_lba = *(int *)&buffer[12];
+ byte_swap_long (&abs_lba);
+ byte_swap_long (&rel_lba);
+
+ if (subchnl.cdsc_format == CDROM_MSF)
+ {
+ lba_to_msf (abs_lba,
+ &subchnl.cdsc_absaddr.msf.minute,
+ &subchnl.cdsc_absaddr.msf.second,
+ &subchnl.cdsc_absaddr.msf.frame);
+ lba_to_msf (rel_lba,
+ &subchnl.cdsc_reladdr.msf.minute,
+ &subchnl.cdsc_reladdr.msf.second,
+ &subchnl.cdsc_reladdr.msf.frame);
+ }
+ else
+ {
+ subchnl.cdsc_absaddr.lba = abs_lba;
+ subchnl.cdsc_reladdr.lba = rel_lba;
+ }
+
+ subchnl.cdsc_audiostatus = buffer[1];
+ subchnl.cdsc_ctrl = buffer[5] & 0xf;
+ subchnl.cdsc_trk = buffer[6];
+ subchnl.cdsc_ind = buffer[7];
+
+ memcpy_tofs ((void *) arg, &subchnl, sizeof (subchnl));
+
+ return stat;
+ }
+
+ case CDROMVOLCTRL:
+ {
+ struct cdrom_volctrl volctrl;
+ char buffer[24], mask[24];
+ int stat;
+
+ stat = verify_area (VERIFY_READ, (void *) arg, sizeof (volctrl));
+ if (stat) return stat;
+ memcpy_fromfs (&volctrl, (void *) arg, sizeof (volctrl));
+
+ stat = cdrom_mode_sense (dev, 0x0e, 0, buffer, sizeof (buffer));
+ if (stat) return stat;
+ stat = cdrom_mode_sense (dev, 0x0e, 1, mask , sizeof (buffer));
+ if (stat) return stat;
+
+ buffer[1] = buffer[2] = 0;
+
+ buffer[17] = volctrl.channel0 & mask[17];
+ buffer[19] = volctrl.channel1 & mask[19];
+ buffer[21] = volctrl.channel2 & mask[21];
+ buffer[23] = volctrl.channel3 & mask[23];
+
+ return cdrom_mode_select (dev, 0x0e, buffer, sizeof (buffer));
+ }
+
+#ifdef TEST
+ case 0x1234:
+ {
+ int stat;
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+
+ stat = verify_area (VERIFY_READ, (void *) arg, sizeof (pc.c));
+ if (stat) return stat;
+ memcpy_fromfs (&pc.c, (void *) arg, sizeof (pc.c));
+
+ return cdrom_queue_packet_command (dev, &pc);
+ }
+
+ case 0x1235:
+ {
+ int stat;
+ struct atapi_request_sense reqbuf;
+
+ stat = verify_area (VERIFY_WRITE, (void *) arg, sizeof (reqbuf));
+ if (stat) return stat;
+
+ stat = cdrom_request_sense (dev, &reqbuf);
+
+ memcpy_tofs ((void *) arg, &reqbuf, sizeof (reqbuf));
+
+ return stat;
+ }
+#endif
+
+ default:
+ return -EPERM;
+ }
+
+}
+
+
+
+/****************************************************************************
+ * Other driver requests (open, close, check media change).
+ */
+
+static int cdrom_check_media_change (ide_dev_t *dev)
+{
+ int retval;
+
+ cdrom_check_status (dev);
+
+ retval = CDROM_FLAGS (dev)->media_changed;
+ CDROM_FLAGS (dev)->media_changed = 0;
+
+ return retval;
+}
+
+
+static int
+cdrom_open (struct inode *ip, struct file *fp, ide_dev_t *dev)
+{
+ /* no write access */
+ if (fp->f_mode & 2) return -EROFS;
+
+#if 0 /* With this, one cannot eject a disk with workman */
+ /* If this is the first open, lock the door. */
+ if (dev->usage == 1)
+ (void) cdrom_lockdoor (dev, 1);
+#endif
+
+ /* Should check that there's a disk in the drive? */
+ return 0;
+}
+
+
+/*
+ * Close down the device. Invalidate all cached blocks.
+ */
+
+static void
+cdrom_release (struct inode *inode, struct file *file, ide_dev_t *dev)
+{
+ if (dev->usage == 0)
+ {
+ invalidate_buffers (inode->i_rdev);
+
+#if 0
+ /* Unlock the door. */
+ (void) cdrom_lockdoor (dev, 0);
+#endif
+ }
+}
+
+
+
+/****************************************************************************
+ * Device initialization.
+ */
+
+static void cdrom_setup (ide_dev_t *dev)
+{
+ /* Just guess at capacity for now. */
+ ide_capacity[DEV_HWIF][dev->select.b.drive] = 0x1fffff;
+
+ ide_blksizes[DEV_HWIF][dev->select.b.drive << PARTN_BITS] = CD_FRAMESIZE;
+
+ dev->special.all = 0;
+
+ CDROM_FLAGS (dev)->media_changed = 0;
+ CDROM_FLAGS (dev)->toc_valid = 0;
+
+ CDROM_FLAGS (dev)->no_playaudio12 = 0;
+ CDROM_FLAGS (dev)->no_lba_toc = 0;
+ CDROM_FLAGS (dev)->msf_as_bcd = 0;
+ CDROM_FLAGS (dev)->drq_interrupt = ((dev->id->config & 0x0060) == 0x20);
+
+ /* Accommodate some broken drives... */
+ if (strcmp (dev->id->model, "CD220E") == 0) /* Creative Labs */
+ CDROM_FLAGS (dev)->no_lba_toc = 1;
+
+ else if (strcmp (dev->id->model, "TO-ICSLYAL") == 0 || /* Acer CD525E */
+ strcmp (dev->id->model, "OTI-SCYLLA") == 0)
+ CDROM_FLAGS (dev)->no_lba_toc = 1;
+
+ else if (strcmp (dev->id->model, "CDA26803I SE") == 0) /* Aztech */
+ {
+ CDROM_FLAGS (dev)->no_lba_toc = 1;
+
+ /* This drive _also_ does not implement PLAYAUDIO12 correctly. */
+ CDROM_FLAGS (dev)->no_playaudio12 = 1;
+ }
+
+ cdrom_info[DEV_HWIF][dev->select.b.drive].toc = NULL;
+ cdrom_info[DEV_HWIF][dev->select.b.drive].sector_buffer = NULL;
+ cdrom_info[DEV_HWIF][dev->select.b.drive].sector_buffered = 0;
+ cdrom_info[DEV_HWIF][dev->select.b.drive].nsectors_buffered = 0;
+}
+
+
+#undef MIN
+#undef SECTOR_SIZE
+#undef SECTOR_BITS
+
+
+/*
+ * TODO:
+ * Read actual disk capacity.
+ * Multisession support.
+ * Direct reading of audio data.
+ * Eject-on-dismount.
+ * Lock door while there's a mounted volume.
+ * Establish interfaces for an IDE port driver, and break out the cdrom
+ * code into a loadable module.
+ */
+
diff --git a/drivers/block/ide.c b/drivers/block/ide.c
new file mode 100644
index 000000000..a72ea3b0a
--- /dev/null
+++ b/drivers/block/ide.c
@@ -0,0 +1,2438 @@
+/*
+ * linux/drivers/block/ide.c Version 3.16 May 30, 1995
+ *
+ * Copyright (C) 1994, 1995 Linus Torvalds & authors (see below)
+ */
+
+/*
+ * This is the dual IDE interface driver, as evolved from hd.c.
+ * It supports up to two IDE interfaces, on one or two IRQs (usually 14 & 15).
+ * There can be up to two drives per interface, as per the ATA-2 spec.
+ *
+ * Primary i/f: ide0: major=3; (hda) minor=0, (hdb) minor=64
+ * Secondary i/f: ide1: major=22; (hdc or hd1a) minor=0, (hdd or hd1b) minor=64
+ *
+ * From hd.c:
+ * |
+ * | It traverses the request-list, using interrupts to jump between functions.
+ * | As nearly all functions can be called within interrupts, we may not sleep.
+ * | Special care is recommended. Have Fun!
+ * |
+ * | modified by Drew Eckhardt to check nr of hd's from the CMOS.
+ * |
+ * | Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
+ * | in the early extended-partition checks and added DM partitions.
+ * |
+ * | Early work on error handling by Mika Liljeberg (liljeber@cs.Helsinki.FI).
+ * |
+ * | IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
+ * | and general streamlining by Mark Lord (mlord@bnr.ca).
+ *
+ * October, 1994 -- Complete line-by-line overhaul for linux 1.1.x, by:
+ *
+ * Mark Lord (mlord@bnr.ca) (IDE Perf.Pkg)
+ * Delman Lee (delman@mipg.upenn.edu) ("Mr. atdisk2")
+ * Petri Mattila (ptjmatti@kruuna.helsinki.fi) (EIDE stuff)
+ * Scott Snyder (snyder@fnald0.fnal.gov) (ATAPI IDE cd-rom)
+ *
+ * This was a rewrite of just about everything from hd.c, though some original
+ * code is still sprinkled about. Think of it as a major evolution, with
+ * inspiration from lots of linux users, esp. hamish@zot.apana.org.au
+ *
+ * Version 1.0 ALPHA initial code, primary i/f working okay
+ * Version 1.1 ALPHA fixes for dual i/f
+ * Version 1.2 ALPHA first serious attempt at sharing irqs
+ * Version 1.3 BETA dual i/f on shared irq tested & working!
+ * Version 1.4 BETA added auto probing for irq(s)
+ * Version 1.5 BETA added ALPHA (untested) support for IDE cd-roms,
+ * fixed hd.c coexistence bug, other minor stuff
+ * Version 1.6 BETA fix link error when cd-rom not configured
+ * Version 2.0 BETA lots of minor fixes; remove annoying messages; ...
+ * Version 2.2 BETA fixed reset_drives; major overhaul of autoprobing
+ * Version 2.3 BETA set DEFAULT_UNMASK_INTR to 0 again; cosmetic changes
+ * Version 2.4 BETA added debounce on reading of drive status reg,
+ * added config flags to remove unwanted features
+ * Version 2.5 BETA fixed problem with leftover phantom IRQ after probe,
+ * allow "set_geometry" even when in LBA (as per spec(?)),
+ * assorted miscellaneous tweaks.
+ * Version 2.6 BETA more config flag stuff, another probing tweak,
+ * (not released) multmode now defaults to status quo from boot time,
+ * moved >16heads check to init time, rearranged reset code
+ * added HDIO_DRIVE_CMD, removed standby/xfermode stuff
+ * hopefully fixed ATAPI probing code, added hdx=cdrom
+ * Version 2.7 BETA fixed invocation of cdrom_setup()
+ * Version 2.8 BETA fixed compile error for DISK_RECOVERY_TIME>0
+ * fixed incorrect drive selection in DO_DRIVE_CMD (Bug!)
+ * Version 2.9 BETA more work on ATAPI CDROM recognition
+ * (not released) changed init order so partition checks go in sequence
+ * Version 3.0 BETA included ide-cd.c update from Steve with Mitsumi fixes
+ * attempt to fix byte-swap problem with Mitsumi id_info
+ * ensure drives on second i/f get initialized on boot
+ * preliminary compile-time support for 32bit IDE i/f chips
+ * added check_region() and snarf_region() to probes
+ * Version 3.1 BETA ensure drives on *both* i/f get initialized on boot
+ * fix byte-swap problem with Mitsumi id_info
+ * changed ide_timermask into ide_timerbit
+ * get rid of unexpected interrupts after probing
+ * don't wait for READY_STAT on cdrom drives
+ * Version 3.2 BETA Ooops.. mistakenly left VLB_32BIT_IDE on by default
+ * new ide-cd.c from Scott
+ * Version 3.3 BETA fix compiling with PROBE_FOR_IRQS==0
+ * (sent to Linus) tweak in do_probe() to fix Delman's DRDY problem
+ * Version 3.4 BETA removed "444" debug message
+ * (sent to Linus)
+ * Version 3.5 correct the bios_cyl field if it's too small
+ * (linux 1.1.76) (to help fdisk with brain-dead BIOSs)
+ * Version 3.6 cosmetic corrections to comments and stuff
+ * (linux 1.1.77) reorganise probing code to make it understandable
+ * added halfway retry to probing for drive identification
+ * added "hdx=noprobe" command line option
+ * allow setting multmode even when identification fails
+ * Version 3.7 move set_geometry=1 from do_identify() to ide_init()
+ * increase DRQ_WAIT to eliminate nuisance messages
+ * wait for DRQ_STAT instead of DATA_READY during probing
+ * (courtesy of Gary Thomas gary@efland.UU.NET)
+ * Version 3.8 fixed byte-swapping for confused Mitsumi cdrom drives
+ * update of ide-cd.c from Scott, allows blocksize=1024
+ * cdrom probe fixes, inspired by jprang@uni-duisburg.de
+ * Version 3.9 don't use LBA if lba_capacity looks funny
+ * correct the drive capacity calculations
+ * fix probing for old Seagates without HD_ALTSTATUS
+ * fix byte-ordering for some NEC cdrom drives
+ * Version 3.10 disable multiple mode by default; was causing trouble
+ * Version 3.11 fix mis-identification of old WD disks as cdroms
+ * Version 3,12 simplify logic for selecting initial mult_count
+ * (fixes problems with buggy WD drives)
+ * Version 3.13 remove excess "multiple mode disabled" messages
+ * Version 3.14 fix ide_error() handling of BUSY_STAT
+ * fix byte-swapped cdrom strings (again.. arghh!)
+ * ignore INDEX bit when checking the ALTSTATUS reg
+ * Version 3.15 add SINGLE_THREADED flag for use with dual-CMD i/f
+ * ignore WRERR_STAT for non-write operations
+ * added VLB_SYNC support for DC-2000A & others,
+ * (incl. some Promise chips), courtesy of Frank Gockel
+ * Version 3.16 convert VLB_32BIT and VLB_SYNC into runtime flags
+ * add ioctls to get/set VLB flags (HDIO_[SG]ET_CHIPSET)
+ * rename SINGLE_THREADED to SUPPORT_SERIALIZE,
+ * add boot flag to "serialize" operation for CMD i/f
+ * add optional support for DTC2278 interfaces,
+ * courtesy of andy@cercle.cts.com (Dyan Wile).
+ * add boot flag to enable "dtc2278" probe
+ * add probe to avoid EATA (SCSI) interfaces,
+ * courtesy of neuffer@goofy.zdv.uni-mainz.de.
+ *
+ * To do:
+ * - improved CMD support: tech info is supposedly "in the mail"
+ * - special 32-bit controller-type detection & support
+ * - figure out how to support oddball "intelligent" caching cards
+ * - reverse-engineer 3/4 drive support on fancy "Promise" cards
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/major.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <asm/bitops.h>
+#include <asm/irq.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+
+/*****************************************************************************
+ * IDE driver configuration options (play with these as desired):
+ */
+#define REALLY_SLOW_IO /* most systems can safely undef this */
+#include <asm/io.h>
+
+#undef REALLY_FAST_IO /* define if ide ports are perfect */
+#define INITIAL_MULT_COUNT 0 /* off=0; on=2,4,8,16,32, etc.. */
+#ifndef SUPPORT_VLB_32BIT /* 1 to support 32bit I/O on VLB */
+#define SUPPORT_VLB_32BIT 1 /* 0 to reduce kernel size */
+#endif
+#ifndef SUPPORT_VLB_SYNC /* 1 to support weird 32-bit chips */
+#define SUPPORT_VLB_SYNC 1 /* 0 to reduce kernel size */
+#endif
+#ifndef DISK_RECOVERY_TIME /* off=0; on=access_delay_time */
+#define DISK_RECOVERY_TIME 0 /* for hardware that needs it */
+#endif
+#ifndef OK_TO_RESET_CONTROLLER /* 1 needed for good error recovery */
+#define OK_TO_RESET_CONTROLLER 1 /* 0 for use with AH2372A/B interface */
+#endif
+#ifndef SUPPORT_TWO_INTERFACES /* 1 to support one/two interfaces */
+#define SUPPORT_TWO_INTERFACES 1 /* 0 for a smaller, faster kernel */
+#endif
+#ifndef OPTIMIZE_IRQS /* 1 for slightly faster code */
+#define OPTIMIZE_IRQS 1 /* 0 to reduce kernel size */
+#endif
+#ifndef SUPPORT_SERIALIZE /* 1 to support CMD dual interfaces */
+#define SUPPORT_SERIALIZE 1 /* 0 to reduce kernel size */
+#endif
+#ifndef SUPPORT_SHARING_IRQ /* 1 to allow two IDE i/f on one IRQ */
+#define SUPPORT_SHARING_IRQ 1 /* 0 to reduce kernel size */
+#endif
+#ifndef SUPPORT_DTC2278 /* 1 to support DTC2278 chipset */
+#define SUPPORT_DTC2278 1 /* 0 to reduce kernel size */
+#endif
+#ifndef FANCY_STATUS_DUMPS /* 1 for human-readable drive errors */
+#define FANCY_STATUS_DUMPS 1 /* 0 to reduce kernel size */
+#endif
+#define PROBE_FOR_IRQS 1 /* 0 to force use of defaults below */
+#define DEFAULT_IDE0_IRQ 14 /* in case irq-probe fails */
+#define DEFAULT_IDE1_IRQ 15 /* in case irq-probe fails */
+
+/* IDE_DRIVE_CMD is used to implement many features of the hdparm utility */
+#define IDE_DRIVE_CMD 99 /* (magic) undef to reduce kernel size*/
+
+/*
+ * "No user-serviceable parts" beyond this point :)
+ ******************************************************************************
+ */
+
+/*
+ * Need to change these elsewhere in the kernel (someday)
+ */
+#ifndef IDE0_TIMER
+#define IDE0_TIMER HD_TIMER
+#define IDE1_TIMER HD_TIMER2
+#endif
+
+/*
+ * Ensure that various configuration flags have compatible settings
+ */
+#ifdef REALLY_SLOW_IO
+#undef REALLY_FAST_IO
+#endif
+#ifdef CONFIG_BLK_DEV_HD
+#undef SUPPORT_TWO_INTERFACES
+#define SUPPORT_TWO_INTERFACES 0
+#endif /* CONFIG_BLK_DEV_HD */
+
+#if SUPPORT_TWO_INTERFACES
+#define HWIF hwif
+#define DEV_HWIF (dev->hwif)
+#if SUPPORT_SERIALIZE
+#undef SUPPORT_SHARING_IRQ
+#define SUPPORT_SHARING_IRQ 1
+#endif
+#else
+#undef SUPPORT_SERIALIZE
+#define SUPPORT_SERIALIZE 0
+#undef OPTIMIZE_IRQS
+#define OPTIMIZE_IRQS 0
+#undef SUPPORT_SHARING_IRQ
+#define SUPPORT_SHARING_IRQ 0
+#ifdef CONFIG_BLK_DEV_HD
+#define HWIF 1
+#else
+#define HWIF 0
+#endif /* CONFIG_BLK_DEV_HD */
+#define DEV_HWIF HWIF
+#endif /* SUPPORT_TWO_INTERFACES */
+
+/*
+ * Definitions for accessing IDE controller registers
+ */
+typedef unsigned char byte; /* used everywhere */
+#define IDE_PORT(p,hwif) ((p)^((hwif)<<7)) /* IDE0: p^0x00 , IDE1: p^0x80 */
+
+#ifdef REALLY_FAST_IO
+#define OUT_BYTE(b,p) outb((b),IDE_PORT(p,DEV_HWIF))
+#define IN_BYTE(p,hwif) (byte)inb(IDE_PORT(p,hwif))
+#else
+#define OUT_BYTE(b,p) outb_p((b),IDE_PORT(p,DEV_HWIF))
+#define IN_BYTE(p,hwif) (byte)inb_p(IDE_PORT(p,hwif))
+#endif /* REALLY_FAST_IO */
+
+#if SUPPORT_VLB_32BIT
+#if SUPPORT_VLB_SYNC
+#define VLB_SYNC __asm__ __volatile__ ("pusha\n movl $0x01f2,%edx\n inb (%dx),%al\n inb (%dx),%al\n inb (%dx),%al\n popa\n")
+#endif /* SUPPORT_VLB_SYNC */
+#endif /* SUPPORT_VLB_32BIT */
+
+#if SUPPORT_DTC2278
+static uint probe_dtc2278 = 0;
+#endif
+
+#define GET_ERR(hwif) IN_BYTE(HD_ERROR,hwif)
+#define GET_STAT(hwif) IN_BYTE(HD_STATUS,hwif)
+#define OK_STAT(stat,good,bad) (((stat)&((good)|(bad)))==(good))
+#define BAD_R_STAT (BUSY_STAT | ERR_STAT)
+#define BAD_W_STAT (BUSY_STAT | ERR_STAT | WRERR_STAT)
+#define BAD_STAT (BAD_R_STAT | DRQ_STAT)
+#define DRIVE_READY (READY_STAT | SEEK_STAT)
+#define DATA_READY (DRIVE_READY | DRQ_STAT)
+
+/*
+ * Some more useful definitions
+ */
+#define BIOS_SECTORS(dev) (dev->bios_head*dev->bios_sect*dev->bios_cyl)
+#define HD_NAME "hd" /* the same for both i/f; see also genhd.c */
+#define PARTN_BITS 6 /* number of minor dev bits for partitions */
+#define PARTN_MASK ((1<<PARTN_BITS)-1) /* a useful bit mask */
+#define MAX_DRIVES 2 /* per interface; 2 assumed by lots of code */
+#define SECTOR_WORDS (512 / 4) /* number of 32bit words per sector */
+
+/*
+ * Timeouts for various operations:
+ */
+#define WAIT_DRQ 5 /* 50msec - spec allows up to 20ms */
+#define WAIT_READY 3 /* 30msec - should be instantaneous */
+#define WAIT_PIDENTIFY 100 /* 1sec - should be less than 3ms (?) */
+#define WAIT_WORSTCASE 3000 /* 30sec - worst case when spinning up */
+#define WAIT_CMD 1000 /* 10sec - maximum wait for an IRQ to happen */
+
+/*
+ * Now for the data we need to maintain per-device: ide_dev_t
+ *
+ * For fast indexing, sizeof(ide_dev_t) = 32 = power_of_2;
+ * Everything is carefully aligned on appropriate boundaries,
+ * and several fields are placed for optimal (gcc) access.
+ */
+typedef enum {disk, cdrom} dev_type;
+
+typedef union {
+ unsigned all : 8; /* all of the bits together */
+ struct {
+ unsigned set_geometry : 1; /* respecify drive geometry */
+ unsigned recalibrate : 1; /* seek to cyl 0 */
+ unsigned set_multmode : 1; /* set multmode count */
+ unsigned reserved : 5; /* unused */
+ } b;
+ } special_t;
+
+typedef union {
+ unsigned all : 8; /* all of the bits together */
+ struct {
+ unsigned head : 4; /* always zeros here */
+ unsigned drive : 1; /* drive number */
+ unsigned bit5 : 1; /* always 1 */
+ unsigned lba : 1; /* LBA instead of CHS */
+ unsigned bit7 : 1; /* always 1 */
+ } b;
+ } select_t;
+
+typedef struct {
+ byte hwif; /* first field gets very fast access */
+ byte unmask; /* pretty quick access to this also */
+ dev_type type : 1; /* disk or cdrom (or tape, floppy..) */
+ unsigned present : 1; /* drive is physically present */
+ unsigned dont_probe : 1; /* from: hdx=noprobe */
+ unsigned keep_settings : 1; /* restore settings after drive reset */
+ unsigned busy : 1; /* mutex for ide_open, revalidate_.. */
+ unsigned vlb_32bit : 1; /* use 32bit in/out for data */
+ unsigned vlb_sync : 1; /* needed for some 32bit chip sets */
+ unsigned reserved0 : 1; /* unused */
+ special_t special; /* special action flags */
+ select_t select; /* basic drive/head select reg value */
+ byte mult_count, chipset, reserved2;
+ byte usage, mult_req, wpcom, ctl;
+ byte head, sect, bios_head, bios_sect;
+ unsigned short cyl, bios_cyl;
+ const char *name;
+ struct hd_driveid *id;
+ struct wait_queue *wqueue;
+ } ide_dev_t;
+
+/*
+ * Stuff prefixed by "ide_" is indexed by the IDE interface number: 0 or 1
+ */
+static const byte ide_major [2] = {IDE0_MAJOR,IDE1_MAJOR};
+static byte ide_irq [2] = {DEFAULT_IDE0_IRQ,DEFAULT_IDE1_IRQ};
+static struct hd_struct ide_hd [2][MAX_DRIVES<<PARTN_BITS] = {{{0,0},},};
+static int ide_sizes [2][MAX_DRIVES<<PARTN_BITS] = {{0,},};
+static int ide_blksizes [2][MAX_DRIVES<<PARTN_BITS] = {{0,},};
+static unsigned long ide_capacity [2][MAX_DRIVES] = {{0,},};
+static ide_dev_t ide_dev [2][MAX_DRIVES] = {{{0,},},};
+static ide_dev_t *ide_cur_dev [2] = {NULL,NULL};
+static void (*ide_handler[2])(ide_dev_t *) = {NULL,NULL};
+static struct request *ide_cur_rq [2] = {NULL,NULL}; /* current request */
+static struct request ide_write_rq [2]; /* copy of *ide_cur_rq for WRITEs */
+static const int ide_timer [2] = {IDE0_TIMER,IDE1_TIMER};
+static const int ide_timerbit[2] = {(1<<IDE0_TIMER),(1<<IDE1_TIMER)};
+static const char *ide_name [2] = {"ide0", "ide1"};
+static const char *ide_devname [2][MAX_DRIVES] = /* for printk()'s */
+ {{HD_NAME "a", HD_NAME "b"}, {HD_NAME "c", HD_NAME "d"}};
+static const char *unsupported = " not supported by this kernel\n";
+
+static byte single_threaded = 0;
+#if SUPPORT_SHARING_IRQ
+static byte sharing_single_irq = 0; /* for two i/f on one IRQ */
+static volatile byte current_hwif = 0; /* for single_threaded==1 */
+#endif /* SUPPORT_SHARING_IRQ */
+
+/*
+ * This structure is used to register our block device(s) with the kernel:
+ */
+static void ide0_geninit(void), ide1_geninit(void);
+static struct gendisk ide_gendisk [2] =
+ {{
+ IDE0_MAJOR, /* major number */
+ HD_NAME, /* same as below; see genhd.c before changing */
+ PARTN_BITS, /* minor_shift (to extract minor number) */
+ 1 << PARTN_BITS,/* max_p (number of partitions per real) */
+ MAX_DRIVES, /* maximum number of real drives */
+ ide0_geninit, /* init function */
+ ide_hd[0], /* hd_struct */
+ ide_sizes[0], /* block sizes */
+ 0, /* nr_real (number of drives present) */
+ ide_dev[0], /* ptr to internal data structure */
+ NULL /* next */
+ },{
+ IDE1_MAJOR, /* major number */
+ HD_NAME, /* same as above; see genhd.c before changing */
+ PARTN_BITS, /* minor_shift (to extract minor number) */
+ 1 << PARTN_BITS,/* max_p (number of partitions per real) */
+ MAX_DRIVES, /* maximum number of real drives */
+ ide1_geninit, /* init function */
+ ide_hd[1], /* hd_struct */
+ ide_sizes[1], /* block sizes */
+ 0, /* nr_real (number of drives present) */
+ ide_dev[1], /* ptr to internal data structure */
+ NULL /* next */
+ }};
+
+/*
+ * One final include file, which references some of the data/defns from above
+ */
+#define IDE_DRIVER /* "parameter" for blk.h */
+#include "blk.h"
+
+/*
+ * For really screwy hardware (hey, at least it *can* be used with Linux!
+ */
+#if (DISK_RECOVERY_TIME > 0)
+static unsigned long ide_lastreq[] = {0,0}; /* completion time of last I/O */
+#define SET_DISK_RECOVERY_TIMER ide_lastreq[DEV_HWIF] = read_timer();
+static unsigned long read_timer(void)
+{
+ unsigned long t, flags;
+ int i;
+
+ save_flags(flags);
+ cli();
+ t = jiffies * 11932;
+ outb_p(0, 0x43);
+ i = inb_p(0x40);
+ i |= inb(0x40) << 8;
+ restore_flags(flags);
+ return (t - i);
+}
+#else
+#define SET_DISK_RECOVERY_TIMER /* nothing */
+#endif /* DISK_RECOVERY_TIME */
+
+/*
+ * The heart of the driver, referenced from lots of other routines:
+ */
+static void do_request (byte hwif);
+#define DO_REQUEST {SET_DISK_RECOVERY_TIMER do_request(DEV_HWIF);}
+
+/*
+ * This is a macro rather than an inline to permit better gcc code.
+ * Caller MUST do sti() before invoking WAIT_STAT() (for jiffies to work).
+ *
+ * This routine should get fixed to not hog the cpu during extra long waits..
+ * That could be done by busy-waiting for the first jiffy or two, and then
+ * setting a timer to wake up at half second intervals thereafter,
+ * until WAIT_WORSTCASE is achieved, before timing out.
+ */
+#define WAIT_STAT(dev,good,bad,timeout,msg,label) \
+{ \
+ byte stat; \
+ udelay(1); /* spec allows drive 400ns to assert "BUSY" */ \
+ if (GET_STAT(DEV_HWIF) & BUSY_STAT) { \
+ unsigned long timer = jiffies + timeout; \
+ do { \
+ if ((GET_STAT(DEV_HWIF) & BUSY_STAT) == 0) \
+ break; \
+ } while (timer > jiffies); \
+ } \
+ udelay(1); /* spec allows 400ns for status to stabilize */ \
+ if (!OK_STAT(stat=GET_STAT(DEV_HWIF), good, bad)) { \
+ ide_error(dev, msg " error", stat); \
+ goto label; \
+ } \
+}
+
+/*
+ * This is used for all data transfers *from* the IDE interface
+ */
+void input_ide_data (ide_dev_t *dev, void *buffer, uint wcount)
+{
+#if SUPPORT_VLB_32BIT
+ if (dev->vlb_32bit) {
+#if SUPPORT_VLB_SYNC
+ if (dev->vlb_sync) {
+ cli();
+ VLB_SYNC;
+ insl(IDE_PORT(HD_DATA,DEV_HWIF), buffer, wcount);
+ if (dev->unmask)
+ sti();
+ } else
+#endif /* SUPPORT_VLB_SYNC */
+ insl(IDE_PORT(HD_DATA,DEV_HWIF), buffer, wcount);
+ } else
+#endif /* SUPPORT_VLB_32BIT */
+ insw(IDE_PORT(HD_DATA,DEV_HWIF), buffer, wcount<<1);
+}
+
+/*
+ * This is used for all data transfers *to* the IDE interface
+ */
+void output_ide_data (ide_dev_t *dev, void *buffer, uint wcount)
+{
+#if SUPPORT_VLB_32BIT
+ if (dev->vlb_32bit) {
+#if SUPPORT_VLB_SYNC
+ if (dev->vlb_sync) {
+ cli();
+ VLB_SYNC;
+ outsl(IDE_PORT(HD_DATA,DEV_HWIF), buffer, wcount);
+ if (dev->unmask)
+ sti();
+ } else
+ outsl(IDE_PORT(HD_DATA,DEV_HWIF), buffer, wcount);
+#endif /* SUPPORT_VLB_SYNC */
+ } else
+#endif /* SUPPORT_VLB_32BIT */
+ outsw(IDE_PORT(HD_DATA,DEV_HWIF), buffer, wcount<<1);
+}
+
+/*
+ * This should get invoked on every exit path from the driver.
+ */
+static inline void start_ide_timer (byte hwif)
+{
+ if (ide_handler[HWIF] != NULL) { /* waiting for an irq? */
+ timer_table[ide_timer[HWIF]].expires = jiffies + WAIT_CMD;
+ timer_active |= ide_timerbit[HWIF];
+ }
+}
+
+static void do_ide_reset (ide_dev_t *dev)
+{
+ byte tmp;
+ unsigned long timer, flags;
+
+ save_flags(flags);
+ sti();
+ for (tmp = 0; tmp < MAX_DRIVES; tmp++) {
+ ide_dev_t *rdev = &ide_dev[DEV_HWIF][tmp];
+ rdev->special.b.set_geometry = 1;
+ rdev->special.b.recalibrate = 1;
+ rdev->special.b.set_multmode = 0;
+ if (OK_TO_RESET_CONTROLLER)
+ rdev->mult_count = 0;
+ if (!rdev->keep_settings) {
+ rdev->mult_req = 0;
+ rdev->unmask = 0;
+ }
+ if (rdev->mult_req != rdev->mult_count)
+ rdev->special.b.set_multmode = 1;
+ }
+
+#if OK_TO_RESET_CONTROLLER
+ cli();
+ OUT_BYTE(dev->ctl|6,HD_CMD); /* set nIEN, set SRST */
+ udelay(10); /* more than enough time */
+ OUT_BYTE(dev->ctl|2,HD_CMD); /* clear SRST */
+ udelay(10); /* more than enough time */
+ sti(); /* needed for jiffies */
+ for (timer = jiffies + WAIT_WORSTCASE; timer > jiffies;) {
+ if ((GET_STAT(DEV_HWIF) & BUSY_STAT) == 0)
+ break;
+ }
+ printk("%s: do_ide_reset: ", ide_name[DEV_HWIF]);
+ /* ATAPI devices usually do *not* assert READY after a reset */
+ if (!OK_STAT(tmp=GET_STAT(DEV_HWIF), 0, BUSY_STAT)) {
+ printk("timed-out, status=0x%02x\n", tmp);
+ } else {
+ if ((tmp = GET_ERR(DEV_HWIF)) == 1)
+ printk("success\n");
+ else {
+ printk("%s: ", ide_devname[DEV_HWIF][0]);
+ switch (tmp & 0x7f) {
+ case 1: printk("passed");
+ break;
+ case 2: printk("formatter device error");
+ break;
+ case 3: printk("sector buffer error");
+ break;
+ case 4: printk("ECC circuitry error");
+ break;
+ case 5: printk("controlling MPU error");
+ break;
+ default:printk("error (0x%02x?)", tmp);
+ }
+ if (tmp & 0x80)
+ printk("; %s: error", ide_devname[DEV_HWIF][1]);
+ printk("\n");
+ }
+ }
+#endif /* OK_TO_RESET_CONTROLLER */
+ restore_flags(flags);
+}
+
+/*
+ * Clean up after success/failure of an explicit (ioctl) drive cmd
+ */
+static void end_drive_cmd (ide_dev_t *dev, byte stat, byte err)
+{
+ unsigned long flags;
+ struct request *rq = ide_cur_rq[DEV_HWIF];
+ byte *args = (byte *) rq->buffer;
+
+ rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);
+ if (args) {
+ args[0] = stat;
+ args[1] = err;
+ args[2] = IN_BYTE(HD_NSECTOR,DEV_HWIF);
+ }
+ save_flags(flags);
+ cli();
+ up(rq->sem);
+ ide_cur_rq[DEV_HWIF] = NULL;
+ restore_flags(flags);
+}
+
+/*
+ * Error reporting, in human readable form (luxurious, but a memory hog).
+ */
+static byte dump_status (byte hwif, const char *msg, byte stat)
+{
+ unsigned long flags;
+ byte err = 0;
+ ide_dev_t *dev = ide_cur_dev[HWIF];
+ const char *name = dev ? dev->name : ide_name[HWIF];
+
+ save_flags (flags);
+ sti();
+ printk("%s: %s: status=0x%02x", name, msg, stat);
+#if FANCY_STATUS_DUMPS
+ if (dev && dev->type == disk) {
+ printk(" { ");
+ if (stat & BUSY_STAT)
+ printk("Busy ");
+ else {
+ if (stat & READY_STAT) printk("DriveReady ");
+ if (stat & WRERR_STAT) printk("WriteFault ");
+ if (stat & SEEK_STAT) printk("SeekComplete ");
+ if (stat & DRQ_STAT) printk("DataRequest ");
+ if (stat & ECC_STAT) printk("CorrectedError ");
+ if (stat & INDEX_STAT) printk("Index ");
+ if (stat & ERR_STAT) printk("Error ");
+ }
+ printk("}");
+ }
+#endif /* FANCY_STATUS_DUMPS */
+ printk("\n");
+ if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) {
+ err = GET_ERR(HWIF);
+ printk("%s: %s: error=0x%02x", name, msg, err);
+#if FANCY_STATUS_DUMPS
+ if (dev && dev->type == disk) {
+ printk(" { ");
+ if (err & BBD_ERR) printk("BadSector ");
+ if (err & ECC_ERR) printk("UncorrectableError ");
+ if (err & ID_ERR) printk("SectorIdNotFound ");
+ if (err & ABRT_ERR) printk("DriveStatusError ");
+ if (err & TRK0_ERR) printk("TrackZeroNotFound ");
+ if (err & MARK_ERR) printk("AddrMarkNotFound ");
+ printk("}");
+ if (err & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) {
+ byte cur = IN_BYTE(HD_CURRENT,HWIF);
+ if (cur & 0x40) { /* using LBA? */
+ printk(", LBAsect=%ld", (unsigned long)
+ ((cur&0xf)<<24)
+ |(IN_BYTE(HD_HCYL,HWIF)<<16)
+ |(IN_BYTE(HD_LCYL,HWIF)<<8)
+ | IN_BYTE(HD_SECTOR,HWIF));
+ } else {
+ printk(", CHS=%d/%d/%d",
+ (IN_BYTE(HD_HCYL,HWIF)<<8) +
+ IN_BYTE(HD_LCYL,HWIF),
+ cur & 0xf,
+ IN_BYTE(HD_SECTOR,HWIF));
+ }
+ if (ide_cur_rq[HWIF])
+ printk(", sector=%ld", ide_cur_rq[HWIF]->sector);
+ }
+ }
+#endif /* FANCY_STATUS_DUMPS */
+ printk("\n");
+ }
+ restore_flags (flags);
+ return err;
+}
+
+/*
+ * ide_error() takes action based on the error returned by the controller.
+ */
+#define ERROR_MAX 8 /* Max read/write errors per sector */
+#define ERROR_RESET 3 /* Reset controller every 4th retry */
+#define ERROR_RECAL 1 /* Recalibrate every 2nd retry */
+static void ide_error (ide_dev_t *dev, const char *msg, byte stat)
+{
+ struct request *rq;
+ byte err;
+
+ err = dump_status(DEV_HWIF, msg, stat);
+ if ((rq = ide_cur_rq[DEV_HWIF]) == NULL || dev == NULL)
+ return;
+#ifdef IDE_DRIVE_CMD
+ if (rq->cmd == IDE_DRIVE_CMD) { /* never retry an explicit DRIVE_CMD */
+ end_drive_cmd(dev, stat, err);
+ return;
+ }
+#endif /* IDE_DRIVE_CMD */
+ if (stat & BUSY_STAT) { /* other bits are useless when BUSY */
+ rq->errors |= ERROR_RESET;
+ } else {
+ if (dev->type == disk && (stat & ERR_STAT)) {
+ /* err has different meaning on cdrom */
+ if (err & BBD_ERR) /* retries won't help this! */
+ rq->errors = ERROR_MAX;
+ else if (err & TRK0_ERR) /* help it find track zero */
+ rq->errors |= ERROR_RECAL;
+ }
+ if ((stat & DRQ_STAT) && rq->cmd == READ) {
+ int i = dev->mult_count ? dev->mult_count<<8 : 1<<8;
+ while (i-- > 0) /* try to flush data */
+ (void) IN_BYTE(HD_DATA, dev->hwif);
+ }
+ }
+ if (GET_STAT(dev->hwif) & (BUSY_STAT|DRQ_STAT))
+ rq->errors |= ERROR_RESET; /* Mmmm.. timing problem */
+
+ if (rq->errors >= ERROR_MAX)
+ end_request(0, DEV_HWIF);
+ else {
+ if ((rq->errors & ERROR_RESET) == ERROR_RESET)
+ do_ide_reset(dev);
+ else if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
+ dev->special.b.recalibrate = 1;
+ ++rq->errors;
+ }
+}
+
+static void read_intr (ide_dev_t *dev)
+{
+ byte stat;
+ int i;
+ unsigned int msect, nsect;
+ struct request *rq;
+
+ if (!OK_STAT(stat=GET_STAT(DEV_HWIF),DATA_READY,BAD_R_STAT)) {
+ sti();
+ ide_error(dev, "read_intr", stat);
+ DO_REQUEST;
+ return;
+ }
+ msect = dev->mult_count;
+read_next:
+ rq = ide_cur_rq[DEV_HWIF];
+ if (msect) {
+ if ((nsect = rq->current_nr_sectors) > msect)
+ nsect = msect;
+ msect -= nsect;
+ } else
+ nsect = 1;
+ input_ide_data(dev, rq->buffer, nsect * SECTOR_WORDS);
+#ifdef DEBUG
+ printk("%s: read: sectors(%ld-%ld), buffer=0x%08lx, remaining=%ld\n",
+ dev->name, rq->sector, rq->sector+nsect-1,
+ (unsigned long) rq->buffer+(nsect<<9), rq->nr_sectors-nsect);
+#endif
+ rq->sector += nsect;
+ rq->buffer += nsect<<9;
+ rq->errors = 0;
+ i = (rq->nr_sectors -= nsect);
+ if ((rq->current_nr_sectors -= nsect) <= 0)
+ end_request(1, DEV_HWIF);
+ if (i > 0) {
+ if (msect)
+ goto read_next;
+ ide_handler[DEV_HWIF] = &read_intr;
+ return;
+ }
+ /* (void) GET_STAT(DEV_HWIF); */ /* hd.c did this */
+ DO_REQUEST;
+}
+
+static void write_intr (ide_dev_t *dev)
+{
+ byte stat;
+ int i;
+ struct request *rq = ide_cur_rq[DEV_HWIF];
+
+ if (OK_STAT(stat=GET_STAT(DEV_HWIF),DRIVE_READY,BAD_W_STAT)) {
+#ifdef DEBUG
+ printk("%s: write: sector %ld, buffer=0x%08lx, remaining=%ld\n",
+ dev->name, rq->sector, (unsigned long) rq->buffer,
+ rq->nr_sectors-1);
+#endif
+ if ((rq->nr_sectors == 1) ^ ((stat & DRQ_STAT) != 0)) {
+ rq->sector++;
+ rq->buffer += 512;
+ rq->errors = 0;
+ i = --rq->nr_sectors;
+ --rq->current_nr_sectors;
+ if (rq->current_nr_sectors <= 0)
+ end_request(1, DEV_HWIF);
+ if (i > 0) {
+ ide_handler[DEV_HWIF] = &write_intr;
+ output_ide_data(dev, rq->buffer, SECTOR_WORDS);
+ return;
+ }
+ DO_REQUEST;
+ return;
+ }
+ }
+ sti();
+ ide_error(dev, "write_intr", stat);
+ DO_REQUEST;
+}
+
+static void multwrite (ide_dev_t *dev)
+{
+ struct request *rq = &ide_write_rq[DEV_HWIF];
+ unsigned int mcount = dev->mult_count;
+
+ do {
+ unsigned int nsect = rq->current_nr_sectors;
+ if (nsect > mcount)
+ nsect = mcount;
+ mcount -= nsect;
+
+ output_ide_data(dev, rq->buffer, nsect<<7);
+#ifdef DEBUG
+ printk("%s: multwrite: sector %ld, buffer=0x%08lx, count=%d, remaining=%ld\n",
+ dev->name, rq->sector, (unsigned long) rq->buffer,
+ nsect, rq->nr_sectors - nsect);
+#endif
+ if ((rq->nr_sectors -= nsect) <= 0)
+ break;
+ if ((rq->current_nr_sectors -= nsect) == 0) {
+ if ((rq->bh = rq->bh->b_reqnext) != NULL) {
+ rq->current_nr_sectors = rq->bh->b_size>>9;
+ rq->buffer = rq->bh->b_data;
+ } else {
+ panic("%s: buffer list corrupted\n", dev->name);
+ break;
+ }
+ } else {
+ rq->buffer += nsect << 9;
+ }
+ } while (mcount);
+}
+
+static void multwrite_intr (ide_dev_t *dev)
+{
+ byte stat;
+ int i;
+ struct request *rq = &ide_write_rq[DEV_HWIF];
+
+ if (OK_STAT(stat=GET_STAT(DEV_HWIF),DRIVE_READY,BAD_W_STAT)) {
+ if (stat & DRQ_STAT) {
+ if (rq->nr_sectors) {
+ if (dev->mult_count)
+ multwrite(dev);
+ ide_handler[DEV_HWIF] = &multwrite_intr;
+ return;
+ }
+ } else {
+ if (!rq->nr_sectors) { /* all done? */
+ rq = ide_cur_rq[DEV_HWIF];
+ for (i = rq->nr_sectors; i > 0;){
+ i -= rq->current_nr_sectors;
+ end_request(1, DEV_HWIF);
+ }
+ DO_REQUEST;
+ return;
+ }
+ }
+ }
+ sti();
+ ide_error(dev, "multwrite_intr", stat);
+ DO_REQUEST;
+}
+
+/*
+ * Issue a simple drive command
+ * The drive must be selected beforehand.
+ */
+static inline void ide_cmd(ide_dev_t *dev, byte cmd, byte nsect,
+ void (*handler)(ide_dev_t *dev))
+{
+ OUT_BYTE(dev->ctl,HD_CMD);
+ OUT_BYTE(nsect,HD_NSECTOR);
+ OUT_BYTE(cmd,HD_COMMAND);
+ ide_handler[DEV_HWIF] = handler;
+}
+
+static void set_multmode_intr (ide_dev_t *dev)
+{
+ byte stat = GET_STAT(DEV_HWIF);
+
+ sti();
+ if (!OK_STAT(stat,READY_STAT,BAD_STAT)) {
+ dev->mult_req = dev->mult_count = 0;
+ dev->special.b.recalibrate = 1;
+ (void) dump_status(DEV_HWIF, "set_multmode", stat);
+ } else {
+ if ((dev->mult_count = dev->mult_req))
+ printk (" %s: enabled %d-sector multiple mode\n",
+ dev->name, dev->mult_count);
+ else
+ printk (" %s: multiple mode turned off\n", dev->name);
+ }
+ DO_REQUEST;
+}
+
+static void set_geometry_intr (ide_dev_t *dev)
+{
+ byte stat = GET_STAT(DEV_HWIF);
+
+ sti();
+ if (!OK_STAT(stat,READY_STAT,BAD_STAT))
+ ide_error(dev, "set_geometry_intr", stat);
+ DO_REQUEST;
+}
+
+static void recal_intr (ide_dev_t *dev)
+{
+ byte stat = GET_STAT(DEV_HWIF);
+
+ sti();
+ if (!OK_STAT(stat,READY_STAT,BAD_STAT))
+ ide_error(dev, "recal_intr", stat);
+ DO_REQUEST;
+}
+
+static void drive_cmd_intr (ide_dev_t *dev)
+{
+ byte stat = GET_STAT(DEV_HWIF);
+
+ sti();
+ if (!OK_STAT(stat,READY_STAT,BAD_STAT))
+ ide_error(dev, "drive_cmd", stat); /* calls end_drive_cmd() */
+ else
+ end_drive_cmd (dev, stat, GET_ERR(DEV_HWIF));
+ DO_REQUEST;
+}
+
+static void timer_expiry (byte hwif)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ if (ide_handler[HWIF] == NULL || (timer_active & ide_timerbit[HWIF])) {
+ /* The drive must have responded just as the timer expired */
+ sti();
+ printk("%s: marginal timeout\n", ide_name[HWIF]);
+ } else {
+ ide_handler[HWIF] = NULL;
+ disable_irq(ide_irq[HWIF]);
+#if SUPPORT_SERIALIZE
+ if (single_threaded && ide_irq[HWIF] != ide_irq[HWIF^1])
+ disable_irq(ide_irq[HWIF^1]);
+#endif /* SUPPORT_SERIALIZE */
+ sti();
+ ide_error(ide_cur_dev[HWIF], "timeout", GET_STAT(HWIF));
+ do_request(HWIF);
+#if SUPPORT_SHARING_IRQ
+ if (single_threaded) /* this line is indeed necessary */
+ hwif = current_hwif;
+#endif /* SUPPORT_SHARING_IRQ */
+ cli();
+ start_ide_timer(HWIF);
+ enable_irq(ide_irq[HWIF]);
+#if SUPPORT_SERIALIZE
+ if (single_threaded && ide_irq[HWIF] != ide_irq[HWIF^1])
+ enable_irq(ide_irq[HWIF^1]);
+#endif /* SUPPORT_SERIALIZE */
+ }
+ restore_flags(flags);
+}
+
+static void ide0_timer_expiry (void) /* invoked from sched.c */
+{
+ timer_expiry (0);
+}
+
+static void ide1_timer_expiry (void) /* invoked from sched.c */
+{
+ timer_expiry (1);
+}
+
+static int do_special (ide_dev_t *dev)
+{
+ special_t *s = &dev->special;
+#ifdef DEBUG
+ printk("%s: do_special: 0x%02x\n", dev->name, s->all);
+#endif
+ if (s->b.set_geometry) {
+ s->b.set_geometry = 0;
+ if (dev->type == disk) {
+ OUT_BYTE(dev->sect,HD_SECTOR);
+ OUT_BYTE(dev->cyl,HD_LCYL);
+ OUT_BYTE(dev->cyl>>8,HD_HCYL);
+ OUT_BYTE(((dev->head-1)|dev->select.all)&0xBF,HD_CURRENT);
+ ide_cmd(dev, WIN_SPECIFY, dev->sect, &set_geometry_intr);
+ }
+ } else if (s->b.recalibrate) {
+ s->b.recalibrate = 0;
+ if (dev->type == disk)
+ ide_cmd(dev,WIN_RESTORE,dev->sect,&recal_intr);
+ } else if (s->b.set_multmode) {
+ if (dev->type == disk) {
+ if (dev->id && dev->mult_req > dev->id->max_multsect)
+ dev->mult_req = dev->id->max_multsect;
+ ide_cmd(dev,WIN_SETMULT,dev->mult_req,&set_multmode_intr);
+ } else {
+ dev->mult_req = 0;
+ printk("%s: multmode not supported by this device\n", dev->name);
+ }
+ s->b.set_multmode = 0;
+ } else {
+ if (s->all) {
+ printk("%s: bad special flag: 0x%02x\n", dev->name, s->all);
+ s->all = 0;
+ }
+ }
+ return (ide_handler[DEV_HWIF] == NULL) ? 1 : 0;
+}
+
+#ifdef CONFIG_BLK_DEV_IDECD
+static byte wait_stat (ide_dev_t *dev, byte good, byte bad, unsigned long timeout)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ sti();
+ WAIT_STAT(dev, good, bad, timeout, "status", error);
+ restore_flags(flags);
+ return 0;
+error:
+ restore_flags(flags);
+ return 1;
+}
+
+#include "ide-cd.c"
+#endif /* CONFIG_BLK_DEV_IDECD */
+
+static inline int do_rw_disk (ide_dev_t *dev, struct request *rq, unsigned long block)
+{
+ OUT_BYTE(dev->ctl,HD_CMD);
+ OUT_BYTE(rq->nr_sectors,HD_NSECTOR);
+ if (dev->select.b.lba) {
+#ifdef DEBUG
+ printk("%s: %sing: LBAsect=%ld, sectors=%ld, buffer=0x%08lx\n",
+ dev->name, (rq->cmd==READ)?"read":"writ",
+ block, rq->nr_sectors, (unsigned long) rq->buffer);
+#endif
+ OUT_BYTE(block,HD_SECTOR);
+ OUT_BYTE(block>>=8,HD_LCYL);
+ OUT_BYTE(block>>=8,HD_HCYL);
+ OUT_BYTE(((block>>8)&0x0f)|dev->select.all,HD_CURRENT);
+ } else {
+ unsigned int sect,head,cyl,track;
+ track = block / dev->sect;
+ sect = block % dev->sect + 1;
+ OUT_BYTE(sect,HD_SECTOR);
+ head = track % dev->head;
+ cyl = track / dev->head;
+ OUT_BYTE(cyl,HD_LCYL);
+ OUT_BYTE(cyl>>8,HD_HCYL);
+ OUT_BYTE(head|dev->select.all,HD_CURRENT);
+#ifdef DEBUG
+ printk("%s: %sing: CHS=%d/%d/%d, sectors=%ld, buffer=0x%08lx\n",
+ dev->name, (rq->cmd==READ)?"read":"writ", cyl,
+ head, sect, rq->nr_sectors, (unsigned long) rq->buffer);
+#endif
+ }
+ if (rq->cmd == READ) {
+ OUT_BYTE(dev->mult_count ? WIN_MULTREAD : WIN_READ, HD_COMMAND);
+ ide_handler[DEV_HWIF] = &read_intr;
+ return 0;
+ }
+ if (rq->cmd == WRITE) {
+ OUT_BYTE(dev->wpcom,HD_PRECOMP); /* for ancient drives */
+ OUT_BYTE(dev->mult_count ? WIN_MULTWRITE : WIN_WRITE, HD_COMMAND);
+ WAIT_STAT(dev, DATA_READY, BAD_W_STAT, WAIT_DRQ, "DRQ", error);
+ if (!dev->unmask)
+ cli();
+ if (dev->mult_count) {
+ ide_write_rq[DEV_HWIF] = *rq; /* scratchpad */
+ multwrite(dev);
+ ide_handler[DEV_HWIF] = &multwrite_intr;
+ } else {
+ output_ide_data(dev, rq->buffer, SECTOR_WORDS);
+ ide_handler[DEV_HWIF] = &write_intr;
+ }
+ return 0;
+ }
+#ifdef IDE_DRIVE_CMD
+ if (rq->cmd == IDE_DRIVE_CMD) {
+ byte *args = rq->buffer;
+ if (args) {
+ OUT_BYTE(args[2],HD_FEATURE);
+ ide_cmd(dev, args[0], args[1], &drive_cmd_intr);
+ printk("%s: DRIVE_CMD cmd=0x%02x sc=0x%02x fr=0x%02x\n",
+ dev->name, args[0], args[1], args[2]);
+ return 0;
+ } else {
+#ifdef DEBUG
+ printk("%s: DRIVE_CMD (null)\n", dev->name);
+#endif
+ end_drive_cmd(dev,GET_STAT(DEV_HWIF),GET_ERR(DEV_HWIF));
+ return 1;
+ }
+ }
+#endif /* IDE_DRIVE_CMD */
+ printk("%s: bad command: %d\n", dev->name, rq->cmd);
+ end_request(0, DEV_HWIF);
+error:
+ return 1;
+}
+
+/*
+ * The driver enables interrupts as much as possible. In order to do this,
+ * (a) the device-interrupt is always masked before entry, and
+ * (b) the timeout-interrupt is always disabled before entry.
+ *
+ * Interrupts are still masked (by default) whenever we are exchanging
+ * data/cmds with a drive, because some drives seem to have very poor
+ * tolerance for latency during I/O. For devices which don't suffer from
+ * this problem (most don't), the ide_dev[][].unmask flag can be set to permit
+ * other interrupts during data/cmd transfers by using the "hdparm" utility.
+ */
+static void do_request (byte hwif)
+{
+ unsigned int minor, drive;
+ unsigned long block, blockend;
+ struct request *rq;
+ ide_dev_t *dev;
+repeat:
+ sti();
+#if SUPPORT_SHARING_IRQ
+ current_hwif = hwif; /* used *only* when single_threaded==1 */
+#endif /* SUPPORT_SHARING_IRQ */
+ if ((rq = ide_cur_rq[HWIF]) == NULL) {
+ rq = blk_dev[ide_major[HWIF]].current_request;
+ if ((rq == NULL) || (rq->dev < 0)) {
+#if SUPPORT_SHARING_IRQ
+ if (single_threaded) {
+ if (sharing_single_irq && (dev = ide_cur_dev[hwif])) /* disable irq */
+ OUT_BYTE(dev->ctl|2,HD_CMD);
+ rq = blk_dev[ide_major[hwif^=1]].current_request;
+ if ((rq != NULL) && (rq->dev >= 0))
+ goto repeat;
+ }
+#endif /* SUPPORT_SHARING_IRQ */
+ return;
+ }
+ blk_dev[ide_major[HWIF]].current_request = rq->next;
+ ide_cur_rq[HWIF] = rq;
+ }
+#ifdef DEBUG
+ printk("%s: do_request: current=0x%08lx\n",ide_name[HWIF],(unsigned long)rq);
+#endif
+ minor = MINOR(rq->dev);
+ drive = minor >> PARTN_BITS;
+ ide_cur_dev[HWIF] = dev = &ide_dev[HWIF][drive];
+ if ((MAJOR(rq->dev) != ide_major[HWIF]) || (drive >= MAX_DRIVES)) {
+ printk("%s: bad device number: 0x%04x\n", ide_name[HWIF], rq->dev);
+ end_request(0, HWIF);
+ goto repeat;
+ }
+ if (rq->bh && !rq->bh->b_lock) {
+ printk("%s: block not locked\n", ide_name[HWIF]);
+ end_request(0, HWIF);
+ goto repeat;
+ }
+ block = rq->sector;
+ blockend = block + rq->nr_sectors;
+ if ((blockend < block) || (blockend > ide_hd[HWIF][minor].nr_sects)) {
+ printk("%s: bad access: block=%ld, count=%ld\n",
+ dev->name, block, rq->nr_sectors);
+ end_request(0, HWIF);
+ goto repeat;
+ }
+ block += ide_hd[HWIF][minor].start_sect;
+#if (DISK_RECOVERY_TIME > 0)
+ while ((read_timer() - ide_lastreq[HWIF]) < DISK_RECOVERY_TIME);
+#endif
+ OUT_BYTE(dev->select.all,HD_CURRENT);
+#ifdef CONFIG_BLK_DEV_IDECD
+ WAIT_STAT(dev, (dev->type == cdrom) ? 0 : READY_STAT,
+ BUSY_STAT|DRQ_STAT, WAIT_READY, "DRDY", repeat);
+#else
+ WAIT_STAT(dev, READY_STAT, BUSY_STAT|DRQ_STAT, WAIT_READY, "DRDY", repeat);
+#endif /* CONFIG_BLK_DEV_IDECD */
+ if (!dev->special.all) {
+#ifdef CONFIG_BLK_DEV_IDECD
+ if (dev->type == disk) {
+#endif /* CONFIG_BLK_DEV_IDECD */
+ if (do_rw_disk(dev, rq, block))
+ goto repeat;
+#ifdef CONFIG_BLK_DEV_IDECD
+ } else {
+ if (do_rw_cdrom(dev, block))
+ goto repeat;
+ }
+#endif /* CONFIG_BLK_DEV_IDECD */
+ } else {
+ if (do_special(dev))
+ goto repeat;
+ }
+}
+
+/*
+ * This is a macro rather than an inline function to
+ * prevent gcc from over-optimizing accesses to current_hwif,
+ * which may have a different value on exit from do_request().
+ */
+#define DO_IDE_REQUEST(hwif) \
+{ \
+ if (ide_handler[hwif] == NULL) { \
+ disable_irq(ide_irq[hwif]); \
+ if (single_threaded && ide_irq[hwif] != ide_irq[hwif^1]) \
+ disable_irq(ide_irq[hwif^1]); \
+ do_request(hwif); \
+ cli(); \
+ start_ide_timer(hwif); \
+ enable_irq(ide_irq[hwif]); \
+ if (single_threaded && ide_irq[hwif] != ide_irq[hwif^1]) \
+ enable_irq(ide_irq[hwif^1]); \
+ } \
+}
+
+#if SUPPORT_TWO_INTERFACES
+static void do_ide0_request (void) /* invoked with cli() */
+{
+ DO_IDE_REQUEST(0);
+}
+
+static void do_ide1_request (void) /* invoked with cli() */
+{
+ DO_IDE_REQUEST(1);
+}
+#else
+#define do_ide1_request do_ide0_request
+static void do_ide0_request (void) /* invoked with cli() */
+{
+ DO_IDE_REQUEST(HWIF);
+}
+#endif /* SUPPORT_TWO_INTERFACES */
+
+#if SUPPORT_SHARING_IRQ
+static void do_shared_request (void) /* invoked with cli() */
+{
+ DO_IDE_REQUEST(current_hwif);
+}
+#endif /* SUPPORT_SHARING_IRQ */
+
+/*
+ * There's nothing really useful we can do with an unexpected interrupt,
+ * other than reading the status register (to clear it), and logging it.
+ * There should be no way that an irq can happen before we're ready for it,
+ * so we needn't worry much about losing an "important" interrupt here.
+ *
+ * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the
+ * drive enters "idle", "standby", or "sleep" mode, so if the status looks
+ * "good", we just ignore the interrupt completely.
+ */
+static void unexpected_intr (byte hwif)
+{
+ byte stat;
+
+ if (!OK_STAT(stat=GET_STAT(HWIF), DRIVE_READY, BAD_STAT))
+ (void) dump_status(HWIF, "unexpected_intr", stat);
+ outb_p(2,IDE_PORT(HD_CMD,hwif)); /* disable device irq */
+#if SUPPORT_SHARING_IRQ
+ if (single_threaded && ide_irq[hwif] == ide_irq[hwif^1]) {
+ if (!OK_STAT(stat=GET_STAT(hwif^1), DRIVE_READY, BAD_STAT))
+ (void) dump_status(hwif^1, "unexpected_intr", stat);
+ outb_p(2,IDE_PORT(HD_CMD,hwif^1)); /* disable device irq */
+ }
+#endif /* SUPPORT_SHARING_IRQ */
+}
+
+/*
+ * This is a macro rather than an inline function to
+ * prevent gcc from over-optimizing accesses to current_hwif,
+ * which may have a different value on exit from handler().
+ */
+#define IDE_INTR(hwif) \
+{ \
+ ide_dev_t *dev; \
+ void (*handler)(ide_dev_t *); \
+ \
+ timer_active &= ~ide_timerbit[hwif]; \
+ if ((handler = ide_handler[hwif]) != NULL) { \
+ ide_handler[hwif] = NULL; \
+ dev = ide_cur_dev[hwif]; \
+ if (dev->unmask) \
+ sti(); \
+ handler(dev); \
+ } else \
+ unexpected_intr(hwif); \
+ cli(); \
+}
+
+#if SUPPORT_SERIALIZE
+/* entry point for all interrupts when single_threaded==1 */
+static void ide_seq_intr (int irq, struct pt_regs *regs)
+{
+ byte hwif = (irq != ide_irq[0]);
+ IDE_INTR(HWIF);
+ start_ide_timer(current_hwif);
+}
+#endif /* SUPPORT_SERIALIZE */
+
+#if OPTIMIZE_IRQS
+
+/* entry point for all interrupts on ide0 when single_threaded==0 */
+static void ide0_intr (int irq, struct pt_regs *regs)
+{
+ IDE_INTR(0);
+ start_ide_timer(0);
+}
+
+/* entry point for all interrupts on ide1 when single_threaded==0 */
+static void ide1_intr (int irq, struct pt_regs *regs)
+{
+ IDE_INTR(1);
+ start_ide_timer(1);
+}
+
+#else /* OPTIMIZE_IRQS */
+
+#define ide0_intr ide_intr
+#define ide1_intr ide_intr
+
+/* entry point for all interrupts when single_threaded==0 */
+static void ide_intr (int irq, struct pt_regs *regs)
+{
+#if SUPPORT_TWO_INTERFACES
+ byte hwif = (irq != ide_irq[0]);
+#endif /* SUPPORT_TWO_INTERFACES */
+ IDE_INTR(HWIF);
+ start_ide_timer(HWIF);
+}
+
+#endif /* OPTIMIZE_IRQS */
+
+#if SUPPORT_SHARING_IRQ
+/* entry point for all interrupts on ide0/ide1 when sharing_single_irq==1 */
+static void ide_shared_intr (int irq, struct pt_regs * regs)
+{
+ IDE_INTR(current_hwif);
+ start_ide_timer(current_hwif);
+}
+#endif /* SUPPORT_SHARING_IRQ */
+
+static ide_dev_t *get_info_ptr (int i_rdev)
+{
+ unsigned int drive = DEVICE_NR(i_rdev);
+ ide_dev_t *dev;
+
+ if (drive < MAX_DRIVES) {
+ switch (MAJOR(i_rdev)) {
+ case IDE0_MAJOR: dev = &ide_dev[0][drive];
+ if (dev->present) return dev;
+ break;
+ case IDE1_MAJOR: dev = &ide_dev[1][drive];
+ if (dev->present) return dev;
+ break;
+ }
+ }
+ return NULL;
+}
+
+static int ide_open(struct inode * inode, struct file * filp)
+{
+ ide_dev_t *dev;
+ unsigned long flags;
+
+ if ((dev = get_info_ptr(inode->i_rdev)) == NULL)
+ return -ENODEV;
+ save_flags(flags);
+ cli();
+ while (dev->busy)
+ sleep_on(&dev->wqueue);
+ dev->usage++;
+ restore_flags(flags);
+#ifdef CONFIG_BLK_DEV_IDECD
+ if (dev->type == cdrom)
+ return cdrom_open (inode, filp, dev);
+#endif /* CONFIG_BLK_DEV_IDECD */
+ return 0;
+}
+
+/*
+ * Releasing a block device means we sync() it, so that it can safely
+ * be forgotten about...
+ */
+static void ide_release(struct inode * inode, struct file * file)
+{
+ ide_dev_t *dev;
+
+ if ((dev = get_info_ptr(inode->i_rdev)) != NULL) {
+ sync_dev(inode->i_rdev);
+ dev->usage--;
+#ifdef CONFIG_BLK_DEV_IDECD
+ if (dev->type == cdrom)
+ cdrom_release (inode, file, dev);
+#endif /* CONFIG_BLK_DEV_IDECD */
+ }
+}
+
+/*
+ * This routine is called to flush all partitions and partition tables
+ * for a changed disk, and then re-read the new partition table.
+ * If we are revalidating a disk because of a media change, then we
+ * enter with usage == 0. If we are using an ioctl, we automatically have
+ * usage == 1 (we need an open channel to use an ioctl :-), so this
+ * is our limit.
+ */
+static int revalidate_disk(int i_rdev)
+{
+ unsigned int i, major, start, drive = DEVICE_NR(i_rdev);
+ ide_dev_t *dev;
+ struct gendisk *gd;
+ long flags;
+
+ if ((dev = get_info_ptr(i_rdev)) == NULL)
+ return -ENODEV;
+
+ save_flags(flags);
+ cli();
+ if (dev->busy || (dev->usage > 1)) {
+ restore_flags(flags);
+ return -EBUSY;
+ };
+ dev->busy = 1;
+ restore_flags(flags);
+
+ gd = &ide_gendisk[DEV_HWIF];
+ major = ide_major[DEV_HWIF] << 8;
+ start = drive << PARTN_BITS;
+
+ for (i = 0; i < (1<<PARTN_BITS); i++) {
+ unsigned int minor = start + i;
+ sync_dev (major | minor);
+ invalidate_inodes (major | minor);
+ invalidate_buffers (major | minor);
+ gd->part[minor].start_sect = 0;
+ gd->part[minor].nr_sects = 0;
+ };
+
+ gd->part[start].nr_sects = ide_capacity[DEV_HWIF][drive];
+ resetup_one_dev(gd, drive);
+
+ dev->busy = 0;
+ wake_up(&dev->wqueue);
+ return 0;
+}
+
+#ifdef IDE_DRIVE_CMD
+/*
+ * This function issues a specific IDE drive command onto the
+ * tail of the request queue, and waits for it to be completed.
+ * If arg is NULL, it goes through all the motions,
+ * but without actually sending a command to the drive.
+ */
+static int do_drive_cmd(int dev, char *args)
+{
+ unsigned long flags;
+ unsigned int major = MAJOR(dev);
+ struct request rq, *cur_rq;
+ struct blk_dev_struct *bdev;
+ struct semaphore sem = MUTEX_LOCKED;
+
+ /* build up a special request, and add it to the queue */
+ rq.buffer = args;
+ rq.cmd = IDE_DRIVE_CMD;
+ rq.errors = 0;
+ rq.sector = 0;
+ rq.nr_sectors = 0;
+ rq.current_nr_sectors = 0;
+ rq.sem = &sem;
+ rq.bh = NULL;
+ rq.bhtail = NULL;
+ rq.next = NULL;
+ rq.dev = dev;
+ bdev = &blk_dev[major];
+
+ save_flags(flags);
+ cli();
+ cur_rq = bdev->current_request;
+ if (cur_rq == NULL) { /* empty request list? */
+ bdev->current_request = &rq; /* service ours immediately */
+ bdev->request_fn();
+ } else {
+ while (cur_rq->next != NULL) /* find end of request list */
+ cur_rq = cur_rq->next;
+ cur_rq->next = &rq; /* add rq to the end */
+ }
+
+ down(&sem); /* wait for it to be serviced */
+ restore_flags(flags);
+ return rq.errors ? -EIO : 0; /* return -EIO if errors */
+}
+#endif /* IDE_DRIVE_CMD */
+
+static int write_fs_long (unsigned long useraddr, long value)
+{
+ int err;
+
+ if (NULL == (long *)useraddr)
+ return -EINVAL;
+ if ((err = verify_area(VERIFY_WRITE, (long *)useraddr, sizeof(long))))
+ return err;
+ put_fs_long((unsigned)value, (long *) useraddr);
+ return 0;
+}
+
+static int ide_ioctl (struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct hd_geometry *loc = (struct hd_geometry *) arg;
+ int err;
+ ide_dev_t *dev;
+ unsigned long flags;
+
+ if (!inode || !inode->i_rdev)
+ return -EINVAL;
+ if ((dev = get_info_ptr(inode->i_rdev)) == NULL)
+ return -ENODEV;
+ switch (cmd) {
+ case HDIO_GETGEO:
+ if (!loc || dev->type != disk) return -EINVAL;
+ err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
+ if (err) return err;
+ put_fs_byte(dev->bios_head,
+ (char *) &loc->heads);
+ put_fs_byte(dev->bios_sect,
+ (char *) &loc->sectors);
+ put_fs_word(dev->bios_cyl,
+ (short *) &loc->cylinders);
+ put_fs_long((unsigned)ide_hd[DEV_HWIF][MINOR(inode->i_rdev)].start_sect,
+ (long *) &loc->start);
+ return 0;
+
+ case BLKFLSBUF:
+ if(!suser()) return -EACCES;
+ fsync_dev(inode->i_rdev);
+ invalidate_buffers(inode->i_rdev);
+ return 0;
+
+ case BLKRASET:
+ if(!suser()) return -EACCES;
+ if(arg > 0xff) return -EINVAL;
+ read_ahead[MAJOR(inode->i_rdev)] = arg;
+ return 0;
+
+ case BLKRAGET:
+ return write_fs_long(arg, read_ahead[MAJOR(inode->i_rdev)]);
+
+ case BLKGETSIZE: /* Return device size */
+ return write_fs_long(arg, ide_hd[DEV_HWIF][MINOR(inode->i_rdev)].nr_sects);
+ case BLKRRPART: /* Re-read partition tables */
+ return revalidate_disk(inode->i_rdev);
+
+ case HDIO_GET_KEEPSETTINGS:
+ return write_fs_long(arg, dev->keep_settings);
+
+ case HDIO_GET_UNMASKINTR:
+ return write_fs_long(arg, dev->unmask);
+
+ case HDIO_GET_CHIPSET:
+ return write_fs_long(arg, dev->chipset);
+
+ case HDIO_GET_MULTCOUNT:
+ return write_fs_long(arg, dev->mult_count);
+
+ case HDIO_GET_IDENTITY:
+ if (!arg || (MINOR(inode->i_rdev) & PARTN_MASK))
+ return -EINVAL;
+ if (dev->id == NULL)
+ return -ENOMSG;
+ err = verify_area(VERIFY_WRITE, (char *)arg, sizeof(*dev->id));
+ if (err) return err;
+ memcpy_tofs((char *)arg, (char *)dev->id, sizeof(*dev->id));
+ return 0;
+
+ case HDIO_SET_KEEPSETTINGS:
+ case HDIO_SET_UNMASKINTR:
+ if (!suser()) return -EACCES;
+ if ((arg > 1) || (MINOR(inode->i_rdev) & PARTN_MASK))
+ return -EINVAL;
+ save_flags(flags);
+ cli();
+ if (cmd == HDIO_SET_KEEPSETTINGS)
+ dev->keep_settings = arg;
+ else
+ dev->unmask = arg;
+ restore_flags(flags);
+ return 0;
+
+ case HDIO_SET_CHIPSET:
+ if (!suser()) return -EACCES;
+ if ((arg > 3) || (MINOR(inode->i_rdev) & PARTN_MASK))
+ return -EINVAL;
+ save_flags(flags);
+ cli();
+ dev->chipset = arg;
+ dev->vlb_sync = (arg & 2) >> 1;
+ dev->vlb_32bit = (arg & 1);
+ restore_flags(flags);
+ return 0;
+
+ case HDIO_SET_MULTCOUNT:
+ if (!suser()) return -EACCES;
+ if (MINOR(inode->i_rdev) & PARTN_MASK)
+ return -EINVAL;
+ if ((dev->id != NULL) && (arg > dev->id->max_multsect))
+ return -EINVAL;
+ save_flags(flags);
+ cli();
+ if (dev->special.b.set_multmode) {
+ restore_flags(flags);
+ return -EBUSY;
+ }
+ dev->mult_req = arg;
+ dev->special.b.set_multmode = 1;
+ restore_flags(flags);
+#ifdef IDE_DRIVE_CMD
+ do_drive_cmd (inode->i_rdev, NULL);
+ return (dev->mult_count == arg) ? 0 : -EIO;
+#else
+ return 0;
+#endif /* IDE_DRIVE_CMD */
+
+#ifdef IDE_DRIVE_CMD
+ case HDIO_DRIVE_CMD:
+ {
+ unsigned long args;
+
+ if (NULL == (long *) arg)
+ err = do_drive_cmd(inode->i_rdev,NULL);
+ else {
+ if (!(err = verify_area(VERIFY_WRITE,(long *)arg,sizeof(long))))
+ {
+ args = get_fs_long((long *)arg);
+ err = do_drive_cmd(inode->i_rdev,(char *)&args);
+ put_fs_long(args,(long *)arg);
+ }
+ }
+ return err;
+ }
+#endif /* IDE_DRIVE_CMD */
+
+ RO_IOCTLS(inode->i_rdev, arg);
+
+ default:
+#ifdef CONFIG_BLK_DEV_IDECD
+ if (dev->type == cdrom)
+ return ide_cdrom_ioctl(dev, inode, file, cmd, arg);
+#endif /* CONFIG_BLK_DEV_IDECD */
+ return -EPERM;
+ }
+}
+
+#ifdef CONFIG_BLK_DEV_IDECD
+static int ide_check_media_change (dev_t full_dev)
+{
+ ide_dev_t *dev;
+
+ if ((dev = get_info_ptr(full_dev)) == NULL)
+ return -ENODEV;
+ if (dev->type != cdrom)
+ return 0;
+ return cdrom_check_media_change (dev);
+}
+#endif /* CONFIG_BLK_DEV_IDECD */
+
+
+static void fixstring (byte *s, int bytecount, int byteswap)
+{
+ byte *p, *end = &s[bytecount &= ~1]; /* bytecount must be even */
+
+ if (byteswap) {
+ /* convert from big-endian to little-endian */
+ for (p = end ; p != s;) {
+ unsigned short *pp = (unsigned short *) (p -= 2);
+ *pp = (*pp >> 8) | (*pp << 8);
+ }
+ }
+ p = s;
+
+ /* strip leading blanks */
+ while (s != end && *s == ' ')
+ ++s;
+
+ /* compress internal blanks and strip trailing blanks */
+ while (s != end && *s) {
+ if (*s++ != ' ' || (s != end && *s && *s != ' '))
+ *p++ = *(s-1);
+ }
+
+ /* wipe out trailing garbage */
+ while (p != end)
+ *p++ = '\0';
+}
+
+static int lba_capacity_is_ok (struct hd_driveid *id)
+/*
+ * Returns: 1 if lba_capacity looks sensible
+ * 0 otherwise
+ */
+{
+ unsigned long lba_sects = id->lba_capacity;
+ unsigned long chs_sects = id->cyls * id->heads * id->sectors;
+ unsigned long _10_percent = chs_sects / 10;
+
+ /* perform a rough sanity check on lba_sects: within 10% is "okay" */
+ if ((lba_sects - chs_sects) < _10_percent)
+ return 1; /* lba_capacity is good */
+
+ /* some drives have the word order reversed */
+ lba_sects = (lba_sects << 16) | (lba_sects >> 16);
+ if ((lba_sects - chs_sects) < _10_percent) {
+ id->lba_capacity = lba_sects; /* fix it */
+ return 1; /* lba_capacity is (now) good */
+ }
+ return 0; /* lba_capacity value is bad */
+}
+
+static unsigned long probe_mem_start; /* used by drive/irq probing routines */
+
+static void do_identify (ide_dev_t *dev, byte cmd)
+{
+ int bswap;
+ struct hd_driveid *id;
+ unsigned long capacity, check;
+
+ id = dev->id = (struct hd_driveid *) probe_mem_start; /* kmalloc() */
+ probe_mem_start += 512;
+ input_ide_data(dev, id, SECTOR_WORDS); /* read 512 bytes of id info */
+ sti();
+
+ /*
+ * EATA SCSI controllers do a hardware ATA emulation: ignore them
+ */
+ if ((id->model[0] == 'P' && id->model[1] == 'M')
+ || (id->model[0] == 'S' && id->model[1] == 'K')) {
+ printk("%s: EATA SCSI HBA %.10s\n", dev->name, id->model);
+ dev->present = 0;
+ return;
+ }
+
+ /*
+ * WIN_IDENTIFY returns little-endian info,
+ * WIN_PIDENTIFY *usually* returns little-endian info.
+ */
+ bswap = 1;
+ if (cmd == WIN_PIDENTIFY) {
+ if ((id->model[0] == 'N' && id->model[1] == 'E')
+ || (id->model[0] == 'F' && id->model[1] == 'X'))
+ bswap = 0; /* NEC and *some* Mitsumi units */
+ } /* Vertos drives may still be weird */
+ fixstring (id->model, sizeof(id->model), bswap);
+ fixstring (id->fw_rev, sizeof(id->fw_rev), bswap);
+ fixstring (id->serial_no, sizeof(id->serial_no), bswap);
+
+ /*
+ * Check for an ATAPI device
+ */
+ if (cmd == WIN_PIDENTIFY) {
+#ifdef CONFIG_BLK_DEV_IDECD
+ byte type = (id->config >> 8) & 0x0f;
+#endif /* CONFIG_BLK_DEV_IDECD */
+ printk("%s: %s, ATAPI,", dev->name, id->model);
+#ifdef CONFIG_BLK_DEV_IDECD
+ if (type == 0 || type == 5)
+ printk(" CDROM drive\n");
+ else
+ printk(" UNKNOWN device\n");
+ dev->type = cdrom; /* until we do it "correctly" above */
+ dev->present = 1;
+#else
+ printk(unsupported);
+#endif /* CONFIG_BLK_DEV_IDECD */
+ return;
+ }
+
+ dev->type = disk;
+ /* Extract geometry if we did not already have one for the drive */
+ if (!dev->present) {
+ dev->present = 1;
+ dev->cyl = dev->bios_cyl = id->cyls;
+ dev->head = dev->bios_head = id->heads;
+ dev->sect = dev->bios_sect = id->sectors;
+ }
+ /* Handle logical geometry translation by the drive */
+ if ((id->field_valid & 1) && id->cur_cyls && id->cur_heads
+ && (id->cur_heads <= 16) && id->cur_sectors)
+ {
+ /*
+ * Extract the physical drive geometry for our use.
+ * Note that we purposely do *not* update the bios info.
+ * This way, programs that use it (like fdisk) will
+ * still have the same logical view as the BIOS does,
+ * which keeps the partition table from being screwed.
+ *
+ * An exception to this is the cylinder count,
+ * which we reexamine later on to correct for 1024 limitations.
+ */
+ dev->cyl = id->cur_cyls;
+ dev->head = id->cur_heads;
+ dev->sect = id->cur_sectors;
+ capacity = dev->cyl * dev->head * dev->sect;
+
+ /* check for word-swapped "capacity" field in id information */
+ check = (id->cur_capacity0 << 16) | id->cur_capacity1;
+ if (check == capacity) /* was it swapped? */
+ *((int *)&id->cur_capacity0) = capacity; /* fix it */
+ }
+ /* Use physical geometry if what we have still makes no sense */
+ if ((!dev->head || dev->head > 16) && id->heads && id->heads <= 16) {
+ dev->cyl = id->cyls;
+ dev->head = id->heads;
+ dev->sect = id->sectors;
+ }
+ /* Correct the number of cyls if the bios value is too small */
+ if (dev->sect == dev->bios_sect && dev->head == dev->bios_head) {
+ if (dev->cyl > dev->bios_cyl)
+ dev->bios_cyl = dev->cyl;
+ }
+ /* Determine capacity, and use LBA if the drive properly supports it */
+ if ((id->capability & 2) && lba_capacity_is_ok(id)) {
+ dev->select.b.lba = 1;
+ capacity = id->lba_capacity;
+ } else {
+ capacity = dev->cyl * dev->head * dev->sect;
+ }
+
+ ide_capacity[DEV_HWIF][dev->select.b.drive] = capacity;
+ printk ("%s: %.40s, %ldMB w/%dKB Cache, %sCHS=%d/%d/%d",
+ dev->name, id->model, capacity/2048L, id->buf_size/2,
+ dev->select.b.lba ? "LBA, " : "",
+ dev->bios_cyl, dev->bios_head, dev->bios_sect);
+
+ dev->mult_count = 0;
+ if (id->max_multsect) {
+ dev->mult_req = INITIAL_MULT_COUNT;
+ if (dev->mult_req > id->max_multsect)
+ dev->mult_req = id->max_multsect;
+ if (dev->mult_req || ((id->multsect_valid & 1) && id->multsect))
+ dev->special.b.set_multmode = 1;
+ printk(", MaxMult=%d", id->max_multsect);
+ }
+ printk("\n");
+}
+
+static void delay_10ms (void)
+{
+ unsigned long timer = jiffies + 2;
+ while (timer > jiffies);
+}
+
+
+static int try_to_identify (ide_dev_t *dev, byte cmd)
+/*
+ * Returns: 0 device was identified
+ * 1 device timed-out (no response to identify request)
+ * 2 device aborted the command (refused to identify itself)
+ */
+{
+ int hd_status, rc;
+ unsigned long timeout;
+#if PROBE_FOR_IRQS
+ int irqs = 0;
+ static byte irq_probed[2] = {0,0};
+#endif /* PROBE_FOR_IRQS */
+
+ OUT_BYTE(dev->ctl|2,HD_CMD); /* disable device irq */
+#if PROBE_FOR_IRQS
+ if (!irq_probed[DEV_HWIF]) { /* already probed for IRQ? */
+ irqs = probe_irq_on(); /* start monitoring irqs */
+ OUT_BYTE(dev->ctl,HD_CMD); /* enable device irq */
+ }
+#endif /* PROBE_FOR_IRQS */
+ delay_10ms(); /* take a deep breath */
+ if ((IN_BYTE(HD_ALTSTATUS,DEV_HWIF) ^ IN_BYTE(HD_STATUS,DEV_HWIF)) & ~INDEX_STAT) {
+ hd_status = HD_STATUS; /* an ancient Seagate drive */
+ printk("%s: probing with STATUS instead of ALTSTATUS\n", dev->name);
+ } else
+ hd_status = HD_ALTSTATUS; /* use non-intrusive polling */
+ OUT_BYTE(cmd,HD_COMMAND); /* ask drive for ID */
+ timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2;
+ timeout += jiffies;
+ do {
+ if (jiffies > timeout) {
+#if PROBE_FOR_IRQS
+ if (!irq_probed[DEV_HWIF])
+ (void) probe_irq_off(irqs);
+#endif /* PROBE_FOR_IRQS */
+ return 1; /* drive timed-out */
+ }
+ delay_10ms(); /* give drive a breather */
+ } while (IN_BYTE(hd_status,DEV_HWIF) & BUSY_STAT);
+ delay_10ms(); /* wait for IRQ and DRQ_STAT */
+ if (OK_STAT(GET_STAT(DEV_HWIF),DRQ_STAT,BAD_R_STAT)) {
+ cli(); /* some systems need this */
+ do_identify(dev, cmd); /* drive returned ID */
+ rc = 0; /* success */
+ } else
+ rc = 2; /* drive refused ID */
+#if PROBE_FOR_IRQS
+ if (!irq_probed[DEV_HWIF]) {
+ irqs = probe_irq_off(irqs); /* get irq number */
+ if (irqs > 0) {
+ irq_probed[DEV_HWIF] = 1;
+ ide_irq[DEV_HWIF] = irqs;
+ } else /* Mmmm.. multiple IRQs */
+ printk("%s: IRQ probe failed (%d)\n", dev->name, irqs);
+ }
+#endif /* PROBE_FOR_IRQS */
+ return rc;
+}
+
+/*
+ * This routine has the difficult job of finding a drive if it exists,
+ * without getting hung up if it doesn't exist, without trampling on
+ * ethernet cards, and without leaving any IRQs dangling to haunt us later.
+ *
+ * If a drive is "known" to exist (from CMOS or kernel parameters),
+ * but does not respond right away, the probe will "hang in there"
+ * for the maximum wait time (about 30 seconds), otherwise it will
+ * exit much more quickly.
+ */
+static int do_probe (ide_dev_t *dev, byte cmd)
+/*
+ * Returns: 0 device was identified
+ * 1 device timed-out (no response to identify request)
+ * 2 device aborted the command (refused to identify itself)
+ * 3 bad status from device (possible for ATAPI drives)
+ * 4 probe was not attempted
+ */
+{
+ int rc;
+
+#ifdef CONFIG_BLK_DEV_IDECD
+ if (dev->present) { /* avoid waiting for inappropriate probes */
+ if ((dev->type == disk) ^ (cmd == WIN_IDENTIFY))
+ return 4;
+ }
+#endif /* CONFIG_BLK_DEV_IDECD */
+#if DEBUG
+ printk("probing for %s: present=%d, type=%s, probetype=%s\n",
+ dev->name, dev->present, dev->type ? "cdrom":"disk",
+ (cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI");
+#endif
+ OUT_BYTE(dev->select.all,HD_CURRENT); /* select target drive */
+ delay_10ms(); /* wait for BUSY_STAT */
+ if (IN_BYTE(HD_CURRENT,DEV_HWIF) != dev->select.all && !dev->present) {
+ OUT_BYTE(0xa0,HD_CURRENT); /* exit with drive0 selected */
+ return 3; /* no i/f present: avoid killing ethernet cards */
+ }
+
+ if (OK_STAT(GET_STAT(DEV_HWIF),READY_STAT,BUSY_STAT)
+ || dev->present || cmd == WIN_PIDENTIFY)
+ {
+ if ((rc = try_to_identify(dev, cmd))) /* send cmd and wait */
+ rc = try_to_identify(dev, cmd); /* failed: try again */
+ if (rc == 1)
+ printk("%s: no response (status = 0x%02x)\n",
+ dev->name, GET_STAT(DEV_HWIF));
+ OUT_BYTE(dev->ctl|2,HD_CMD); /* disable device irq */
+ delay_10ms();
+ (void) GET_STAT(DEV_HWIF); /* ensure drive irq is clear */
+ } else {
+ rc = 3; /* not present or maybe ATAPI */
+ }
+ if (dev->select.b.drive == 1) {
+ OUT_BYTE(0xa0,HD_CURRENT); /* exit with drive0 selected */
+ delay_10ms();
+ OUT_BYTE(dev->ctl|2,HD_CMD); /* disable device irq */
+ delay_10ms();
+ (void) GET_STAT(DEV_HWIF); /* ensure drive irq is clear */
+ }
+ return rc;
+}
+
+static byte probe_for_drive (ide_dev_t *dev)
+/*
+ * Returns: 0 no device was found
+ * 1 device was found (note: dev->present might still be 0)
+ */
+{
+ if (dev->dont_probe) /* skip probing? */
+ return dev->present;
+ if (do_probe(dev, WIN_IDENTIFY) >= 2) { /* if !(success || timed-out) */
+#ifdef CONFIG_BLK_DEV_IDECD
+ (void) do_probe(dev, WIN_PIDENTIFY); /* look for ATAPI device */
+#endif /* CONFIG_BLK_DEV_IDECD */
+ }
+ if (!dev->present)
+ return 0; /* drive not found */
+ if (dev->id == NULL) { /* identification failed? */
+ if (dev->type == disk) {
+ printk ("%s: non-IDE device, CHS=%d/%d/%d\n",
+ dev->name, dev->cyl, dev->head, dev->sect);
+ }
+#ifdef CONFIG_BLK_DEV_IDECD
+ else if (dev->type == cdrom) {
+ printk("%s: ATAPI cdrom (?)\n", dev->name);
+ }
+#endif /* CONFIG_BLK_DEV_IDECD */
+ else {
+ dev->present = 0; /* nuke it */
+ return 1; /* drive was found */
+ }
+ }
+#ifdef CONFIG_BLK_DEV_IDECD
+ if (dev->type == cdrom)
+ cdrom_setup(dev);
+#endif /* CONFIG_BLK_DEV_IDECD */
+ if (dev->type == disk && !dev->select.b.lba) {
+ if (!dev->head || dev->head > 16) {
+ printk("%s: cannot handle disk with %d physical heads\n",
+ dev->name, dev->head);
+ dev->present = 0;
+ }
+ }
+ return 1; /* drive was found */
+}
+
+static void probe_for_drives (byte hwif)
+{
+ ide_dev_t *devs = &ide_dev[HWIF][0]; /* for convenience */
+
+ if (check_region(IDE_PORT(HD_DATA,HWIF),8)
+ || check_region(IDE_PORT(HD_CMD,HWIF),1))
+ {
+ if (devs[0].present || devs[1].present)
+ printk("ERROR: ");
+ printk("%s: port(s) already in use\n", ide_name[HWIF]);
+ devs[0].present = 0;
+ devs[1].present = 0;
+ } else {
+ unsigned long flags;
+ save_flags(flags);
+ sti(); /* needed for jiffies and irq probing */
+
+ /* second drive should only exist if first drive was found */
+ if (probe_for_drive(&devs[0]) || devs[1].present)
+ (void) probe_for_drive(&devs[1]);
+#if PROBE_FOR_IRQS
+ (void) probe_irq_off(probe_irq_on()); /* clear dangling irqs */
+#endif /* PROBE_FOR_IRQS */
+ if (devs[0].present || devs[1].present) {
+ request_region(IDE_PORT(HD_DATA,HWIF),8,ide_name[HWIF]);
+ request_region(IDE_PORT(HD_CMD,HWIF),1,ide_name[HWIF]);
+ }
+ restore_flags(flags);
+ }
+}
+
+static int next_drive = 0; /* used by the ide_setup() routines below */
+
+void ide_setup(char *str, int *ints)
+{
+ ide_dev_t *dev;
+ const char *p[] = {"cyls","heads","sects","wpcom","irq"};
+ int i, hwif, drive = next_drive++;
+#ifdef CONFIG_BLK_DEV_HD
+ extern void hd_setup(char *, int *);
+
+ if (drive < 2) {
+ hd_setup (str, ints);
+ return;
+ }
+#endif /* CONFIG_BLK_DEV_HD */
+ hwif = (drive > 1);
+ printk("%s: ", ide_name[hwif]);
+ if (drive > 3) {
+ printk("too many drives defined\n");
+ return;
+ }
+ drive = drive & 1;
+ printk("%s: ", ide_devname[hwif][drive]);
+ if (!SUPPORT_TWO_INTERFACES && hwif != HWIF) {
+ printk(unsupported);
+ return;
+ }
+ dev = &ide_dev[hwif][drive];
+ if (dev->present)
+ printk("(redefined) ");
+ if (ints[0] == 0) {
+#if SUPPORT_DTC2278
+ if (!strcmp(str,"dtc2278")) {
+ printk("%s\n",str);
+ probe_dtc2278 = 1; /* try to init DTC-2278 at boot */
+ return;
+ }
+#endif /* SUPPORT_DTC2278 */
+#if SUPPORT_SERIALIZE
+ if (!strcmp(str,"serialize") || !strcmp(str,"cmd")) {
+ printk("%s\n",str);
+ single_threaded = 1; /* serialize all drive access */
+ return;
+ }
+#endif /* SUPPORT_SERIALIZE */
+ if (!strcmp(str,"noprobe")) {
+ printk("%s\n",str);
+ dev->dont_probe = 1; /* don't probe for this drive */
+ return;
+ }
+#ifdef CONFIG_BLK_DEV_IDECD
+ if (!strcmp(str,"cdrom")) {
+ printk("cdrom\n");
+ dev->present = 1; /* force autoprobe to find it */
+ dev->type = cdrom;
+ return;
+ }
+#endif /* CONFIG_BLK_DEV_IDECD */
+ }
+ if (ints[0] < 3 || ints[0] > 5) {
+ printk("bad parms, expected: cyls,heads,sects[,wpcom[,irq]]\n");
+ } else {
+ for (i=0; i++ < ints[0];)
+ printk("%s=%d%c",p[i-1],ints[i],i<ints[0]?',':'\n');
+ dev->type = disk;
+ dev->cyl = dev->bios_cyl = ints[1];
+ dev->head = dev->bios_head = ints[2];
+ dev->ctl = (ints[2] > 8 ? 8 : 0);
+ dev->sect = dev->bios_sect = ints[3];
+ dev->wpcom = (ints[0] >= 4) ? ints[4] : 0;
+ if (ints[0] >= 5)
+ ide_irq[HWIF] = ints[5];
+ ide_capacity[HWIF][drive] = BIOS_SECTORS(dev);
+ dev->present = 1;
+ }
+}
+
+void hda_setup(char *str, int *ints)
+{
+ next_drive = 0;
+ ide_setup (str, ints);
+}
+
+void hdb_setup(char *str, int *ints)
+{
+ next_drive = 1;
+ ide_setup (str, ints);
+}
+
+void hdc_setup(char *str, int *ints)
+{
+ next_drive = 2;
+ ide_setup (str, ints);
+}
+
+void hdd_setup(char *str, int *ints)
+{
+ next_drive = 3;
+ ide_setup (str, ints);
+}
+
+#ifndef CONFIG_BLK_DEV_HD
+/*
+ * We query CMOS about hard disks : it could be that we have a SCSI/ESDI/etc
+ * controller that is BIOS compatible with ST-506, and thus showing up in our
+ * BIOS table, but not register compatible, and therefore not present in CMOS.
+ *
+ * Furthermore, we will assume that our ST-506 drives <if any> are the primary
+ * drives in the system -- the ones reflected as drive 1 or 2. The first
+ * drive is stored in the high nibble of CMOS byte 0x12, the second in the low
+ * nibble. This will be either a 4 bit drive type or 0xf indicating use byte
+ * 0x19 for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. A non-zero value
+ * means we have an AT controller hard disk for that drive.
+ */
+extern struct drive_info_struct drive_info;
+static void probe_cmos_for_drives (void)
+{
+ byte drive, cmos_disks, *BIOS = (byte *) &drive_info;
+
+ outb_p(0x12,0x70); /* specify CMOS address 0x12 */
+ cmos_disks = inb_p(0x71); /* read the data from 0x12 */
+ /* Extract drive geometry from CMOS+BIOS if not already setup */
+ for (drive = 0; drive < MAX_DRIVES; drive++) {
+ ide_dev_t *dev = &ide_dev[0][drive];
+ if ((cmos_disks & (0xf0 >> (drive*4))) && !dev->present) {
+ dev->cyl = dev->bios_cyl = *(unsigned short *)BIOS;
+ dev->head = dev->bios_head = * (BIOS+2);
+ dev->sect = dev->bios_sect = * (BIOS+14);
+ dev->wpcom = (*(unsigned short *)(BIOS+5))>>2;
+ dev->ctl = *(BIOS+8);
+ dev->wpcom = 0;
+ dev->type = disk;
+ dev->present = 1;
+ ide_capacity[0][drive] = BIOS_SECTORS(dev);
+ }
+ BIOS += 16;
+ }
+}
+#endif /* CONFIG_BLK_DEV_HD */
+
+static void init_ide_data (byte hwif)
+{
+ int drive;
+
+ for (drive = 0; drive < (MAX_DRIVES<<PARTN_BITS); drive++)
+ ide_blksizes[hwif][drive] = 1024;
+ blksize_size[ide_major[hwif]] = ide_blksizes[hwif];
+
+ /* Initialize non-geometry fields -- ide_setup() runs before we do */
+ for (drive = 0; drive < MAX_DRIVES; drive++) {
+ ide_dev_t *dev = &ide_dev[hwif][drive];
+ dev->select.all = (drive<<4)|0xa0;
+ dev->hwif = hwif;
+ dev->unmask = 0;
+ dev->busy = 0;
+ dev->mult_count = 0; /* set by do_identify() */
+ dev->mult_req = 0; /* set by do_identify() */
+ dev->usage = 0;
+ dev->vlb_32bit = 0;
+ dev->vlb_sync = 0;
+ dev->id = NULL;
+ dev->ctl = 0x08;
+ dev->wqueue = NULL;
+ dev->special.all = 0;
+ dev->special.b.recalibrate = 1;
+ dev->special.b.set_geometry = 1;
+ dev->keep_settings = 0;
+ ide_hd[hwif][drive<<PARTN_BITS].start_sect = 0;
+ dev->name = ide_devname[hwif][drive];
+ }
+}
+
+/*
+ * This is the harddisk IRQ description. The SA_INTERRUPT in sa_flags
+ * means we enter the IRQ-handler with interrupts disabled: this is bad for
+ * interrupt latency, but anything else has led to problems on some
+ * machines. We enable interrupts as much as we can safely do in most places.
+ */
+static byte setup_irq (byte hwif)
+{
+ static byte rc = 0;
+ unsigned long flags;
+ const char *msg = "", *primary_secondary[] = {"primary", "secondary"};
+ void (*handler)(int, struct pt_regs *) = HWIF ? &ide1_intr : &ide0_intr;
+
+#if SUPPORT_SHARING_IRQ
+ if (sharing_single_irq) {
+ if (HWIF != 0 && !rc) { /* IRQ already allocated? */
+ msg = " (shared with ide0)";
+ goto done;
+ }
+ handler = &ide_shared_intr;
+ }
+#if SUPPORT_SERIALIZE
+ else if (single_threaded) {
+ handler = &ide_seq_intr;
+ if (HWIF != 0)
+ msg = " (single-threaded with ide0)";
+ }
+#endif /* SUPPORT_SERIALIZE */
+#endif /* SUPPORT_SHARING_IRQ */
+ save_flags(flags);
+ cli();
+ if ((rc = request_irq(ide_irq[HWIF],handler,SA_INTERRUPT,ide_name[HWIF])))
+ msg = ": FAILED! unable to allocate IRQ";
+ restore_flags(flags);
+#if SUPPORT_SHARING_IRQ
+done:
+#endif /* SUPPORT_SHARING_IRQ */
+ printk("%s: %s interface on irq %d%s\n",
+ ide_name[HWIF], primary_secondary[HWIF], ide_irq[HWIF], msg);
+ return rc;
+}
+
+static void ide_geninit(byte hwif)
+{
+ static int drive;
+
+ for (drive = 0; drive < MAX_DRIVES; drive++) {
+ ide_dev_t *dev = &ide_dev[HWIF][drive];
+ if (dev->present) {
+ ide_hd[HWIF][drive<<PARTN_BITS].nr_sects = ide_capacity[HWIF][drive];
+ /* Skip partition check for cdroms. */
+ if (dev->type == cdrom)
+ ide_hd[HWIF][drive<<PARTN_BITS].start_sect = -1;
+ }
+ }
+}
+
+static void ide0_geninit(void)
+{
+ ide_geninit(0);
+}
+
+static void ide1_geninit(void)
+{
+ ide_geninit(1);
+}
+
+static struct file_operations ide_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ ide_ioctl, /* ioctl */
+ NULL, /* mmap */
+ ide_open, /* open */
+ ide_release, /* release */
+ block_fsync /* fsync */
+#ifdef CONFIG_BLK_DEV_IDECD
+ ,NULL, /* fasync */
+ ide_check_media_change, /* check_media_change */
+ NULL /* revalidate */
+#endif CONFIG_BLK_DEV_IDECD
+};
+
+
+#if SUPPORT_DTC2278
+/*
+ * From: andy@cercle.cts.com (Dyan Wile)
+ *
+ * Below is a patch for DTC-2278 - alike software-programmable controllers
+ * The code enables the secondary IDE controller and the PIO4 (3?) timings on
+ * the primary (EIDE). You may probably have to enable the 32-bit support to
+ * get the full speed. You better get the disk interrupts disabled ( hdparm -u0
+ * /dev/hd.. ) for the drives connected to the EIDE interface. (I get my
+ * filesystem corrupted with -u 1, but under heavy disk load only :-)
+ */
+
+static void sub22 (char b, char c)
+{
+ int i;
+
+ for(i = 0; i < 3; i++) {
+ __inb(0x3f6);
+ outb_p(b,0xb0);
+ __inb(0x3f6);
+ outb_p(c,0xb4);
+ __inb(0x3f6);
+ if(__inb(0xb4) == c) {
+ outb_p(7,0xb0);
+ __inb(0x3f6);
+ return; /* success */
+ }
+ }
+}
+
+static void try_to_init_dtc2278 (void)
+{
+/* This (presumably) enables PIO mode4 (3?) on the first interface */
+ cli();
+ sub22(1,0xc3);
+ sub22(0,0xa0);
+ sti();
+
+/* This enables the second interface */
+
+ outb_p(4,0xb0);
+ __inb(0x3f6);
+ outb_p(0x20,0xb4);
+ __inb(0x3f6);
+}
+#endif /* SUPPORT_DTC2278 */
+
+/*
+ * This is gets invoked once during initialization, to set *everything* up
+ */
+unsigned long ide_init (unsigned long mem_start, unsigned long mem_end)
+{
+ byte hwif;
+
+#if SUPPORT_DTC2278
+ if (probe_dtc2278)
+ try_to_init_dtc2278();
+#endif /* SUPPORT_DTC2278 */
+ /* single_threaded = 0; */ /* zero by default, override at boot */
+ for (hwif = 0; hwif < 2; hwif++) {
+ init_ide_data (hwif);
+ if (SUPPORT_TWO_INTERFACES || hwif == HWIF) {
+ if (hwif == 0)
+#ifdef CONFIG_BLK_DEV_HD
+ continue;
+#else
+ probe_cmos_for_drives ();
+#endif /* CONFIG_BLJ_DEV_HD */
+ probe_mem_start = (mem_start + 3uL) & ~3uL;
+ probe_for_drives (hwif);
+ mem_start = probe_mem_start;
+ }
+ }
+
+ /* At this point, all methods of drive detection have completed */
+ ide_gendisk[0].nr_real = ide_dev[0][0].present + ide_dev[0][1].present;
+ ide_gendisk[1].nr_real = ide_dev[1][0].present + ide_dev[1][1].present;
+ if (ide_gendisk[1].nr_real && (ide_irq[0] == ide_irq[1])) {
+ if (!ide_gendisk[0].nr_real) {
+ ide_irq[0] = 0; /* needed by ide_intr() */
+ } else {
+#if SUPPORT_SHARING_IRQ
+ sharing_single_irq = 1;
+ single_threaded = 1;
+#else /* SUPPORT_SHARING_IRQ */
+ printk("%s: ide irq-sharing%s", ide_name[1], unsupported);
+ return mem_start;
+#endif /* SUPPORT_SHARING_IRQ */
+ }
+ }
+#ifdef CONFIG_BLK_DEV_HD
+#if SUPPORT_SHARING_IRQ
+ if (ide_irq[1] == 14 || sharing_single_irq) {
+#else
+ if (ide_irq[1] == 14) {
+#endif /* SUPPORT_SHARING_IRQ */
+ printk("%s: irq-sharing not possible with old harddisk driver (hd.c)\n", ide_name[1]);
+ return mem_start;
+ }
+#endif /* CONFIG_BLK_DEV_HD */
+
+ for (hwif = 2; hwif-- > 0;) {
+ if (ide_gendisk[hwif].nr_real != 0 && !setup_irq(hwif)) {
+ const char *name = ide_name[HWIF];
+ unsigned int major = ide_major[HWIF];
+ if (register_blkdev(major, name, &ide_fops)) {
+ printk("%s: unable to get major number %d\n", name, major);
+ } else {
+ timer_table[ide_timer[HWIF]].fn
+ = HWIF ? ide1_timer_expiry : ide0_timer_expiry;
+#if SUPPORT_SHARING_IRQ
+ if (single_threaded)
+ blk_dev[major].request_fn = &do_shared_request;
+ else
+#endif /* SUPPORT_SHARING_IRQ */
+ blk_dev[major].request_fn =
+ HWIF ? &do_ide1_request : &do_ide0_request;
+ read_ahead[major] = 8; /* (4kB) */
+ ide_gendisk[HWIF].next = gendisk_head;
+ gendisk_head = &ide_gendisk[HWIF];
+ }
+ }
+ }
+ return mem_start;
+}
diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c
index 05fcf157d..53c1f97a0 100644
--- a/drivers/block/ll_rw_blk.c
+++ b/drivers/block/ll_rw_blk.c
@@ -15,6 +15,7 @@
#include <linux/string.h>
#include <linux/config.h>
#include <linux/locks.h>
+#include <linux/mm.h>
#include <asm/system.h>
#include <asm/io.h>
@@ -40,16 +41,29 @@ int read_ahead[MAX_BLKDEV] = {0, };
* next-request
*/
struct blk_dev_struct blk_dev[MAX_BLKDEV] = {
- { NULL, NULL }, /* no_dev */
- { NULL, NULL }, /* dev mem */
- { NULL, NULL }, /* dev fd */
- { NULL, NULL }, /* dev hd */
- { NULL, NULL }, /* dev ttyx */
- { NULL, NULL }, /* dev tty */
- { NULL, NULL }, /* dev lp */
- { NULL, NULL }, /* dev pipes */
- { NULL, NULL }, /* dev sd */
- { NULL, NULL } /* dev st */
+ { NULL, NULL }, /* 0 no_dev */
+ { NULL, NULL }, /* 1 dev mem */
+ { NULL, NULL }, /* 2 dev fd */
+ { NULL, NULL }, /* 3 dev ide0 or hd */
+ { NULL, NULL }, /* 4 dev ttyx */
+ { NULL, NULL }, /* 5 dev tty */
+ { NULL, NULL }, /* 6 dev lp */
+ { NULL, NULL }, /* 7 dev pipes */
+ { NULL, NULL }, /* 8 dev sd */
+ { NULL, NULL }, /* 9 dev st */
+ { NULL, NULL }, /* 10 */
+ { NULL, NULL }, /* 11 */
+ { NULL, NULL }, /* 12 */
+ { NULL, NULL }, /* 13 */
+ { NULL, NULL }, /* 14 */
+ { NULL, NULL }, /* 15 */
+ { NULL, NULL }, /* 16 */
+ { NULL, NULL }, /* 17 */
+ { NULL, NULL }, /* 18 */
+ { NULL, NULL }, /* 19 */
+ { NULL, NULL }, /* 20 */
+ { NULL, NULL }, /* 21 */
+ { NULL, NULL } /* 22 dev ide1 */
};
/*
@@ -72,6 +86,57 @@ int * blk_size[MAX_BLKDEV] = { NULL, NULL, };
int * blksize_size[MAX_BLKDEV] = { NULL, NULL, };
/*
+ * hardsect_size contains the size of the hardware sector of a device.
+ *
+ * hardsect_size[MAJOR][MINOR]
+ *
+ * if (!hardsect_size[MAJOR])
+ * then 512 bytes is assumed.
+ * else
+ * sector_size is hardsect_size[MAJOR][MINOR]
+ * This is currently set by some scsi device and read by the msdos fs driver
+ * This might be a some uses later.
+ */
+int * hardsect_size[MAX_BLKDEV] = { NULL, NULL, };
+
+/*
+ * "plug" the device if there are no outstanding requests: this will
+ * force the transfer to start only after we have put all the requests
+ * on the list.
+ */
+static void plug_device(struct blk_dev_struct * dev, struct request * plug)
+{
+ unsigned long flags;
+
+ plug->dev = -1;
+ plug->cmd = -1;
+ plug->next = NULL;
+ save_flags(flags);
+ cli();
+ if (!dev->current_request)
+ dev->current_request = plug;
+ restore_flags(flags);
+}
+
+/*
+ * remove the plug and let it rip..
+ */
+static void unplug_device(struct blk_dev_struct * dev)
+{
+ struct request * req;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ req = dev->current_request;
+ if (req && req->dev == -1 && req->cmd == -1) {
+ dev->current_request = req->next;
+ (dev->request_fn)();
+ }
+ restore_flags(flags);
+}
+
+/*
* look for a free request in the first N entries.
* NOTE: interrupts must be disabled on the way in, and will still
* be disabled on the way out.
@@ -104,18 +169,40 @@ static inline struct request * get_request(int n, int dev)
/*
* wait until a free request in the first N entries is available.
- * NOTE: interrupts must be disabled on the way in, and will still
- * be disabled on the way out.
*/
-static inline struct request * get_request_wait(int n, int dev)
+static struct request * __get_request_wait(int n, int dev)
{
register struct request *req;
+ struct wait_queue wait = { current, NULL };
- while ((req = get_request(n, dev)) == NULL)
- sleep_on(&wait_for_request);
+ add_wait_queue(&wait_for_request, &wait);
+ for (;;) {
+ unplug_device(MAJOR(dev)+blk_dev);
+ current->state = TASK_UNINTERRUPTIBLE;
+ cli();
+ req = get_request(n, dev);
+ sti();
+ if (req)
+ break;
+ schedule();
+ }
+ remove_wait_queue(&wait_for_request, &wait);
+ current->state = TASK_RUNNING;
return req;
}
+static inline struct request * get_request_wait(int n, int dev)
+{
+ register struct request *req;
+
+ cli();
+ req = get_request(n, dev);
+ sti();
+ if (req)
+ return req;
+ return __get_request_wait(n, dev);
+}
+
/* RO fail safe mechanism */
static long ro_bits[MAX_BLKDEV][8];
@@ -157,10 +244,11 @@ static void add_request(struct blk_dev_struct * dev, struct request * req)
kstat.dk_drive[disk_index]++;
break;
case HD_MAJOR:
- case XT_DISK_MAJOR: disk_index = (MINOR(req->dev) & 0x00C0) >> 6;
- if (disk_index < 4)
- kstat.dk_drive[disk_index]++;
+ case XT_DISK_MAJOR: disk_index = (MINOR(req->dev) & 0x0040) >> 6;
+ kstat.dk_drive[disk_index]++;
break;
+ case IDE1_MAJOR: disk_index = ((MINOR(req->dev) & 0x0040) >> 6) + 2;
+ kstat.dk_drive[disk_index]++;
default: break;
}
@@ -219,6 +307,10 @@ static void make_request(int major,int rw, struct buffer_head * bh)
bh->b_req = 0;
return;
}
+ /* Uhhuh.. Nasty dead-lock possible here.. */
+ if (bh->b_lock)
+ return;
+ /* Maybe the above fixes it, and maybe it doesn't boot. Life is interesting */
lock_buffer(bh);
if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) {
unlock_buffer(bh);
@@ -231,22 +323,25 @@ static void make_request(int major,int rw, struct buffer_head * bh)
*/
max_req = (rw == READ) ? NR_REQUEST : ((NR_REQUEST*2)/3);
-/* big loop: look for a free request. */
-
-repeat:
+/* look for a free request. */
cli();
-/* The scsi disk drivers completely remove the request from the queue when
- * they start processing an entry. For this reason it is safe to continue
- * to add links to the top entry for scsi devices.
+/* The scsi disk drivers and the IDE driver completely remove the request
+ * from the queue when they start processing an entry. For this reason
+ * it is safe to continue to add links to the top entry for those devices.
*/
- if ((major == HD_MAJOR
+ if (( major == IDE0_MAJOR /* same as HD_MAJOR */
+ || major == IDE1_MAJOR
|| major == FLOPPY_MAJOR
|| major == SCSI_DISK_MAJOR
|| major == SCSI_CDROM_MAJOR)
&& (req = blk_dev[major].current_request))
{
+#ifdef CONFIG_BLK_DEV_HD
if (major == HD_MAJOR || major == FLOPPY_MAJOR)
+#else
+ if (major == FLOPPY_MAJOR)
+#endif CONFIG_BLK_DEV_HD
req = req->next;
while (req) {
if (req->dev == bh->b_dev &&
@@ -286,22 +381,17 @@ repeat:
/* find an unused request. */
req = get_request(max_req, bh->b_dev);
+ sti();
-/* if no request available: if rw_ahead, forget it; otherwise try again. */
- if (! req) {
+/* if no request available: if rw_ahead, forget it; otherwise try again blocking.. */
+ if (!req) {
if (rw_ahead) {
- sti();
unlock_buffer(bh);
return;
}
- sleep_on(&wait_for_request);
- sti();
- goto repeat;
+ req = __get_request_wait(max_req, bh->b_dev);
}
-/* we found a request. */
- sti();
-
/* fill up the request-info, and add it to the queue */
req->cmd = rw;
req->errors = 0;
@@ -316,14 +406,15 @@ repeat:
add_request(major+blk_dev,req);
}
-void ll_rw_page(int rw, int dev, int page, char * buffer)
+void ll_rw_page(int rw, int dev, unsigned long page, char * buffer)
{
struct request * req;
unsigned int major = MAJOR(dev);
+ unsigned long sector = page * (PAGE_SIZE / 512);
struct semaphore sem = MUTEX_LOCKED;
if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
- printk("Trying to read nonexistent block-device %04x (%d)\n",dev,page*8);
+ printk("Trying to read nonexistent block-device %04x (%ld)\n",dev,sector);
return;
}
if (rw!=READ && rw!=WRITE)
@@ -332,15 +423,13 @@ void ll_rw_page(int rw, int dev, int page, char * buffer)
printk("Can't page to read-only device 0x%X\n",dev);
return;
}
- cli();
req = get_request_wait(NR_REQUEST, dev);
- sti();
/* fill up the request-info, and add it to the queue */
req->cmd = rw;
req->errors = 0;
- req->sector = page<<3;
- req->nr_sectors = 8;
- req->current_nr_sectors = 8;
+ req->sector = sector;
+ req->nr_sectors = PAGE_SIZE / 512;
+ req->current_nr_sectors = PAGE_SIZE / 512;
req->buffer = buffer;
req->sem = &sem;
req->bh = NULL;
@@ -357,7 +446,6 @@ void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
{
unsigned int major;
struct request plug;
- int plugged;
int correct_size;
struct blk_dev_struct * dev;
int i;
@@ -407,15 +495,8 @@ void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
from starting until we have shoved all of the blocks into the
queue, and then we let it rip. */
- plugged = 0;
- cli();
- if (!dev->current_request && nr > 1) {
- dev->current_request = &plug;
- plug.dev = -1;
- plug.next = NULL;
- plugged = 1;
- }
- sti();
+ if (nr > 1)
+ plug_device(dev, &plug);
for (i = 0; i < nr; i++) {
if (bh[i]) {
bh[i]->b_req = 1;
@@ -426,12 +507,7 @@ void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
kstat.pgpgout++;
}
}
- if (plugged) {
- cli();
- dev->current_request = plug.next;
- (dev->request_fn)();
- sti();
- }
+ unplug_device(dev);
return;
sorry:
@@ -468,9 +544,7 @@ void ll_rw_swap_file(int rw, int dev, unsigned int *b, int nb, char *buf)
for (i=0; i<nb; i++, buf += buffersize)
{
- cli();
req = get_request_wait(NR_REQUEST, dev);
- sti();
req->cmd = rw;
req->errors = 0;
req->sector = (b[i] * buffersize) >> 9;
@@ -498,15 +572,24 @@ long blk_dev_init(long mem_start, long mem_end)
#ifdef CONFIG_BLK_DEV_HD
mem_start = hd_init(mem_start,mem_end);
#endif
+#ifdef CONFIG_BLK_DEV_IDE
+ mem_start = ide_init(mem_start,mem_end);
+#endif
#ifdef CONFIG_BLK_DEV_XD
mem_start = xd_init(mem_start,mem_end);
#endif
#ifdef CONFIG_CDU31A
mem_start = cdu31a_init(mem_start,mem_end);
#endif
+#ifdef CONFIG_CDU535
+ mem_start = sony535_init(mem_start,mem_end);
+#endif
#ifdef CONFIG_MCD
mem_start = mcd_init(mem_start,mem_end);
#endif
+#ifdef CONFIG_AZTCD
+ mem_start = aztcd_init(mem_start,mem_end);
+#endif
#ifdef CONFIG_BLK_DEV_FD
floppy_init();
#else
diff --git a/drivers/block/mcd.c b/drivers/block/mcd.c
index 42dab9061..f20b4d0ab 100644
--- a/drivers/block/mcd.c
+++ b/drivers/block/mcd.c
@@ -43,6 +43,7 @@
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
+#include <linux/mm.h>
#include <linux/timer.h>
#include <linux/fs.h>
#include <linux/kernel.h>
@@ -247,9 +248,7 @@ mcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd,
struct cdrom_tocentry entry;
struct mcd_Toc *tocPtr;
struct cdrom_subchnl subchnl;
-#if 0
struct cdrom_volctrl volctrl;
-#endif
if (!ip)
return -EINVAL;
@@ -491,42 +490,30 @@ mcd_Play.end.min, mcd_Play.end.sec, mcd_Play.end.frame);
return 0;
case CDROMVOLCTRL: /* Volume control */
- /*
- * This is not working yet. Setting the volume by itself does
- * nothing. Following the 'set' by a 'play' results in zero
- * volume. Something to work on for the next release.
- */
-#if 0
st = verify_area(VERIFY_READ, (void *) arg, sizeof(volctrl));
if (st)
return st;
memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
-printk("VOL %d %d\n", volctrl.channel0 & 0xFF, volctrl.channel1 & 0xFF);
outb(MCMD_SET_VOLUME, MCDPORT(0));
outb(volctrl.channel0, MCDPORT(0));
- outb(0, MCDPORT(0));
+ outb(255, MCDPORT(0));
outb(volctrl.channel1, MCDPORT(0));
- outb(1, MCDPORT(0));
+ outb(255, MCDPORT(0));
i = getMcdStatus(MCD_STATUS_DELAY);
if (i < 0)
return -EIO;
{
- int a, b, c, d;
+ char a, b, c, d;
getValue(&a);
getValue(&b);
getValue(&c);
getValue(&d);
- printk("%02X %02X %02X %02X\n", a, b, c, d);
}
- outb(0xF8, MCDPORT(0));
- i = getMcdStatus(MCD_STATUS_DELAY);
- printk("F8 -> %02X\n", i & 0xFF);
-#endif
return 0;
case CDROMEJECT:
@@ -597,7 +584,7 @@ mcd_transfer(void)
*/
static void
-mcd_interrupt(int unused)
+mcd_interrupt(int irq, struct pt_regs * regs)
{
int st;
@@ -1168,7 +1155,7 @@ mcd_init(unsigned long mem_start, unsigned long mem_end)
printk("Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq);
return mem_start;
}
- snarf_region(mcd_port, 4);
+ request_region(mcd_port, 4,"mcd");
outb(MCMD_CONFIG_DRIVE, MCDPORT(0));
outb(0x02,MCDPORT(0));
diff --git a/drivers/block/ramdisk.c b/drivers/block/ramdisk.c
index bddf8a6f4..8d0d8552d 100644
--- a/drivers/block/ramdisk.c
+++ b/drivers/block/ramdisk.c
@@ -8,13 +8,17 @@
*/
-#include <linux/config.h>
#include <linux/sched.h>
#include <linux/minix_fs.h>
#include <linux/ext2_fs.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/string.h>
+#include <linux/mm.h>
+
+#ifdef __mips__
+#include <asm/bootinfo.h>
+#endif
#include <asm/system.h>
#include <asm/segment.h>
@@ -28,6 +32,7 @@ extern void wait_for_keypress(void);
char *rd_start;
int rd_length = 0;
static int rd_blocksizes[2] = {0, 0};
+extern char _end;
static void do_rd_request(void)
{
@@ -77,18 +82,30 @@ static struct file_operations rd_fops = {
long rd_init(long mem_start, int length)
{
int i;
+#if __i386__
char *cp;
+#endif
if (register_blkdev(MEM_MAJOR,"rd",&rd_fops)) {
printk("RAMDISK: Unable to get major %d.\n", MEM_MAJOR);
return 0;
}
blk_dev[MEM_MAJOR].request_fn = DEVICE_REQUEST;
+#if __i386__
rd_start = (char *) mem_start;
rd_length = length;
cp = rd_start;
for (i=0; i < length; i++)
*cp++ = '\0';
+#elif defined(__mips__)
+ rd_start = (char *) &_end;
+ rd_length = length;
+ /*
+ * Don't reserve memory - this has already been done
+ * in arch/mips/kernel/setup.c.
+ */
+ length = 0;
+#endif
for(i=0;i<2;i++) rd_blocksizes[i] = 1024;
blksize_size[MAJOR_NR] = rd_blocksizes;
@@ -125,6 +142,7 @@ static void do_load(void)
*/
for (tries = 0; tries < 1000; tries += 512) {
block = tries;
+#if defined (__i386__)
bh = breada(ROOT_DEV,block+1,BLOCK_SIZE, 0, PAGE_SIZE);
if (!bh) {
printk("RAMDISK: I/O error while looking for super block!\n");
@@ -135,7 +153,17 @@ static void do_load(void)
*((struct super_block *) &sb) =
*((struct super_block *) bh->b_data);
brelse(bh);
-
+#else /* !defined (__i386__) */
+ /*
+ * Linux/MIPS loads ramdisk images via the host machine's
+ * bootroms. This enables us not only to load the ramdisk
+ * from floppy - we might also use CDROM or via network
+ * from BOOTP/TFTP, DLC/RIPL boot servers.
+ * The same applies for Linux/68k machines.
+ */
+ *((struct super_block *) &sb) =
+ *((struct super_block *) rd_start + BLOCK_SIZE);
+#endif /* !defined (__i386__) */
/* Try Minix */
nblocks = -1;
@@ -167,6 +195,7 @@ static void do_load(void)
nblocks, rd_length >> BLOCK_SIZE_BITS);
return;
}
+#ifdef __i386__
printk("RAMDISK: Loading %d blocks into RAM disk", nblocks);
/* We found an image file system. Load it into core! */
@@ -189,6 +218,7 @@ static void do_load(void)
i++;
}
printk("\ndone\n");
+#endif /* __i386__ */
/* We loaded the file system image. Prepare for mounting it. */
ROOT_DEV = ((MEM_MAJOR << 8) | RAMDISK_MINOR);
@@ -209,8 +239,8 @@ void rd_load(void)
/* If no RAM disk specified, give up early. */
if (!rd_length)
return;
- printk("RAMDISK: %d bytes, starting at 0x%x\n",
- rd_length, (int) rd_start);
+ printk("RAMDISK: %d bytes, starting at 0x%p\n",
+ rd_length, rd_start);
/* If we are doing a diskette boot, we might have to pre-load it. */
if (MAJOR(ROOT_DEV) != FLOPPY_MAJOR)
diff --git a/drivers/block/sbpcd.c b/drivers/block/sbpcd.c
index a48dc6593..5f536e941 100644
--- a/drivers/block/sbpcd.c
+++ b/drivers/block/sbpcd.c
@@ -6,12 +6,12 @@
* Panasonic CI-101P.
* Also for the Longshine LCS-7260 drive.
* Also for the IBM "External ISA CD-Rom" drive.
+ * Also for the CreativeLabs CD200 drive (but I still need some
+ * detailed bug reports).
+ * Also for the TEAC CD-55A drive.
+ * Not for Funai or Sanyo drives.
*
- * NOTE: This is release 2.8.
- * 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.
- *
+ * NOTE: This is release 3.7.
*
* VERSION HISTORY
*
@@ -28,8 +28,8 @@
*
* 0.4 use MAJOR 25 definitely.
* Almost total re-design to support double-speed drives and
- * "naked" (no sound) interface cards.
- * Flow control should be exact now (tell me if not).
+ * "naked" (no sound) interface cards ("LaserMate" interface type).
+ * Flow control should be exact now.
* Don't occupy the SbPro IRQ line (not needed either); will
* live together with Hannu Savolainen's sndkit now.
* Speeded up data transfer to 150 kB/sec, with help from Kai
@@ -52,12 +52,12 @@
* Formal redesign to add DDI debugging.
* There are still flaws in IOCTL (workman with double speed drive).
*
- * 1.0 Added support for all drive ids (0...3, no longer only 0)
+ * 1.0 Added support for all drive IDs (0...3, no longer only 0)
* and up to 4 drives on one controller.
* Added "#define MANY_SESSION" for "old" multi session CDs.
*
* 1.1 Do SpinUp for new drives, too.
- * Revised for clean compile under "old" kernels (pl9).
+ * Revised for clean compile under "old" kernels (0.99pl9).
*
* 1.2 Found the "workman with double-speed drive" bug: use the driver's
* audio_state, not what the drive is reporting with ReadSubQ.
@@ -65,8 +65,8 @@
* 1.3 Minor cleanups.
* Refinements regarding Workman.
*
- * 1.4 Read XA disks (PhotoCDs) with "old" drives, too (but possibly only
- * the first session - I could not try a "multi-session" CD yet).
+ * 1.4 Read XA disks (PhotoCDs) with "old" drives, too (but only the first
+ * session - no chance to fully access a "multi-session" CD).
* This currently still is too slow (50 kB/sec) - but possibly
* the old drives won't do it faster.
* Implemented "door (un)lock" for new drives (still does not work
@@ -120,7 +120,7 @@
* Implemented "close tray" (done automatically during open).
*
* 2.4 Use different names for device registering.
- *
+ *
* 2.5 Added "#if EJECT" code (default: enabled) to automatically eject
* the tray during last call to "sbpcd_release".
* Added "#if JUKEBOX" code (default: disabled) to automatically eject
@@ -129,11 +129,11 @@
* SOUND_BASE (in sbpcd.h) accordingly (default: disabled).
*
* 2.6 Nothing new.
- *
+ *
* 2.7 Added CDROMEJECT_SW ioctl to set the "EJECT" behavior on the fly:
* 0 disables, 1 enables auto-ejecting. Useful to keep the tray in
* during shutdown.
- *
+ *
* 2.8 Added first support (still BETA, I need feedback or a drive) for
* the Longshine LCS-7260 drives. They appear as double-speed drives
* using the "old" command scheme, extended by tray control and door
@@ -145,28 +145,88 @@
* 16..20).
* Changed default of the "JUKEBOX" define. If you use this default,
* your tray will eject if you try to mount without a disk in. Next
- * mount command will insert the tray - so, just insert a disk. ;-)
+ * mount command will insert the tray - so, just fill in a disk. ;-)
+ *
+ * 2.9 Fulfilled the Longshine LCS-7260 support; with great help and
+ * experiments by Serge Robyns.
+ * First attempts to support the TEAC CD-55A drives; but still not
+ * usable yet.
+ * Implemented the CDROMMULTISESSION ioctl; this is an attempt to handle
+ * multi session CDs more "transparent" (redirection handling has to be
+ * done within the isofs routines, and only for the special purpose of
+ * obtaining the "right" volume descriptor; accesses to the raw device
+ * should not get redirected).
+ *
+ * 3.0 Just a "normal" increment, with some provisions to do it better. ;-)
+ * Introduced "#define READ_AUDIO" to specify the maximum number of
+ * audio frames to grab with one request. This defines a buffer size
+ * within kernel space; a value of 0 will reserve no such space and
+ * disable the CDROMREADAUDIO ioctl. A value of 75 enables the reading
+ * of a whole second with one command, but will use a buffer of more
+ * than 172 kB.
+ * Started CD200 support. Drive detection should work, but nothing
+ * more.
+ *
+ * 3.1 Working to support the CD200 and the Teac CD-55A drives.
+ * AT-BUS style device numbering no longer used: use SCSI style now.
+ * So, the first "found" device has MINOR 0, regardless of the
+ * jumpered drive ID. This implies modifications to the /dev/sbpcd*
+ * entries for some people, but will help the DAU (german TLA, english:
+ * "newbie", maybe ;-) to install his "first" system from a CD.
+ *
+ * 3.2 Still testing with CD200 and CD-55A drives.
+ *
+ * 3.3 Working with CD200 support.
+ *
+ * 3.4 Auto-probing stops if an address of 0 is seen (to be entered with
+ * the kernel command line).
+ * Made the driver "loadable". If used as a module, "audio copy" is
+ * disabled, and the internal read ahead data buffer has a reduced size
+ * of 4 kB; so, throughput may be reduced a little bit with slow CPUs.
+ *
+ * 3.5 Provisions to handle weird photoCDs which have an interrupted
+ * "formatting" immediately after the last frames of some files: simply
+ * never "read ahead" with MultiSession CDs. By this, CPU usage may be
+ * increased with those CDs, and there may be a loss in speed.
+ * Re-structured the messaging system.
+ * The "loadable" version no longer has a limited READ_AUDIO buffer
+ * size.
+ * Removed "MANY_SESSION" handling for "old" multi session CDs.
+ * Added "private" IOCTLs CDROMRESET and CDROMVOLREAD.
+ * Started again to support the TEAC CD-55A drives, now that I found
+ * the money for "my own" drive. ;-)
+ * The TEAC CD-55A support is fairly working now.
+ * I have measured that the drive "delivers" at 600 kB/sec (even with
+ * bigger requests than the drive's 64 kB buffer can satisfy), but
+ * the "real" rate does not exceed 520 kB/sec at the moment.
+ * Caused by the various changes to build in TEAC support, the timed
+ * loops are de-optimized at the moment (less throughput with CR-52x
+ * drives, and the TEAC will give speed only with SBP_BUFFER_FRAMES 64).
+ *
+ * 3.6 Fixed TEAC data read problems with SbPro interfaces.
+ * Initial size of the READ_AUDIO buffer is 0. Can get set to any size
+ * during runtime.
+ *
+ * 3.7 Introduced MAX_DRIVES for some poor interface cards (seen with TEAC
+ * drives) which allow only one drive (ID 0); this avoids repetitive
+ * detection under IDs 1..3.
+ * Elongated cmd_out_T response waiting; necessary for photo CDs with
+ * a lot of sessions.
+ * Bettered the sbpcd_open() behavior with TEAC drives.
*
* TODO
*
* disk change detection
* allow & synchronize multi-activity
* (data + audio + ioctl + disk change, multiple drives)
- * implement multi-controller-support with a single driver
* implement "read all subchannel data" (96 bytes per frame)
*
- *
* special thanks to Kai Makisara (kai.makisara@vtt.fi) for his fine
* elaborated speed-up experiments (and the fabulous results!), for
* the "push" towards load-free wait loops, and for the extensive mail
* thread which brought additional hints and bug fixes.
- *
*
- * Copyright (C) 1993, 1994 Eberhard Moenkeberg <emoenke@gwdg.de>
- * or <eberhard_moenkeberg@rollo.central.de>
- *
- * The FTP-home of this driver is
- * ftp.gwdg.de:/pub/linux/cdrom/drivers/sbpcd/.
+ * Copyright (C) 1993, 1994, 1995 Eberhard Moenkeberg <emoenke@gwdg.de>
*
* If you change this software, you should mail a .diff
* file with some description lines to emoenke@gwdg.de.
@@ -187,25 +247,38 @@
*
*/
-#define SBPCD_ISSUE 1 /* change to 2, 3, 4 for multiple interface boards */
+#ifndef SBPCD_ISSUE
+#define SBPCD_ISSUE 1
+#endif SBPCD_ISSUE
#include <linux/config.h>
-#include <linux/errno.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#ifndef CONFIG_MODVERSIONS
+char kernel_version[]=UTS_RELEASE;
+#endif
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif MODULE
+
+#include <linux/errno.h>
#include <linux/sched.h>
-/* #undef DS */
+#include <linux/mm.h>
#include <linux/timer.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/cdrom.h>
#include <linux/ioport.h>
#include <linux/major.h>
-#include <linux/sbpcd.h>
#include <linux/string.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <stdarg.h>
+#include <linux/sbpcd.h>
#if !(SBPCD_ISSUE-1)
#define MAJOR_NR MATSUSHITA_CDROM_MAJOR
@@ -222,37 +295,8 @@
#include "blk.h"
-#define VERSION "2.8 Eberhard Moenkeberg <emoenke@gwdg.de>"
-
-#define SBPCD_DEBUG
-
-#ifndef CONFIG_ISO9660_FS
-#error "SBPCD: \"make config\" again. File system iso9660 is necessary."
-#endif
-
-/*
- * This may come back some day..
- */
-#define DDIOCSDBG 0x9000
+#define VERSION "v3.7 Eberhard Moenkeberg <emoenke@gwdg.de>"
-/*
- * still testing around...
- */
-#define JUKEBOX 1 /* tray control: eject tray 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
-#define WORKMAN 1 /* some testing stuff to make it better */
-#define CDMKE /* makes timing independent of processor speed */
-
-#undef XA_TEST1
-#define XA_TEST2
-
-#define TEST_UPC 0
-#define SPEA_TEST 0
-#define PRINTK_BUG 0
-#define TEST_STI 0
/*==========================================================================*/
/*
* provisions for more than 1 driver issues
@@ -275,11 +319,6 @@
#define SBPCD_INIT(a,b) sbpcd4_init(a,b)
#endif
/*==========================================================================*/
-#if MANY_SESSION
-#undef LONG_TIMING
-#define LONG_TIMING 1
-#endif
-/*==========================================================================*/
#if SBPCD_DIS_IRQ
#define SBPCD_CLI cli()
#define SBPCD_STI sti()
@@ -298,45 +337,58 @@
* The possibly conflicting ethernet card addresses get NOT probed
* by default - to minimize the hang possibilities.
*
- * The SB Pro addresses get "mirrored" at 0x6xx - to avoid a type error,
- * the 0x2xx-addresses must get checked before 0x6xx.
+ * The SB Pro addresses get "mirrored" at 0x6xx and some more locations - to
+ * avoid a type error, the 0x2xx-addresses must get checked before 0x6xx.
*
* send mail to emoenke@gwdg.de if your interface card is not FULLY
* represented here.
*/
-static int autoprobe[] =
-{
- CDROM_PORT, SBPRO, /* probe with user's setup first */
- 0x230, 1, /* Soundblaster Pro and 16 (default) */
- 0x300, 0, /* CI-101P (default), WDH-7001C (default),
- Galaxy (default), Reveal (one default) */
- 0x250, 1, /* OmniCD default, Soundblaster Pro and 16 */
- 0x260, 1, /* OmniCD */
- 0x320, 0, /* Lasermate, CI-101P, WDH-7001C, Galaxy, Reveal (other default) */
- 0x340, 0, /* Lasermate, CI-101P */
- 0x360, 0, /* Lasermate, CI-101P */
- 0x270, 1, /* Soundblaster 16 */
- 0x670, 0, /* "sound card #9" */
- 0x690, 0, /* "sound card #9" */
- 0x330, 2, /* SPEA Media FX (default) */
- 0x320, 2, /* SPEA Media FX */
- 0x340, 2, /* SPEA Media FX */
- 0x350, 2, /* SPEA Media FX */
-/* due to incomplete address decoding of the SbPro card, these must be last */
- 0x630, 0, /* "sound card #9" (default) */
- 0x650, 0, /* "sound card #9" */
-#if 0
-/* some "hazardous" locations (ethernet cards) */
- 0x330, 0, /* Lasermate, CI-101P, WDH-7001C */
- 0x350, 0, /* Lasermate, CI-101P */
- 0x370, 0, /* Lasermate, CI-101P */
- 0x290, 1, /* Soundblaster 16 */
- 0x310, 0, /* Lasermate, CI-101P, WDH-7001C */
-#endif
+#if !(SBPCD_ISSUE-1)
+static int sbpcd[] =
+{
+ CDROM_PORT, SBPRO, /* probe with user's setup first */
+#if DISTRIBUTION
+ 0x230, 1, /* Soundblaster Pro and 16 (default) */
+ 0x300, 0, /* CI-101P (default), WDH-7001C (default),
+ Galaxy (default), Reveal (one default) */
+ 0x250, 1, /* OmniCD default, Soundblaster Pro and 16 */
+ 0x260, 1, /* OmniCD */
+ 0x320, 0, /* Lasermate, CI-101P, WDH-7001C, Galaxy, Reveal (other default),
+ Longshine LCS-6853 (default) */
+ 0x338, 0, /* Reveal Sound Wave 32 card model #SC600 */
+ 0x340, 0, /* Mozart sound card (default), Lasermate, CI-101P */
+ 0x360, 0, /* Lasermate, CI-101P */
+ 0x270, 1, /* Soundblaster 16 */
+ 0x670, 0, /* "sound card #9" */
+ 0x690, 0, /* "sound card #9" */
+ 0x330, 2, /* SPEA Media FX (default) */
+ 0x320, 2, /* SPEA Media FX */
+ 0x340, 2, /* SPEA Media FX */
+ 0x634, 0, /* some newer sound cards */
+ 0x638, 0, /* some newer sound cards */
+ 0x230, 1, /* some newer sound cards */
+ /* due to incomplete address decoding of the SbPro card, these must be last */
+ 0x630, 0, /* "sound card #9" (default) */
+ 0x650, 0, /* "sound card #9" */
+#ifdef MODULE
+ /*
+ * some "hazardous" locations (no harm with the loadable version)
+ * (will stop the bus if a NE2000 ethernet card resides at offset -0x10)
+ */
+ 0x330, 0, /* Lasermate, CI-101P, WDH-7001C */
+ 0x350, 0, /* Lasermate, CI-101P */
+ 0x350, 2, /* SPEA Media FX */
+ 0x370, 0, /* Lasermate, CI-101P */
+ 0x290, 1, /* Soundblaster 16 */
+ 0x310, 0, /* Lasermate, CI-101P, WDH-7001C */
+#endif MODULE
+#endif DISTRIBUTION
};
+#else
+static int sbpcd[] = {CDROM_PORT, SBPRO}; /* probe with user's setup only */
+#endif
-#define NUM_AUTOPROBE (sizeof(autoprobe) / sizeof(int))
-
+#define NUM_PROBE (sizeof(sbpcd) / sizeof(int))
/*==========================================================================*/
/*
@@ -355,15 +407,24 @@ extern unsigned long sbpcd4_init(unsigned long, unsigned long);
#endif
/*==========================================================================*/
+
+#define INLINE inline
+
/*==========================================================================*/
/*
* the forward references:
*/
+static void sbp_sleep(u_int);
+static void mark_timeout_delay(u_long);
+static void mark_timeout_data(u_long);
+#if 0
+static void mark_timeout_audio(u_long);
+#endif
static void sbp_read_cmd(void);
static int sbp_data(void);
static int cmd_out(void);
static int DiskInfo(void);
-static int check_media_change(dev_t);
+static int sbpcd_chk_disk_change(dev_t);
/*==========================================================================*/
@@ -379,11 +440,11 @@ static int check_media_change(dev_t);
* (1<<DBG_TOC) tell TocEntry values
* (1<<DBG_IOC) ioctl trace
* (1<<DBG_STA) "ResponseStatus" trace
- * (1<<DBG_ERR) "xx_ReadError" trace
+ * (1<<DBG_ERR) "cc_ReadError" trace
* (1<<DBG_CMD) "cmd_out" trace
* (1<<DBG_WRN) give explanation before auto-probing
* (1<<DBG_MUL) multi session code test
- * (1<<DBG_ID) "drive_id != 0" test code
+ * (1<<DBG_IDX) "drive_id != 0" test code
* (1<<DBG_IOX) some special information
* (1<<DBG_DID) drive ID test
* (1<<DBG_RES) drive reset info
@@ -391,32 +452,31 @@ static int check_media_change(dev_t);
* (1<<DBG_IOS) ioctl trace: "subchannel"
* (1<<DBG_IO2) ioctl trace: general
* (1<<DBG_UPC) show UPC info
- * (1<<DBG_XA) XA mode debugging
+ * (1<<DBG_XA1) XA mode debugging
* (1<<DBG_LCK) door (un)lock info
- * (1<<DBG_SQ) dump SubQ frame
+ * (1<<DBG_SQ1) dump SubQ frame
* (1<<DBG_AUD) "read audio" debugging
* (1<<DBG_SEQ) Sequoia interface configuration trace
+ * (1<<DBG_LCS) Longshine LCS-7260 debugging trace
+ * (1<<DBG_CD2) MKE CD200 debugging trace
+ * (1<<DBG_TEA) TEAC CD-55A debugging trace
+ * (1<<DBG_TE2) TEAC CD-55A debugging trace, 2nd level
* (1<<DBG_000) unnecessary information
*/
-#if 1
-static int sbpcd_debug = (1<<DBG_INF) | (1<<DBG_WRN);
-#else
-#if SPEA_TEST
-static int sbpcd_debug = (1<<DBG_INF) |
- (1<<DBG_INI) |
- (1<<DBG_ID) |
- (1<<DBG_SEQ);
+#if DISTRIBUTION
+static int sbpcd_debug = (1<<DBG_INF);
#else
-static int sbpcd_debug = (1<<DBG_INF) |
- (1<<DBG_TOC) |
- (1<<DBG_MUL) |
- (1<<DBG_UPC);
-#endif
-#endif
+static int sbpcd_debug = ((1<<DBG_INF) |
+ (1<<DBG_TOC) |
+ (1<<DBG_MUL) |
+ (1<<DBG_UPC));
+#endif DISTRIBUTION
+
static int sbpcd_ioaddr = CDROM_PORT; /* default I/O base address */
static int sbpro_type = SBPRO;
+static unsigned char setup_done = 0;
static int CDo_command, CDo_reset;
-static int CDo_sel_d_i, CDo_enable;
+static int CDo_sel_i_d, CDo_enable;
static int CDi_info, CDi_status, CDi_data;
static int MIXER_addr, MIXER_data;
static struct cdrom_msf msf;
@@ -426,10 +486,18 @@ static struct cdrom_tocentry tocentry;
static struct cdrom_subchnl SC;
static struct cdrom_volctrl volctrl;
static struct cdrom_read_audio read_audio;
+static struct cdrom_multisession ms_info;
+
+static unsigned char msgnum=0;
+static char msgbuf[80];
+
static char *str_sb = "SoundBlaster";
+static char *str_sb_l = "soundblaster";
static char *str_lm = "LaserMate";
static char *str_sp = "SPEA";
+static char *str_sp_l = "spea";
char *type;
+
#if !(SBPCD_ISSUE-1)
static char *major_name="sbpcd";
#endif
@@ -450,26 +518,33 @@ static struct wait_queue *sbp_waitq = NULL;
#endif FUTURE
/*==========================================================================*/
-
-#define SBP_BUFFER_FRAMES 4 /* driver's own read_ahead, data mode */
-#define SBP_BUFFER_AUDIO_FRAMES 4 /* driver's own read_ahead, read audio mode */
-
+#define SBP_BUFFER_FRAMES 8 /* driver's own read_ahead, data mode */
/*==========================================================================*/
-static u_char drive_family[]="CR-5"; /* Panasonic CR-56x */
-static u_char lcs_family[]="LCS-"; /* Longshine LCS-7260 */
-static u_char drive_vendor[]="MATSHITA"; /* Matsushita CR-52x */
+static u_char family0[]="MATSHITA"; /* MKE CR-52x */
+static u_char family1[]="CR-56"; /* MKE CR-56x */
+static u_char family2[]="CD200"; /* MKE CD200 */
+static u_char familyL[]="LCS-7260"; /* Longshine LCS-7260 */
+static u_char familyT[]="CD-55"; /* TEAC CD-55A */
+static u_int recursion=0; /* internal testing only */
+static u_int fatal_err=0; /* internal testing only */
static u_int response_count=0;
static u_int flags_cmd_out;
static u_char cmd_type=0;
-static u_char drvcmd[7];
+static u_char drvcmd[10];
static u_char infobuf[20];
static u_char xa_head_buf[CD_XA_HEAD];
static u_char xa_tail_buf[CD_XA_TAIL];
-static u_char busy_data=0, busy_audio=0; /* true semaphores would be safer */
-static u_char timed_out=0;
+static volatile u_char busy_data=0;
+static volatile u_char busy_audio=0; /* true semaphores would be safer */
+static u_long timeout;
+static volatile u_char timed_out_delay=0;
+static volatile u_char timed_out_data=0;
+#if 0
+static volatile u_char timed_out_audio=0;
+#endif
static u_int datarate= 1000000;
static u_int maxtim16=16000000;
static u_int maxtim04= 4000000;
@@ -480,15 +555,15 @@ static u_int maxtim_data= 9000;
#else
static u_int maxtim_data= 3000;
#endif LONG_TIMING
-
+#if DISTRIBUTION
+static int n_retries=3;
+#else
+static int n_retries=1;
+#endif
/*==========================================================================*/
static int ndrives=0;
-static u_char drv_pattern[4]={ 0x80, 0x80, 0x80, 0x80 }; /* auto speed */
-/* /X:... drv_pattern[0] |= (sax_n1|sax_n2); */
-/* /A:... for (i=0;i<4;i++) drv_pattern[i] |= sax_a; */
-/* /N:... ndrives=i-'0'; */
-
+static u_char drv_pattern[NR_SBPCD]={speed_auto,speed_auto,speed_auto,speed_auto};
static int sbpcd_blocksizes[NR_SBPCD] = {0, };
/*==========================================================================*/
@@ -498,1622 +573,2859 @@ static int sbpcd_blocksizes[NR_SBPCD] = {0, };
static int d=0; /* DriveStruct index: drive number */
static struct {
- char drv_minor; /* minor number or -1 */
-
- char drive_model[4];
- char firmware_version[4];
- char f_eject; /* auto-eject flag: 0 or 1 */
- u_char *sbp_buf; /* Pointer to internal data buffer,
- space allocated during sbpcd_init() */
- int sbp_first_frame; /* First frame in buffer */
- int sbp_last_frame; /* Last frame in buffer */
- int sbp_read_frames; /* Number of frames being read to buffer */
- int sbp_current; /* Frame being currently read */
-
- u_char mode; /* read_mode: READ_M1, READ_M2, READ_SC, READ_AU */
- u_char *aud_buf; /* Pointer to audio data buffer,
- space allocated during sbpcd_init() */
- u_char drv_type;
- u_char drv_options;
- u_char status_byte;
- u_char diskstate_flags;
- u_char sense_byte;
-
- u_char CD_changed;
- u_char open_count;
- u_char error_byte;
-
- u_char f_multisession;
- u_int lba_multi;
- u_int last_redirect;
-
- u_char audio_state;
- u_int pos_audio_start;
- u_int pos_audio_end;
- char vol_chan0;
- u_char vol_ctrl0;
- char vol_chan1;
- u_char vol_ctrl1;
-#if 000
- char vol_chan2;
- u_char vol_ctrl2;
- char vol_chan3;
- u_char vol_ctrl3;
+ char drv_id; /* "jumpered" drive ID or -1 */
+ char drv_sel; /* drive select lines bits */
+
+ char drive_model[9];
+ u_char firmware_version[4];
+ char f_eject; /* auto-eject flag: 0 or 1 */
+ u_char *sbp_buf; /* Pointer to internal data buffer,
+ space allocated during sbpcd_init() */
+ u_int sbp_bufsiz; /* size of sbp_buf (# of frames) */
+ int sbp_first_frame; /* First frame in buffer */
+ int sbp_last_frame; /* Last frame in buffer */
+ int sbp_read_frames; /* Number of frames being read to buffer */
+ int sbp_current; /* Frame being currently read */
+
+ u_char mode; /* read_mode: READ_M1, READ_M2, READ_SC, READ_AU */
+ u_char *aud_buf; /* Pointer to audio data buffer,
+ space allocated during sbpcd_init() */
+ u_int sbp_audsiz; /* size of aud_buf (# of raw frames) */
+ u_char drv_type;
+ u_char drv_options;
+ int status_bits;
+ u_char diskstate_flags;
+ u_char sense_byte;
+
+ u_char CD_changed;
+ char open_count;
+ u_char error_byte;
+
+ u_char f_multisession;
+ u_int lba_multi;
+ int first_session;
+ int last_session;
+
+ u_char audio_state;
+ u_int pos_audio_start;
+ u_int pos_audio_end;
+ char vol_chan0;
+ u_char vol_ctrl0;
+ char vol_chan1;
+ u_char vol_ctrl1;
+#if 000 /* no supported drive has it */
+ char vol_chan2;
+ u_char vol_ctrl2;
+ char vol_chan3;
+ u_char vol_ctrl3;
#endif 000
-
- u_char SubQ_ctl_adr;
- u_char SubQ_trk;
- u_char SubQ_pnt_idx;
- u_int SubQ_run_tot;
- u_int SubQ_run_trk;
- u_char SubQ_whatisthis;
-
- u_char UPC_ctl_adr;
- u_char UPC_buf[7];
-
- int CDsize_blk;
- int frame_size;
- int CDsize_frm;
-
- u_char xa_byte; /* 0x20: XA capabilities */
- u_char n_first_track; /* binary */
- u_char n_last_track; /* binary (not bcd), 0x01...0x63 */
- u_int size_msf; /* time of whole CD, position of LeadOut track */
- u_int size_blk;
-
- u_char TocEnt_nixbyte; /* em */
- u_char TocEnt_ctl_adr;
- u_char TocEnt_number;
- u_char TocEnt_format; /* em */
- u_int TocEnt_address;
- u_char ored_ctl_adr; /* to detect if CDROM contains data tracks */
-
- struct {
- u_char nixbyte; /* em */
- u_char ctl_adr; /* 0x4x: data, 0x0x: audio */
- u_char number;
- u_char format; /* em */ /* 0x00: lba, 0x01: msf */
- u_int address;
- } TocBuffer[MAX_TRACKS+1]; /* last entry faked */
-
- int in_SpinUp;
-
-} DriveStruct[NR_SBPCD];
+ u_char volume_control; /* TEAC on/off bits */
+
+ u_char SubQ_ctl_adr;
+ u_char SubQ_trk;
+ u_char SubQ_pnt_idx;
+ u_int SubQ_run_tot;
+ u_int SubQ_run_trk;
+ u_char SubQ_whatisthis;
+
+ u_char UPC_ctl_adr;
+ u_char UPC_buf[7];
+
+ int CDsize_blk;
+ int frame_size;
+ int CDsize_frm;
+
+ u_char xa_byte; /* 0x20: XA capabilities */
+ u_char n_first_track; /* binary */
+ u_char n_last_track; /* binary (not bcd), 0x01...0x63 */
+ u_int size_msf; /* time of whole CD, position of LeadOut track */
+ u_int size_blk;
+
+ u_char TocEnt_nixbyte; /* em */
+ u_char TocEnt_ctl_adr;
+ u_char TocEnt_number;
+ u_char TocEnt_format; /* em */
+ u_int TocEnt_address;
+ u_char ored_ctl_adr; /* to detect if CDROM contains data tracks */
+
+ struct {
+ u_char nixbyte; /* em */
+ u_char ctl_adr; /* 0x4x: data, 0x0x: audio */
+ u_char number;
+ u_char format; /* em */ /* 0x00: lba, 0x01: msf */
+ u_int address;
+ } TocBuffer[MAX_TRACKS+1]; /* last entry faked */
+
+ int in_SpinUp; /* CR-52x test flag */
+ int n_bytes; /* TEAC awaited response count */
+ u_char error_state, b3, b4; /* TEAC command error state */
+ u_char f_drv_error; /* TEAC command error flag */
+ u_char speed_byte;
+ int frmsiz;
+ u_char f_XA; /* 1: XA */
+ u_char type_byte; /* 0, 1, 3 */
+ u_char mode_xb_6;
+ u_char mode_yb_7;
+ u_char mode_xb_8;
+ u_char delay;
+
+} D_S[NR_SBPCD];
/*
* drive space ends here (needed separate for each unit)
*/
/*==========================================================================*/
+#if 0
+unsigned long cli_sti; /* for saving the processor flags */
+#endif
+/*==========================================================================*/
+static struct timer_list delay_timer = { NULL, NULL, 0, 0, mark_timeout_delay};
+static struct timer_list data_timer = { NULL, NULL, 0, 0, mark_timeout_data};
+#if 0
+static struct timer_list audio_timer = { NULL, NULL, 0, 0, mark_timeout_audio};
+#endif
/*==========================================================================*/
/*
- * DDI interface definitions
+ * DDI interface
*/
-#ifdef SBPCD_DEBUG
-# define DPRINTF(x) sbpcd_dprintf x
-
-static void sbpcd_dprintf(int level, char *fmt, ...)
+static void msg(int level, char *fmt, ...)
{
- char buff[256];
- va_list args;
- extern int vsprintf(char *buf, const char *fmt, va_list args);
-
- if (! (sbpcd_debug & (1 << level))) return;
-
- va_start(args, fmt);
- vsprintf(buff, fmt, args);
- va_end(args);
- printk(buff);
-#if PRINTK_BUG
- sti(); /* to avoid possible "printk" bug */
-#endif
+ char buf[256];
+ va_list args;
+ extern int vsprintf(char *, const char *, va_list);
+
+ if (!(sbpcd_debug&(1<<level))) return;
+
+ msgnum++;
+ if (msgnum>99) msgnum=0;
+ sprintf(buf, "%s-%d [%02d]: ", major_name, d, msgnum);
+ va_start(args, fmt);
+ vsprintf(&buf[15], fmt, args);
+ va_end(args);
+ printk(buf);
+ sbp_sleep(55); /* else messages get lost */
+ return;
}
-
-#else
-# define DPRINTF(x) /* nothing */
-
-#endif SBPCD_DEBUG
-
+/*==========================================================================*/
/*
- * maintain trace bit pattern
+ * DDI interface: runtime trace bit pattern maintenance
*/
static int sbpcd_dbg_ioctl(unsigned long arg, int level)
{
- int val;
-
- val = get_fs_long((int *) arg);
- switch(val)
- {
- case 0: /* OFF */
- sbpcd_debug = 0;
- break;
-
- default:
- if (val >= 128) sbpcd_debug &= ~(1 << (val - 128));
- else sbpcd_debug |= (1 << val);
- }
- return(0);
+ switch(arg)
+ {
+ case 0: /* OFF */
+ sbpcd_debug = DBG_INF;
+ break;
+
+ default:
+ if (arg>=128) sbpcd_debug &= ~(1<<(arg-128));
+ else sbpcd_debug |= (1<<arg);
+ }
+ return (arg);
}
-
-
/*==========================================================================*/
+static void mark_timeout_delay(u_long i)
+{
+ timed_out_delay=1;
+ msg(DBG_TIM,"delay timer expired.\n");
+}
+/*==========================================================================*/
+static void mark_timeout_data(u_long i)
+{
+ timed_out_data=1;
+ msg(DBG_TIM,"data timer expired.\n");
+}
+/*==========================================================================*/
+#if 0
+static void mark_timeout_audio(u_long i)
+{
+ timed_out_audio=1;
+ msg(DBG_TIM,"audio timer expired.\n");
+}
+#endif
/*==========================================================================*/
/*
- * Wait a little while (used for polling the drive). If in initialization,
- * setting a timeout doesn't work, so just loop for a while.
+ * Wait a little while (used for polling the drive).
*/
-static inline void sbp_sleep(u_int jifs)
+static void sbp_sleep(u_int time)
{
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + jifs;
- schedule();
+#ifndef MODULE
+ if (current == task[0])
+ {
+ del_timer(&delay_timer);
+ delay_timer.expires=time;
+ timed_out_delay=0;
+ add_timer(&delay_timer);
+ while (!timed_out_delay) ;
+ return;
+ }
+#endif MODULE
+ sti();
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + time;
+ schedule();
+ sti();
}
-
-/*==========================================================================*/
/*==========================================================================*/
/*
* convert logical_block_address to m-s-f_number (3 bytes only)
*/
-static void lba2msf(int lba, u_char *msf)
+static INLINE void lba2msf(int lba, u_char *msf)
{
- lba += CD_BLOCK_OFFSET;
- msf[0] = lba / (CD_SECS*CD_FRAMES);
- lba %= CD_SECS*CD_FRAMES;
- msf[1] = lba / CD_FRAMES;
- msf[2] = lba % CD_FRAMES;
+ lba += CD_BLOCK_OFFSET;
+ msf[0] = lba / (CD_SECS*CD_FRAMES);
+ lba %= CD_SECS*CD_FRAMES;
+ msf[1] = lba / CD_FRAMES;
+ msf[2] = lba % CD_FRAMES;
}
/*==========================================================================*/
/*==========================================================================*/
/*
* convert msf-bin to msf-bcd
*/
-static void bin2bcdx(u_char *p) /* must work only up to 75 or 99 */
+static INLINE void bin2bcdx(u_char *p) /* must work only up to 75 or 99 */
{
- *p=((*p/10)<<4)|(*p%10);
+ *p=((*p/10)<<4)|(*p%10);
}
/*==========================================================================*/
-static u_int blk2msf(u_int blk)
+static INLINE u_int blk2msf(u_int blk)
{
- MSF msf;
- u_int mm;
-
- msf.c[3] = 0;
- msf.c[2] = (blk + CD_BLOCK_OFFSET) / (CD_SECS * CD_FRAMES);
- mm = (blk + CD_BLOCK_OFFSET) % (CD_SECS * CD_FRAMES);
- msf.c[1] = mm / CD_FRAMES;
- msf.c[0] = mm % CD_FRAMES;
- return (msf.n);
+ MSF msf;
+ u_int mm;
+
+ msf.c[3] = 0;
+ msf.c[2] = (blk + CD_BLOCK_OFFSET) / (CD_SECS * CD_FRAMES);
+ mm = (blk + CD_BLOCK_OFFSET) % (CD_SECS * CD_FRAMES);
+ msf.c[1] = mm / CD_FRAMES;
+ msf.c[0] = mm % CD_FRAMES;
+ return (msf.n);
}
/*==========================================================================*/
-static u_int make16(u_char rh, u_char rl)
+static INLINE u_int make16(u_char rh, u_char rl)
{
- return ((rh<<8)|rl);
+ return ((rh<<8)|rl);
}
/*==========================================================================*/
-static u_int make32(u_int rh, u_int rl)
+static INLINE u_int make32(u_int rh, u_int rl)
{
- return ((rh<<16)|rl);
+ return ((rh<<16)|rl);
}
/*==========================================================================*/
-static u_char swap_nibbles(u_char i)
+static INLINE u_char swap_nibbles(u_char i)
{
- return ((i<<4)|(i>>4));
+ return ((i<<4)|(i>>4));
}
/*==========================================================================*/
-static u_char byt2bcd(u_char i)
+static INLINE u_char byt2bcd(u_char i)
{
- return (((i/10)<<4)+i%10);
+ return (((i/10)<<4)+i%10);
}
/*==========================================================================*/
-static u_char bcd2bin(u_char bcd)
+static INLINE u_char bcd2bin(u_char bcd)
{
- return ((bcd>>4)*10+(bcd&0x0F));
+ return ((bcd>>4)*10+(bcd&0x0F));
}
/*==========================================================================*/
-static int msf2blk(int msfx)
+static INLINE int msf2blk(int msfx)
{
- MSF msf;
- int i;
-
- msf.n=msfx;
- i=(msf.c[2] * CD_SECS + msf.c[1]) * CD_FRAMES + msf.c[0] - CD_BLOCK_OFFSET;
- if (i<0) return (0);
- return (i);
+ MSF msf;
+ int i;
+
+ msf.n=msfx;
+ i=(msf.c[2] * CD_SECS + msf.c[1]) * CD_FRAMES + msf.c[0] - CD_BLOCK_OFFSET;
+ if (i<0) return (0);
+ return (i);
}
/*==========================================================================*/
/*
* convert m-s-f_number (3 bytes only) to logical_block_address
*/
-static int msf2lba(u_char *msf)
+static INLINE int msf2lba(u_char *msf)
{
- int i;
-
- i=(msf[0] * CD_SECS + msf[1]) * CD_FRAMES + msf[2] - CD_BLOCK_OFFSET;
- if (i<0) return (0);
- return (i);
+ int i;
+
+ i=(msf[0] * CD_SECS + msf[1]) * CD_FRAMES + msf[2] - CD_BLOCK_OFFSET;
+ if (i<0) return (0);
+ return (i);
}
/*==========================================================================*/
-/* evaluate xx_ReadError code (still mysterious) */
+/* evaluate cc_ReadError code */
static int sta2err(int sta)
{
- if (sta<=2) return (sta);
- if (sta==0x05) return (-4); /* CRC error */
- if (sta==0x06) return (-6); /* seek error */
- if (sta==0x0d) return (-6); /* seek error */
- if (sta==0x0e) return (-3); /* unknown command */
- if (sta==0x14) return (-3); /* unknown command */
- if (sta==0x0c) return (-11); /* read fault */
- if (sta==0x0f) return (-11); /* read fault */
- if (sta==0x10) return (-11); /* read fault */
- if (sta>=0x16) return (-12); /* general failure */
- DriveStruct[d].CD_changed=0xFF;
- if (sta==0x11) return (-15); /* invalid disk change */
- return (-2); /* drive not ready */
+ if (famT_drive)
+ {
+ if (sta==0x00) return (0);
+ if (sta==0x01) return (-604); /* CRC error */
+ if (sta==0x02) return (-602); /* drive not ready */
+ if (sta==0x03) return (-607); /* unknown media */
+ if (sta==0x04) return (-612); /* general failure */
+ if (sta==0x05) return (0);
+ if (sta==0x06) return (-615); /* invalid disk change */
+ if (sta==0x0b) return (-612); /* general failure */
+ if (sta==0xff) return (-612); /* general failure */
+ return (0);
+ }
+ else
+ {
+ if (sta<=2) return (sta);
+ if (sta==0x05) return (-604); /* CRC error */
+ if (sta==0x06) return (-606); /* seek error */
+ if (sta==0x0d) return (-606); /* seek error */
+ if (sta==0x0e) return (-603); /* unknown command */
+ if (sta==0x14) return (-603); /* unknown command */
+ if (sta==0x0c) return (-611); /* read fault */
+ if (sta==0x0f) return (-611); /* read fault */
+ if (sta==0x10) return (-611); /* read fault */
+ if (sta>=0x16) return (-612); /* general failure */
+ D_S[d].CD_changed=0xFF;
+ if (sta==0x11) return (-615); /* invalid disk change (LCS: removed) */
+ if (famL_drive)
+ if (sta==0x12) return (-615); /* invalid disk change (inserted) */
+ return (-602); /* drive not ready */
+ }
}
/*==========================================================================*/
-static void clr_cmdbuf(void)
+static INLINE void clr_cmdbuf(void)
{
- int i;
-
- for (i=0;i<7;i++) drvcmd[i]=0;
- cmd_type=0;
+ int i;
+
+ for (i=0;i<10;i++) drvcmd[i]=0;
+ cmd_type=0;
}
/*==========================================================================*/
-static void mark_timeout(unsigned long i)
+static void flush_status(void)
{
- timed_out=1;
- DPRINTF((DBG_TIM,"SBPCD: timer expired.\n"));
+ int i;
+
+#ifdef MODULE
+ sbp_sleep(150);
+ for (i=maxtim_data;i!=0;i--) inb(CDi_status);
+#else
+ if (current == task[0])
+ for (i=maxtim02;i!=0;i--) inb(CDi_status);
+ else
+ {
+ sbp_sleep(150);
+ for (i=maxtim_data;i!=0;i--) inb(CDi_status);
+ }
+#endif MODULE
}
/*==========================================================================*/
-static struct timer_list delay_timer = { NULL, NULL, 0, 0, mark_timeout};
-#if 0
-static struct timer_list data_timer = { NULL, NULL, 0, 0, mark_timeout};
-static struct timer_list audio_timer = { NULL, NULL, 0, 0, mark_timeout};
-#endif
-/*==========================================================================*/
-static void flush_status(void)
+static int CDi_stat_loop(void)
{
-#ifdef CDMKE
- int i;
-
- if (current == task[0])
- for (i=maxtim02;i!=0;i--) inb(CDi_status);
- else
- {
- sbp_sleep(150);
- for (i=maxtim_data;i!=0;i--) inb(CDi_status);
- }
+ int i,j;
+
+#ifdef MODULE
+ for(timeout = jiffies + 1000, i=maxtim_data; timeout > jiffies; )
+ {
+ for ( ;i!=0;i--)
+ {
+ j=inb(CDi_status);
+ if (!(j&s_not_data_ready)) return (j);
+ if (!(j&s_not_result_ready)) return (j);
+ if (fam0L_drive) if (j&s_attention) return (j);
+ }
+ sbp_sleep(1);
+ i = 1;
+ }
#else
- timed_out=0;
-#if 0
- del_timer(&delay_timer);
-#endif
- delay_timer.expires = 150;
- add_timer(&delay_timer);
- do { }
- while (!timed_out);
-#if 0
- del_timer(&delay_timer);
-#endif 0
- inb(CDi_status);
-#endif CDMKE
+ if (current == task[0])
+ for(i=maxtim16;i!=0;i--)
+ {
+ j=inb(CDi_status);
+ if (!(j&s_not_data_ready)) return (j);
+ if (!(j&s_not_result_ready)) return (j);
+ if (fam0L_drive) if (j&s_attention) return (j);
+ }
+ else
+ for(timeout = jiffies + 1000, i=maxtim_data; timeout > jiffies; )
+ {
+ for ( ;i!=0;i--)
+ {
+ j=inb(CDi_status);
+ if (!(j&s_not_data_ready)) return (j);
+ if (!(j&s_not_result_ready)) return (j);
+ if (fam0L_drive) if (j&s_attention) return (j);
+ }
+ sbp_sleep(1);
+ i = 1;
+ }
+#endif MODULE
+ msg(DBG_LCS,"CDi_stat_loop failed\n");
+ return (-1);
}
/*==========================================================================*/
-static int CDi_stat_loop(void)
+#if 00000
+/*==========================================================================*/
+static int tst_DataReady(void)
+{
+ int i;
+
+ i=inb(CDi_status);
+ if (i&s_not_data_ready) return (0);
+ return (1);
+}
+/*==========================================================================*/
+static int tst_ResultReady(void)
{
- int i,j;
- u_long timeout;
-
- if (current == task[0])
- for(i=maxtim16;i!=0;i--)
- {
- j=inb(CDi_status);
- if (!(j&s_not_data_ready)) return (j);
- if (!(j&s_not_result_ready)) return (j);
- if (!new_drive) if (j&s_attention) return (j);
- }
- else
- for(timeout = jiffies + 1000, i=maxtim_data; timeout > jiffies; )
- {
- for ( ;i!=0;i--)
- {
- j=inb(CDi_status);
- if (!(j&s_not_data_ready)) return (j);
- if (!(j&s_not_result_ready)) return (j);
- if (!new_drive) if (j&s_attention) return (j);
- }
- sbp_sleep(1);
- i = 1;
- }
- return (-1);
+ int i;
+
+ i=inb(CDi_status);
+ if (i&s_not_result_ready) return (0);
+ return (1);
+}
+/*==========================================================================*/
+static int tst_Attention(void)
+{
+ int i;
+
+ i=inb(CDi_status);
+ if (i&s_attention) return (1);
+ return (0);
}
/*==========================================================================*/
+#endif 00000
+/*==========================================================================*/
static int ResponseInfo(void)
{
- int i,j, st=0;
- u_long timeout;
-
- DPRINTF((DBG_000,"SBPCD: ResponseInfo entered.\n"));
- if (current == task[0])
- for (i=0;i<response_count;i++)
- {
- for (j=maxtim_8;j!=0;j--)
- {
- st=inb(CDi_status);
- if (!(st&s_not_result_ready)) break;
- }
- if (j==0)
- {
- DPRINTF((DBG_SEQ,"SBPCD: ResponseInfo: not_result_ready (got %d of %d bytes).\n", i, response_count));
- return (-1);
- }
- infobuf[i]=inb(CDi_info);
- }
- else
- {
- for (i=0, timeout = jiffies + 100; i < response_count; i++)
- {
- for (j=maxtim_data; ; )
- {
- for ( ;j!=0;j-- )
- {
- st=inb(CDi_status);
- if (!(st&s_not_result_ready)) break;
- }
- if (j != 0 || timeout <= jiffies) break;
- sbp_sleep(0);
- j = 1;
- }
- if (timeout <= jiffies) return (-1);
- infobuf[i]=inb(CDi_info);
+ int i,j,st=0;
+ u_long timeout;
+
+#ifdef MODULE
+ if (0)
+#else
+ if (current == task[0])
+#endif MODULE
+ for (i=0;i<response_count;i++)
+ {
+ for (j=maxtim_8;j!=0;j--)
+ {
+ st=inb(CDi_status);
+ if (!(st&s_not_result_ready)) break;
+ }
+ if (j==0)
+ {
+ msg(DBG_SEQ,"ResponseInfo: not_result_ready (got %d of %d bytes).\n", i, response_count);
+ break;
+ }
+ infobuf[i]=inb(CDi_info);
+ }
+ else
+ {
+ for (i=0,timeout=jiffies+100;i<response_count;i++)
+ {
+ for (j=maxtim_data; ; )
+ {
+ for ( ;j!=0;j-- )
+ {
+ st=inb(CDi_status);
+ if (!(st&s_not_result_ready)) break;
+ }
+ if ((j!=0)||(timeout<=jiffies)) break;
+ sbp_sleep(1);
+ j = 1;
+ }
+ if (timeout<=jiffies) break;
+ infobuf[i]=inb(CDi_info);
+ }
}
- }
- DPRINTF((DBG_000,"SBPCD: ResponseInfo: done.\n"));
- return (0);
+#if 000
+ while (!(inb(CDi_status)&s_not_result_ready))
+ {
+ infobuf[i++]=inb(CDi_info);
+ }
+ j=i-response_count;
+ if (j>0) msg(DBG_INF,"ResponseInfo: got %d trailing bytes.\n",j);
+#endif 000
+ for (j=0;j<i;j++)
+ sprintf(&msgbuf[j*3]," %02X",infobuf[j]);
+ msgbuf[j*3]=0;
+ msg(DBG_CMD,"ResponseInfo:%s (%d,%d)\n",msgbuf,response_count,i);
+ j=response_count-i;
+ if (j>0) return (-j);
+ else return (i);
}
/*==========================================================================*/
-static int EvaluateStatus(int st)
+static void EvaluateStatus(int st)
{
- if (!new_drive)
- {
- DriveStruct[d].status_byte=0;
- if (st&p_caddin_old) DriveStruct[d].status_byte |= p_door_closed|p_caddy_in;
- if (st&p_spinning) DriveStruct[d].status_byte |= p_spinning;
- if (st&p_check) DriveStruct[d].status_byte |= p_check;
- if (st&p_busy_old) DriveStruct[d].status_byte |= p_busy_new;
- if (st&p_disk_ok) DriveStruct[d].status_byte |= p_disk_ok;
- }
- else { DriveStruct[d].status_byte=st;
- st=p_success_old; /* for new drives: fake "successful" bit of old drives */
- }
- return (st);
+ D_S[d].status_bits=0;
+ if (fam1_drive) D_S[d].status_bits=st|p_success;
+ else if (fam0_drive)
+ {
+ if (st&p_caddin_old) D_S[d].status_bits |= p_door_closed|p_caddy_in;
+ if (st&p_spinning) D_S[d].status_bits |= p_spinning;
+ if (st&p_check) D_S[d].status_bits |= p_check;
+ if (st&p_success_old) D_S[d].status_bits |= p_success;
+ if (st&p_busy_old) D_S[d].status_bits |= p_busy_new;
+ if (st&p_disk_ok) D_S[d].status_bits |= p_disk_ok;
+ }
+ else if (famL_drive)
+ {
+ D_S[d].status_bits |= p_success;
+ if (st&p_caddin_old) D_S[d].status_bits |= p_disk_ok|p_caddy_in;
+ if (st&p_spinning) D_S[d].status_bits |= p_spinning;
+ if (st&p_check) D_S[d].status_bits |= p_check;
+ if (st&p_busy_old) D_S[d].status_bits |= p_busy_new;
+ if (st&p_lcs_door_closed) D_S[d].status_bits |= p_door_closed;
+ if (st&p_lcs_door_locked) D_S[d].status_bits |= p_door_locked;
+ }
+ else if (fam2_drive)
+ {
+ D_S[d].status_bits |= p_success;
+ if (st&p2_check) D_S[d].status_bits |= p1_check;
+ if (st&p2_door_closed) D_S[d].status_bits |= p1_door_closed;
+ if (st&p2_disk_in) D_S[d].status_bits |= p1_disk_in;
+ if (st&p2_busy1) D_S[d].status_bits |= p1_busy;
+ if (st&p2_busy2) D_S[d].status_bits |= p1_busy;
+ if (st&p2_spinning) D_S[d].status_bits |= p1_spinning;
+ if (st&p2_door_locked) D_S[d].status_bits |= p1_door_locked;
+ if (st&p2_disk_ok) D_S[d].status_bits |= p1_disk_ok;
+ }
+ else if (famT_drive)
+ {
+ return; /* still needs to get coded */
+ D_S[d].status_bits |= p_success;
+ if (st&p2_check) D_S[d].status_bits |= p1_check;
+ if (st&p2_door_closed) D_S[d].status_bits |= p1_door_closed;
+ if (st&p2_disk_in) D_S[d].status_bits |= p1_disk_in;
+ if (st&p2_busy1) D_S[d].status_bits |= p1_busy;
+ if (st&p2_busy2) D_S[d].status_bits |= p1_busy;
+ if (st&p2_spinning) D_S[d].status_bits |= p1_spinning;
+ if (st&p2_door_locked) D_S[d].status_bits |= p1_door_locked;
+ if (st&p2_disk_ok) D_S[d].status_bits |= p1_disk_ok;
+ }
+ return;
+}
+/*==========================================================================*/
+static int get_state_T(void)
+{
+ int i;
+
+ static int cmd_out_T(void);
+
+ msg(DBG_TE2,"doing get_state_T...\n");
+ clr_cmdbuf();
+ D_S[d].n_bytes=1;
+ drvcmd[0]=CMDT_STATUS;
+ i=cmd_out_T();
+ if (i>=0) i=infobuf[0];
+ else
+ {
+ msg(DBG_TEA,"get_state_T error %d\n", i);
+ return (i);
+ }
+ if (i>=0)
+ /* 2: closed, disk in */
+ D_S[d].status_bits=p1_door_closed|p1_disk_in|p1_spinning|p1_disk_ok;
+ else if (D_S[d].error_state==6)
+ /* 3: closed, disk in, changed ("06 xx xx") */
+ D_S[d].status_bits=p1_door_closed|p1_disk_in;
+ else if ((D_S[d].error_state!=2)||(D_S[d].b3!=0x3A)||(D_S[d].b4==0x00))
+ {
+ /* 1: closed, no disk ("xx yy zz"or "02 3A 00") */
+ D_S[d].status_bits=p1_door_closed;
+ D_S[d].open_count=0;
+ }
+ else if (D_S[d].b4==0x01)
+ {
+ /* 0: open ("02 3A 01") */
+ D_S[d].status_bits=0;
+ D_S[d].open_count=0;
+ }
+ else
+ {
+ /* 1: closed, no disk ("02 3A xx") */
+ D_S[d].status_bits=p1_door_closed;
+ D_S[d].open_count=0;
+ }
+ msg(DBG_TE2,"get_state_T done (%02X)...\n", D_S[d].status_bits);
+ return (D_S[d].status_bits);
}
/*==========================================================================*/
static int ResponseStatus(void)
{
- int i,j;
- u_long timeout;
-
- DPRINTF((DBG_STA,"SBPCD: doing ResponseStatus...\n"));
-
- if (current == task[0])
- {
- if (flags_cmd_out & f_respo3) j = maxtim_8;
- else if (flags_cmd_out&f_respo2) j=maxtim16;
- else j=maxtim04;
- for (;j!=0;j--)
+ int i,j;
+ u_long timeout;
+
+ msg(DBG_STA,"doing ResponseStatus...\n");
+ if (famT_drive) return (get_state_T());
+#ifdef MODULE
+ if (0)
+#else
+ if (current == task[0])
+#endif MODULE
{
- i=inb(CDi_status);
- if (!(i&s_not_result_ready)) break;
+ if (flags_cmd_out & f_respo3) j = maxtim_8;
+ else if (flags_cmd_out&f_respo2) j=maxtim16;
+ else j=maxtim04;
+ for (;j!=0;j--)
+ {
+ i=inb(CDi_status);
+ if (!(i&s_not_result_ready)) break;
+ }
}
- }
- else
- {
- if (flags_cmd_out & f_respo3) timeout = jiffies;
- else if (flags_cmd_out & f_respo2) timeout = jiffies + 1600;
- else timeout = jiffies + 400;
- j=maxtim_8;
- do
- {
- for ( ;j!=0;j--)
- {
- i=inb(CDi_status);
- if (!(i&s_not_result_ready)) break;
- }
- if (j != 0 || timeout <= jiffies) break;
- sbp_sleep(0);
- j = 1;
- }
- while (1);
- }
- if (j==0)
- { if ((flags_cmd_out & f_respo3) == 0)
- DPRINTF((DBG_STA,"SBPCD: ResponseStatus: timeout.\n"));
- EvaluateStatus(0);
- return (-1);
- }
- i=inb(CDi_info);
- i=EvaluateStatus(i);
- return (i);
+ else
+ {
+ if (flags_cmd_out & f_respo3) timeout = jiffies;
+ else if (flags_cmd_out & f_respo2) timeout = jiffies + 1600;
+ else timeout = jiffies + 400;
+ j=maxtim_8;
+ do
+ {
+ for ( ;j!=0;j--)
+ {
+ i=inb(CDi_status);
+ if (!(i&s_not_result_ready)) break;
+ }
+ if ((j!=0)||(timeout<jiffies)) break;
+ sbp_sleep(1);
+ j = 1;
+ }
+ while (1);
+ }
+ if (j==0)
+ {
+ if ((flags_cmd_out & f_respo3) == 0)
+ msg(DBG_STA,"ResponseStatus: timeout.\n");
+ D_S[d].status_bits=0;
+ return (-401);
+ }
+ i=inb(CDi_info);
+ msg(DBG_STA,"ResponseStatus: response %02X.\n", i);
+ EvaluateStatus(i);
+#if 0
+ if (fam0_drive)
+#endif
+ msg(DBG_STA,"status_bits=%02X, i=%02X\n",D_S[d].status_bits,i);
+#if 1
+ return (D_S[d].status_bits);
+#else
+ return (i);
+#endif 0
}
/*==========================================================================*/
-static void xx_ReadStatus(void)
+static void cc_ReadStatus(void)
{
- int i;
-
- DPRINTF((DBG_STA,"SBPCD: giving xx_ReadStatus command\n"));
-
- if (!new_drive) OUT(CDo_command,0x81);
- else
- {
- SBPCD_CLI;
- OUT(CDo_command,0x05);
- for (i=0;i<6;i++) OUT(CDo_command,0);
- SBPCD_STI;
- }
+ int i;
+
+ msg(DBG_STA,"giving cc_ReadStatus command\n");
+ if (famT_drive) return;
+ SBPCD_CLI;
+ if (fam0L_drive) OUT(CDo_command,CMD0_STATUS);
+ else if (fam1_drive) OUT(CDo_command,CMD1_STATUS);
+ else if (fam2_drive) OUT(CDo_command,CMD2_STATUS);
+ if (!fam0L_drive) for (i=0;i<6;i++) OUT(CDo_command,0);
+ SBPCD_STI;
}
/*==========================================================================*/
-static int xx_ReadError(void)
+static int cc_ReadError(void)
{
- int i;
+ int i;
- clr_cmdbuf();
- DPRINTF((DBG_ERR,"SBPCD: giving xx_ReadError command.\n"));
- if (new_drive)
- {
- drvcmd[0]=0x82;
- response_count=8;
- flags_cmd_out=f_putcmd|f_ResponseStatus;
- }
- else
- {
- drvcmd[0]=0x82;
- response_count=6;
- flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus;
- }
- i=cmd_out();
- DriveStruct[d].error_byte=0;
- DPRINTF((DBG_ERR,"SBPCD: xx_ReadError: cmd_out(82) returns %d (%02X)\n",i,i));
- if (i<0) return (i);
- if (new_drive) i=2;
- else i=1;
- DriveStruct[d].error_byte=infobuf[i];
- DPRINTF((DBG_ERR,"SBPCD: xx_ReadError: infobuf[%d] is %d (%02X)\n",i,DriveStruct[d].error_byte,DriveStruct[d].error_byte));
- i=sta2err(infobuf[i]);
- return (i);
+ clr_cmdbuf();
+ msg(DBG_ERR,"giving cc_ReadError command.\n");
+ if (fam1_drive)
+ {
+ drvcmd[0]=CMD1_READ_ERR;
+ response_count=8;
+ flags_cmd_out=f_putcmd|f_ResponseStatus;
+ }
+ else if (fam0L_drive)
+ {
+ drvcmd[0]=CMD0_READ_ERR;
+ response_count=6;
+ if (famL_drive)
+ flags_cmd_out=f_putcmd;
+ else
+ flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus;
+ }
+ else if (fam2_drive)
+ {
+ drvcmd[0]=CMD2_READ_ERR;
+ response_count=6;
+ flags_cmd_out=f_putcmd;
+ }
+ else if (famT_drive)
+ {
+ response_count=5;
+ drvcmd[0]=CMDT_READ_ERR;
+ }
+ i=cmd_out();
+ D_S[d].error_byte=0;
+ msg(DBG_ERR,"cc_ReadError: cmd_out(CMDx_READ_ERR) returns %d (%02X)\n",i,i);
+ if (i<0) return (i);
+ if (fam0_drive) i=1;
+ else i=2;
+ D_S[d].error_byte=infobuf[i];
+ msg(DBG_ERR,"cc_ReadError: infobuf[%d] is %d (%02X)\n",i,D_S[d].error_byte,D_S[d].error_byte);
+ i=sta2err(infobuf[i]);
+ return (i);
+}
+/*==========================================================================*/
+static int cmd_out_T(void)
+{
+#undef CMDT_TRIES
+#define CMDT_TRIES 1000
+
+ static int cc_DriveReset(void);
+ int i, j, l, ntries;
+
+ D_S[d].error_state=0;
+ D_S[d].b3=0;
+ D_S[d].b4=0;
+ D_S[d].f_drv_error=0;
+ for (i=0;i<10;i++) sprintf(&msgbuf[i*3]," %02X",drvcmd[i]);
+ msgbuf[i*3]=0;
+ msg(DBG_CMD,"cmd_out_T:%s\n",msgbuf);
+
+ OUT(CDo_sel_i_d,0);
+ OUT(CDo_enable,D_S[d].drv_sel);
+ i=inb(CDi_status);
+ if (!(i&s_not_result_ready))
+ do
+ {
+ j=inb(CDi_info);
+ i=inb(CDi_status);
+ sbp_sleep(0);
+ msg(DBG_TEA,"cmd_out_T: spurious !s_not_result_ready. (%02X)\n", j);
+ }
+ while (!(i&s_not_result_ready));
+ cli();
+ for (i=0;i<10;i++) OUT(CDo_command,drvcmd[i]);
+ sti();
+ for (ntries=CMDT_TRIES;ntries>0;ntries--)
+ {
+ if (drvcmd[0]==CMDT_READ_VER) sbp_sleep(100);
+#if 1
+ OUT(CDo_sel_i_d,0);
+#endif
+ i=inb(CDi_status);
+ if (!(i&s_not_data_ready)) /* f.e. CMDT_DISKINFO */
+ {
+ OUT(CDo_sel_i_d,1);
+ if (drvcmd[0]==CMDT_READ) return (0); /* handled elsewhere */
+ if (drvcmd[0]==CMDT_DISKINFO)
+ {
+ l=0;
+ do
+ {
+ infobuf[l++]=inb(CDi_data);
+ i=inb(CDi_status);
+ }
+ while (!(i&s_not_data_ready));
+ for (j=0;j<l;j++) sprintf(&msgbuf[j*3]," %02X",infobuf[j]);
+ msgbuf[j*3]=0;
+ msg(DBG_CMD,"cmd_out_T data response:%s\n", msgbuf);
+ }
+ else
+ {
+ msg(DBG_TEA,"cmd_out_T: data response with cmd_%02X !!!!!!!!!!!!!!!!!!!!\n", drvcmd[0]);
+ j=0;
+ do
+ {
+ i=inb(CDi_data);
+ j++;
+ i=inb(CDi_status);
+ }
+ while (!(i&s_not_data_ready));
+ msg(DBG_TEA,"cmd_out_T: data response: discarded %d bytes.\n", j);
+ fatal_err++;
+ }
+ }
+ i=inb(CDi_status);
+ if (!(i&s_not_result_ready))
+ {
+ OUT(CDo_sel_i_d,0);
+ l=0;
+ do
+ {
+ infobuf[l++]=inb(CDi_info);
+ i=inb(CDi_status);
+ }
+ while (!(i&s_not_result_ready));
+ for (j=0;j<l;j++) sprintf(&msgbuf[j*3]," %02X",infobuf[j]);
+ msgbuf[j*3]=0;
+ msg(DBG_CMD,"cmd_out_T info response:%s\n", msgbuf);
+ if (infobuf[0]!=0x02) return (l); /* info length */
+ do
+ {
+ ++recursion;
+ if (recursion>1) msg(DBG_TEA,"cmd_out_T READ_ERR recursion (%02X): %d !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n", drvcmd[0], recursion);
+ clr_cmdbuf();
+ drvcmd[0]=CMDT_READ_ERR;
+ j=cmd_out_T(); /* !!! recursive here !!! */
+ --recursion;
+ sbp_sleep(1);
+ }
+ while (j<0);
+ D_S[d].error_state=infobuf[2];
+ D_S[d].b3=infobuf[3];
+ D_S[d].b4=infobuf[4];
+ if (D_S[d].f_drv_error)
+ {
+ D_S[d].f_drv_error=0;
+ cc_DriveReset();
+ D_S[d].error_state=2;
+ }
+ return (-D_S[d].error_state-400);
+ }
+ if (drvcmd[0]==CMDT_READ) return (0); /* handled elsewhere */
+ sbp_sleep(10);
+ if (ntries>(CMDT_TRIES-50)) continue;
+ msg(DBG_TEA,"cmd_out_T: next CMDT_TRIES (%02X): %d.\n", drvcmd[0], ntries-1);
+ }
+ D_S[d].f_drv_error=1;
+ cc_DriveReset();
+ D_S[d].error_state=2;
+ return (-99);
}
/*==========================================================================*/
static int cmd_out(void)
{
- int i=0;
-
- if (flags_cmd_out&f_putcmd)
- {
- DPRINTF((DBG_CMD,"SBPCD: cmd_out: put"));
- for (i=0;i<7;i++) DPRINTF((DBG_CMD," %02X",drvcmd[i]));
- DPRINTF((DBG_CMD,"\n"));
-
- SBPCD_CLI;
- for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
- SBPCD_STI;
- }
- if (response_count!=0)
- {
- if (cmd_type!=0)
+ int i=0;
+
+ if (famT_drive) return(cmd_out_T());
+
+ if (flags_cmd_out&f_putcmd)
+ {
+ for (i=0;i<7;i++)
+ sprintf(&msgbuf[i*3], " %02X", drvcmd[i]);
+ msgbuf[i*3]=0;
+ msg(DBG_CMD,"cmd_out:%s\n", msgbuf);
+ cli();
+ for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
+ sti();
+ }
+ if (response_count!=0)
{
- if (sbpro_type==1) OUT(CDo_sel_d_i,0x01);
- DPRINTF((DBG_INF,"SBPCD: misleaded to try ResponseData.\n"));
- if (sbpro_type==1) OUT(CDo_sel_d_i,0x00);
- return (-22);
+ if (cmd_type!=0)
+ {
+ if (sbpro_type==1) OUT(CDo_sel_i_d,1);
+ msg(DBG_INF,"misleaded to try ResponseData.\n");
+ if (sbpro_type==1) OUT(CDo_sel_i_d,0);
+ return (-22);
+ }
+ else i=ResponseInfo();
+ if (i<0) return (i);
}
- else i=ResponseInfo();
- if (i<0) return (-9);
- }
- if (DriveStruct[d].in_SpinUp != 0) DPRINTF((DBG_SPI,"SBPCD: to CDi_stat_loop.\n"));
- if (flags_cmd_out&f_lopsta)
- {
- i=CDi_stat_loop();
- if ((i<0)||!(i&s_attention)) return (-9);
- }
- if (!(flags_cmd_out&f_getsta)) goto LOC_229;
-
-LOC_228:
- if (DriveStruct[d].in_SpinUp != 0) DPRINTF((DBG_SPI,"SBPCD: to xx_ReadStatus.\n"));
- xx_ReadStatus();
-
-LOC_229:
- if (flags_cmd_out&f_ResponseStatus)
- {
- if (DriveStruct[d].in_SpinUp != 0) DPRINTF((DBG_SPI,"SBPCD: to ResponseStatus.\n"));
- i=ResponseStatus();
- /* builds status_byte, returns orig. status or p_busy_new */
- if (i<0) return (-9);
- if (flags_cmd_out&(f_bit1|f_wait_if_busy))
- {
- if (!st_check)
- {
- if (flags_cmd_out&f_bit1) if (i&p_success_old) goto LOC_232;
- if (!(flags_cmd_out&f_wait_if_busy)) goto LOC_228;
- if (!st_busy) goto LOC_228;
- }
+ if (D_S[d].in_SpinUp) msg(DBG_SPI,"in_SpinUp: to CDi_stat_loop.\n");
+ if (flags_cmd_out&f_lopsta)
+ {
+ i=CDi_stat_loop();
+ if ((i<0)||!(i&s_attention)) return (-8);
}
- }
-LOC_232:
- if (!(flags_cmd_out&f_obey_p_check)) return (0);
- if (!st_check) return (0);
- if (DriveStruct[d].in_SpinUp != 0) DPRINTF((DBG_SPI,"SBPCD: to xx_ReadError.\n"));
- i=xx_ReadError();
- if (DriveStruct[d].in_SpinUp != 0) DPRINTF((DBG_SPI,"SBPCD: to cmd_out OK.\n"));
- return (i);
+ if (!(flags_cmd_out&f_getsta)) goto LOC_229;
+
+ LOC_228:
+ if (D_S[d].in_SpinUp) msg(DBG_SPI,"in_SpinUp: to cc_ReadStatus.\n");
+ cc_ReadStatus();
+
+ LOC_229:
+ if (flags_cmd_out&f_ResponseStatus)
+ {
+ if (D_S[d].in_SpinUp) msg(DBG_SPI,"in_SpinUp: to ResponseStatus.\n");
+ i=ResponseStatus();
+ /* builds status_bits, returns orig. status or p_busy_new */
+ if (i<0) return (i);
+ if (flags_cmd_out&(f_bit1|f_wait_if_busy))
+ {
+ if (!st_check)
+ {
+ if ((flags_cmd_out&f_bit1)&&(i&p_success)) goto LOC_232;
+ if ((!(flags_cmd_out&f_wait_if_busy))||(!st_busy)) goto LOC_228;
+ }
+ }
+ }
+ LOC_232:
+ if (!(flags_cmd_out&f_obey_p_check)) return (0);
+ if (!st_check) return (0);
+ if (D_S[d].in_SpinUp) msg(DBG_SPI,"in_SpinUp: to cc_ReadError.\n");
+ i=cc_ReadError();
+ if (D_S[d].in_SpinUp) msg(DBG_SPI,"in_SpinUp: to cmd_out OK.\n");
+ msg(DBG_000,"cmd_out: cc_ReadError=%d\n", i);
+ return (i);
}
/*==========================================================================*/
-static int xx_Seek(u_int pos, char f_blk_msf)
+static int cc_Seek(u_int pos, char f_blk_msf)
{
- int i;
-
+ int i;
+
clr_cmdbuf();
- if (f_blk_msf>1) return (-3);
- if (!new_drive)
- {
- if (f_blk_msf==1) pos=msf2blk(pos);
- drvcmd[2]=(pos>>16)&0x00FF;
- drvcmd[3]=(pos>>8)&0x00FF;
- drvcmd[4]=pos&0x00FF;
- flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta |
- f_ResponseStatus | f_obey_p_check | f_bit1;
- }
- else
- {
- if (f_blk_msf==0) pos=blk2msf(pos);
- drvcmd[1]=(pos>>16)&0x00FF;
- drvcmd[2]=(pos>>8)&0x00FF;
- drvcmd[3]=pos&0x00FF;
- flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
- }
- drvcmd[0]=0x01;
- response_count=0;
- i=cmd_out();
- return (i);
+ if (f_blk_msf>1) return (-3);
+ if (fam0_drive)
+ {
+ drvcmd[0]=CMD0_SEEK;
+ if (f_blk_msf==1) pos=msf2blk(pos);
+ drvcmd[2]=(pos>>16)&0x00FF;
+ drvcmd[3]=(pos>>8)&0x00FF;
+ drvcmd[4]=pos&0x00FF;
+ flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta |
+ f_ResponseStatus | f_obey_p_check | f_bit1;
+ }
+ else if (fam1L_drive)
+ {
+ drvcmd[0]=CMD1_SEEK; /* same as CMD1_ and CMDL_ */
+ if (f_blk_msf==0) pos=blk2msf(pos);
+ drvcmd[1]=(pos>>16)&0x00FF;
+ drvcmd[2]=(pos>>8)&0x00FF;
+ drvcmd[3]=pos&0x00FF;
+ if (famL_drive)
+ flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
+ else
+ flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+ }
+ else if (fam2_drive)
+ {
+ drvcmd[0]=CMD2_SEEK;
+ if (f_blk_msf==0) pos=blk2msf(pos);
+ drvcmd[2]=(pos>>24)&0x00FF;
+ drvcmd[3]=(pos>>16)&0x00FF;
+ drvcmd[4]=(pos>>8)&0x00FF;
+ drvcmd[5]=pos&0x00FF;
+ flags_cmd_out=f_putcmd|f_ResponseStatus;
+ }
+ else if (famT_drive)
+ {
+ drvcmd[0]=CMDT_SEEK;
+ if (f_blk_msf==1) pos=msf2blk(pos);
+ drvcmd[2]=(pos>>24)&0x00FF;
+ drvcmd[3]=(pos>>16)&0x00FF;
+ drvcmd[4]=(pos>>8)&0x00FF;
+ drvcmd[5]=pos&0x00FF;
+ D_S[d].n_bytes=1;
+ }
+ response_count=0;
+ i=cmd_out();
+ return (i);
}
/*==========================================================================*/
-static int xx_SpinUp(void)
+static int cc_SpinUp(void)
{
- int i;
-
- DPRINTF((DBG_SPI,"SBPCD: SpinUp.\n"));
- DriveStruct[d].in_SpinUp = 1;
- clr_cmdbuf();
- if (old_drive)
- {
- drvcmd[0]=0x05;
- flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
- }
- else if (new_drive)
- {
- drvcmd[0]=0x02;
- flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
- }
- else /* lcs_drive */
- {
- drvcmd[0]=0x0D;
- flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
- }
- response_count=0;
- i=cmd_out();
- DriveStruct[d].in_SpinUp = 0;
- return (i);
+ int i;
+
+ msg(DBG_SPI,"SpinUp.\n");
+ D_S[d].in_SpinUp = 1;
+ clr_cmdbuf();
+ if (fam0L_drive)
+ {
+ drvcmd[0]=CMD0_SPINUP;
+ flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|
+ f_ResponseStatus|f_obey_p_check|f_bit1;
+ }
+ else if (fam1_drive)
+ {
+ drvcmd[0]=CMD1_SPINUP;
+ flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+ }
+ else if (fam2_drive)
+ {
+ drvcmd[0]=CMD2_TRAY_CTL;
+ drvcmd[4]=0x01; /* "spinup" */
+ flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+ }
+ else if (famT_drive)
+ {
+ drvcmd[0]=CMDT_TRAY_CTL;
+ drvcmd[4]=0x03; /* "insert", it hopefully spins the drive up */
+ }
+ response_count=0;
+ i=cmd_out();
+ D_S[d].in_SpinUp = 0;
+ return (i);
}
/*==========================================================================*/
-static int yy_SpinDown(void)
+static int cc_SpinDown(void)
{
- int i;
-
- if (old_drive) return (-3);
- clr_cmdbuf();
- if (new_drive)
- {
- drvcmd[0]=0x06;
- flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
- response_count=0;
- }
- else /* lcs_drive */
- {
- drvcmd[0]=0x0D;
- drvcmd[1]=1;
- flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
- response_count=0;
- }
- i=cmd_out();
- return (i);
+ int i;
+
+ if (fam0_drive) return (0);
+ clr_cmdbuf();
+ response_count=0;
+ if (fam1_drive)
+ {
+ drvcmd[0]=CMD1_SPINDOWN;
+ flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+ }
+ else if (fam2_drive)
+ {
+ drvcmd[0]=CMD2_TRAY_CTL;
+ drvcmd[4]=0x02; /* "eject" */
+ flags_cmd_out=f_putcmd|f_ResponseStatus;
+ }
+ else if (famL_drive)
+ {
+ drvcmd[0]=CMDL_SPINDOWN;
+ drvcmd[1]=1;
+ flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
+ }
+ else if (famT_drive)
+ {
+ drvcmd[0]=CMDT_TRAY_CTL;
+ drvcmd[4]=0x02; /* "eject" */
+ }
+ i=cmd_out();
+ return (i);
}
/*==========================================================================*/
-static int yy_SetSpeed(u_char speed, u_char x1, u_char x2)
+static int cc_get_mode_T(void)
{
- int i;
-
- if (!new_drive) return (-3);
- clr_cmdbuf();
- drvcmd[0]=0x09;
- drvcmd[1]=0x03;
- drvcmd[2]=speed;
- drvcmd[3]=x1;
- drvcmd[4]=x2;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- response_count=0;
- i=cmd_out();
- return (i);
-}
-/*==========================================================================*/
-static int xx_SetVolume(void)
-{
- int i;
- u_char channel0,channel1,volume0,volume1;
- u_char control0,value0,control1,value1;
-
- DriveStruct[d].diskstate_flags &= ~volume_bit;
- clr_cmdbuf();
- channel0=DriveStruct[d].vol_chan0;
- volume0=DriveStruct[d].vol_ctrl0;
- channel1=control1=DriveStruct[d].vol_chan1;
- volume1=value1=DriveStruct[d].vol_ctrl1;
- control0=value0=0;
-
- if (((DriveStruct[d].drv_options&sax_a)!=0)&&(DriveStruct[d].drv_type>=drv_211))
- {
- if ((volume0!=0)&&(volume1==0))
+ int i;
+
+ clr_cmdbuf();
+ response_count=10;
+ drvcmd[0]=CMDT_GETMODE;
+ drvcmd[4]=response_count;
+ i=cmd_out_T();
+ return (i);
+}
+/*==========================================================================*/
+static int cc_set_mode_T(void)
+{
+ int i;
+
+ clr_cmdbuf();
+ response_count=1;
+ drvcmd[0]=CMDT_SETMODE;
+ drvcmd[1]=D_S[d].speed_byte;
+ drvcmd[2]=D_S[d].frmsiz>>8;
+ drvcmd[3]=D_S[d].frmsiz&0x0FF;
+ drvcmd[4]=D_S[d].f_XA; /* 1: XA */
+ drvcmd[5]=D_S[d].type_byte; /* 0, 1, 3 */
+ drvcmd[6]=D_S[d].mode_xb_6;
+ drvcmd[7]=D_S[d].mode_yb_7|D_S[d].volume_control;
+ drvcmd[8]=D_S[d].mode_xb_8;
+ drvcmd[9]=D_S[d].delay;
+ i=cmd_out_T();
+ return (i);
+}
+/*==========================================================================*/
+static int cc_prep_mode_T(void)
+{
+ int i, j;
+
+ i=cc_get_mode_T();
+ if (i<0) return (i);
+ for (i=0;i<10;i++)
+ sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
+ msgbuf[i*3]=0;
+ msg(DBG_TEA,"CMDT_GETMODE:%s\n", msgbuf);
+ D_S[d].speed_byte=0x02; /* 0x02: auto quad, 0x82: quad, 0x81: double, 0x80: single */
+ D_S[d].frmsiz=make16(infobuf[2],infobuf[3]);
+ D_S[d].f_XA=infobuf[4];
+ if (D_S[d].f_XA==0) D_S[d].type_byte=0;
+ else D_S[d].type_byte=1;
+ D_S[d].mode_xb_6=infobuf[6];
+ D_S[d].mode_yb_7=1;
+ D_S[d].mode_xb_8=infobuf[8];
+ D_S[d].delay=0; /* 0, 1, 2, 3 */
+ j=cc_set_mode_T();
+ i=cc_get_mode_T();
+ for (i=0;i<10;i++)
+ sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
+ msgbuf[i*3]=0;
+ msg(DBG_TEA,"CMDT_GETMODE:%s\n", msgbuf);
+ return (j);
+}
+/*==========================================================================*/
+static int cc_SetSpeed(u_char speed, u_char x1, u_char x2)
+{
+ int i;
+
+ if (fam0L_drive) return (-3);
+ clr_cmdbuf();
+ response_count=0;
+ if (fam1_drive)
{
- volume1=volume0;
- channel1=channel0;
+ drvcmd[0]=CMD1_SETMODE;
+ drvcmd[1]=0x03;
+ drvcmd[2]=speed;
+ drvcmd[3]=x1;
+ drvcmd[4]=x2;
+ flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
}
- else if ((volume0==0)&&(volume1!=0))
+ else if (fam2_drive)
{
- volume0=volume1;
- channel0=channel1;
+ drvcmd[0]=CMD2_SETSPEED;
+ if (speed&speed_auto)
+ {
+ drvcmd[2]=0xFF;
+ drvcmd[3]=0xFF;
+ }
+ else
+ {
+ drvcmd[2]=0;
+ drvcmd[3]=150;
+ }
+ flags_cmd_out=f_putcmd|f_ResponseStatus;
}
- }
- if (channel0>1)
- {
- channel0=0;
- volume0=0;
- }
- if (channel1>1)
- {
- channel1=1;
- volume1=0;
- }
-
- if (new_drive)
- {
- control0=channel0+1;
- control1=channel1+1;
- value0=(volume0>volume1)?volume0:volume1;
- value1=value0;
- if (volume0==0) control0=0;
- if (volume1==0) control1=0;
- drvcmd[0]=0x09;
- drvcmd[1]=0x05;
- drvcmd[3]=control0;
- drvcmd[4]=value0;
- drvcmd[5]=control1;
- drvcmd[6]=value1;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- }
- else if (lcs_drive)
- {
- if ((volume0==0)||(channel0!=0)) control0 |= 0x80;
- if ((volume1==0)||(channel1!=1)) control0 |= 0x40;
- if (volume0|volume1) value0=0x80;
- drvcmd[0]=0x84;
- drvcmd[1]=0x03;
- drvcmd[4]=control0;
- drvcmd[5]=value0;
- flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
- }
- else /* old_drive, different firmware levels */
- {
- if (DriveStruct[d].drv_type>=drv_300)
- {
- control0=volume0&0xFC;
- value0=volume1&0xFC;
- if ((volume0!=0)&&(volume0<4)) control0 |= 0x04;
- if ((volume1!=0)&&(volume1<4)) value0 |= 0x04;
- if (channel0!=0) control0 |= 0x01;
- if (channel1==1) value0 |= 0x01;
- }
- else
- {
- value0=(volume0>volume1)?volume0:volume1;
- if (DriveStruct[d].drv_type<drv_211)
- {
- if (channel0!=0)
- {
- i=channel1;
- channel1=channel0;
- channel0=i;
- i=volume1;
- volume1=volume0;
- volume0=i;
- }
- if (channel0==channel1)
- {
- if (channel0==0)
- {
- channel1=1;
- volume1=0;
- volume0=value0;
- }
- else
- {
- channel0=0;
- volume0=0;
- volume1=value0;
- }
- }
- }
-
- if ((volume0!=0)&&(volume1!=0))
- {
- if (volume0==0xFF) volume1=0xFF;
- else if (volume1==0xFF) volume0=0xFF;
- }
- else if (DriveStruct[d].drv_type<drv_201) volume0=volume1=value0;
-
- if (DriveStruct[d].drv_type>=drv_201)
- {
- if (volume0==0) control0 |= 0x80;
- if (volume1==0) control0 |= 0x40;
- }
- if (DriveStruct[d].drv_type>=drv_211)
- {
- if (channel0!=0) control0 |= 0x20;
- if (channel1!=1) control0 |= 0x10;
- }
- }
- drvcmd[0]=0x84;
- drvcmd[1]=0x83;
- drvcmd[4]=control0;
- drvcmd[5]=value0;
- flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
- }
-
- response_count=0;
- i=cmd_out();
- if (i>0) return (i);
- DriveStruct[d].diskstate_flags |= volume_bit;
- return (0);
+ else if (famT_drive)
+ {
+ return (0);
+ }
+ i=cmd_out();
+ return (i);
+}
+/*==========================================================================*/
+static int cc_SetVolume(void)
+{
+ int i;
+ u_char channel0,channel1,volume0,volume1;
+ u_char control0,value0,control1,value1;
+
+ D_S[d].diskstate_flags &= ~volume_bit;
+ clr_cmdbuf();
+ channel0=D_S[d].vol_chan0;
+ volume0=D_S[d].vol_ctrl0;
+ channel1=control1=D_S[d].vol_chan1;
+ volume1=value1=D_S[d].vol_ctrl1;
+ control0=value0=0;
+
+ if (((D_S[d].drv_options&audio_mono)!=0)&&(D_S[d].drv_type>=drv_211))
+ {
+ if ((volume0!=0)&&(volume1==0))
+ {
+ volume1=volume0;
+ channel1=channel0;
+ }
+ else if ((volume0==0)&&(volume1!=0))
+ {
+ volume0=volume1;
+ channel0=channel1;
+ }
+ }
+ if (channel0>1)
+ {
+ channel0=0;
+ volume0=0;
+ }
+ if (channel1>1)
+ {
+ channel1=1;
+ volume1=0;
+ }
+
+ if (fam1_drive)
+ {
+ control0=channel0+1;
+ control1=channel1+1;
+ value0=(volume0>volume1)?volume0:volume1;
+ value1=value0;
+ if (volume0==0) control0=0;
+ if (volume1==0) control1=0;
+ drvcmd[0]=CMD1_SETMODE;
+ drvcmd[1]=0x05;
+ drvcmd[3]=control0;
+ drvcmd[4]=value0;
+ drvcmd[5]=control1;
+ drvcmd[6]=value1;
+ flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+ }
+ else if (fam2_drive)
+ {
+ control0=channel0+1;
+ control1=channel1+1;
+ value0=(volume0>volume1)?volume0:volume1;
+ value1=value0;
+ if (volume0==0) control0=0;
+ if (volume1==0) control1=0;
+ drvcmd[0]=CMD2_SETMODE;
+ drvcmd[1]=0x0E;
+ drvcmd[3]=control0;
+ drvcmd[4]=value0;
+ drvcmd[5]=control1;
+ drvcmd[6]=value1;
+ flags_cmd_out=f_putcmd|f_ResponseStatus;
+ }
+ else if (famL_drive)
+ {
+ if ((volume0==0)||(channel0!=0)) control0 |= 0x80;
+ if ((volume1==0)||(channel1!=1)) control0 |= 0x40;
+ if (volume0|volume1) value0=0x80;
+ drvcmd[0]=CMDL_SETMODE;
+ drvcmd[1]=0x03;
+ drvcmd[4]=control0;
+ drvcmd[5]=value0;
+ flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
+ }
+ else if (fam0_drive) /* different firmware levels */
+ {
+ if (D_S[d].drv_type>=drv_300)
+ {
+ control0=volume0&0xFC;
+ value0=volume1&0xFC;
+ if ((volume0!=0)&&(volume0<4)) control0 |= 0x04;
+ if ((volume1!=0)&&(volume1<4)) value0 |= 0x04;
+ if (channel0!=0) control0 |= 0x01;
+ if (channel1==1) value0 |= 0x01;
+ }
+ else
+ {
+ value0=(volume0>volume1)?volume0:volume1;
+ if (D_S[d].drv_type<drv_211)
+ {
+ if (channel0!=0)
+ {
+ i=channel1;
+ channel1=channel0;
+ channel0=i;
+ i=volume1;
+ volume1=volume0;
+ volume0=i;
+ }
+ if (channel0==channel1)
+ {
+ if (channel0==0)
+ {
+ channel1=1;
+ volume1=0;
+ volume0=value0;
+ }
+ else
+ {
+ channel0=0;
+ volume0=0;
+ volume1=value0;
+ }
+ }
+ }
+
+ if ((volume0!=0)&&(volume1!=0))
+ {
+ if (volume0==0xFF) volume1=0xFF;
+ else if (volume1==0xFF) volume0=0xFF;
+ }
+ else if (D_S[d].drv_type<drv_201) volume0=volume1=value0;
+
+ if (D_S[d].drv_type>=drv_201)
+ {
+ if (volume0==0) control0 |= 0x80;
+ if (volume1==0) control0 |= 0x40;
+ }
+ if (D_S[d].drv_type>=drv_211)
+ {
+ if (channel0!=0) control0 |= 0x20;
+ if (channel1!=1) control0 |= 0x10;
+ }
+ }
+ drvcmd[0]=CMD0_SETMODE;
+ drvcmd[1]=0x83;
+ drvcmd[4]=control0;
+ drvcmd[5]=value0;
+ flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+ }
+ else if (famT_drive)
+ {
+ D_S[d].volume_control=0;
+ if (!volume0) D_S[d].volume_control|=0x10;
+ if (!volume1) D_S[d].volume_control|=0x20;
+ i=cc_prep_mode_T();
+ if (i<0) return (i);
+ }
+ if (!famT_drive)
+ {
+ response_count=0;
+ i=cmd_out();
+ if (i<0) return (i);
+ }
+ D_S[d].diskstate_flags |= volume_bit;
+ return (0);
}
/*==========================================================================*/
static int GetStatus(void)
{
- int i;
-
- flags_cmd_out=f_getsta|f_ResponseStatus|f_obey_p_check;
- response_count=0;
- cmd_type=0;
- i=cmd_out();
- return (i);
+ int i;
+
+ if (famT_drive) return (0);
+ flags_cmd_out=f_getsta|f_ResponseStatus|f_obey_p_check;
+ response_count=0;
+ cmd_type=0;
+ i=cmd_out();
+ return (i);
}
/*==========================================================================*/
-static int xy_DriveReset(void)
+static int cc_DriveReset(void)
{
- int i;
-
- DPRINTF((DBG_RES,"SBPCD: xy_DriveReset called.\n"));
- if (!new_drive) OUT(CDo_reset,0x00);
- else
- {
- clr_cmdbuf();
- drvcmd[0]=0x0A;
- flags_cmd_out=f_putcmd;
- response_count=0;
- i=cmd_out();
- }
- sbp_sleep(100); /* wait a second */
- flush_status();
- i=GetStatus();
- if (i>=0) return -1;
- if (DriveStruct[d].error_byte!=aud_12) return -1;
- return (0);
+ int i;
+
+ msg(DBG_RES,"cc_DriveReset called.\n");
+ clr_cmdbuf();
+ response_count=0;
+ if (fam0L_drive) OUT(CDo_reset,0x00);
+ else if (fam1_drive)
+ {
+ drvcmd[0]=CMD1_RESET;
+ flags_cmd_out=f_putcmd;
+ i=cmd_out();
+ }
+ else if (fam2_drive)
+ {
+ drvcmd[0]=CMD2_RESET;
+ flags_cmd_out=f_putcmd;
+ i=cmd_out();
+ OUT(CDo_reset,0x00);
+ }
+ else if (famT_drive)
+ {
+ OUT(CDo_sel_i_d,0);
+ OUT(CDo_enable,D_S[d].drv_sel);
+ OUT(CDo_command,CMDT_RESET);
+ for (i=1;i<10;i++) OUT(CDo_command,0);
+ }
+ if (fam0L_drive) sbp_sleep(500); /* wait 5 seconds */
+ else sbp_sleep(100); /* wait a second */
+#if 1
+ if (famT_drive)
+ {
+ msg(DBG_TEA, "================CMDT_RESET given=================.\n");
+ sbp_sleep(300);
+ }
+#endif 1
+ flush_status();
+ i=GetStatus();
+ if (i<0) return i;
+ if (!famT_drive)
+ if (D_S[d].error_byte!=aud_12) return -501;
+ return (0);
}
/*==========================================================================*/
static int SetSpeed(void)
{
- int i, speed;
-
- if (!(DriveStruct[d].drv_options&(speed_auto|speed_300|speed_150))) return (0);
- speed=speed_auto;
- if (!(DriveStruct[d].drv_options&speed_auto))
- {
- speed |= speed_300;
- if (!(DriveStruct[d].drv_options&speed_300)) speed=0;
- }
- i=yy_SetSpeed(speed,0,0);
- return (i);
+ int i, speed;
+
+ if (!(D_S[d].drv_options&(speed_auto|speed_300|speed_150))) return (0);
+ speed=speed_auto;
+ if (!(D_S[d].drv_options&speed_auto))
+ {
+ speed |= speed_300;
+ if (!(D_S[d].drv_options&speed_300)) speed=0;
+ }
+ i=cc_SetSpeed(speed,0,0);
+ return (i);
}
/*==========================================================================*/
static int DriveReset(void)
{
- int i;
-
- i=xy_DriveReset();
- if (i<0) return (-2);
- do
- {
- i=GetStatus();
- if ((i<0)&&(i!=-15)) return (-2); /* i!=-15 is from sta2err */
- if (!st_caddy_in) break;
- }
- while (!st_diskok);
+ int i;
+
+ i=cc_DriveReset();
+ if (i<0) return (-22);
+ do
+ {
+ i=GetStatus();
+ if ((i<0)&&(i!=-615)) return (-2); /* i!=-615 is from sta2err */
+ if (!st_caddy_in) break;
+ sbp_sleep(1);
+ }
+ while (!st_diskok);
#if 000
- DriveStruct[d].CD_changed=1;
+ D_S[d].CD_changed=1;
#endif
- if ((st_door_closed) && (st_caddy_in))
- {
- i=DiskInfo();
- if (i<0) return (-2);
- }
- return (0);
+ if ((st_door_closed) && (st_caddy_in))
+ {
+ i=DiskInfo();
+ if (i<0) return (-23);
+ }
+ return (0);
}
/*==========================================================================*/
-static int xx_Pause_Resume(int pau_res)
+static int cc_PlayAudio(int pos_audio_start,int pos_audio_end)
{
- int i;
-
- clr_cmdbuf();
- if (new_drive)
- {
- drvcmd[0]=0x0D;
- flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
- }
- else
- {
- drvcmd[0]=0x8D;
- flags_cmd_out=f_putcmd|f_respo2|f_getsta|f_ResponseStatus|f_obey_p_check;
- }
- if (pau_res!=1) drvcmd[1]=0x80;
- response_count=0;
- i=cmd_out();
- return (i);
+ int i, j, n;
+
+ if (D_S[d].audio_state==audio_playing) return (-EINVAL);
+ clr_cmdbuf();
+ response_count=0;
+ if (famL_drive)
+ {
+ drvcmd[0]=CMDL_PLAY;
+ i=msf2blk(pos_audio_start);
+ n=msf2blk(pos_audio_end)+1-i;
+ drvcmd[1]=(i>>16)&0x00FF;
+ drvcmd[2]=(i>>8)&0x00FF;
+ drvcmd[3]=i&0x00FF;
+ drvcmd[4]=(n>>16)&0x00FF;
+ drvcmd[5]=(n>>8)&0x00FF;
+ drvcmd[6]=n&0x00FF;
+ flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta |
+ f_ResponseStatus | f_obey_p_check | f_wait_if_busy;
+ }
+ else
+ {
+ j=1;
+ if (fam1_drive)
+ {
+ drvcmd[0]=CMD1_PLAY_MSF;
+ flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus |
+ f_obey_p_check | f_wait_if_busy;
+ }
+ else if (fam2_drive)
+ {
+ drvcmd[0]=CMD2_PLAY_MSF;
+ flags_cmd_out = f_putcmd | f_ResponseStatus;
+ }
+ else if (famT_drive)
+ {
+ drvcmd[0]=CMDT_PLAY_MSF;
+ j=3;
+ response_count=1;
+ }
+ else if (fam0_drive)
+ {
+ drvcmd[0]=CMD0_PLAY_MSF;
+ flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta |
+ f_ResponseStatus | f_obey_p_check | f_wait_if_busy;
+ }
+ drvcmd[j]=(pos_audio_start>>16)&0x00FF;
+ drvcmd[j+1]=(pos_audio_start>>8)&0x00FF;
+ drvcmd[j+2]=pos_audio_start&0x00FF;
+ drvcmd[j+3]=(pos_audio_end>>16)&0x00FF;
+ drvcmd[j+4]=(pos_audio_end>>8)&0x00FF;
+ drvcmd[j+5]=pos_audio_end&0x00FF;
+ }
+ i=cmd_out();
+ return (i);
}
/*==========================================================================*/
-static int yy_LockDoor(char lock)
+static int cc_Pause_Resume(int pau_res)
{
- int i;
-
- if (old_drive) return (0);
- DPRINTF((DBG_LCK,"SBPCD: yy_LockDoor: %d (drive %d)\n", lock, d));
- clr_cmdbuf();
- if (new_drive)
- {
- drvcmd[0]=0x0C;
- if (lock==1) drvcmd[1]=0x01;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- response_count=0;
- }
- else /* lcs_drive */
- {
- drvcmd[0]=0x0E;
- if (lock==1) drvcmd[1]=0x01;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- response_count=0;
- }
- i=cmd_out();
- return (i);
+ int i;
+
+ clr_cmdbuf();
+ response_count=0;
+ if (fam1_drive)
+ {
+ drvcmd[0]=CMD1_PAU_RES;
+ if (pau_res!=1) drvcmd[1]=0x80;
+ flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+ }
+ else if (fam2_drive)
+ {
+ drvcmd[0]=CMD2_PAU_RES;
+ if (pau_res!=1) drvcmd[2]=0x01;
+ flags_cmd_out=f_putcmd|f_ResponseStatus;
+ }
+ else if (fam0L_drive)
+ {
+ drvcmd[0]=CMD0_PAU_RES;
+ if (pau_res!=1) drvcmd[1]=0x80;
+ if (famL_drive)
+ flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|
+ f_obey_p_check|f_bit1;
+ else
+ flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|
+ f_obey_p_check;
+ }
+ else if (famT_drive)
+ {
+ if (pau_res==3) return (cc_PlayAudio(D_S[d].pos_audio_start,D_S[d].pos_audio_end));
+ else if (pau_res==1) drvcmd[0]=CMDT_PAUSE;
+ else return (-56);
+ }
+ i=cmd_out();
+ return (i);
}
/*==========================================================================*/
-static int yy_CloseTray(void)
+static int cc_LockDoor(char lock)
{
- int i;
-
- if (!new_drive) return (0);
- DPRINTF((DBG_LCK,"SBPCD: yy_CloseTray (drive %d)\n", d));
-
- clr_cmdbuf();
- drvcmd[0]=0x07;
- flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
- response_count=0;
- i=cmd_out();
- return (i);
+ int i;
+
+ if (fam0_drive) return (0);
+ msg(DBG_LCK,"cc_LockDoor: %d (drive %d)\n", lock, d);
+ msg(DBG_LCS,"p_door_locked bit %d before\n", st_door_locked);
+ clr_cmdbuf();
+ response_count=0;
+ if (fam1_drive)
+ {
+ drvcmd[0]=CMD1_LOCK_CTL;
+ if (lock==1) drvcmd[1]=0x01;
+ flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+ }
+ else if (fam2_drive)
+ {
+ drvcmd[0]=CMD2_LOCK_CTL;
+ if (lock==1) drvcmd[4]=0x01;
+ flags_cmd_out=f_putcmd|f_ResponseStatus;
+ }
+ else if (famL_drive)
+ {
+ drvcmd[0]=CMDL_LOCK_CTL;
+ if (lock==1) drvcmd[1]=0x01;
+ flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
+ }
+ else if (famT_drive)
+ {
+ drvcmd[0]=CMDT_LOCK_CTL;
+ if (lock==1) drvcmd[4]=0x01;
+ }
+ i=cmd_out();
+ msg(DBG_LCS,"p_door_locked bit %d after\n", st_door_locked);
+ return (i);
}
/*==========================================================================*/
-static int xx_ReadSubQ(void)
+/*==========================================================================*/
+static int UnLockDoor(void)
{
- int i,j;
-
- DriveStruct[d].diskstate_flags &= ~subq_bit;
- for (j=255;j>0;j--)
- {
- clr_cmdbuf();
- if (new_drive)
+ int i,j;
+
+ j=20;
+ do
{
- drvcmd[0]=0x87;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- response_count=11;
+ i=cc_LockDoor(0);
+ --j;
+ sbp_sleep(1);
}
- else
+ while ((i<0)&&(j));
+ if (i<0)
{
- drvcmd[0]=0x89;
- drvcmd[1]=0x02;
- flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
- response_count=13;
+ cc_DriveReset();
+ return -84;
}
- i=cmd_out();
- if (i<0) return (i);
- DPRINTF((DBG_SQ,"SBPCD: xx_ReadSubQ:"));
- for (i=0;i<(new_drive?11:13);i++)
+ return (0);
+}
+/*==========================================================================*/
+static int LockDoor(void)
+{
+ int i,j;
+
+ j=20;
+ do
{
- DPRINTF((DBG_SQ," %02X", infobuf[i]));
+ i=cc_LockDoor(1);
+ --j;
+ sbp_sleep(1);
}
- DPRINTF((DBG_SQ,"\n"));
- if (infobuf[0]!=0) break;
- if ((!st_spinning) || (j==1))
+ while ((i<0)&&(j));
+ if (j==0)
+ {
+ cc_DriveReset();
+ j=20;
+ do
+ {
+ i=cc_LockDoor(1);
+ --j;
+ sbp_sleep(1);
+ }
+ while ((i<0)&&(j));
+ }
+ return (i);
+}
+/*==========================================================================*/
+static int cc_CloseTray(void)
+{
+ int i;
+
+ if (fam0_drive) return (0);
+ msg(DBG_LCK,"cc_CloseTray (drive %d)\n", d);
+ msg(DBG_LCS,"p_door_closed bit %d before\n", st_door_closed);
+
+ clr_cmdbuf();
+ response_count=0;
+ if (fam1_drive)
{
- DriveStruct[d].SubQ_ctl_adr=DriveStruct[d].SubQ_trk=DriveStruct[d].SubQ_pnt_idx=DriveStruct[d].SubQ_whatisthis=0;
- DriveStruct[d].SubQ_run_tot=DriveStruct[d].SubQ_run_trk=0;
- return (0);
+ drvcmd[0]=CMD1_TRAY_CTL;
+ flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
}
- }
- DriveStruct[d].SubQ_ctl_adr=swap_nibbles(infobuf[1]);
- DriveStruct[d].SubQ_trk=byt2bcd(infobuf[2]);
- DriveStruct[d].SubQ_pnt_idx=byt2bcd(infobuf[3]);
- i=4;
- if (!new_drive) i=5;
- DriveStruct[d].SubQ_run_tot=make32(make16(0,infobuf[i]),make16(infobuf[i+1],infobuf[i+2])); /* msf-bin */
- i=7;
- if (!new_drive) i=9;
- DriveStruct[d].SubQ_run_trk=make32(make16(0,infobuf[i]),make16(infobuf[i+1],infobuf[i+2])); /* msf-bin */
- DriveStruct[d].SubQ_whatisthis=infobuf[i+3];
- DriveStruct[d].diskstate_flags |= subq_bit;
- return (0);
-}
-/*==========================================================================*/
-static int xx_ModeSense(void)
-{
- int i;
-
- DriveStruct[d].diskstate_flags &= ~frame_size_bit;
- clr_cmdbuf();
- if (new_drive)
- {
- drvcmd[0]=0x84;
- drvcmd[1]=0x00;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- response_count=5;
- }
- else
- {
- drvcmd[0]=0x85;
- drvcmd[1]=0x00;
- flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
- response_count=2;
- }
- i=cmd_out();
- if (i<0) return (i);
- i=0;
- if (new_drive) DriveStruct[d].sense_byte=infobuf[i++];
- DriveStruct[d].frame_size=make16(infobuf[i],infobuf[i+1]);
-
- DPRINTF((DBG_XA,"SBPCD: xx_ModeSense: "));
- for (i=0;i<(new_drive?5:2);i++)
- {
- DPRINTF((DBG_XA,"%02X ", infobuf[i]));
- }
- DPRINTF((DBG_XA,"\n"));
+ else if (fam2_drive)
+ {
+ drvcmd[0]=CMD2_TRAY_CTL;
+ drvcmd[1]=0x01;
+ drvcmd[4]=0x03; /* "insert" */
+ flags_cmd_out=f_putcmd|f_ResponseStatus;
+ }
+ else if (famL_drive)
+ {
+ drvcmd[0]=CMDL_TRAY_CTL;
+ flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|
+ f_ResponseStatus|f_obey_p_check|f_bit1;
+ }
+ else if (famT_drive)
+ {
+ drvcmd[0]=CMDT_TRAY_CTL;
+ drvcmd[4]=0x03; /* "insert" */
+ }
+ i=cmd_out();
+ msg(DBG_LCS,"p_door_closed bit %d after\n", st_door_closed);
+ return (i);
+}
+/*==========================================================================*/
+static int cc_ReadSubQ(void)
+{
+ int i,j;
- DriveStruct[d].diskstate_flags |= frame_size_bit;
- return (0);
+ D_S[d].diskstate_flags &= ~subq_bit;
+ for (j=255;j>0;j--)
+ {
+ clr_cmdbuf();
+ if (fam1_drive)
+ {
+ drvcmd[0]=CMD1_READSUBQ;
+ flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+ response_count=11;
+ }
+ else if (fam2_drive)
+ {
+ drvcmd[0]=CMD2_READSUBQ;
+ drvcmd[1]=0x02;
+ drvcmd[3]=0x01;
+ flags_cmd_out=f_putcmd;
+ response_count=10;
+ }
+ else if (fam0L_drive)
+ {
+ drvcmd[0]=CMD0_READSUBQ;
+ drvcmd[1]=0x02;
+ if (famL_drive)
+ flags_cmd_out=f_putcmd;
+ else
+ flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+ response_count=13;
+ }
+ else if (famT_drive)
+ {
+ response_count=12;
+ drvcmd[0]=CMDT_READSUBQ;
+ drvcmd[1]=0x02;
+ drvcmd[2]=0x40;
+ drvcmd[3]=0x01;
+ drvcmd[8]=response_count;
+ }
+ i=cmd_out();
+ if (i<0) return (i);
+ for (i=0;i<response_count;i++)
+ {
+ sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
+ msgbuf[i*3]=0;
+ msg(DBG_SQ1,"cc_ReadSubQ:%s\n", msgbuf);
+ }
+ if (famT_drive) break;
+ if (infobuf[0]!=0) break;
+ if ((!st_spinning) || (j==1))
+ {
+ D_S[d].SubQ_ctl_adr=D_S[d].SubQ_trk=D_S[d].SubQ_pnt_idx=D_S[d].SubQ_whatisthis=0;
+ D_S[d].SubQ_run_tot=D_S[d].SubQ_run_trk=0;
+ return (0);
+ }
+ }
+ if (famT_drive) D_S[d].SubQ_ctl_adr=infobuf[1];
+ else D_S[d].SubQ_ctl_adr=swap_nibbles(infobuf[1]);
+ D_S[d].SubQ_trk=byt2bcd(infobuf[2]);
+ D_S[d].SubQ_pnt_idx=byt2bcd(infobuf[3]);
+ if (fam0L_drive) i=5;
+ else if (fam12_drive) i=4;
+ else if (famT_drive) i=8;
+ D_S[d].SubQ_run_tot=make32(make16(0,infobuf[i]),make16(infobuf[i+1],infobuf[i+2])); /* msf-bin */
+ i=7;
+ if (fam0L_drive) i=9;
+ else if (fam12_drive) i=7;
+ else if (famT_drive) i=4;
+ D_S[d].SubQ_run_trk=make32(make16(0,infobuf[i]),make16(infobuf[i+1],infobuf[i+2])); /* msf-bin */
+ D_S[d].SubQ_whatisthis=infobuf[i+3];
+ D_S[d].diskstate_flags |= subq_bit;
+ return (0);
+}
+/*==========================================================================*/
+static int cc_ModeSense(void)
+{
+ int i;
+
+ if (fam2_drive) return (0);
+ D_S[d].diskstate_flags &= ~frame_size_bit;
+ clr_cmdbuf();
+ if (fam1_drive)
+ {
+ response_count=5;
+ drvcmd[0]=CMD1_GETMODE;
+ flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+ }
+ else if (fam0L_drive)
+ {
+ response_count=2;
+ drvcmd[0]=CMD0_GETMODE;
+ if (famL_drive) flags_cmd_out=f_putcmd;
+ else flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+ }
+ else if (famT_drive)
+ {
+ response_count=10;
+ drvcmd[0]=CMDT_GETMODE;
+ drvcmd[4]=response_count;
+ }
+ i=cmd_out();
+ if (i<0) return (i);
+ i=0;
+ if (fam1_drive) D_S[d].sense_byte=infobuf[i++];
+ else if (fam0L_drive) D_S[d].sense_byte=0;
+ else if (famT_drive)
+ {
+ D_S[d].sense_byte=0;
+ if (infobuf[4]==0x01) D_S[d].xa_byte=0x20; /* wrong!!!! */
+ i=2;
+ }
+ D_S[d].frame_size=make16(infobuf[i],infobuf[i+1]);
+ for (i=0;i<response_count;i++)
+ sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
+ msgbuf[i*3]=0;
+ msg(DBG_XA1,"cc_ModeSense:%s\n", msgbuf);
+
+ D_S[d].diskstate_flags |= frame_size_bit;
+ return (0);
}
/*==========================================================================*/
/*==========================================================================*/
-static int xx_ModeSelect(int framesize)
+static int cc_ModeSelect(int framesize)
{
- int i;
-
- DriveStruct[d].diskstate_flags &= ~frame_size_bit;
- clr_cmdbuf();
- DriveStruct[d].frame_size=framesize;
- if (framesize==CD_FRAMESIZE_RAW) DriveStruct[d].sense_byte=0x82;
- else DriveStruct[d].sense_byte=0x00;
-
- DPRINTF((DBG_XA,"SBPCD: xx_ModeSelect: %02X %04X\n",
- DriveStruct[d].sense_byte, DriveStruct[d].frame_size));
-
- if (new_drive)
- {
- drvcmd[0]=0x09;
- drvcmd[1]=0x00;
- drvcmd[2]=DriveStruct[d].sense_byte;
- drvcmd[3]=(DriveStruct[d].frame_size>>8)&0xFF;
- drvcmd[4]=DriveStruct[d].frame_size&0xFF;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- }
- else
- {
- drvcmd[0]=0x84;
- drvcmd[1]=0x00;
- drvcmd[2]=(DriveStruct[d].frame_size>>8)&0xFF;
- drvcmd[3]=DriveStruct[d].frame_size&0xFF;
- drvcmd[4]=0x00;
- flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
- }
- response_count=0;
- i=cmd_out();
- if (i<0) return (i);
- DriveStruct[d].diskstate_flags |= frame_size_bit;
- return (0);
+ int i;
+
+ if (fam2_drive) return (0);
+ D_S[d].diskstate_flags &= ~frame_size_bit;
+ clr_cmdbuf();
+ D_S[d].frame_size=framesize;
+ if (framesize==CD_FRAMESIZE_RAW) D_S[d].sense_byte=0x82;
+ else D_S[d].sense_byte=0x00;
+
+ msg(DBG_XA1,"cc_ModeSelect: %02X %04X\n",
+ D_S[d].sense_byte, D_S[d].frame_size);
+
+ if (fam1_drive)
+ {
+ drvcmd[0]=CMD1_SETMODE;
+ drvcmd[1]=0x00;
+ drvcmd[2]=D_S[d].sense_byte;
+ drvcmd[3]=(D_S[d].frame_size>>8)&0xFF;
+ drvcmd[4]=D_S[d].frame_size&0xFF;
+ flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+ }
+ else if (fam0L_drive)
+ {
+ drvcmd[0]=CMD0_SETMODE;
+ drvcmd[1]=0x00;
+ drvcmd[2]=(D_S[d].frame_size>>8)&0xFF;
+ drvcmd[3]=D_S[d].frame_size&0xFF;
+ drvcmd[4]=0x00;
+ if(famL_drive)
+ flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check;
+ else
+ flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+ }
+ else if (famT_drive)
+ {
+ return (-1);
+ }
+ response_count=0;
+ i=cmd_out();
+ if (i<0) return (i);
+ D_S[d].diskstate_flags |= frame_size_bit;
+ return (0);
}
/*==========================================================================*/
-#if 0000
-static int xx_TellVolume(void)
+static int cc_GetVolume(void)
+{
+ int i;
+ u_char switches;
+ u_char chan0=0;
+ u_char vol0=0;
+ u_char chan1=1;
+ u_char vol1=0;
+
+ D_S[d].diskstate_flags &= ~volume_bit;
+ clr_cmdbuf();
+ if (fam1_drive)
+ {
+ drvcmd[0]=CMD1_GETMODE;
+ drvcmd[1]=0x05;
+ response_count=5;
+ flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+ }
+ else if (fam2_drive)
+ {
+ drvcmd[0]=CMD2_GETMODE;
+ drvcmd[1]=0x0E;
+ response_count=5;
+ flags_cmd_out=f_putcmd;
+ }
+ else if (fam0L_drive)
+ {
+ drvcmd[0]=CMD0_GETMODE;
+ drvcmd[1]=0x03;
+ response_count=2;
+ if(famL_drive)
+ flags_cmd_out=f_putcmd;
+ else
+ flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+ }
+ else if (famT_drive)
+ {
+ i=cc_get_mode_T();
+ if (i<0) return (i);
+ }
+ if (!famT_drive)
+ {
+ i=cmd_out();
+ if (i<0) return (i);
+ }
+ if (fam1_drive)
+ {
+ chan0=infobuf[1]&0x0F;
+ vol0=infobuf[2];
+ chan1=infobuf[3]&0x0F;
+ vol1=infobuf[4];
+ if (chan0==0)
+ {
+ chan0=1;
+ vol0=0;
+ }
+ if (chan1==0)
+ {
+ chan1=2;
+ vol1=0;
+ }
+ chan0 >>= 1;
+ chan1 >>= 1;
+ }
+ else if (fam2_drive)
+ {
+ chan0=infobuf[1];
+ vol0=infobuf[2];
+ chan1=infobuf[3];
+ vol1=infobuf[4];
+ }
+ else if (famL_drive)
+ {
+ chan0=0;
+ chan1=1;
+ vol0=vol1=infobuf[1];
+ switches=infobuf[0];
+ if ((switches&0x80)!=0) chan0=1;
+ if ((switches&0x40)!=0) chan1=0;
+ }
+ else if (fam0_drive) /* different firmware levels */
+ {
+ chan0=0;
+ chan1=1;
+ vol0=vol1=infobuf[1];
+ if (D_S[d].drv_type>=drv_201)
+ {
+ if (D_S[d].drv_type<drv_300)
+ {
+ switches=infobuf[0];
+ if ((switches&0x80)!=0) vol0=0;
+ if ((switches&0x40)!=0) vol1=0;
+ if (D_S[d].drv_type>=drv_211)
+ {
+ if ((switches&0x20)!=0) chan0=1;
+ if ((switches&0x10)!=0) chan1=0;
+ }
+ }
+ else
+ {
+ vol0=infobuf[0];
+ if ((vol0&0x01)!=0) chan0=1;
+ if ((vol1&0x01)==0) chan1=0;
+ vol0 &= 0xFC;
+ vol1 &= 0xFC;
+ if (vol0!=0) vol0 += 3;
+ if (vol1!=0) vol1 += 3;
+ }
+ }
+ }
+ else if (famT_drive)
+ {
+ D_S[d].volume_control=infobuf[7];
+ chan0=0;
+ chan1=1;
+ if (D_S[d].volume_control&0x10) vol0=0;
+ else vol0=0xff;
+ if (D_S[d].volume_control&0x20) vol1=0;
+ else vol1=0xff;
+ }
+ D_S[d].vol_chan0=chan0;
+ D_S[d].vol_ctrl0=vol0;
+ D_S[d].vol_chan1=chan1;
+ D_S[d].vol_ctrl1=vol1;
+#if 000
+ D_S[d].vol_chan2=2;
+ D_S[d].vol_ctrl2=0xFF;
+ D_S[d].vol_chan3=3;
+ D_S[d].vol_ctrl3=0xFF;
+#endif 000
+ D_S[d].diskstate_flags |= volume_bit;
+ return (0);
+}
+/*==========================================================================*/
+static int cc_ReadCapacity(void)
{
- int i;
- u_char switches;
- u_char chan0,vol0,chan1,vol1;
+ int i, j;
+
+ if (famL_drive) return (0);
+ D_S[d].diskstate_flags &= ~cd_size_bit;
+ for (j=3;j>0;j--)
+ {
+ clr_cmdbuf();
+ if (fam1_drive)
+ {
+ drvcmd[0]=CMD1_CAPACITY;
+ response_count=5;
+ flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+ }
+ else if (fam2_drive)
+ {
+ drvcmd[0]=CMD2_CAPACITY;
+ response_count=8;
+ flags_cmd_out=f_putcmd;
+ }
+ else if (fam0_drive)
+ {
+ drvcmd[0]=CMD0_CAPACITY;
+ response_count=5;
+ flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+ }
+ else if (famT_drive)
+ {
+ response_count=12;
+ drvcmd[0]=CMDT_DISKINFO;
+ drvcmd[1]=0x02;
+ drvcmd[6]=CDROM_LEADOUT;
+ drvcmd[8]=response_count;
+ drvcmd[9]=0x00;
+ }
+ i=cmd_out();
+ if (i>=0) break;
+ msg(DBG_000,"cc_ReadCapacity: cmd_out: err %d\n", i);
+ cc_ReadError();
+ }
+ if (j==0) return (i);
+ if (fam1_drive) D_S[d].CDsize_frm=msf2blk(make32(make16(0,infobuf[0]),make16(infobuf[1],infobuf[2])))+CD_BLOCK_OFFSET;
+ else if (fam0_drive) D_S[d].CDsize_frm=make32(make16(0,infobuf[0]),make16(infobuf[1],infobuf[2]));
+ else if (fam2_drive) D_S[d].CDsize_frm=make32(make16(infobuf[0],infobuf[1]),make16(infobuf[2],infobuf[3]));
+ else if (famT_drive)
+ {
+ D_S[d].CDsize_frm=make32(make16(infobuf[8],infobuf[9]),make16(infobuf[10],infobuf[11]));
+ D_S[d].n_first_track=infobuf[2];
+ D_S[d].n_last_track=infobuf[3];
- DriveStruct[d].diskstate_flags &= ~volume_bit;
- clr_cmdbuf();
- if (new_drive)
- {
- drvcmd[0]=0x84;
- drvcmd[1]=0x05;
- response_count=5;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- }
- else
- {
- drvcmd[0]=0x85;
- drvcmd[1]=0x03;
- response_count=2;
- flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
- }
- i=cmd_out();
- if (i<0) return (i);
- if (new_drive)
- {
- chan0=infobuf[1]&0x0F;
- vol0=infobuf[2];
- chan1=infobuf[3]&0x0F;
- vol1=infobuf[4];
- if (chan0==0)
- {
- chan0=1;
- vol0=0;
- }
- if (chan1==0)
- {
- chan1=2;
- vol1=0;
- }
- chan0 >>= 1;
- chan1 >>= 1;
- }
- else if (lcs_drive)
- {
- chan0=0;
- chan1=1;
- vol0=vol1=infobuf[1];
- switches=infobuf[0];
- if ((switches&0x80)!=0) chan0=1;
- if ((switches&0x40)!=0) chan1=0;
- }
- else /* old_drive, different firmware levels */
- {
- chan0=0;
- chan1=1;
- vol0=vol1=infobuf[1];
- if (DriveStruct[d].drv_type>=drv_201)
- {
- if (DriveStruct[d].drv_type<drv_300)
- {
- switches=infobuf[0];
- if ((switches&0x80)!=0) vol0=0;
- if ((switches&0x40)!=0) vol1=0;
- if (DriveStruct[d].drv_type>=drv_211)
- {
- if ((switches&0x20)!=0) chan0=1;
- if ((switches&0x10)!=0) chan1=0;
- }
- }
- else
- {
- vol0=infobuf[0];
- if ((vol0&0x01)!=0) chan0=1;
- if ((vol1&0x01)==0) chan1=0;
- vol0 &= 0xFC;
- vol1 &= 0xFC;
- if (vol0!=0) vol0 += 3;
- if (vol1!=0) vol1 += 3;
- }
}
- }
- DriveStruct[d].vol_chan0=chan0;
- DriveStruct[d].vol_ctrl0=vol0;
- DriveStruct[d].vol_chan1=chan1;
- DriveStruct[d].vol_ctrl1=vol1;
- DriveStruct[d].vol_chan2=2;
- DriveStruct[d].vol_ctrl2=0xFF;
- DriveStruct[d].vol_chan3=3;
- DriveStruct[d].vol_ctrl3=0xFF;
- DriveStruct[d].diskstate_flags |= volume_bit;
- return (0);
+ D_S[d].diskstate_flags |= cd_size_bit;
+ msg(DBG_000,"cc_ReadCapacity: %d frames.\n", D_S[d].CDsize_frm);
+ return (0);
}
+/*==========================================================================*/
+static int cc_ReadTocDescr(void)
+{
+ int i;
+
+ D_S[d].diskstate_flags &= ~toc_bit;
+ clr_cmdbuf();
+ if (fam1_drive)
+ {
+ drvcmd[0]=CMD1_DISKINFO;
+ response_count=6;
+ flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+ }
+ else if (fam0L_drive)
+ {
+ drvcmd[0]=CMD0_DISKINFO;
+ response_count=6;
+ if(famL_drive)
+ flags_cmd_out=f_putcmd;
+ else
+ flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+ }
+ else if (fam2_drive)
+ {
+ /* possibly longer timeout periods necessary */
+ D_S[d].f_multisession=0;
+ drvcmd[0]=CMD2_DISKINFO;
+ drvcmd[1]=0x02;
+ drvcmd[2]=0xAB;
+ drvcmd[3]=0xFF; /* session */
+ response_count=8;
+ flags_cmd_out=f_putcmd;
+ }
+ else if (famT_drive)
+ {
+ D_S[d].f_multisession=0;
+ response_count=12;
+ drvcmd[0]=CMDT_DISKINFO;
+ drvcmd[1]=0x02;
+ drvcmd[6]=CDROM_LEADOUT;
+ drvcmd[8]=response_count;
+ drvcmd[9]=0x00;
+ }
+ i=cmd_out();
+ if (i<0) return (i);
+ if ((fam1_drive)||(fam2_drive)||(famL_drive)||(fam0_drive))
+ D_S[d].xa_byte=infobuf[0];
+ if (fam2_drive)
+ {
+ D_S[d].first_session=infobuf[1];
+ D_S[d].last_session=infobuf[2];
+ D_S[d].n_first_track=infobuf[3];
+ D_S[d].n_last_track=infobuf[4];
+ if (D_S[d].first_session!=D_S[d].last_session)
+ {
+ D_S[d].f_multisession=1;
+ D_S[d].lba_multi=msf2blk(make32(make16(0,infobuf[5]),make16(infobuf[6],infobuf[7])));
+ }
+#if 0
+ if (D_S[d].first_session!=D_S[d].last_session)
+ {
+ if (D_S[d].last_session<=20)
+ zwanzig=D_S[d].last_session+1;
+ else zwanzig=20;
+ for (count=D_S[d].first_session;count<zwanzig;count++)
+ {
+ drvcmd[0]=CMD2_DISKINFO;
+ drvcmd[1]=0x02;
+ drvcmd[2]=0xAB;
+ drvcmd[3]=count;
+ response_count=8;
+ flags_cmd_out=f_putcmd;
+ i=cmd_out();
+ if (i<0) return (i);
+ D_S[d].msf_multi_n[count]=make32(make16(0,infobuf[5]),make16(infobuf[6],infobuf[7]));
+ }
+ D_S[d].diskstate_flags |= multisession_bit;
+ }
#endif
+ drvcmd[0]=CMD2_DISKINFO;
+ drvcmd[1]=0x02;
+ drvcmd[2]=0xAA;
+ drvcmd[3]=0xFF;
+ response_count=5;
+ flags_cmd_out=f_putcmd;
+ i=cmd_out();
+ if (i<0) return (i);
+ D_S[d].size_msf=make32(make16(0,infobuf[2]),make16(infobuf[3],infobuf[4]));
+ D_S[d].size_blk=msf2blk(D_S[d].size_msf);
+ }
+ else if (famT_drive)
+ {
+ D_S[d].size_msf=make32(make16(infobuf[8],infobuf[9]),make16(infobuf[10],infobuf[11]));
+ D_S[d].size_blk=msf2blk(D_S[d].size_msf);
+ D_S[d].n_first_track=infobuf[2];
+ D_S[d].n_last_track=infobuf[3];
+ }
+ else
+ {
+ D_S[d].n_first_track=infobuf[1];
+ D_S[d].n_last_track=infobuf[2];
+ D_S[d].size_msf=make32(make16(0,infobuf[3]),make16(infobuf[4],infobuf[5]));
+ D_S[d].size_blk=msf2blk(D_S[d].size_msf);
+ if (famL_drive) D_S[d].CDsize_frm=D_S[d].size_blk+1;
+ }
+ D_S[d].diskstate_flags |= toc_bit;
+ msg(DBG_TOC,"TocDesc: %02X %02X %02X %08X\n",
+ D_S[d].xa_byte,
+ D_S[d].n_first_track,
+ D_S[d].n_last_track,
+ D_S[d].size_msf);
+ return (0);
+}
/*==========================================================================*/
-static int xx_ReadCapacity(void)
+static int cc_ReadTocEntry(int num)
{
- int i;
-
- DriveStruct[d].diskstate_flags &= ~cd_size_bit;
- clr_cmdbuf();
- if (new_drive)
- {
- drvcmd[0]=0x85;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- }
- else
- {
- drvcmd[0]=0x88;
- flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
- }
- response_count=5;
- i=cmd_out();
- if (i<0) return (i);
- DriveStruct[d].CDsize_blk=make32(make16(0,infobuf[0]),make16(infobuf[1],infobuf[2]));
- if (new_drive) DriveStruct[d].CDsize_blk=msf2blk(DriveStruct[d].CDsize_blk);
- DriveStruct[d].CDsize_frm = (DriveStruct[d].CDsize_blk * make16(infobuf[3],infobuf[4])) / CD_FRAMESIZE;
- DriveStruct[d].CDsize_blk += 151;
- DriveStruct[d].diskstate_flags |= cd_size_bit;
- return (0);
+ int i;
+
+ clr_cmdbuf();
+ if (fam1_drive)
+ {
+ drvcmd[0]=CMD1_READTOC;
+ drvcmd[2]=num;
+ response_count=8;
+ flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+ }
+ else if (fam2_drive)
+ {
+ /* possibly longer timeout periods necessary */
+ drvcmd[0]=CMD2_DISKINFO;
+ drvcmd[1]=0x02;
+ drvcmd[2]=num;
+ response_count=5;
+ flags_cmd_out=f_putcmd;
+ }
+ else if (fam0L_drive)
+ {
+ drvcmd[0]=CMD0_READTOC;
+ drvcmd[1]=0x02;
+ drvcmd[2]=num;
+ response_count=8;
+ if(famL_drive)
+ flags_cmd_out=f_putcmd;
+ else
+ flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+ }
+ else if (famT_drive)
+ {
+ response_count=12;
+ drvcmd[0]=CMDT_DISKINFO;
+ drvcmd[1]=0x02;
+ drvcmd[6]=num;
+ drvcmd[8]=response_count;
+ drvcmd[9]=0x00;
+ }
+ i=cmd_out();
+ if (i<0) return (i);
+ if ((fam1_drive)||(famL_drive)||(fam0_drive))
+ {
+ D_S[d].TocEnt_nixbyte=infobuf[0];
+ i=1;
+ }
+ else if (fam2_drive) i=0;
+ else if (famT_drive)
+ {
+ i=5;
+ }
+ D_S[d].TocEnt_ctl_adr=swap_nibbles(infobuf[i++]);
+ if ((fam1_drive)||(famL_drive)||(fam0_drive))
+ {
+ D_S[d].TocEnt_number=infobuf[i++];
+ D_S[d].TocEnt_format=infobuf[i];
+ }
+ else D_S[d].TocEnt_number=num;
+ if (fam1_drive) i=4;
+ else if (fam0L_drive) i=5;
+ else if (fam2_drive) i=2;
+ else if (famT_drive) i=9;
+ D_S[d].TocEnt_address=make32(make16(0,infobuf[i]),
+ make16(infobuf[i+1],infobuf[i+2]));
+ msg(DBG_TOC,"TocEntry: %02X %02X %02X %02X %08X\n",
+ D_S[d].TocEnt_nixbyte, D_S[d].TocEnt_ctl_adr,
+ D_S[d].TocEnt_number, D_S[d].TocEnt_format,
+ D_S[d].TocEnt_address);
+ return (0);
}
/*==========================================================================*/
-static int xx_ReadTocDescr(void)
+static int cc_ReadPacket(void)
{
- int i;
-
- DriveStruct[d].diskstate_flags &= ~toc_bit;
- clr_cmdbuf();
- if (new_drive)
- {
- drvcmd[0]=0x8B;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- }
- else
- {
- drvcmd[0]=0x8B;
- flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
- }
- response_count=6;
- i=cmd_out();
- if (i<0) return (i);
- DriveStruct[d].xa_byte=infobuf[0];
- DriveStruct[d].n_first_track=infobuf[1];
- DriveStruct[d].n_last_track=infobuf[2];
- DriveStruct[d].size_msf=make32(make16(0,infobuf[3]),make16(infobuf[4],infobuf[5]));
- DriveStruct[d].size_blk=msf2blk(DriveStruct[d].size_msf);
- DriveStruct[d].diskstate_flags |= toc_bit;
- DPRINTF((DBG_TOC,"SBPCD: TocDesc: %02X %02X %02X %08X\n",
- DriveStruct[d].xa_byte,DriveStruct[d].n_first_track,DriveStruct[d].n_last_track,DriveStruct[d].size_msf));
- return (0);
-}
-/*==========================================================================*/
-static int xx_ReadTocEntry(int num)
-{
- int i;
-
- clr_cmdbuf();
- if (new_drive)
- {
- drvcmd[0]=0x8C;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- }
- else
- {
- drvcmd[0]=0x8C;
- drvcmd[1]=0x02;
- flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
- }
- drvcmd[2]=num;
- response_count=8;
- i=cmd_out();
- if (i<0) return (i);
- DriveStruct[d].TocEnt_nixbyte=infobuf[0];
- DriveStruct[d].TocEnt_ctl_adr=swap_nibbles(infobuf[1]);
- DriveStruct[d].TocEnt_number=infobuf[2];
- DriveStruct[d].TocEnt_format=infobuf[3];
- if (new_drive) i=4;
- else i=5;
- DriveStruct[d].TocEnt_address=make32(make16(0,infobuf[i]),
- make16(infobuf[i+1],infobuf[i+2]));
- DPRINTF((DBG_TOC,"SBPCD: TocEntry: %02X %02X %02X %02X %08X\n",
- DriveStruct[d].TocEnt_nixbyte, DriveStruct[d].TocEnt_ctl_adr,
- DriveStruct[d].TocEnt_number, DriveStruct[d].TocEnt_format,
- DriveStruct[d].TocEnt_address));
- return (0);
-}
-/*==========================================================================*/
-static int xx_ReadPacket(void)
-{
- int i;
-
- clr_cmdbuf();
- drvcmd[0]=0x8E;
- drvcmd[1]=response_count;
- flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
- i=cmd_out();
- return (i);
+ int i;
+
+ clr_cmdbuf();
+ drvcmd[0]=CMD0_PACKET;
+ drvcmd[1]=response_count;
+ if(famL_drive) flags_cmd_out=f_putcmd;
+ else if (fam01_drive)
+ flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+ else if (fam2_drive) return (-1); /* not implemented yet */
+ else if (famT_drive)
+ {
+ return (-1);
+ }
+ i=cmd_out();
+ return (i);
}
/*==========================================================================*/
static int convert_UPC(u_char *p)
{
- int i;
-
- p++;
- if (!new_drive) p[13]=0;
- for (i=0;i<7;i++)
- {
- if (new_drive) DriveStruct[d].UPC_buf[i]=swap_nibbles(*p++);
- else
+ int i;
+
+ p++;
+ if (fam0L_drive) p[13]=0;
+ for (i=0;i<7;i++)
{
- DriveStruct[d].UPC_buf[i]=((*p++)<<4)&0xFF;
- DriveStruct[d].UPC_buf[i] |= *p++;
+ if (fam1_drive) D_S[d].UPC_buf[i]=swap_nibbles(*p++);
+ else if (fam0L_drive)
+ {
+ D_S[d].UPC_buf[i]=((*p++)<<4)&0xFF;
+ D_S[d].UPC_buf[i] |= *p++;
+ }
+ else if (famT_drive)
+ {
+ return (-1);
+ }
+ else /* CD200 */
+ {
+ return (-1);
+ }
}
- }
- DriveStruct[d].UPC_buf[6] &= 0xF0;
- return (0);
+ D_S[d].UPC_buf[6] &= 0xF0;
+ return (0);
}
/*==========================================================================*/
-static int xx_ReadUPC(void)
+static int cc_ReadUPC(void)
{
- int i;
+ int i;
#if TEST_UPC
- int block, checksum;
+ int block, checksum;
#endif TEST_UPC
-
- DriveStruct[d].diskstate_flags &= ~upc_bit;
+
+ if (fam2_drive) return (0); /* not implemented yet */
+ if (famT_drive) return (0); /* not implemented yet */
+#if 1
+ if (fam0_drive) return (0); /* but it should work */
+#endif 1
+
+ D_S[d].diskstate_flags &= ~upc_bit;
#if TEST_UPC
- for (block=CD_BLOCK_OFFSET+1;block<CD_BLOCK_OFFSET+200;block++)
- {
-#endif TEST_UPC
- clr_cmdbuf();
- if (new_drive)
+ for (block=CD_BLOCK_OFFSET+1;block<CD_BLOCK_OFFSET+200;block++)
{
- drvcmd[0]=0x88;
+#endif TEST_UPC
+ clr_cmdbuf();
+ if (fam1_drive)
+ {
+ drvcmd[0]=CMD1_READ_UPC;
#if TEST_UPC
- drvcmd[1]=(block>>16)&0xFF;
- drvcmd[2]=(block>>8)&0xFF;
- drvcmd[3]=block&0xFF;
+ drvcmd[1]=(block>>16)&0xFF;
+ drvcmd[2]=(block>>8)&0xFF;
+ drvcmd[3]=block&0xFF;
#endif TEST_UPC
- response_count=8;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- }
- else
- {
- drvcmd[0]=0x08;
+ response_count=8;
+ flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+ }
+ else if (fam0L_drive)
+ {
+ drvcmd[0]=CMD0_READ_UPC;
#if TEST_UPC
- drvcmd[2]=(block>>16)&0xFF;
- drvcmd[3]=(block>>8)&0xFF;
- drvcmd[4]=block&0xFF;
+ drvcmd[2]=(block>>16)&0xFF;
+ drvcmd[3]=(block>>8)&0xFF;
+ drvcmd[4]=block&0xFF;
#endif TEST_UPC
- response_count=0;
- flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
- }
- i=cmd_out();
- if (i<0) return (i);
- if (!new_drive)
- {
- response_count=16;
- i=xx_ReadPacket();
- if (i<0) return (i);
- }
+ response_count=0;
+ flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
+ }
+ else if (fam2_drive)
+ {
+ return (-1);
+ }
+ else if (famT_drive)
+ {
+ return (-1);
+ }
+ i=cmd_out();
+ if (i<0)
+ {
+ msg(DBG_000,"cc_ReadUPC cmd_out: err %d\n", i);
+ return (i);
+ }
+ if (fam0L_drive)
+ {
+ response_count=16;
+ if (famL_drive) flags_cmd_out=f_putcmd;
+ i=cc_ReadPacket();
+ if (i<0)
+ {
+ msg(DBG_000,"cc_ReadUPC ReadPacket: err %d\n", i);
+ return (i);
+ }
+ }
#if TEST_UPC
- checksum=0;
+ checksum=0;
#endif TEST_UPC
- DPRINTF((DBG_UPC,"SBPCD: UPC info: "));
- for (i=0;i<(new_drive?8:16);i++)
- {
+ for (i=0;i<(fam1_drive?8:16);i++)
+ {
#if TEST_UPC
- checksum |= infobuf[i];
+ checksum |= infobuf[i];
#endif TEST_UPC
- DPRINTF((DBG_UPC,"%02X ", infobuf[i]));
- }
- DPRINTF((DBG_UPC,"\n"));
+ sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
+ }
+ msgbuf[i*3]=0;
+ msg(DBG_UPC,"UPC info:%s\n", msgbuf);
#if TEST_UPC
- if ((checksum&0x7F)!=0) break;
- }
+ if ((checksum&0x7F)!=0) break;
+ }
#endif TEST_UPC
- DriveStruct[d].UPC_ctl_adr=0;
- if (new_drive) i=0;
- else i=2;
- if ((infobuf[i]&0x80)!=0)
- {
- convert_UPC(&infobuf[i]);
- DriveStruct[d].UPC_ctl_adr = (DriveStruct[d].TocEnt_ctl_adr & 0xF0) | 0x02;
- }
-
- DPRINTF((DBG_UPC,"SBPCD: UPC code: "));
- DPRINTF((DBG_UPC,"(%02X) ", DriveStruct[d].UPC_ctl_adr));
- for (i=0;i<7;i++)
- {
- DPRINTF((DBG_UPC,"%02X ", DriveStruct[d].UPC_buf[i]));
- }
- DPRINTF((DBG_UPC,"\n"));
-
- DriveStruct[d].diskstate_flags |= upc_bit;
- return (0);
+ D_S[d].UPC_ctl_adr=0;
+ if (fam1_drive) i=0;
+ else i=2;
+ if ((infobuf[i]&0x80)!=0)
+ {
+ convert_UPC(&infobuf[i]);
+ D_S[d].UPC_ctl_adr = (D_S[d].TocEnt_ctl_adr & 0xF0) | 0x02;
+ }
+ for (i=0;i<7;i++)
+ sprintf(&msgbuf[i*3], " %02X", D_S[d].UPC_buf[i]);
+ sprintf(&msgbuf[i*3], " (%02X)", D_S[d].UPC_ctl_adr);
+ msgbuf[i*3+5]=0;
+ msg(DBG_UPC,"UPC code:%s\n", msgbuf);
+ D_S[d].diskstate_flags |= upc_bit;
+ return (0);
}
/*==========================================================================*/
-static int yy_CheckMultiSession(void)
+static int cc_CheckMultiSession(void)
{
- int i;
-
- DriveStruct[d].f_multisession=0;
- clr_cmdbuf();
- if (new_drive)
- {
- drvcmd[0]=0x8D;
- response_count=6;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- i=cmd_out();
- if (i<0) return (i);
- if ((infobuf[0]&0x80)!=0)
- {
- DPRINTF((DBG_MUL,"SBPCD: MultiSession CD detected: %02X %02X %02X %02X %02X %02X\n",
- infobuf[0], infobuf[1], infobuf[2],
- infobuf[3], infobuf[4], infobuf[5]));
- DriveStruct[d].f_multisession=1;
- DriveStruct[d].lba_multi=msf2blk(make32(make16(0,infobuf[1]),
- make16(infobuf[2],infobuf[3])));
- DriveStruct[d].last_redirect=20;
- /* preliminary - has to get adjusted the following way:
- * look at the first byte of frames 17 ff. until 0xFF is seen
- * the frame before this one is the last continuation frame
- */
+ int i;
+
+ if (fam2_drive) return (0);
+ D_S[d].f_multisession=0;
+ D_S[d].lba_multi=0;
+ if (fam0_drive) return (0);
+ clr_cmdbuf();
+ if (fam1_drive)
+ {
+ drvcmd[0]=CMD1_MULTISESS;
+ response_count=6;
+ flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+ i=cmd_out();
+ if (i<0) return (i);
+ if ((infobuf[0]&0x80)!=0)
+ {
+ D_S[d].f_multisession=1;
+ D_S[d].lba_multi=msf2blk(make32(make16(0,infobuf[1]),
+ make16(infobuf[2],infobuf[3])));
+ }
}
- }
- else if (lcs_drive)
- {
- drvcmd[0]=0x8C;
- drvcmd[1]=3;
- drvcmd[2]=1;
- response_count=8;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- i=cmd_out();
- if (i<0) return (i);
- DPRINTF((DBG_MUL,"SBPCD: MultiSession Info: %02X %02X %02X %02X %02X %02X %02X %02X\n",
- infobuf[0], infobuf[1], infobuf[2], infobuf[3],
- infobuf[4], infobuf[5], infobuf[6], infobuf[7]));
- DriveStruct[d].lba_multi=msf2blk(make32(make16(0,infobuf[5]),
- make16(infobuf[6],infobuf[7])));
- if (DriveStruct[d].lba_multi>200)
- {
- DPRINTF((DBG_MUL,"SBPCD: MultiSession base: %06X\n", DriveStruct[d].lba_multi));
- DriveStruct[d].f_multisession=1;
- DriveStruct[d].last_redirect=20;
- /* preliminary - has to get adjusted the following way:
- * look at the first byte of frames 17 ff. until 0xFF is seen;
- * the frame before this one is the last repetition frame.
- */
+ else if (famL_drive)
+ {
+ drvcmd[0]=CMDL_MULTISESS;
+ drvcmd[1]=3;
+ drvcmd[2]=1;
+ response_count=8;
+ flags_cmd_out=f_putcmd;
+ i=cmd_out();
+ if (i<0) return (i);
+ D_S[d].lba_multi=msf2blk(make32(make16(0,infobuf[5]),
+ make16(infobuf[6],infobuf[7])));
}
- }
- return (0);
+ else if (famT_drive)
+ {
+ response_count=12;
+ drvcmd[0]=CMDT_DISKINFO;
+ drvcmd[1]=0x02;
+ drvcmd[6]=0;
+ drvcmd[8]=response_count;
+ drvcmd[9]=0x40;
+ i=cmd_out();
+ if (i<0) return (i);
+ D_S[d].lba_multi=msf2blk(make32(make16(0,infobuf[9]),make16(infobuf[10],infobuf[11])));
+ }
+ for (i=0;i<response_count;i++)
+ sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
+ msgbuf[i*3]=0;
+ msg(DBG_MUL,"MultiSession Info:%s (%d)\n", msgbuf, D_S[d].lba_multi);
+ if (D_S[d].lba_multi>200)
+ {
+ D_S[d].f_multisession=1;
+ msg(DBG_MUL,"MultiSession base: %06X\n", D_S[d].lba_multi);
+ }
+ return (0);
}
/*==========================================================================*/
#if FUTURE
-static int yy_SubChanInfo(int frame, int count, u_char *buffer)
-/* "frame" is a RED BOOK (msf-bin) address */
+static int cc_SubChanInfo(int frame, int count, u_char *buffer)
+ /* "frame" is a RED BOOK (msf-bin) address */
{
- int i;
-
- if (!new_drive) return (-ENOSYS); /* drive firmware lacks it */
+ int i;
+
+ if (fam0L_drive) return (-ENOSYS); /* drive firmware lacks it */
+ if (famT_drive)
+ {
+ return (-1);
+ }
#if 0
- if (DriveStruct[d].audio_state!=audio_playing) return (-ENODATA);
+ if (D_S[d].audio_state!=audio_playing) return (-ENODATA);
#endif
- clr_cmdbuf();
- drvcmd[0]=0x11;
- drvcmd[1]=(frame>>16)&0xFF;
- drvcmd[2]=(frame>>8)&0xFF;
- drvcmd[3]=frame&0xFF;
- drvcmd[5]=(count>>8)&0xFF;
- drvcmd[6]=count&0xFF;
- flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
- cmd_type=READ_SC;
- DriveStruct[d].frame_size=CD_FRAMESIZE_SUB;
- i=cmd_out(); /* which buffer to use? */
- return (i);
+ clr_cmdbuf();
+ drvcmd[0]=CMD1_SUBCHANINF;
+ drvcmd[1]=(frame>>16)&0xFF;
+ drvcmd[2]=(frame>>8)&0xFF;
+ drvcmd[3]=frame&0xFF;
+ drvcmd[5]=(count>>8)&0xFF;
+ drvcmd[6]=count&0xFF;
+ flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+ cmd_type=READ_SC;
+ D_S[d].frame_size=CD_FRAMESIZE_SUB;
+ i=cmd_out(); /* which buffer to use? */
+ return (i);
}
#endif FUTURE
/*==========================================================================*/
static void check_datarate(void)
{
-#ifdef CDMKE
- int i=0;
-
- DPRINTF((DBG_IOX,"SBPCD: check_datarate entered.\n"));
- datarate=0;
+ int i=0;
+
+ msg(DBG_IOX,"check_datarate entered.\n");
+ datarate=0;
#if TEST_STI
- for (i=0;i<=1000;i++) printk(".");
+ for (i=0;i<=1000;i++) printk(".");
#endif
- /* set a timer to make (timed_out!=0) after 1.1 seconds */
-#if 0
- del_timer(&delay_timer);
+ /* set a timer to make (timed_out_delay!=0) after 1.1 seconds */
+#if 1
+ del_timer(&delay_timer);
#endif
- delay_timer.expires = 110;
- timed_out=0;
- add_timer(&delay_timer);
- DPRINTF((DBG_TIM,"SBPCD: timer started (110).\n"));
- do
- {
- i=inb(CDi_status);
- datarate++;
-#if 00000
- if (datarate>0x0FFFFFFF) break;
+ delay_timer.expires=110;
+ timed_out_delay=0;
+ add_timer(&delay_timer);
+ msg(DBG_TIM,"delay timer started (110).\n");
+ do
+ {
+ i=inb(CDi_status);
+ datarate++;
+#if 1
+ if (datarate>0x6FFFFFFF) break;
#endif 00000
- }
- while (!timed_out); /* originally looping for 1.1 seconds */
-#if 0
- del_timer(&delay_timer);
-#endif 0
- DPRINTF((DBG_TIM,"SBPCD: datarate: %d\n", datarate));
- if (datarate<65536) datarate=65536;
-
- maxtim16=datarate*16;
- maxtim04=datarate*4;
- maxtim02=datarate*2;
- maxtim_8=datarate/32;
+ }
+ while (!timed_out_delay);
+ del_timer(&delay_timer);
+ msg(DBG_TIM,"datarate: %04X\n", datarate);
+ if (datarate<65536) datarate=65536;
+ maxtim16=datarate*16;
+ maxtim04=datarate*4;
+ maxtim02=datarate*2;
+ maxtim_8=datarate/32;
#if LONG_TIMING
- maxtim_data=datarate/100;
+ maxtim_data=datarate/100;
#else
- maxtim_data=datarate/300;
+ maxtim_data=datarate/300;
#endif LONG_TIMING
- DPRINTF((DBG_TIM,"SBPCD: maxtim_8 %d, maxtim_data %d.\n",
- maxtim_8, maxtim_data));
-#endif CDMKE
+ msg(DBG_TIM,"maxtim_8 %d, maxtim_data %d.\n",
+ maxtim_8, maxtim_data);
+}
+/*==========================================================================*/
+#if 0
+static int c2_ReadError(int fam)
+{
+ int i;
+
+ clr_cmdbuf();
+ response_count=9;
+ clr_respo_buf(9);
+ if (fam==1)
+ {
+ drvcmd[0]=CMD0_READ_ERR; /* same as CMD1_ and CMDL_ */
+ i=do_cmd(f_putcmd|f_lopsta|f_getsta|f_ResponseStatus);
+ }
+ else if (fam==2)
+ {
+ drvcmd[0]=CMD2_READ_ERR;
+ i=do_cmd(f_putcmd);
+ }
+ else return (-1);
+ return (i);
+}
+#endif
+/*==========================================================================*/
+static void ask_mail(void)
+{
+ int i;
+
+ msg(DBG_INF, "please mail the following lines to emoenke@gwdg.de:\n");
+ msg(DBG_INF, "%s\n", VERSION);
+ msg(DBG_INF, "address %03X, type %s, drive %s (ID %d)\n",
+ CDo_command, type, D_S[d].drive_model, D_S[d].drv_id);
+ for (i=0;i<12;i++)
+ sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
+ msgbuf[i*3]=0;
+ msg(DBG_INF,"infobuf =%s\n", msgbuf);
+ for (i=0;i<12;i++)
+ sprintf(&msgbuf[i*3], " %c ", infobuf[i]);
+ msgbuf[i*3]=0;
+ msg(DBG_INF,"infobuf =%s\n", msgbuf);
}
/*==========================================================================*/
static int check_version(void)
{
- int i, j;
-
- DPRINTF((DBG_INI,"SBPCD: check_version entered.\n"));
- /* clear any pending error state */
- clr_cmdbuf();
- drvcmd[0]=0x82;
- response_count=9;
- flags_cmd_out=f_putcmd;
- i=cmd_out();
- if (i<0) DPRINTF((DBG_INI,"SBPCD: cmd_82 returns %d (ok anyway).\n",i));
-
- /* read drive version */
- clr_cmdbuf();
- for (i=0;i<12;i++) infobuf[i]=0;
- drvcmd[0]=0x83;
- response_count=12;
- flags_cmd_out=f_putcmd;
- i=cmd_out();
- if (i<0) DPRINTF((DBG_INI,"SBPCD: cmd_83 returns %d\n",i));
-
- DPRINTF((DBG_INI,"SBPCD: infobuf = \""));
- for (i=0;i<12;i++) DPRINTF((DBG_INI,"%c",infobuf[i]));
- DPRINTF((DBG_INI,"\"\n"));
-
- DriveStruct[d].drv_type=0;
- for (i=0;i<4;i++) if (infobuf[i]!=drive_family[i]) break;
- if (i==4)
- {
- DriveStruct[d].drive_model[0]=infobuf[i++];
- DriveStruct[d].drive_model[1]=infobuf[i++];
- DriveStruct[d].drive_model[2]='-';
- DriveStruct[d].drive_model[3]='x';
- DriveStruct[d].drv_type=drv_new;
- }
- if (!DriveStruct[d].drv_type)
- {
- for (i=0;i<8;i++) if (infobuf[i]!=drive_vendor[i]) break;
- if (i==8)
+ int i, j, l;
+ int teac_possible=0;
+
+ msg(DBG_INI,"check_version entered.\n");
+ msg(DBG_TE2,"check_version: id=%d, d=%d.\n", D_S[d].drv_id, d);
+ D_S[d].drv_type=0;
+
+ /* check for CR-52x, CR-56x and LCS-7260 */
+ /* clear any pending error state */
+ clr_cmdbuf();
+ drvcmd[0]=CMD0_READ_ERR; /* same as CMD1_ and CMDL_ */
+ response_count=9;
+ flags_cmd_out=f_putcmd;
+ i=cmd_out();
+ if (i<0) msg(DBG_INI,"CMD0_READERR returns %d (ok anyway).\n",i);
+ /* read drive version */
+ clr_cmdbuf();
+ for (i=0;i<12;i++) infobuf[i]=0;
+ drvcmd[0]=CMD0_READ_VER; /* same as CMD1_ and CMDL_ */
+ response_count=12; /* fam1: only 11 */
+ flags_cmd_out=f_putcmd;
+ i=cmd_out();
+ if (i<-1) msg(DBG_INI,"CMD0_READ_VER returns %d\n",i);
+ if (i==-11) teac_possible++;
+ j=0;
+ for (i=0;i<12;i++) j+=infobuf[i];
+ if (j)
{
- DriveStruct[d].drive_model[0]='2';
- DriveStruct[d].drive_model[1]='x';
- DriveStruct[d].drive_model[2]='-';
- DriveStruct[d].drive_model[3]='x';
- DriveStruct[d].drv_type=drv_old;
+ for (i=0;i<12;i++)
+ sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
+ msgbuf[i*3]=0;
+ msg(DBG_IDX,"infobuf =%s\n", msgbuf);
+ msg(DBG_000,"infobuf =%s\n", msgbuf);
+ for (i=0;i<12;i++)
+ sprintf(&msgbuf[i*3], " %c ", infobuf[i]);
+ msgbuf[i*3]=0;
+ msg(DBG_IDX,"infobuf =%s\n", msgbuf);
+ msg(DBG_000,"infobuf =%s\n", msgbuf);
}
- }
- if (!DriveStruct[d].drv_type)
- {
- for (i=0;i<4;i++) if (infobuf[i]!=lcs_family[i]) break;
- if (i==4)
+ for (i=0;i<4;i++) if (infobuf[i]!=family1[i]) break;
+ if (i==4)
{
- DriveStruct[d].drive_model[0]=infobuf[i++];
- DriveStruct[d].drive_model[1]=infobuf[i++];
- DriveStruct[d].drive_model[2]=infobuf[i++];
- DriveStruct[d].drive_model[3]=infobuf[i++];
- DriveStruct[d].drv_type=drv_lcs;
+ D_S[d].drive_model[0]='C';
+ D_S[d].drive_model[1]='R';
+ D_S[d].drive_model[2]='-';
+ D_S[d].drive_model[3]='5';
+ D_S[d].drive_model[4]=infobuf[i++];
+ D_S[d].drive_model[5]=infobuf[i++];
+ D_S[d].drive_model[6]=0;
+ D_S[d].drv_type=drv_fam1;
}
- }
- if (!DriveStruct[d].drv_type)
- {
- DPRINTF((DBG_INI,"SBPCD: check_version: error.\n"));
- return (-1);
- }
- if (lcs_drive) DriveStruct[d].drv_type=drv_260; /* still needs a closer look */
- else
- {
- for (j=0;j<4;j++) DriveStruct[d].firmware_version[j]=infobuf[i+j];
- j = (DriveStruct[d].firmware_version[0] & 0x0F) * 100 +
- (DriveStruct[d].firmware_version[2] & 0x0F) *10 +
- (DriveStruct[d].firmware_version[3] & 0x0F);
- if (new_drive)
- {
- if (j<100) DriveStruct[d].drv_type=drv_099;
- else DriveStruct[d].drv_type=drv_100;
- }
- else if (j<200) DriveStruct[d].drv_type=drv_199;
- else if (j<201) DriveStruct[d].drv_type=drv_200;
- else if (j<210) DriveStruct[d].drv_type=drv_201;
- else if (j<211) DriveStruct[d].drv_type=drv_210;
- else if (j<300) DriveStruct[d].drv_type=drv_211;
- else DriveStruct[d].drv_type=drv_300;
- }
- DPRINTF((DBG_INI,"SBPCD: check_version done.\n"));
- return (0);
+ if (!D_S[d].drv_type)
+ {
+ for (i=0;i<8;i++) if (infobuf[i]!=family0[i]) break;
+ if (i==8)
+ {
+ D_S[d].drive_model[0]='C';
+ D_S[d].drive_model[1]='R';
+ D_S[d].drive_model[2]='-';
+ D_S[d].drive_model[3]='5';
+ D_S[d].drive_model[4]='2';
+ D_S[d].drive_model[5]='x';
+ D_S[d].drive_model[6]=0;
+ D_S[d].drv_type=drv_fam0;
+ }
+ }
+ if (!D_S[d].drv_type)
+ {
+ for (i=0;i<8;i++) if (infobuf[i]!=familyL[i]) break;
+ if (i==8)
+ {
+ for (j=0;j<8;j++)
+ D_S[d].drive_model[j]=infobuf[j];
+ D_S[d].drive_model[8]=0;
+ D_S[d].drv_type=drv_famL;
+ }
+ }
+ if (!D_S[d].drv_type)
+ {
+ /* check for CD200 */
+ clr_cmdbuf();
+ drvcmd[0]=CMD2_READ_ERR;
+ response_count=9;
+ flags_cmd_out=f_putcmd;
+ i=cmd_out();
+ if (i<0) msg(DBG_INI,"CMD2_READERR returns %d (ok anyway).\n",i);
+ if (i<0) msg(DBG_000,"CMD2_READERR returns %d (ok anyway).\n",i);
+ /* read drive version */
+ clr_cmdbuf();
+ for (i=0;i<12;i++) infobuf[i]=0;
+ if (sbpro_type==1) OUT(CDo_sel_i_d,0);
+#if 0
+ OUT(CDo_reset,0);
+ sbp_sleep(600);
+ OUT(CDo_enable,D_S[d].drv_sel);
+#endif 0
+ drvcmd[0]=CMD2_READ_VER;
+ response_count=12;
+ flags_cmd_out=f_putcmd;
+ i=cmd_out();
+ if (i<0) msg(DBG_INI,"CMD2_READ_VER returns %d\n",i);
+ if (i==-7) teac_possible++;
+ j=0;
+ for (i=0;i<12;i++) j+=infobuf[i];
+ if (j)
+ {
+ for (i=0;i<12;i++)
+ sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
+ msgbuf[i*3]=0;
+ msg(DBG_IDX,"infobuf =%s\n", msgbuf);
+ for (i=0;i<12;i++)
+ sprintf(&msgbuf[i*3], " %c ", infobuf[i]);
+ msgbuf[i*3]=0;
+ msg(DBG_IDX,"infobuf =%s\n", msgbuf);
+ }
+ if (i>=0)
+ {
+ for (i=0;i<5;i++) if (infobuf[i]!=family2[i]) break;
+ if (i==5)
+ {
+ D_S[d].drive_model[0]='C';
+ D_S[d].drive_model[1]='D';
+ D_S[d].drive_model[2]='2';
+ D_S[d].drive_model[3]='0';
+ D_S[d].drive_model[4]='0';
+ D_S[d].drive_model[5]=infobuf[i++];
+ D_S[d].drive_model[6]=infobuf[i++];
+ D_S[d].drive_model[7]=0;
+ D_S[d].drv_type=drv_fam2;
+ }
+ }
+ }
+ if (!D_S[d].drv_type)
+ {
+ /* check for TEAC CD-55A */
+ msg(DBG_TEA,"teac_possible: %d\n",teac_possible);
+ for (j=1;j<=((D_S[d].drv_id==0)?3:1);j++)
+ {
+ for (l=1;l<=((D_S[d].drv_id==0)?10:1);l++)
+ {
+ msg(DBG_TEA,"TEAC reset #%d-%d.\n", j, l);
+ if (sbpro_type==1) OUT(CDo_reset,0);
+ else
+ {
+ OUT(CDo_enable,D_S[d].drv_sel);
+ OUT(CDo_sel_i_d,0);
+ OUT(CDo_command,CMDT_RESET);
+ for (i=0;i<9;i++) OUT(CDo_command,0);
+ }
+ sbp_sleep(50);
+ OUT(CDo_enable,D_S[d].drv_sel);
+ OUT(CDo_sel_i_d,0);
+ i=inb(CDi_status);
+ msg(DBG_TEA,"TEAC CDi_status: %02X.\n",i);
+#if 0
+ if (i&s_not_result_ready) continue; /* drive not present or ready */
+#endif
+ i=inb(CDi_info);
+ msg(DBG_TEA,"TEAC CDi_info: %02X.\n",i);
+ if (i==0x55) break; /* drive found */
+ }
+ if (i==0x55) break; /* drive found */
+ }
+ if (i==0x55) /* drive found */
+ {
+ msg(DBG_TEA,"TEAC drive found.\n");
+ clr_cmdbuf();
+ flags_cmd_out=f_putcmd;
+ response_count=12;
+ drvcmd[0]=CMDT_READ_VER;
+ drvcmd[4]=response_count;
+ for (i=0;i<12;i++) infobuf[i]=0;
+ i=cmd_out_T();
+ if (i!=0) msg(DBG_TEA,"cmd_out_T(CMDT_READ_VER) returns %d.\n",i);
+ for (i=1;i<6;i++) if (infobuf[i]!=familyT[i-1]) break;
+ if (i==6)
+ {
+ D_S[d].drive_model[0]='C';
+ D_S[d].drive_model[1]='D';
+ D_S[d].drive_model[2]='-';
+ D_S[d].drive_model[3]='5';
+ D_S[d].drive_model[4]='5';
+ D_S[d].drive_model[5]=0;
+ D_S[d].drv_type=drv_famT;
+ }
+ }
+ }
+ if (!D_S[d].drv_type)
+ {
+ msg(DBG_TEA,"no drive found at address %03X under ID %d.\n",CDo_command,D_S[d].drv_id);
+ return (-522);
+ }
+ for (j=0;j<4;j++) D_S[d].firmware_version[j]=infobuf[i+j];
+ if (famL_drive)
+ {
+ u_char lcs_firm_e1[]="A E1";
+ u_char lcs_firm_f4[]="A4F4";
+
+ for (j=0;j<4;j++)
+ if (D_S[d].firmware_version[j]!=lcs_firm_e1[j]) break;
+ if (j==4) D_S[d].drv_type=drv_e1;
+
+ for (j=0;j<4;j++)
+ if (D_S[d].firmware_version[j]!=lcs_firm_f4[j]) break;
+ if (j==4) D_S[d].drv_type=drv_f4;
+
+ if (D_S[d].drv_type==drv_famL) ask_mail();
+ }
+ else if (famT_drive)
+ {
+ j=infobuf[4]; /* one-byte version??? - here: 0x15 */
+ if (j=='5')
+ {
+ D_S[d].firmware_version[0]=infobuf[7];
+ D_S[d].firmware_version[1]=infobuf[8];
+ D_S[d].firmware_version[2]=infobuf[10];
+ D_S[d].firmware_version[3]=infobuf[11];
+ }
+ else
+ {
+ if (j!=0x15) ask_mail();
+ D_S[d].firmware_version[0]='0';
+ D_S[d].firmware_version[1]='.';
+ D_S[d].firmware_version[2]='0'+(j>>4);
+ D_S[d].firmware_version[3]='0'+(j&0x0f);
+ }
+ }
+ else /* CR-52x, CR-56x, CD200 */
+ {
+ j = (D_S[d].firmware_version[0] & 0x0F) * 100 +
+ (D_S[d].firmware_version[2] & 0x0F) *10 +
+ (D_S[d].firmware_version[3] & 0x0F);
+ if (fam0_drive)
+ {
+ if (j<200) D_S[d].drv_type=drv_199;
+ else if (j<201) D_S[d].drv_type=drv_200;
+ else if (j<210) D_S[d].drv_type=drv_201;
+ else if (j<211) D_S[d].drv_type=drv_210;
+ else if (j<300) D_S[d].drv_type=drv_211;
+ else if (j>=300) D_S[d].drv_type=drv_300;
+ }
+ else if (fam1_drive)
+ {
+ if (j<100) D_S[d].drv_type=drv_099;
+ else
+ {
+ D_S[d].drv_type=drv_100;
+ if ((j!=500)||(j!=102)) ask_mail();
+ }
+ }
+ else if (fam2_drive)
+ {
+ msg(DBG_INF,"new drive CD200 (%s)detected.\n", D_S[d].firmware_version);
+ msg(DBG_INF,"support is not fulfilled yet - audio should work.\n");
+ if ((j!=101)&&(j!=35)) ask_mail(); /* only 1.01 and 0.35 known at time */
+ }
+ }
+ msg(DBG_LCS,"drive type %02X\n",D_S[d].drv_type);
+ msg(DBG_INI,"check_version done.\n");
+ return (0);
}
/*==========================================================================*/
-static int switch_drive(int num)
+static void switch_drive(int i)
{
- int i;
-
- d=num;
-
- i=num;
- if (sbpro_type==1) i=(i&0x01)<<1|(i&0x02)>>1;
- OUT(CDo_enable,i);
- DPRINTF((DBG_DID,"SBPCD: switch_drive: drive %d activated.\n",DriveStruct[d].drv_minor));
- return (0);
+ d=i;
+ OUT(CDo_enable,D_S[d].drv_sel);
+ msg(DBG_DID,"drive %d (ID=%d) activated.\n", i, D_S[d].drv_id);
+ return;
}
/*==========================================================================*/
+#ifdef PATH_CHECK
/*
- * probe for the presence of drives on the selected controller
+ * probe for the presence of an interface card
*/
-static int check_drives(void)
+static int check_card(int port)
{
- int i, j;
- char *printk_header="";
-
- DPRINTF((DBG_INI,"SBPCD: check_drives entered.\n"));
-
- ndrives=0;
- for (j=0;j<NR_SBPCD;j++)
- {
- DriveStruct[j].drv_minor=j;
- switch_drive(j);
- DPRINTF((DBG_ID,"SBPCD: check_drives: drive %d activated.\n",j));
- i=check_version();
- DPRINTF((DBG_ID,"SBPCD: check_version returns %d.\n",i));
- if (i>=0)
- {
- ndrives++;
- DriveStruct[d].drv_options=drv_pattern[j];
- if (!new_drive)
- DriveStruct[d].drv_options&=~(speed_auto|speed_300|speed_150);
- printk("%sDrive %d: %s%.4s (%.4s)\n", printk_header,
- DriveStruct[d].drv_minor,
- drive_family,
- DriveStruct[d].drive_model,
- DriveStruct[d].firmware_version);
- printk_header=" - ";
- }
- else DriveStruct[d].drv_minor=-1;
- }
- if (ndrives==0) return (-1);
- return (0);
+#undef N_RESPO
+#define N_RESPO 20
+ int i, j, k;
+ u_char response[N_RESPO];
+ u_char save_port0;
+ u_char save_port3;
+
+ msg(DBG_INI,"check_card entered.\n");
+ save_port0=inb(port+0);
+ save_port3=inb(port+3);
+
+ for (j=0;j<NR_SBPCD;j++)
+ {
+ OUT(port+3,j) ; /* enable drive #j */
+ OUT(port+0,CMD0_PATH_CHECK);
+ for (i=10;i>0;i--) OUT(port+0,0);
+ for (k=0;k<N_RESPO;k++) response[k]=0;
+ for (k=0;k<N_RESPO;k++)
+ {
+ for (i=10000;i>0;i--)
+ {
+ if (inb(port+1)&s_not_result_ready) continue;
+ response[k]=inb(port+0);
+ break;
+ }
+ }
+ for (i=0;i<N_RESPO;i++)
+ sprintf(&msgbuf[i*3], " %02X", response[i]);
+ msgbuf[i*3]=0;
+ msg(DBG_TEA,"path check 00 (%d): %s\n", j, msgbuf);
+ OUT(port+0,CMD0_PATH_CHECK);
+ for (i=10;i>0;i--) OUT(port+0,0);
+ for (k=0;k<N_RESPO;k++) response[k]=0xFF;
+ for (k=0;k<N_RESPO;k++)
+ {
+ for (i=10000;i>0;i--)
+ {
+ if (inb(port+1)&s_not_result_ready) continue;
+ response[k]=inb(port+0);
+ break;
+ }
+ }
+ for (i=0;i<N_RESPO;i++)
+ sprintf(&msgbuf[i*3], " %02X", response[i]);
+ msgbuf[i*3]=0;
+ msg(DBG_TEA,"path check 00 (%d): %s\n", j, msgbuf);
+
+ if (response[0]==0xAA)
+ if (response[1]==0x55)
+ return (0);
+ }
+ for (j=0;j<NR_SBPCD;j++)
+ {
+ OUT(port+3,j) ; /* enable drive #j */
+ OUT(port+0,CMD2_READ_VER);
+ for (i=10;i>0;i--) OUT(port+0,0);
+ for (k=0;k<N_RESPO;k++) response[k]=0;
+ for (k=0;k<N_RESPO;k++)
+ {
+ for (i=1000000;i>0;i--)
+ {
+ if (inb(port+1)&s_not_result_ready) continue;
+ response[k]=inb(port+0);
+ break;
+ }
+ }
+ for (i=0;i<N_RESPO;i++)
+ sprintf(&msgbuf[i*3], " %02X", response[i]);
+ msgbuf[i*3]=0;
+ msg(DBG_TEA,"path check 12 (%d): %s\n", j, msgbuf);
+
+ OUT(port+0,CMD2_READ_VER);
+ for (i=10;i>0;i--) OUT(port+0,0);
+ for (k=0;k<N_RESPO;k++) response[k]=0xFF;
+ for (k=0;k<N_RESPO;k++)
+ {
+ for (i=1000000;i>0;i--)
+ {
+ if (inb(port+1)&s_not_result_ready) continue;
+ response[k]=inb(port+0);
+ break;
+ }
+ }
+ for (i=0;i<N_RESPO;i++)
+ sprintf(&msgbuf[i*3], " %02X", response[i]);
+ msgbuf[i*3]=0;
+ msg(DBG_TEA,"path check 12 (%d): %s\n", j, msgbuf);
+
+ if (response[0]==0xAA)
+ if (response[1]==0x55)
+ return (0);
+ }
+ OUT(port+0,save_port0);
+ OUT(port+3,save_port3);
+ return (0); /* in any case - no real "function" at time */
}
+#endif PATH_CHECK
/*==========================================================================*/
-#if 000
-static void timewait(void)
+/*==========================================================================*/
+/*
+ * probe for the presence of drives on the selected controller
+ */
+static int check_drives(void)
{
- int i;
- for (i=0; i<65500; i++);
+ int i, j;
+
+ msg(DBG_INI,"check_drives entered.\n");
+ ndrives=0;
+ for (j=0;j<MAX_DRIVES;j++)
+ {
+ D_S[ndrives].drv_id=j;
+ if (sbpro_type==1) D_S[ndrives].drv_sel=(j&0x01)<<1|(j&0x02)>>1;
+ else D_S[ndrives].drv_sel=j;
+ switch_drive(ndrives);
+ msg(DBG_INI,"check_drives: drive %d (ID=%d) activated.\n",ndrives,j);
+ msg(DBG_000,"check_drives: drive %d (ID=%d) activated.\n",ndrives,j);
+ i=check_version();
+ if (i<0) msg(DBG_INI,"check_version returns %d.\n",i);
+ else
+ {
+ D_S[d].drv_options=drv_pattern[j];
+ if (fam0L_drive) D_S[d].drv_options&=~(speed_auto|speed_300|speed_150);
+ msg(DBG_INF, "Drive %d (ID=%d): %.9s (%.4s) at 0x%03X (type %d)\n",
+ d,
+ D_S[d].drv_id,
+ D_S[d].drive_model,
+ D_S[d].firmware_version,
+ CDo_command,
+ sbpro_type);
+ ndrives++;
+ }
+ }
+ for (j=ndrives;j<NR_SBPCD;j++) D_S[j].drv_id=-1;
+ if (ndrives==0) return (-1);
+ return (0);
}
-#endif 000
/*==========================================================================*/
#if FUTURE
/*
@@ -2121,41 +3433,40 @@ static void timewait(void)
*/
static int obey_audio_state(u_char audio_state, u_char func,u_char subfunc)
{
- switch (audio_state) /* audio status from controller */
- {
- case aud_11: /* "audio play in progress" */
- case audx11:
- switch (func) /* DOS command code */
- {
- case cmd_07: /* input flush */
- case cmd_0d: /* open device */
- case cmd_0e: /* close device */
- case cmd_0c: /* ioctl output */
- return (1);
- case cmd_03: /* ioctl input */
- switch (subfunc)
- /* DOS ioctl input subfunction */
- {
- case cxi_00:
- case cxi_06:
- case cxi_09:
- return (1);
- default:
- return (ERROR15);
- }
- return (1);
+ switch (audio_state) /* audio status from controller */
+ {
+ case aud_11: /* "audio play in progress" */
+ case audx11:
+ switch (func) /* DOS command code */
+ {
+ case cmd_07: /* input flush */
+ case cmd_0d: /* open device */
+ case cmd_0e: /* close device */
+ case cmd_0c: /* ioctl output */
+ return (1);
+ case cmd_03: /* ioctl input */
+ switch (subfunc)
+ /* DOS ioctl input subfunction */
+ {
+ case cxi_00:
+ case cxi_06:
+ case cxi_09:
+ return (1);
+ default:
+ return (ERROR15);
+ }
+ return (1);
+ default:
+ return (ERROR15);
+ }
+ return (1);
+ case aud_12: /* "audio play paused" */
+ case audx12:
+ return (1);
default:
- return (ERROR15);
- }
- return (1);
- case aud_12: /* "audio play paused" */
- case audx12:
- return (1);
- default:
- return (2);
- }
+ return (2);
+ }
}
-#endif FUTURE
/*==========================================================================*/
/* allowed is only
* ioctl_o, flush_input, open_device, close_device,
@@ -2165,314 +3476,266 @@ static int obey_audio_state(u_char audio_state, u_char func,u_char subfunc)
static int check_allowed1(u_char func1, u_char func2)
{
#if 000
- if (func1==ioctl_o) return (0);
- if (func1==read_long) return (-1);
- if (func1==read_long_prefetch) return (-1);
- if (func1==seek) return (-1);
- if (func1==audio_play) return (-1);
- if (func1==audio_pause) return (-1);
- if (func1==audio_resume) return (-1);
- if (func1!=ioctl_i) return (0);
- if (func2==tell_SubQ_run_tot) return (-1);
- if (func2==tell_cdsize) return (-1);
- if (func2==tell_TocDescrip) return (-1);
- if (func2==tell_TocEntry) return (-1);
- if (func2==tell_subQ_info) return (-1);
- if (new_drive) if (func2==tell_SubChanInfo) return (-1);
- if (func2==tell_UPC) return (-1);
+ if (func1==ioctl_o) return (0);
+ if (func1==read_long) return (-1);
+ if (func1==read_long_prefetch) return (-1);
+ if (func1==seek) return (-1);
+ if (func1==audio_play) return (-1);
+ if (func1==audio_pause) return (-1);
+ if (func1==audio_resume) return (-1);
+ if (func1!=ioctl_i) return (0);
+ if (func2==tell_SubQ_run_tot) return (-1);
+ if (func2==tell_cdsize) return (-1);
+ if (func2==tell_TocDescrip) return (-1);
+ if (func2==tell_TocEntry) return (-1);
+ if (func2==tell_subQ_info) return (-1);
+ if (fam1_drive) if (func2==tell_SubChanInfo) return (-1);
+ if (func2==tell_UPC) return (-1);
#else
- return (0);
+ return (0);
#endif 000
}
/*==========================================================================*/
static int check_allowed2(u_char func1, u_char func2)
{
#if 000
- if (func1==read_long) return (-1);
- if (func1==read_long_prefetch) return (-1);
- if (func1==seek) return (-1);
- if (func1==audio_play) return (-1);
+ if (func1==read_long) return (-1);
+ if (func1==read_long_prefetch) return (-1);
+ if (func1==seek) return (-1);
+ if (func1==audio_play) return (-1);
if (func1!=ioctl_o) return (0);
- if (new_drive)
- {
- if (func2==EjectDisk) return (-1);
- if (func2==CloseTray) return (-1);
- }
+ if (fam1_drive)
+ {
+ if (func2==EjectDisk) return (-1);
+ if (func2==CloseTray) return (-1);
+ }
#else
- return (0);
+ return (0);
#endif 000
}
/*==========================================================================*/
static int check_allowed3(u_char func1, u_char func2)
{
#if 000
- if (func1==ioctl_i)
- {
- if (func2==tell_address) return (0);
- if (func2==tell_capabiliti) return (0);
- if (func2==tell_CD_changed) return (0);
- if (!new_drive) if (func2==tell_SubChanInfo) return (0);
- return (-1);
- }
- if (func1==ioctl_o)
- {
- if (func2==DriveReset) return (0);
- if (!new_drive)
+ if (func1==ioctl_i)
{
- if (func2==EjectDisk) return (0);
- if (func2==LockDoor) return (0);
- if (func2==CloseTray) return (0);
+ if (func2==tell_address) return (0);
+ if (func2==tell_capabiliti) return (0);
+ if (func2==tell_CD_changed) return (0);
+ if (fam0L_drive) if (func2==tell_SubChanInfo) return (0);
+ return (-1);
}
- return (-1);
+ if (func1==ioctl_o)
+ {
+ if (func2==DriveReset) return (0);
+ if (fam0L_drive)
+ {
+ if (func2==EjectDisk) return (0);
+ if (func2==LockDoor) return (0);
+ if (func2==CloseTray) return (0);
+ }
+ return (-1);
}
- if (func1==flush_input) return (-1);
- if (func1==read_long) return (-1);
- if (func1==read_long_prefetch) return (-1);
- if (func1==seek) return (-1);
- if (func1==audio_play) return (-1);
- if (func1==audio_pause) return (-1);
- if (func1==audio_resume) return (-1);
+ if (func1==flush_input) return (-1);
+ if (func1==read_long) return (-1);
+ if (func1==read_long_prefetch) return (-1);
+ if (func1==seek) return (-1);
+ if (func1==audio_play) return (-1);
+ if (func1==audio_pause) return (-1);
+ if (func1==audio_resume) return (-1);
#else
- return (0);
+ return (0);
#endif 000
}
/*==========================================================================*/
static int seek_pos_audio_end(void)
{
- int i;
+ int i;
- i=msf2blk(DriveStruct[d].pos_audio_end)-1;
- if (i<0) return (-1);
- i=xx_Seek(i,0);
- return (i);
+ i=msf2blk(D_S[d].pos_audio_end)-1;
+ if (i<0) return (-1);
+ i=cc_Seek(i,0);
+ return (i);
}
+#endif FUTURE
/*==========================================================================*/
static int ReadToC(void)
{
- int i, j;
- DriveStruct[d].diskstate_flags &= ~toc_bit;
- DriveStruct[d].ored_ctl_adr=0;
- for (j=DriveStruct[d].n_first_track;j<=DriveStruct[d].n_last_track;j++)
- {
- i=xx_ReadTocEntry(j);
- if (i<0) return (i);
- DriveStruct[d].TocBuffer[j].nixbyte=DriveStruct[d].TocEnt_nixbyte;
- DriveStruct[d].TocBuffer[j].ctl_adr=DriveStruct[d].TocEnt_ctl_adr;
- DriveStruct[d].TocBuffer[j].number=DriveStruct[d].TocEnt_number;
- DriveStruct[d].TocBuffer[j].format=DriveStruct[d].TocEnt_format;
- DriveStruct[d].TocBuffer[j].address=DriveStruct[d].TocEnt_address;
- DriveStruct[d].ored_ctl_adr |= DriveStruct[d].TocEnt_ctl_adr;
- }
-/* fake entry for LeadOut Track */
- DriveStruct[d].TocBuffer[j].nixbyte=0;
- DriveStruct[d].TocBuffer[j].ctl_adr=0;
- DriveStruct[d].TocBuffer[j].number=CDROM_LEADOUT;
- DriveStruct[d].TocBuffer[j].format=0;
- DriveStruct[d].TocBuffer[j].address=DriveStruct[d].size_msf;
-
- DriveStruct[d].diskstate_flags |= toc_bit;
- return (0);
+ int i, j;
+ D_S[d].diskstate_flags &= ~toc_bit;
+ D_S[d].ored_ctl_adr=0;
+ for (j=D_S[d].n_first_track;j<=D_S[d].n_last_track;j++)
+ {
+ i=cc_ReadTocEntry(j);
+ if (i<0)
+ {
+ msg(DBG_INF,"cc_ReadTocEntry(%d) returns %d.\n",j,i);
+ return (i);
+ }
+ D_S[d].TocBuffer[j].nixbyte=D_S[d].TocEnt_nixbyte;
+ D_S[d].TocBuffer[j].ctl_adr=D_S[d].TocEnt_ctl_adr;
+ D_S[d].TocBuffer[j].number=D_S[d].TocEnt_number;
+ D_S[d].TocBuffer[j].format=D_S[d].TocEnt_format;
+ D_S[d].TocBuffer[j].address=D_S[d].TocEnt_address;
+ D_S[d].ored_ctl_adr |= D_S[d].TocEnt_ctl_adr;
+ }
+ /* fake entry for LeadOut Track */
+ D_S[d].TocBuffer[j].nixbyte=0;
+ D_S[d].TocBuffer[j].ctl_adr=0;
+ D_S[d].TocBuffer[j].number=CDROM_LEADOUT;
+ D_S[d].TocBuffer[j].format=0;
+ D_S[d].TocBuffer[j].address=D_S[d].size_msf;
+
+ D_S[d].diskstate_flags |= toc_bit;
+ return (0);
}
/*==========================================================================*/
static int DiskInfo(void)
{
- int i, j;
-
- DriveStruct[d].mode=READ_M1;
-
+ int i, j;
+
+ D_S[d].mode=READ_M1;
+
#undef LOOP_COUNT
-#define LOOP_COUNT 20 /* needed for some "old" drives */
-
- for (j=1;j<LOOP_COUNT;j++)
- {
- i=SetSpeed();
- if (i<0)
+#define LOOP_COUNT 10 /* needed for some "old" drives */
+
+ msg(DBG_000,"DiskInfo entered.\n");
+ for (j=1;j<LOOP_COUNT;j++)
{
- DPRINTF((DBG_INF,"SBPCD: DiskInfo: SetSpeed returns %d\n", i));
- continue;
+ i=SetSpeed();
+ if (i<0)
+ {
+ msg(DBG_INF,"DiskInfo: SetSpeed returns %d\n", i);
+ continue;
+ }
+ i=cc_ModeSense();
+ if (i<0)
+ {
+ msg(DBG_INF,"DiskInfo: cc_ModeSense returns %d\n", i);
+ continue;
+ }
+ i=cc_ReadCapacity();
+ if (i>=0) break;
+ msg(DBG_INF,"DiskInfo: ReadCapacity #%d returns %d\n", j, i);
+ i=cc_DriveReset();
}
- i=xx_ModeSense();
- if (i<0)
+ if (j==LOOP_COUNT) return (-33); /* give up */
+
+ i=cc_ReadTocDescr();
+ if (i<0)
{
- DPRINTF((DBG_INF,"SBPCD: DiskInfo: xx_ModeSense returns %d\n", i));
- continue;
+ msg(DBG_INF,"DiskInfo: ReadTocDescr returns %d\n", i);
+ return (i);
}
- i=xx_ReadCapacity();
- if (i>=0) break;
- DPRINTF((DBG_INF,"SBPCD: DiskInfo: ReadCapacity #%d returns %d\n", j, i));
- i=DriveReset();
- }
- if (j==LOOP_COUNT) return (-2); /* give up */
-
- i=xx_ReadTocDescr();
- if (i<0)
- {
- DPRINTF((DBG_INF,"SBPCD: DiskInfo: ReadTocDescr returns %d\n", i));
- return (i);
- }
- i=ReadToC();
- if (i<0)
- {
- DPRINTF((DBG_INF,"SBPCD: DiskInfo: ReadToC returns %d\n", i));
- return (i);
- }
- i=yy_CheckMultiSession();
- if (i<0)
- {
- DPRINTF((DBG_INF,"SBPCD: DiskInfo: yy_CheckMultiSession returns %d\n", i));
- return (i);
- }
- i=xx_ReadTocEntry(DriveStruct[d].n_first_track);
- if (i<0)
- {
- DPRINTF((DBG_INF,"SBPCD: DiskInfo: xx_ReadTocEntry(1) returns %d\n", i));
- return (i);
- }
- i=xx_ReadUPC();
- if (i<0)
- {
- DPRINTF((DBG_INF,"SBPCD: DiskInfo: xx_ReadUPC returns %d\n", i));
- return (i);
- }
-#ifdef XA_TEST2
- if ((!new_drive) && (DriveStruct[d].xa_byte==0x20)) /* XA disk with old drive */
- {
- xx_ModeSelect(CD_FRAMESIZE_XA);
- xx_ModeSense();
- }
-#endif XA_TEST2
-
- return (0);
+ i=ReadToC();
+ if (i<0)
+ {
+ msg(DBG_INF,"DiskInfo: ReadToC returns %d\n", i);
+ return (i);
+ }
+ i=cc_CheckMultiSession();
+ if (i<0)
+ {
+ msg(DBG_INF,"DiskInfo: cc_CheckMultiSession returns %d\n", i);
+ return (i);
+ }
+ if (D_S[d].f_multisession) D_S[d].sbp_bufsiz=1; /* possibly a weird PhotoCD */
+ else D_S[d].sbp_bufsiz=SBP_BUFFER_FRAMES;
+ i=cc_ReadTocEntry(D_S[d].n_first_track);
+ if (i<0)
+ {
+ msg(DBG_INF,"DiskInfo: cc_ReadTocEntry(1) returns %d\n", i);
+ return (i);
+ }
+ i=cc_ReadUPC();
+ if (i<0) msg(DBG_INF,"DiskInfo: cc_ReadUPC returns %d\n", i);
+ if ((fam0L_drive) && (D_S[d].xa_byte==0x20))
+ {
+ /* XA disk with old drive */
+ cc_ModeSelect(CD_FRAMESIZE_XA);
+ cc_ModeSense();
+ }
+ if (famT_drive) cc_prep_mode_T();
+ msg(DBG_000,"DiskInfo done.\n");
+ return (0);
}
/*==========================================================================*/
+#if FUTURE
/*
* called always if driver gets entered
* returns 0 or ERROR2 or ERROR15
*/
static int prepare(u_char func, u_char subfunc)
{
- int i;
-
- if (!new_drive)
- {
- i=inb(CDi_status);
- if (i&s_attention) GetStatus();
- }
- else GetStatus();
- if (DriveStruct[d].CD_changed==0xFF)
- {
-#if MANY_SESSION
-#else
- DriveStruct[d].diskstate_flags=0;
-#endif MANY_SESSION
- DriveStruct[d].audio_state=0;
- if (!st_diskok)
+ int i;
+
+ if (fam0L_drive)
{
- i=check_allowed1(func,subfunc);
- if (i<0) return (-2);
+ i=inb(CDi_status);
+ if (i&s_attention) GetStatus();
}
- else
+ else if (fam1_drive) GetStatus();
+ else if (fam2_drive) GetStatus();
+ else if (famT_drive) GetStatus();
+ if (D_S[d].CD_changed==0xFF)
{
- i=check_allowed3(func,subfunc);
- if (i<0)
- {
- DriveStruct[d].CD_changed=1;
- return (-15);
- }
+ D_S[d].diskstate_flags=0;
+ D_S[d].audio_state=0;
+ if (!st_diskok)
+ {
+ i=check_allowed1(func,subfunc);
+ if (i<0) return (-2);
+ }
+ else
+ {
+ i=check_allowed3(func,subfunc);
+ if (i<0)
+ {
+ D_S[d].CD_changed=1;
+ return (-15);
+ }
+ }
}
- }
- else
- {
- if (!st_diskok)
+ else
{
-#if MANY_SESSION
-#else
- DriveStruct[d].diskstate_flags=0;
-#endif MANY_SESSION
- DriveStruct[d].audio_state=0;
- i=check_allowed1(func,subfunc);
- if (i<0) return (-2);
- }
- else
- {
- if (st_busy)
- {
- if (DriveStruct[d].audio_state!=audio_pausing)
- {
- i=check_allowed2(func,subfunc);
- if (i<0) return (-2);
- }
- }
- else
- {
- if (DriveStruct[d].audio_state==audio_playing) seek_pos_audio_end();
- DriveStruct[d].audio_state=0;
- }
- if (!frame_size_valid)
- {
- i=DiskInfo();
- if (i<0)
- {
-#if MANY_SESSION
-#else
- DriveStruct[d].diskstate_flags=0;
-#endif MANY_SESSION
- DriveStruct[d].audio_state=0;
- i=check_allowed1(func,subfunc);
- if (i<0) return (-2);
+ if (!st_diskok)
+ {
+ D_S[d].diskstate_flags=0;
+ D_S[d].audio_state=0;
+ i=check_allowed1(func,subfunc);
+ if (i<0) return (-2);
+ }
+ else
+ {
+ if (st_busy)
+ {
+ if (D_S[d].audio_state!=audio_pausing)
+ {
+ i=check_allowed2(func,subfunc);
+ if (i<0) return (-2);
+ }
+ }
+ else
+ {
+ if (D_S[d].audio_state==audio_playing) seek_pos_audio_end();
+ D_S[d].audio_state=0;
+ }
+ if (!frame_size_valid)
+ {
+ i=DiskInfo();
+ if (i<0)
+ {
+ D_S[d].diskstate_flags=0;
+ D_S[d].audio_state=0;
+ i=check_allowed1(func,subfunc);
+ if (i<0) return (-2);
+ }
+ }
}
- }
- }
- }
- return (0);
-}
-/*==========================================================================*/
-static int xx_PlayAudio(int pos_audio_start,int pos_audio_end)
-{
- int i, n;
-
- if (DriveStruct[d].audio_state==audio_playing) return (-EINVAL);
- clr_cmdbuf();
- if (lcs_drive)
- {
- drvcmd[0]=0x0A;
- i=msf2blk(pos_audio_start);
- n=msf2blk(pos_audio_end)+1-i;
- drvcmd[1]=(i>>16)&0x00FF;
- drvcmd[2]=(i>>8)&0x00FF;
- drvcmd[3]=i&0x00FF;
- drvcmd[4]=(n>>16)&0x00FF;
- drvcmd[5]=(n>>8)&0x00FF;
- drvcmd[6]=n&0x00FF;
- flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta |
- f_ResponseStatus | f_obey_p_check | f_wait_if_busy;
- }
- else
- {
- if (new_drive)
- {
- drvcmd[0]=0x0E;
- flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus |
- f_obey_p_check | f_wait_if_busy;
- }
- else /* old_drive */
- {
- drvcmd[0]=0x0B;
- flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta |
- f_ResponseStatus | f_obey_p_check | f_wait_if_busy;
- }
- drvcmd[1]=(pos_audio_start>>16)&0x00FF;
- drvcmd[2]=(pos_audio_start>>8)&0x00FF;
- drvcmd[3]=pos_audio_start&0x00FF;
- drvcmd[4]=(pos_audio_end>>16)&0x00FF;
- drvcmd[5]=(pos_audio_end>>8)&0x00FF;
- drvcmd[6]=pos_audio_end&0x00FF;
}
- response_count=0;
- i=cmd_out();
- return (i);
+ return (0);
}
-/*==========================================================================*/
+#endif FUTURE
/*==========================================================================*/
/*==========================================================================*/
/*
@@ -2480,43 +3743,43 @@ static int xx_PlayAudio(int pos_audio_start,int pos_audio_end)
*/
static int sbp_status(void)
{
- int st;
-
- st=ResponseStatus();
- if (st<0)
- {
- DPRINTF((DBG_INF,"SBPCD: sbp_status: timeout.\n"));
- return (0);
- }
-
- if (!st_spinning) DPRINTF((DBG_SPI,"SBPCD: motor got off - ignoring.\n"));
-
- if (st_check)
- {
- DPRINTF((DBG_INF,"SBPCD: st_check detected - retrying.\n"));
- return (0);
- }
- if (!st_door_closed)
- {
- DPRINTF((DBG_INF,"SBPCD: door is open - retrying.\n"));
- return (0);
- }
- if (!st_caddy_in)
- {
- DPRINTF((DBG_INF,"SBPCD: disk removed - retrying.\n"));
- return (0);
- }
- if (!st_diskok)
- {
- DPRINTF((DBG_INF,"SBPCD: !st_diskok detected - retrying.\n"));
- return (0);
- }
- if (st_busy)
- {
- DPRINTF((DBG_INF,"SBPCD: st_busy detected - retrying.\n"));
- return (0);
- }
- return (1);
+ int st;
+
+ st=ResponseStatus();
+ if (st<0)
+ {
+ msg(DBG_INF,"sbp_status: timeout.\n");
+ return (0);
+ }
+
+ if (!st_spinning) msg(DBG_SPI,"motor got off - ignoring.\n");
+
+ if (st_check)
+ {
+ msg(DBG_INF,"st_check detected - retrying.\n");
+ return (0);
+ }
+ if (!st_door_closed)
+ {
+ msg(DBG_INF,"door is open - retrying.\n");
+ return (0);
+ }
+ if (!st_caddy_in)
+ {
+ msg(DBG_INF,"disk removed - retrying.\n");
+ return (0);
+ }
+ if (!st_diskok)
+ {
+ msg(DBG_INF,"!st_diskok detected - retrying.\n");
+ return (0);
+ }
+ if (st_busy)
+ {
+ msg(DBG_INF,"st_busy detected - retrying.\n");
+ return (0);
+ }
+ return (1);
}
/*==========================================================================*/
@@ -2528,476 +3791,546 @@ static int sbp_status(void)
static int sbpcd_ioctl(struct inode *inode, struct file *file, u_int cmd,
u_long arg)
{
- int i, st;
-
- DPRINTF((DBG_IO2,"SBPCD: ioctl(%d, 0x%08lX, 0x%08lX)\n",
- MINOR(inode->i_rdev), cmd, arg));
- if (!inode) return (-EINVAL);
- i=MINOR(inode->i_rdev);
- if ( (i<0) || (i>=NR_SBPCD) )
- {
- printk("SBPCD: ioctl: bad device: %d\n", i);
- return (-ENODEV); /* no such drive */
- }
- switch_drive(i);
-
- st=GetStatus();
- if (st<0) return (-EIO);
-
- if (!toc_valid)
- {
- i=DiskInfo();
- if (i<0) return (-EIO); /* error reading TOC */
- }
-
- DPRINTF((DBG_IO2,"SBPCD: ioctl: device %d, request %04X\n",i,cmd));
- switch (cmd) /* Sun-compatible */
- {
- case DDIOCSDBG: /* DDI Debug */
- if (! suser()) return (-EPERM);
- i = verify_area(VERIFY_READ, (int *) arg, sizeof(int));
- if (i>=0) i=sbpcd_dbg_ioctl(arg,1);
- return (i);
-
- case CDROMPAUSE: /* Pause the drive */
- DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMPAUSE entered.\n"));
- /* pause the drive unit when it is currently in PLAY mode, */
- /* or reset the starting and ending locations when in PAUSED mode. */
- /* If applicable, at the next stopping point it reaches */
- /* the drive will discontinue playing. */
- switch (DriveStruct[d].audio_state)
- {
- case audio_playing:
- i=xx_Pause_Resume(1);
- if (i<0) return (-EIO);
- DriveStruct[d].audio_state=audio_pausing;
- i=xx_ReadSubQ();
- if (i<0) return (-EIO);
- DriveStruct[d].pos_audio_start=DriveStruct[d].SubQ_run_tot;
- return (0);
- case audio_pausing:
- i=xx_Seek(DriveStruct[d].pos_audio_start,1);
- if (i<0) return (-EIO);
- return (0);
- default:
- return (-EINVAL);
- }
-
- case CDROMRESUME: /* resume paused audio play */
- DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMRESUME entered.\n"));
- /* resume playing audio tracks when a previous PLAY AUDIO call has */
- /* been paused with a PAUSE command. */
- /* It will resume playing from the location saved in SubQ_run_tot. */
- if (DriveStruct[d].audio_state!=audio_pausing) return -EINVAL;
- i=xx_Pause_Resume(3);
- if (i<0) return (-EIO);
- DriveStruct[d].audio_state=audio_playing;
- return (0);
-
- case CDROMPLAYMSF:
- DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMPLAYMSF entered.\n"));
- if (DriveStruct[d].audio_state==audio_playing)
- {
- i=xx_Pause_Resume(1);
- if (i<0) return (-EIO);
- i=xx_ReadSubQ();
- if (i<0) return (-EIO);
- DriveStruct[d].pos_audio_start=DriveStruct[d].SubQ_run_tot;
- i=xx_Seek(DriveStruct[d].pos_audio_start,1);
- }
- st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_msf));
- if (st) return (st);
- memcpy_fromfs(&msf, (void *) arg, sizeof(struct cdrom_msf));
- /* values come as msf-bin */
- DriveStruct[d].pos_audio_start = (msf.cdmsf_min0<<16) |
+ int i, st;
+
+ msg(DBG_IO2,"ioctl(%d, 0x%08lX, 0x%08lX)\n",
+ MINOR(inode->i_rdev), cmd, arg);
+ if (!inode) return (-EINVAL);
+ i=MINOR(inode->i_rdev);
+ if ((i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1))
+ {
+ msg(DBG_INF, "ioctl: bad device: %04X\n", inode->i_rdev);
+ return (-ENXIO); /* no such drive */
+ }
+ if (d!=i) switch_drive(i);
+
+#if 0
+ st=GetStatus();
+ if (st<0) return (-EIO);
+
+ if (!toc_valid)
+ {
+ i=DiskInfo();
+ if (i<0) return (-EIO); /* error reading TOC */
+ }
+#endif
+
+ msg(DBG_IO2,"ioctl: device %d, request %04X\n",i,cmd);
+ switch (cmd) /* Sun-compatible */
+ {
+ case DDIOCSDBG: /* DDI Debug */
+ if (!suser()) return (-EPERM);
+ i=sbpcd_dbg_ioctl(arg,1);
+ return (i);
+
+ case CDROMPAUSE: /* Pause the drive */
+ msg(DBG_IOC,"ioctl: CDROMPAUSE entered.\n");
+ /* pause the drive unit when it is currently in PLAY mode, */
+ /* or reset the starting and ending locations when in PAUSED mode. */
+ /* If applicable, at the next stopping point it reaches */
+ /* the drive will discontinue playing. */
+ switch (D_S[d].audio_state)
+ {
+ case audio_playing:
+ if (famL_drive) i=cc_ReadSubQ();
+ else i=cc_Pause_Resume(1);
+ if (i<0) return (-EIO);
+ if (famL_drive) i=cc_Pause_Resume(1);
+ else i=cc_ReadSubQ();
+ if (i<0) return (-EIO);
+ D_S[d].pos_audio_start=D_S[d].SubQ_run_tot;
+ D_S[d].audio_state=audio_pausing;
+ return (0);
+ case audio_pausing:
+ i=cc_Seek(D_S[d].pos_audio_start,1);
+ if (i<0) return (-EIO);
+ return (0);
+ default:
+ return (-EINVAL);
+ }
+
+ case CDROMRESUME: /* resume paused audio play */
+ msg(DBG_IOC,"ioctl: CDROMRESUME entered.\n");
+ /* resume playing audio tracks when a previous PLAY AUDIO call has */
+ /* been paused with a PAUSE command. */
+ /* It will resume playing from the location saved in SubQ_run_tot. */
+ if (D_S[d].audio_state!=audio_pausing) return -EINVAL;
+ if (famL_drive)
+ i=cc_PlayAudio(D_S[d].pos_audio_start,
+ D_S[d].pos_audio_end);
+ else i=cc_Pause_Resume(3);
+ if (i<0) return (-EIO);
+ D_S[d].audio_state=audio_playing;
+ return (0);
+
+ case CDROMPLAYMSF:
+ msg(DBG_IOC,"ioctl: CDROMPLAYMSF entered.\n");
+ if (D_S[d].audio_state==audio_playing)
+ {
+ i=cc_Pause_Resume(1);
+ if (i<0) return (-EIO);
+ i=cc_ReadSubQ();
+ if (i<0) return (-EIO);
+ D_S[d].pos_audio_start=D_S[d].SubQ_run_tot;
+ i=cc_Seek(D_S[d].pos_audio_start,1);
+ }
+ st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_msf));
+ if (st) return (st);
+ memcpy_fromfs(&msf, (void *) arg, sizeof(struct cdrom_msf));
+ /* values come as msf-bin */
+ D_S[d].pos_audio_start = (msf.cdmsf_min0<<16) |
(msf.cdmsf_sec0<<8) |
- msf.cdmsf_frame0;
- DriveStruct[d].pos_audio_end = (msf.cdmsf_min1<<16) |
- (msf.cdmsf_sec1<<8) |
- msf.cdmsf_frame1;
- DPRINTF((DBG_IOX,"SBPCD: ioctl: CDROMPLAYMSF %08X %08X\n",
- DriveStruct[d].pos_audio_start,DriveStruct[d].pos_audio_end));
- i=xx_PlayAudio(DriveStruct[d].pos_audio_start,DriveStruct[d].pos_audio_end);
- DPRINTF((DBG_IOC,"SBPCD: ioctl: xx_PlayAudio returns %d\n",i));
+ msf.cdmsf_frame0;
+ D_S[d].pos_audio_end = (msf.cdmsf_min1<<16) |
+ (msf.cdmsf_sec1<<8) |
+ msf.cdmsf_frame1;
+ msg(DBG_IOX,"ioctl: CDROMPLAYMSF %08X %08X\n",
+ D_S[d].pos_audio_start,D_S[d].pos_audio_end);
+ i=cc_PlayAudio(D_S[d].pos_audio_start,D_S[d].pos_audio_end);
+ msg(DBG_IOC,"ioctl: cc_PlayAudio returns %d\n",i);
#if 0
- if (i<0) return (-EIO);
+ if (i<0) return (-EIO);
#endif 0
- DriveStruct[d].audio_state=audio_playing;
- return (0);
-
- case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */
- DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMPLAYTRKIND entered.\n"));
- if (DriveStruct[d].audio_state==audio_playing)
- {
- DPRINTF((DBG_IOX,"SBPCD: CDROMPLAYTRKIND: already audio_playing.\n"));
- return (0);
- return (-EINVAL);
- }
- st=verify_area(VERIFY_READ,(void *) arg,sizeof(struct cdrom_ti));
- if (st<0)
- {
- DPRINTF((DBG_IOX,"SBPCD: CDROMPLAYTRKIND: verify_area error.\n"));
- return (st);
- }
- memcpy_fromfs(&ti,(void *) arg,sizeof(struct cdrom_ti));
- DPRINTF((DBG_IOX,"SBPCD: ioctl: trk0: %d, ind0: %d, trk1:%d, ind1:%d\n",
- ti.cdti_trk0,ti.cdti_ind0,ti.cdti_trk1,ti.cdti_ind1));
- if (ti.cdti_trk0<DriveStruct[d].n_first_track) return (-EINVAL);
- if (ti.cdti_trk0>DriveStruct[d].n_last_track) return (-EINVAL);
- if (ti.cdti_trk1<ti.cdti_trk0) ti.cdti_trk1=ti.cdti_trk0;
- if (ti.cdti_trk1>DriveStruct[d].n_last_track) ti.cdti_trk1=DriveStruct[d].n_last_track;
- DriveStruct[d].pos_audio_start=DriveStruct[d].TocBuffer[ti.cdti_trk0].address;
- DriveStruct[d].pos_audio_end=DriveStruct[d].TocBuffer[ti.cdti_trk1+1].address;
- i=xx_PlayAudio(DriveStruct[d].pos_audio_start,DriveStruct[d].pos_audio_end);
+ D_S[d].audio_state=audio_playing;
+ return (0);
+
+ case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */
+ msg(DBG_IOC,"ioctl: CDROMPLAYTRKIND entered.\n");
+ if (D_S[d].audio_state==audio_playing)
+ {
+ msg(DBG_IOX,"CDROMPLAYTRKIND: already audio_playing.\n");
+ return (0);
+ return (-EINVAL);
+ }
+ st=verify_area(VERIFY_READ,(void *) arg,sizeof(struct cdrom_ti));
+ if (st<0)
+ {
+ msg(DBG_IOX,"CDROMPLAYTRKIND: verify_area error.\n");
+ return (st);
+ }
+ memcpy_fromfs(&ti,(void *) arg,sizeof(struct cdrom_ti));
+ msg(DBG_IOX,"ioctl: trk0: %d, ind0: %d, trk1:%d, ind1:%d\n",
+ ti.cdti_trk0,ti.cdti_ind0,ti.cdti_trk1,ti.cdti_ind1);
+ if (ti.cdti_trk0<D_S[d].n_first_track) return (-EINVAL);
+ if (ti.cdti_trk0>D_S[d].n_last_track) return (-EINVAL);
+ if (ti.cdti_trk1<ti.cdti_trk0) ti.cdti_trk1=ti.cdti_trk0;
+ if (ti.cdti_trk1>D_S[d].n_last_track) ti.cdti_trk1=D_S[d].n_last_track;
+ D_S[d].pos_audio_start=D_S[d].TocBuffer[ti.cdti_trk0].address;
+ D_S[d].pos_audio_end=D_S[d].TocBuffer[ti.cdti_trk1+1].address;
+ i=cc_PlayAudio(D_S[d].pos_audio_start,D_S[d].pos_audio_end);
#if 0
- if (i<0) return (-EIO);
+ if (i<0) return (-EIO);
#endif 0
- DriveStruct[d].audio_state=audio_playing;
- return (0);
-
- case CDROMREADTOCHDR: /* Read the table of contents header */
- DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMREADTOCHDR entered.\n"));
- tochdr.cdth_trk0=DriveStruct[d].n_first_track;
- tochdr.cdth_trk1=DriveStruct[d].n_last_track;
- st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_tochdr));
- if (st) return (st);
- memcpy_tofs((void *) arg, &tochdr, sizeof(struct cdrom_tochdr));
- return (0);
-
- case CDROMREADTOCENTRY: /* Read an entry in the table of contents */
- DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMREADTOCENTRY entered.\n"));
- st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_tocentry));
- if (st) return (st);
- memcpy_fromfs(&tocentry, (void *) arg, sizeof(struct cdrom_tocentry));
- i=tocentry.cdte_track;
- if (i==CDROM_LEADOUT) i=DriveStruct[d].n_last_track+1;
- else if (i<DriveStruct[d].n_first_track||i>DriveStruct[d].n_last_track) return (-EINVAL);
- tocentry.cdte_adr=DriveStruct[d].TocBuffer[i].ctl_adr&0x0F;
- tocentry.cdte_ctrl=(DriveStruct[d].TocBuffer[i].ctl_adr>>4)&0x0F;
- tocentry.cdte_datamode=DriveStruct[d].TocBuffer[i].format;
- if (tocentry.cdte_format==CDROM_MSF) /* MSF-bin required */
- { tocentry.cdte_addr.msf.minute=(DriveStruct[d].TocBuffer[i].address>>16)&0x00FF;
- tocentry.cdte_addr.msf.second=(DriveStruct[d].TocBuffer[i].address>>8)&0x00FF;
- tocentry.cdte_addr.msf.frame=DriveStruct[d].TocBuffer[i].address&0x00FF;
- }
- else if (tocentry.cdte_format==CDROM_LBA) /* blk required */
- tocentry.cdte_addr.lba=msf2blk(DriveStruct[d].TocBuffer[i].address);
- else return (-EINVAL);
- st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_tocentry));
- if (st) return (st);
- memcpy_tofs((void *) arg, &tocentry, sizeof(struct cdrom_tocentry));
- return (0);
-
- case CDROMSTOP: /* Spin down the drive */
- DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMSTOP entered.\n"));
- i=xx_Pause_Resume(1);
- DriveStruct[d].audio_state=0;
- return (0);
-
- case CDROMSTART: /* Spin up the drive */
- DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMSTART entered.\n"));
- xx_SpinUp();
- DriveStruct[d].audio_state=0;
- return (0);
-
- case CDROMEJECT:
- DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMEJECT entered.\n"));
- if (old_drive) return (0);
- do i=yy_LockDoor(0);
- while (i!=0);
- DriveStruct[d].open_count=0; /* to get it locked next time again */
- i=yy_SpinDown();
- DPRINTF((DBG_IOX,"SBPCD: ioctl: yy_SpinDown returned %d.\n", i));
- if (i<0) return (-EIO);
- DriveStruct[d].CD_changed=0xFF;
- DriveStruct[d].diskstate_flags=0;
- DriveStruct[d].audio_state=0;
- return (0);
-
- case CDROMEJECT_SW:
- DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMEJECT_SW entered.\n"));
- if (!new_drive) return (0);
- DriveStruct[d].f_eject=arg;
- return (0);
-
- case CDROMVOLCTRL: /* Volume control */
- DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMVOLCTRL entered.\n"));
- st=verify_area(VERIFY_READ,(void *) arg,sizeof(volctrl));
- if (st) return (st);
- memcpy_fromfs(&volctrl,(char *) arg,sizeof(volctrl));
- DriveStruct[d].vol_chan0=0;
- DriveStruct[d].vol_ctrl0=volctrl.channel0;
- DriveStruct[d].vol_chan1=1;
- DriveStruct[d].vol_ctrl1=volctrl.channel1;
- i=xx_SetVolume();
- return (0);
-
- case CDROMSUBCHNL: /* Get subchannel info */
- DPRINTF((DBG_IOS,"SBPCD: ioctl: CDROMSUBCHNL entered.\n"));
- if ((st_spinning)||(!subq_valid)) { i=xx_ReadSubQ();
- if (i<0) return (-EIO);
- }
- st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_subchnl));
- if (st) return (st);
- memcpy_fromfs(&SC, (void *) arg, sizeof(struct cdrom_subchnl));
- switch (DriveStruct[d].audio_state)
- {
- case audio_playing:
- SC.cdsc_audiostatus=CDROM_AUDIO_PLAY;
- break;
- case audio_pausing:
- SC.cdsc_audiostatus=CDROM_AUDIO_PAUSED;
- break;
- default:
- SC.cdsc_audiostatus=CDROM_AUDIO_NO_STATUS;
- break;
- }
- SC.cdsc_adr=DriveStruct[d].SubQ_ctl_adr;
- SC.cdsc_ctrl=DriveStruct[d].SubQ_ctl_adr>>4;
- SC.cdsc_trk=bcd2bin(DriveStruct[d].SubQ_trk);
- SC.cdsc_ind=bcd2bin(DriveStruct[d].SubQ_pnt_idx);
- if (SC.cdsc_format==CDROM_LBA)
- {
- SC.cdsc_absaddr.lba=msf2blk(DriveStruct[d].SubQ_run_tot);
- SC.cdsc_reladdr.lba=msf2blk(DriveStruct[d].SubQ_run_trk);
- }
- else /* not only if (SC.cdsc_format==CDROM_MSF) */
- {
- SC.cdsc_absaddr.msf.minute=(DriveStruct[d].SubQ_run_tot>>16)&0x00FF;
- SC.cdsc_absaddr.msf.second=(DriveStruct[d].SubQ_run_tot>>8)&0x00FF;
- SC.cdsc_absaddr.msf.frame=DriveStruct[d].SubQ_run_tot&0x00FF;
- SC.cdsc_reladdr.msf.minute=(DriveStruct[d].SubQ_run_trk>>16)&0x00FF;
- SC.cdsc_reladdr.msf.second=(DriveStruct[d].SubQ_run_trk>>8)&0x00FF;
- SC.cdsc_reladdr.msf.frame=DriveStruct[d].SubQ_run_trk&0x00FF;
- }
- memcpy_tofs((void *) arg, &SC, sizeof(struct cdrom_subchnl));
- DPRINTF((DBG_IOS,"SBPCD: CDROMSUBCHNL: %1X %02X %08X %08X %02X %02X %06X %06X\n",
- SC.cdsc_format,SC.cdsc_audiostatus,
- SC.cdsc_adr,SC.cdsc_ctrl,
- SC.cdsc_trk,SC.cdsc_ind,
- SC.cdsc_absaddr,SC.cdsc_reladdr));
- return (0);
-
- case CDROMREADMODE1:
- DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMREADMODE1 requested.\n"));
- xx_ModeSelect(CD_FRAMESIZE);
- xx_ModeSense();
- DriveStruct[d].mode=READ_M1;
- return (0);
-
- case CDROMREADMODE2: /* not usable at the moment */
- DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMREADMODE2 requested.\n"));
- xx_ModeSelect(CD_FRAMESIZE_XA);
- xx_ModeSense();
- DriveStruct[d].mode=READ_M2;
- return (0);
-
- case CDROMREADAUDIO:
- { /* start of CDROMREADAUDIO */
- int i=0, j=0, frame, block;
- u_int try=0;
- u_long timeout;
- u_char *p;
- u_int data_tries = 0;
- u_int data_waits = 0;
- u_int data_retrying = 0;
- int status_tries;
- int error_flag;
-
- DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMREADAUDIO requested.\n"));
-#if 0
- if (!new_drive) return (-EINVAL);
-#endif
- if (DriveStruct[d].aud_buf==NULL) return (-EINVAL);
- i=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_read_audio));
- if (i) return (i);
- memcpy_fromfs(&read_audio, (void *) arg, sizeof(struct cdrom_read_audio));
- if (read_audio.nframes>SBP_BUFFER_AUDIO_FRAMES) return (-EINVAL);
- i=verify_area(VERIFY_WRITE, read_audio.buf,
- read_audio.nframes*CD_FRAMESIZE_RAW);
- if (i) return (i);
-
- if (read_audio.addr_format==CDROM_MSF) /* MSF-bin specification of where to start */
- block=msf2lba(&read_audio.addr.msf.minute);
- else if (read_audio.addr_format==CDROM_LBA) /* lba specification of where to start */
- block=read_audio.addr.lba;
- else return (-EINVAL);
- DPRINTF((DBG_AUD,"SBPCD: read_audio: lba: %d, msf: %06X\n",
- block, blk2msf(block)));
- DPRINTF((DBG_AUD,"SBPCD: read_audio: before xx_ReadStatus.\n"));
- while (busy_data) sbp_sleep(10); /* wait a bit */
- busy_audio=1;
- error_flag=0;
- for (data_tries=5; data_tries>0; data_tries--)
- {
- DPRINTF((DBG_AUD,"SBPCD: data_tries=%d ...\n", data_tries));
- DriveStruct[d].mode=READ_AU;
- xx_ModeSelect(CD_FRAMESIZE_RAW);
- xx_ModeSense();
- for (status_tries=3; status_tries > 0; status_tries--)
- {
- flags_cmd_out |= f_respo3;
- xx_ReadStatus();
- if (sbp_status() != 0) break;
- sbp_sleep(1); /* wait a bit, try again */
- }
- if (status_tries == 0)
- {
- DPRINTF((DBG_AUD,"SBPCD: read_audio: sbp_status: failed after 3 tries.\n"));
- continue;
- }
- DPRINTF((DBG_AUD,"SBPCD: read_audio: sbp_status: ok.\n"));
-
- flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus | f_obey_p_check;
- if (!new_drive)
- {
- flags_cmd_out |= f_lopsta | f_getsta | f_bit1;
- cmd_type=READ_M2;
- drvcmd[0]=0x03; /* "read XA frames" command for old drives */
- drvcmd[1]=(block>>16)&0x000000ff;
- drvcmd[2]=(block>>8)&0x000000ff;
- drvcmd[3]=block&0x000000ff;
- drvcmd[4]=0;
- drvcmd[5]=read_audio.nframes; /* # of frames */
- drvcmd[6]=0;
- }
- else /* if new_drive */
- {
- drvcmd[0]=0x10; /* "read frames" command for new drives */
- lba2msf(block,&drvcmd[1]); /* msf-bin format required */
- drvcmd[4]=0;
- drvcmd[5]=0;
- drvcmd[6]=1; /* # of frames */
- }
- DPRINTF((DBG_AUD,"SBPCD: read_audio: before giving \"read\" command.\n"));
- for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
- sbp_sleep(0);
- DPRINTF((DBG_AUD,"SBPCD: read_audio: after giving \"read\" command.\n"));
- for (frame=1;frame<2 && !error_flag; frame++)
- {
- try=maxtim_data;
- for (timeout=jiffies+900; ; )
- {
- for ( ; try!=0;try--)
- {
- j=inb(CDi_status);
- if (!(j&s_not_data_ready)) break;
- if (!(j&s_not_result_ready)) break;
- if (!new_drive) if (j&s_attention) break;
- }
- if (try != 0 || timeout <= jiffies) break;
- if (data_retrying == 0) data_waits++;
- data_retrying = 1;
- sbp_sleep(1);
- try = 1;
- }
- if (try==0)
- {
- DPRINTF((DBG_INF,"SBPCD: read_audio: sbp_data: CDi_status timeout.\n"));
- error_flag++;
- break;
- }
- DPRINTF((DBG_AUD,"SBPCD: read_audio: sbp_data: CDi_status ok.\n"));
- if (j&s_not_data_ready)
- {
- printk("SBPCD: read_audio: sbp_data: DATA_READY timeout.\n");
- error_flag++;
- break;
- }
- DPRINTF((DBG_AUD,"SBPCD: read_audio: before reading data.\n"));
+ D_S[d].audio_state=audio_playing;
+ return (0);
+
+ case CDROMREADTOCHDR: /* Read the table of contents header */
+ msg(DBG_IOC,"ioctl: CDROMREADTOCHDR entered.\n");
+ tochdr.cdth_trk0=D_S[d].n_first_track;
+ tochdr.cdth_trk1=D_S[d].n_last_track;
+ st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_tochdr));
+ if (st) return (st);
+ memcpy_tofs((void *) arg, &tochdr, sizeof(struct cdrom_tochdr));
+ return (0);
+
+ case CDROMREADTOCENTRY: /* Read an entry in the table of contents */
+ msg(DBG_IOC,"ioctl: CDROMREADTOCENTRY entered.\n");
+ st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_tocentry));
+ if (st) return (st);
+ memcpy_fromfs(&tocentry, (void *) arg, sizeof(struct cdrom_tocentry));
+ i=tocentry.cdte_track;
+ if (i==CDROM_LEADOUT) i=D_S[d].n_last_track+1;
+ else if (i<D_S[d].n_first_track||i>D_S[d].n_last_track) return (-EINVAL);
+ tocentry.cdte_adr=D_S[d].TocBuffer[i].ctl_adr&0x0F;
+ tocentry.cdte_ctrl=(D_S[d].TocBuffer[i].ctl_adr>>4)&0x0F;
+ tocentry.cdte_datamode=D_S[d].TocBuffer[i].format;
+ if (tocentry.cdte_format==CDROM_MSF) /* MSF-bin required */
+ {
+ tocentry.cdte_addr.msf.minute=(D_S[d].TocBuffer[i].address>>16)&0x00FF;
+ tocentry.cdte_addr.msf.second=(D_S[d].TocBuffer[i].address>>8)&0x00FF;
+ tocentry.cdte_addr.msf.frame=D_S[d].TocBuffer[i].address&0x00FF;
+ }
+ else if (tocentry.cdte_format==CDROM_LBA) /* blk required */
+ tocentry.cdte_addr.lba=msf2blk(D_S[d].TocBuffer[i].address);
+ else return (-EINVAL);
+ st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_tocentry));
+ if (st) return (st);
+ memcpy_tofs((void *) arg, &tocentry, sizeof(struct cdrom_tocentry));
+ return (0);
+
+ case CDROMRESET: /* hard reset the drive */
+ msg(DBG_IOC,"ioctl: CDROMRESET entered.\n");
+ i=DriveReset();
+ D_S[d].audio_state=0;
+ return (i);
+
+ case CDROMSTOP: /* Spin down the drive */
+ msg(DBG_IOC,"ioctl: CDROMSTOP entered.\n");
+ i=cc_Pause_Resume(1);
+ D_S[d].audio_state=0;
+ return (i);
+
+ case CDROMSTART: /* Spin up the drive */
+ msg(DBG_IOC,"ioctl: CDROMSTART entered.\n");
+ cc_SpinUp();
+ D_S[d].audio_state=0;
+ return (0);
+
+ case CDROMEJECT:
+ msg(DBG_IOC,"ioctl: CDROMEJECT entered.\n");
+ if (fam0_drive) return (0);
+ i=UnLockDoor();
+ D_S[d].open_count=-9; /* to get it locked next time again */
+ i=cc_SpinDown();
+ msg(DBG_IOX,"ioctl: cc_SpinDown returned %d.\n", i);
+ msg(DBG_TEA,"ioctl: cc_SpinDown returned %d.\n", i);
+ if (i<0) return (-EIO);
+ D_S[d].CD_changed=0xFF;
+ D_S[d].diskstate_flags=0;
+ D_S[d].audio_state=0;
+ return (0);
+
+ case CDROMEJECT_SW:
+ msg(DBG_IOC,"ioctl: CDROMEJECT_SW entered.\n");
+ if (fam0_drive) return (0);
+ D_S[d].f_eject=arg;
+ return (0);
+
+ case CDROMVOLCTRL: /* Volume control */
+ msg(DBG_IOC,"ioctl: CDROMVOLCTRL entered.\n");
+ st=verify_area(VERIFY_READ,(void *) arg,sizeof(volctrl));
+ if (st) return (st);
+ memcpy_fromfs(&volctrl,(char *) arg,sizeof(volctrl));
+ D_S[d].vol_chan0=0;
+ D_S[d].vol_ctrl0=volctrl.channel0;
+ D_S[d].vol_chan1=1;
+ D_S[d].vol_ctrl1=volctrl.channel1;
+ i=cc_SetVolume();
+ return (0);
+
+ case CDROMVOLREAD: /* read Volume settings from drive */
+ msg(DBG_IOC,"ioctl: CDROMVOLREAD entered.\n");
+ st=verify_area(VERIFY_WRITE,(void *)arg,sizeof(volctrl));
+ if (st) return (st);
+ st=cc_GetVolume();
+ if (st<0) return (st);
+ volctrl.channel0=D_S[d].vol_ctrl0;
+ volctrl.channel1=D_S[d].vol_ctrl1;
+ volctrl.channel2=0;
+ volctrl.channel2=0;
+ memcpy_tofs((void *)arg,&volctrl,sizeof(volctrl));
+ return (0);
+
+ case CDROMSUBCHNL: /* Get subchannel info */
+ msg(DBG_IOS,"ioctl: CDROMSUBCHNL entered.\n");
+ if ((st_spinning)||(!subq_valid)) { i=cc_ReadSubQ();
+ if (i<0) return (-EIO);
+ }
+ st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_subchnl));
+ if (st) return (st);
+ memcpy_fromfs(&SC, (void *) arg, sizeof(struct cdrom_subchnl));
+ switch (D_S[d].audio_state)
+ {
+ case audio_playing:
+ SC.cdsc_audiostatus=CDROM_AUDIO_PLAY;
+ break;
+ case audio_pausing:
+ SC.cdsc_audiostatus=CDROM_AUDIO_PAUSED;
+ break;
+ default:
+ SC.cdsc_audiostatus=CDROM_AUDIO_NO_STATUS;
+ break;
+ }
+ SC.cdsc_adr=D_S[d].SubQ_ctl_adr;
+ SC.cdsc_ctrl=D_S[d].SubQ_ctl_adr>>4;
+ SC.cdsc_trk=bcd2bin(D_S[d].SubQ_trk);
+ SC.cdsc_ind=bcd2bin(D_S[d].SubQ_pnt_idx);
+ if (SC.cdsc_format==CDROM_LBA)
+ {
+ SC.cdsc_absaddr.lba=msf2blk(D_S[d].SubQ_run_tot);
+ SC.cdsc_reladdr.lba=msf2blk(D_S[d].SubQ_run_trk);
+ }
+ else /* not only if (SC.cdsc_format==CDROM_MSF) */
+ {
+ SC.cdsc_absaddr.msf.minute=(D_S[d].SubQ_run_tot>>16)&0x00FF;
+ SC.cdsc_absaddr.msf.second=(D_S[d].SubQ_run_tot>>8)&0x00FF;
+ SC.cdsc_absaddr.msf.frame=D_S[d].SubQ_run_tot&0x00FF;
+ SC.cdsc_reladdr.msf.minute=(D_S[d].SubQ_run_trk>>16)&0x00FF;
+ SC.cdsc_reladdr.msf.second=(D_S[d].SubQ_run_trk>>8)&0x00FF;
+ SC.cdsc_reladdr.msf.frame=D_S[d].SubQ_run_trk&0x00FF;
+ }
+ memcpy_tofs((void *) arg, &SC, sizeof(struct cdrom_subchnl));
+ msg(DBG_IOS,"CDROMSUBCHNL: %1X %02X %08X %08X %02X %02X %06X %06X\n",
+ SC.cdsc_format,SC.cdsc_audiostatus,
+ SC.cdsc_adr,SC.cdsc_ctrl,
+ SC.cdsc_trk,SC.cdsc_ind,
+ SC.cdsc_absaddr,SC.cdsc_reladdr);
+ return (0);
+
+ case CDROMREADMODE1:
+ msg(DBG_IOC,"ioctl: CDROMREADMODE1 requested.\n");
+ cc_ModeSelect(CD_FRAMESIZE);
+ cc_ModeSense();
+ D_S[d].mode=READ_M1;
+ return (0);
+
+ case CDROMREADMODE2: /* not usable at the moment */
+ msg(DBG_IOC,"ioctl: CDROMREADMODE2 requested.\n");
+ cc_ModeSelect(CD_FRAMESIZE_XA);
+ cc_ModeSense();
+ D_S[d].mode=READ_M2;
+ return (0);
+
+ case CDROMAUDIOBUFSIZ: /* configure the audio buffer size */
+ msg(DBG_IOC,"ioctl: CDROMAUDIOBUFSIZ entered.\n");
+#ifdef MODULE
+ if (D_S[d].sbp_audsiz>0)
+ vfree(D_S[d].aud_buf);
+#endif MODULE
+ D_S[d].aud_buf=NULL;
+ D_S[d].sbp_audsiz=arg;
+ if (D_S[d].sbp_audsiz>0)
+ {
+ D_S[d].aud_buf=(u_char *) vmalloc(D_S[d].sbp_audsiz*CD_FRAMESIZE_RAW);
+ if (D_S[d].aud_buf==NULL)
+ {
+ msg(DBG_INF,"audio buffer (%d frames) not available.\n",D_S[d].sbp_audsiz);
+ D_S[d].sbp_audsiz=0;
+ }
+ else msg(DBG_INF,"audio buffer size: %d frames.\n",D_S[d].sbp_audsiz);
+ }
+ return (D_S[d].sbp_audsiz);
+
+ case CDROMREADAUDIO:
+ { /* start of CDROMREADAUDIO */
+ int i=0, j=0, frame, block;
+ u_int try=0;
+ u_long timeout;
+ u_char *p;
+ u_int data_tries = 0;
+ u_int data_waits = 0;
+ u_int data_retrying = 0;
+ int status_tries;
+ int error_flag;
+
+ msg(DBG_IOC,"ioctl: CDROMREADAUDIO entered.\n");
+ if (fam0_drive) return (-EINVAL);
+ if (famL_drive) return (-EINVAL);
+ if (fam2_drive) return (-EINVAL);
+ if (famT_drive) return (-EINVAL);
+ if (D_S[d].aud_buf==NULL) return (-EINVAL);
+ i=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_read_audio));
+ if (i) return (i);
+ memcpy_fromfs(&read_audio, (void *) arg, sizeof(struct cdrom_read_audio));
+ if (read_audio.nframes>D_S[d].sbp_audsiz) return (-EINVAL);
+ i=verify_area(VERIFY_WRITE, read_audio.buf,
+ read_audio.nframes*CD_FRAMESIZE_RAW);
+ if (i) return (i);
+
+ if (read_audio.addr_format==CDROM_MSF) /* MSF-bin specification of where to start */
+ block=msf2lba(&read_audio.addr.msf.minute);
+ else if (read_audio.addr_format==CDROM_LBA) /* lba specification of where to start */
+ block=read_audio.addr.lba;
+ else return (-EINVAL);
+ i=cc_SetSpeed(speed_150,0,0);
+ if (i) msg(DBG_AUD,"read_audio: SetSpeed error %d\n", i);
+ msg(DBG_AUD,"read_audio: lba: %d, msf: %06X\n",
+ block, blk2msf(block));
+ msg(DBG_AUD,"read_audio: before cc_ReadStatus.\n");
+ while (busy_data) sbp_sleep(10); /* wait a bit */
+ busy_audio=1;
error_flag=0;
- p = DriveStruct[d].aud_buf;
- if (sbpro_type==1) OUT(CDo_sel_d_i,0x01);
-#if 0
- cli();
-#endif
- READ_DATA(CDi_data, p, read_audio.nframes*CD_FRAMESIZE_RAW);
-#if 0
- sti();
-#endif
- if (sbpro_type==1) OUT(CDo_sel_d_i,0x00);
- data_retrying = 0;
- }
- DPRINTF((DBG_AUD,"SBPCD: read_audio: after reading data.\n"));
- if (error_flag) /* must have been spurious D_RDY or (ATTN&&!D_RDY) */
- {
- DPRINTF((DBG_AUD,"SBPCD: read_audio: read aborted by drive\n"));
+ for (data_tries=5; data_tries>0; data_tries--)
+ {
+ msg(DBG_AUD,"data_tries=%d ...\n", data_tries);
+ D_S[d].mode=READ_AU;
+ cc_ModeSelect(CD_FRAMESIZE_RAW);
+ cc_ModeSense();
+ for (status_tries=3; status_tries > 0; status_tries--)
+ {
+ flags_cmd_out |= f_respo3;
+ cc_ReadStatus();
+ if (sbp_status() != 0) break;
+ sbp_sleep(1); /* wait a bit, try again */
+ }
+ if (status_tries == 0)
+ {
+ msg(DBG_AUD,"read_audio: sbp_status: failed after 3 tries.\n");
+ continue;
+ }
+ msg(DBG_AUD,"read_audio: sbp_status: ok.\n");
+
+ flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus | f_obey_p_check;
+ if (fam0L_drive)
+ {
+ flags_cmd_out |= f_lopsta | f_getsta | f_bit1;
+ cmd_type=READ_M2;
+ drvcmd[0]=CMD0_READ_XA; /* "read XA frames", old drives */
+ drvcmd[1]=(block>>16)&0x000000ff;
+ drvcmd[2]=(block>>8)&0x000000ff;
+ drvcmd[3]=block&0x000000ff;
+ drvcmd[4]=0;
+ drvcmd[5]=read_audio.nframes; /* # of frames */
+ drvcmd[6]=0;
+ }
+ else if (fam1_drive)
+ {
+ drvcmd[0]=CMD1_READ; /* "read frames", new drives */
+ lba2msf(block,&drvcmd[1]); /* msf-bin format required */
+ drvcmd[4]=0;
+ drvcmd[5]=0;
+ drvcmd[6]=read_audio.nframes; /* # of frames */
+ }
+ else if (fam2_drive) /* CD200: not tested yet */
+ {
+ }
+ else if (famT_drive) /* CD-55A: not tested yet */
+ {
+ }
+ msg(DBG_AUD,"read_audio: before giving \"read\" command.\n");
+ for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
+ sbp_sleep(0);
+ msg(DBG_AUD,"read_audio: after giving \"read\" command.\n");
+ for (frame=1;frame<2 && !error_flag; frame++)
+ {
+ try=maxtim_data;
+ for (timeout=jiffies+900; ; )
+ {
+ for ( ; try!=0;try--)
+ {
+ j=inb(CDi_status);
+ if (!(j&s_not_data_ready)) break;
+ if (!(j&s_not_result_ready)) break;
+ if (fam0L_drive) if (j&s_attention) break;
+ }
+ if (try != 0 || timeout <= jiffies) break;
+ if (data_retrying == 0) data_waits++;
+ data_retrying = 1;
+ sbp_sleep(1);
+ try = 1;
+ }
+ if (try==0)
+ {
+ msg(DBG_INF,"read_audio: sbp_data: CDi_status timeout.\n");
+ error_flag++;
+ break;
+ }
+ msg(DBG_AUD,"read_audio: sbp_data: CDi_status ok.\n");
+ if (j&s_not_data_ready)
+ {
+ msg(DBG_INF, "read_audio: sbp_data: DATA_READY timeout.\n");
+ error_flag++;
+ break;
+ }
+ msg(DBG_AUD,"read_audio: before reading data.\n");
+ error_flag=0;
+ p = D_S[d].aud_buf;
+ if (sbpro_type==1) OUT(CDo_sel_i_d,1);
+ insb(CDi_data, p, read_audio.nframes*CD_FRAMESIZE_RAW);
+ if (sbpro_type==1) OUT(CDo_sel_i_d,0);
+ data_retrying = 0;
+ }
+ msg(DBG_AUD,"read_audio: after reading data.\n");
+ if (error_flag) /* must have been spurious D_RDY or (ATTN&&!D_RDY) */
+ {
+ msg(DBG_AUD,"read_audio: read aborted by drive\n");
#if 0000
- i=DriveReset(); /* ugly fix to prevent a hang */
+ i=cc_DriveReset(); /* ugly fix to prevent a hang */
+#else
+ i=cc_ReadError();
#endif 0000
- continue;
- }
- if (!new_drive)
- {
- i=maxtim_data;
- for (timeout=jiffies+900; timeout > jiffies; timeout--)
- {
- for ( ;i!=0;i--)
- {
- j=inb(CDi_status);
- if (!(j&s_not_data_ready)) break;
- if (!(j&s_not_result_ready)) break;
- if (j&s_attention) break;
- }
- if (i != 0 || timeout <= jiffies) break;
- sbp_sleep(0);
- i = 1;
- }
- if (i==0) { DPRINTF((DBG_AUD,"SBPCD: read_audio: STATUS TIMEOUT AFTER READ")); }
- if (!(j&s_attention))
- {
- DPRINTF((DBG_AUD,"SBPCD: read_audio: sbp_data: timeout waiting DRV_ATTN - retrying\n"));
- i=DriveReset(); /* ugly fix to prevent a hang */
- continue;
- }
- }
- do
- {
- if (!new_drive) xx_ReadStatus();
- i=ResponseStatus(); /* builds status_byte, returns orig. status (old) or faked p_success_old (new) */
- if (i<0) { DPRINTF((DBG_AUD,
- "SBPCD: read_audio: xx_ReadStatus error after read: %02X\n",
- DriveStruct[d].status_byte));
- continue; /* FIXME */
- }
- }
- while ((!new_drive)&&(!st_check)&&(!(i&p_success_old)));
- if (st_check)
- {
- i=xx_ReadError();
- DPRINTF((DBG_AUD,"SBPCD: read_audio: xx_ReadError was necessary after read: %02X\n",i));
- continue;
- }
- memcpy_tofs((u_char *) read_audio.buf,
- (u_char *) DriveStruct[d].aud_buf,
- read_audio.nframes*CD_FRAMESIZE_RAW);
- DPRINTF((DBG_AUD,"SBPCD: read_audio: memcpy_tofs done.\n"));
- break;
- }
- xx_ModeSelect(CD_FRAMESIZE);
- xx_ModeSense();
- DriveStruct[d].mode=READ_M1;
- busy_audio=0;
- if (data_tries == 0)
- {
- DPRINTF((DBG_AUD,"SBPCD: read_audio: failed after 5 tries.\n"));
- return (-8);
- }
- DPRINTF((DBG_AUD,"SBPCD: read_audio: successful return.\n"));
- return (0);
- } /* end of CDROMREADAUDIO */
-
- case BLKRASET:
- if(!suser()) return -EACCES;
- if(!inode->i_rdev) return -EINVAL;
- if(arg > 0xff) return -EINVAL;
- read_ahead[MAJOR(inode->i_rdev)] = arg;
- return (0);
-
- default:
- DPRINTF((DBG_IOC,"SBPCD: ioctl: unknown function request %04X\n", cmd));
- return (-EINVAL);
- } /* end switch(cmd) */
+ continue;
+ }
+ if (fam0L_drive)
+ {
+ i=maxtim_data;
+ for (timeout=jiffies+900; timeout > jiffies; timeout--)
+ {
+ for ( ;i!=0;i--)
+ {
+ j=inb(CDi_status);
+ if (!(j&s_not_data_ready)) break;
+ if (!(j&s_not_result_ready)) break;
+ if (j&s_attention) break;
+ }
+ if (i != 0 || timeout <= jiffies) break;
+ sbp_sleep(0);
+ i = 1;
+ }
+ if (i==0) msg(DBG_AUD,"read_audio: STATUS TIMEOUT AFTER READ");
+ if (!(j&s_attention))
+ {
+ msg(DBG_AUD,"read_audio: sbp_data: timeout waiting DRV_ATTN - retrying\n");
+ i=cc_DriveReset(); /* ugly fix to prevent a hang */
+ continue;
+ }
+ }
+ do
+ {
+ if (fam0L_drive) cc_ReadStatus();
+ i=ResponseStatus(); /* builds status_bits, returns orig. status (old) or faked p_success (new) */
+ if (i<0) { msg(DBG_AUD,
+ "read_audio: cc_ReadStatus error after read: %02X\n",
+ D_S[d].status_bits);
+ continue; /* FIXME */
+ }
+ }
+ while ((fam0L_drive)&&(!st_check)&&(!(i&p_success)));
+ if (st_check)
+ {
+ i=cc_ReadError();
+ msg(DBG_AUD,"read_audio: cc_ReadError was necessary after read: %02X\n",i);
+ continue;
+ }
+ memcpy_tofs((u_char *) read_audio.buf,
+ (u_char *) D_S[d].aud_buf,
+ read_audio.nframes*CD_FRAMESIZE_RAW);
+ msg(DBG_AUD,"read_audio: memcpy_tofs done.\n");
+ break;
+ }
+ cc_ModeSelect(CD_FRAMESIZE);
+ cc_ModeSense();
+ D_S[d].mode=READ_M1;
+ busy_audio=0;
+ if (data_tries == 0)
+ {
+ msg(DBG_AUD,"read_audio: failed after 5 tries.\n");
+ return (-8);
+ }
+ msg(DBG_AUD,"read_audio: successful return.\n");
+ return (0);
+ } /* end of CDROMREADAUDIO */
+
+ case CDROMMULTISESSION: /* tell start-of-last-session */
+ msg(DBG_IOC,"ioctl: CDROMMULTISESSION entered.\n");
+ st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_multisession));
+ if (st) return (st);
+ memcpy_fromfs(&ms_info, (void *) arg, sizeof(struct cdrom_multisession));
+ if (ms_info.addr_format==CDROM_MSF) /* MSF-bin requested */
+ lba2msf(D_S[d].lba_multi,&ms_info.addr.msf.minute);
+ else if (ms_info.addr_format==CDROM_LBA) /* lba requested */
+ ms_info.addr.lba=D_S[d].lba_multi;
+ else return (-EINVAL);
+ if (D_S[d].f_multisession) ms_info.xa_flag=1; /* valid redirection address */
+ else ms_info.xa_flag=0; /* invalid redirection address */
+ st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_multisession));
+ if (st) return (st);
+ memcpy_tofs((void *) arg, &ms_info, sizeof(struct cdrom_multisession));
+ msg(DBG_MUL,"ioctl: CDROMMULTISESSION done (%d, %08X).\n",
+ ms_info.xa_flag, ms_info.addr.lba);
+ return (0);
+
+ case BLKRASET:
+ if(!suser()) return -EACCES;
+ if(!inode->i_rdev) return -EINVAL;
+ if(arg > 0xff) return -EINVAL;
+ read_ahead[MAJOR(inode->i_rdev)] = arg;
+ return (0);
+
+ default:
+ msg(DBG_IOC,"ioctl: unknown function request %04X\n", cmd);
+ return (-EINVAL);
+ } /* end switch(cmd) */
}
/*==========================================================================*/
/*
@@ -3005,18 +4338,18 @@ static int sbpcd_ioctl(struct inode *inode, struct file *file, u_int cmd,
*/
static void sbp_transfer(void)
{
- long offs;
-
- while ( (CURRENT->nr_sectors > 0) &&
- (CURRENT->sector/4 >= DriveStruct[d].sbp_first_frame) &&
- (CURRENT->sector/4 <= DriveStruct[d].sbp_last_frame) )
- {
- offs = (CURRENT->sector - DriveStruct[d].sbp_first_frame * 4) * 512;
- memcpy(CURRENT->buffer, DriveStruct[d].sbp_buf + offs, 512);
- CURRENT->nr_sectors--;
- CURRENT->sector++;
- CURRENT->buffer += 512;
- }
+ long offs;
+
+ while ( (CURRENT->nr_sectors > 0) &&
+ (CURRENT->sector/4 >= D_S[d].sbp_first_frame) &&
+ (CURRENT->sector/4 <= D_S[d].sbp_last_frame) )
+ {
+ offs = (CURRENT->sector - D_S[d].sbp_first_frame * 4) * 512;
+ memcpy(CURRENT->buffer, D_S[d].sbp_buf + offs, 512);
+ CURRENT->nr_sectors--;
+ CURRENT->sector++;
+ CURRENT->buffer += 512;
+ }
}
/*==========================================================================*/
/*
@@ -3024,345 +4357,459 @@ static void sbp_transfer(void)
*/
static void DO_SBPCD_REQUEST(void)
{
- u_int block;
- int dev;
- u_int nsect;
- int i, status_tries, data_tries;
-
-request_loop:
-
- sti();
-
- if ((CURRENT==NULL)||(CURRENT->dev<0)) goto done;
- if (CURRENT -> sector == -1) goto done;
-
- dev = MINOR(CURRENT->dev);
- if ( (dev<0) || (dev>=NR_SBPCD) )
- {
- printk("SBPCD: do_request: bad device: %d\n", dev);
- goto done;
- }
- switch_drive(dev);
-
- INIT_REQUEST;
- block = CURRENT->sector; /* always numbered as 512-byte-pieces */
- nsect = CURRENT->nr_sectors; /* always counted as 512-byte-pieces */
- if (CURRENT->cmd != READ)
- {
- printk("SBPCD: bad cmd %d\n", CURRENT->cmd);
- end_request(0);
- goto request_loop;
- }
-
- DPRINTF((DBG_BSZ,"SBPCD: read sector %d (%d sectors)\n", block, nsect));
-#if 0
- DPRINTF((DBG_MUL,"SBPCD: read LBA %d\n", block/4));
-#endif
-
- sbp_transfer();
- /* if we satisfied the request from the buffer, we're done. */
- if (CURRENT->nr_sectors == 0)
- {
- end_request(1);
- goto request_loop;
- }
-
- i=prepare(0,0); /* at moment not really a hassle check, but ... */
- if (i!=0)
- DPRINTF((DBG_INF,"SBPCD: \"prepare\" tells error %d -- ignored\n", i));
-
- while (busy_audio) sbp_sleep(100); /* wait a bit */
- busy_data=1;
-
- if (!st_spinning) xx_SpinUp();
-
-#ifdef XA_TEST1
- if ((!new_drive) && (DriveStruct[d].xa_byte==0x20)) /* XA disk with old drive */
- {
- xx_ModeSelect(CD_FRAMESIZE_XA);
- xx_ModeSense();
- }
-#endif XA_TEST1
-
- for (data_tries=3; data_tries > 0; data_tries--)
- {
- for (status_tries=3; status_tries > 0; status_tries--)
+ u_int block;
+ u_int nsect;
+ int i, status_tries, data_tries;
+
+ request_loop:
+ INIT_REQUEST;
+ sti();
+
+ if ((CURRENT==NULL)||(CURRENT->dev<0)) goto err_done;
+ if (CURRENT -> sector == -1) goto err_done;
+ if (CURRENT->cmd != READ)
{
- flags_cmd_out |= f_respo3;
- xx_ReadStatus();
- if (sbp_status() != 0) break;
- sbp_sleep(1); /* wait a bit, try again */
+ msg(DBG_INF, "bad cmd %d\n", CURRENT->cmd);
+ goto err_done;
}
- if (status_tries == 0)
+ i = MINOR(CURRENT->dev);
+ if ( (i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1))
{
- DPRINTF((DBG_INF,"SBPCD: sbp_status: failed after 3 tries\n"));
- break;
+ msg(DBG_INF, "do_request: bad device: %04X\n", CURRENT->dev);
+ goto err_done;
}
-
- sbp_read_cmd();
- sbp_sleep(0);
- if (sbp_data() != 0)
+ while (busy_audio) sbp_sleep(100); /* wait a bit */
+ busy_data=1;
+
+ if (D_S[i].audio_state==audio_playing) goto err_done;
+ if (d!=i) switch_drive(i);
+
+ block = CURRENT->sector; /* always numbered as 512-byte-pieces */
+ nsect = CURRENT->nr_sectors; /* always counted as 512-byte-pieces */
+
+ msg(DBG_BSZ,"read sector %d (%d sectors)\n", block, nsect);
+#if 0
+ msg(DBG_MUL,"read LBA %d\n", block/4);
+#endif
+
+ sbp_transfer();
+ /* if we satisfied the request from the buffer, we're done. */
+ if (CURRENT->nr_sectors == 0)
{
- end_request(1);
- goto request_loop;
+ end_request(1);
+ goto request_loop;
}
- }
-
- end_request(0);
- sbp_sleep(10); /* wait a bit, try again */
- goto request_loop;
-done:
- busy_data=0;
- return;
+#if FUTURE
+ i=prepare(0,0); /* at moment not really a hassle check, but ... */
+ if (i!=0)
+ msg(DBG_INF,"\"prepare\" tells error %d -- ignored\n", i);
+#endif FUTURE
+
+ if (!st_spinning) cc_SpinUp();
+
+ for (data_tries=n_retries; data_tries > 0; data_tries--)
+ {
+ for (status_tries=3; status_tries > 0; status_tries--)
+ {
+ flags_cmd_out |= f_respo3;
+ cc_ReadStatus();
+ if (sbp_status() != 0) break;
+ if (st_check) cc_ReadError();
+ sbp_sleep(1); /* wait a bit, try again */
+ }
+ if (status_tries == 0)
+ {
+ msg(DBG_INF,"sbp_status: failed after 3 tries\n");
+ break;
+ }
+
+ sbp_read_cmd();
+ sbp_sleep(0);
+ if (sbp_data() != 0)
+ {
+ end_request(1);
+ goto request_loop;
+ }
+ }
+
+ err_done:
+ busy_data=0;
+ end_request(0);
+ sbp_sleep(0); /* wait a bit, try again */
+ goto request_loop;
}
/*==========================================================================*/
/*
* build and send the READ command.
- * Maybe it would be better to "set mode1" before ...
*/
static void sbp_read_cmd(void)
{
- int i;
- int block;
-
- DriveStruct[d].sbp_first_frame=DriveStruct[d].sbp_last_frame=-1; /* purge buffer */
- block=CURRENT->sector/4;
-
- if (new_drive)
- {
-#if MANY_SESSION
- DPRINTF((DBG_MUL,"SBPCD: read MSF %08X\n", blk2msf(block)));
- if (DriveStruct[d].f_multisession)
+#undef OLD
+
+ int i;
+ int block;
+
+ D_S[d].sbp_first_frame=D_S[d].sbp_last_frame=-1; /* purge buffer */
+ D_S[d].sbp_current = 0;
+ block=CURRENT->sector/4;
+ if (block+D_S[d].sbp_bufsiz <= D_S[d].CDsize_frm)
+ D_S[d].sbp_read_frames = D_S[d].sbp_bufsiz;
+ else
{
- DPRINTF((DBG_MUL,"SBPCD: ManySession: use %08X for %08X (msf)\n",
- blk2msf(DriveStruct[d].lba_multi+block),
- blk2msf(block)));
- block=DriveStruct[d].lba_multi+block;
+ D_S[d].sbp_read_frames=D_S[d].CDsize_frm-block;
+ /* avoid reading past end of data */
+ if (D_S[d].sbp_read_frames < 1)
+ {
+ msg(DBG_INF,"requested frame %d, CD size %d ???\n",
+ block, D_S[d].CDsize_frm);
+ D_S[d].sbp_read_frames=1;
+ }
}
-#else
- if ((block<=DriveStruct[d].last_redirect)
- && (DriveStruct[d].f_multisession))
- {
- DPRINTF((DBG_MUL,"SBPCD: MultiSession: use %08X for %08X (msf)\n",
- blk2msf(DriveStruct[d].lba_multi+block),
- blk2msf(block)));
- block=DriveStruct[d].lba_multi+block;
- }
-#endif MANY_SESSION
- }
-
- if (block+SBP_BUFFER_FRAMES <= DriveStruct[d].CDsize_frm)
- DriveStruct[d].sbp_read_frames = SBP_BUFFER_FRAMES;
- else
- {
- DriveStruct[d].sbp_read_frames=DriveStruct[d].CDsize_frm-block;
- /* avoid reading past end of data */
- if (DriveStruct[d].sbp_read_frames < 1)
- {
- DPRINTF((DBG_INF,"SBPCD: requested frame %d, CD size %d ???\n",
- block, DriveStruct[d].CDsize_frm));
- DriveStruct[d].sbp_read_frames=1;
+
+ flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus | f_obey_p_check;
+ clr_cmdbuf();
+ if (fam0L_drive)
+ {
+ flags_cmd_out |= f_lopsta | f_getsta | f_bit1;
+ if (D_S[d].xa_byte==0x20)
+ {
+ cmd_type=READ_M2;
+ drvcmd[0]=CMD0_READ_XA; /* "read XA frames", old drives */
+ drvcmd[1]=(block>>16)&0x000000ff;
+ drvcmd[2]=(block>>8)&0x000000ff;
+ drvcmd[3]=block&0x000000ff;
+ drvcmd[5]=D_S[d].sbp_read_frames;
+ }
+ else
+ {
+ drvcmd[0]=CMD0_READ; /* "read frames", old drives */
+ if (D_S[d].drv_type>=drv_201)
+ {
+ lba2msf(block,&drvcmd[1]); /* msf-bcd format required */
+ bin2bcdx(&drvcmd[1]);
+ bin2bcdx(&drvcmd[2]);
+ bin2bcdx(&drvcmd[3]);
+ }
+ else
+ {
+ drvcmd[1]=(block>>16)&0x000000ff;
+ drvcmd[2]=(block>>8)&0x000000ff;
+ drvcmd[3]=block&0x000000ff;
+ }
+ drvcmd[5]=D_S[d].sbp_read_frames;
+ drvcmd[6]=(D_S[d].drv_type<drv_201)?0:2; /* flag "lba or msf-bcd format" */
+ }
}
- }
- DriveStruct[d].sbp_current = 0;
-
- flags_cmd_out = f_putcmd |
- f_respo2 |
- f_ResponseStatus |
- f_obey_p_check;
-
- if (!new_drive)
- {
- flags_cmd_out |= f_lopsta | f_getsta | f_bit1;
- if (DriveStruct[d].xa_byte==0x20)
- {
- cmd_type=READ_M2;
- drvcmd[0]=0x03; /* "read XA frames" command for old drives */
- drvcmd[1]=(block>>16)&0x000000ff;
- drvcmd[2]=(block>>8)&0x000000ff;
- drvcmd[3]=block&0x000000ff;
- drvcmd[4]=0;
- drvcmd[5]=DriveStruct[d].sbp_read_frames;
- drvcmd[6]=0;
- }
- else
- {
- drvcmd[0]=0x02; /* "read frames" command for old drives */
-
- if (DriveStruct[d].drv_type>=drv_201)
- {
- lba2msf(block,&drvcmd[1]); /* msf-bcd format required */
- bin2bcdx(&drvcmd[1]);
- bin2bcdx(&drvcmd[2]);
- bin2bcdx(&drvcmd[3]);
- }
- else
- {
- drvcmd[1]=(block>>16)&0x000000ff;
- drvcmd[2]=(block>>8)&0x000000ff;
- drvcmd[3]=block&0x000000ff;
- }
- drvcmd[4]=0;
- drvcmd[5]=DriveStruct[d].sbp_read_frames;
- drvcmd[6]=(DriveStruct[d].drv_type<drv_201)?0:2; /* flag "lba or msf-bcd format" */
+ else if (fam1_drive)
+ {
+ drvcmd[0]=CMD1_READ;
+ lba2msf(block,&drvcmd[1]); /* msf-bin format required */
+ drvcmd[6]=D_S[d].sbp_read_frames;
}
- }
- else /* if new_drive */
- {
- drvcmd[0]=0x10; /* "read frames" command for new drives */
- lba2msf(block,&drvcmd[1]); /* msf-bin format required */
- drvcmd[4]=0;
- drvcmd[5]=0;
- drvcmd[6]=DriveStruct[d].sbp_read_frames;
- }
- SBPCD_CLI;
- for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
- SBPCD_STI;
-
- return;
+ else if (fam2_drive)
+ {
+ drvcmd[0]=CMD2_READ;
+ lba2msf(block,&drvcmd[1]); /* msf-bin format required */
+ drvcmd[5]=D_S[d].sbp_read_frames;
+ drvcmd[6]=0x02;
+ }
+ else if (famT_drive)
+ {
+ drvcmd[0]=CMDT_READ;
+ drvcmd[2]=(block>>24)&0x0ff;
+ drvcmd[3]=(block>>16)&0x0ff;
+ drvcmd[4]=(block>>8)&0x0ff;
+ drvcmd[5]=block&0x0ff;
+ drvcmd[7]=(D_S[d].sbp_read_frames>>8)&0x0ff;
+ drvcmd[8]=D_S[d].sbp_read_frames&0x0ff;
+ }
+#ifdef OLD
+ SBPCD_CLI;
+ for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
+ if (famT_drive) for (i=7;i<10;i++) OUT(CDo_command,drvcmd[i]);
+ SBPCD_STI;
+#else
+ flags_cmd_out=f_putcmd;
+ response_count=0;
+ i=cmd_out(); /* immediate return here - read data "ourselves" */
+ if (i<0) msg(DBG_INF,"error giving READ command: %0d\n", i);
+#endif OLD
+ return;
}
/*==========================================================================*/
/*
* Check the completion of the read-data command. On success, read
- * the SBP_BUFFER_FRAMES * 2048 bytes of data from the disk into buffer.
+ * the D_S[d].sbp_bufsiz * 2048 bytes of data from the disk into buffer.
*/
static int sbp_data(void)
{
- int i=0, j=0, frame;
- u_int try=0;
- u_long timeout;
- u_char *p;
- u_int data_tries = 0;
- u_int data_waits = 0;
- u_int data_retrying = 0;
- int error_flag;
- int xa_count;
- error_flag=0;
-
- for (frame=DriveStruct[d].sbp_current;frame<DriveStruct[d].sbp_read_frames&&!error_flag; frame++)
- {
- SBPCD_CLI;
- try=maxtim_data;
+ int i=0, j=0, l, frame;
+ u_int try=0;
+ u_long timeout;
+ u_char *p;
+ u_int data_tries = 0;
+ u_int data_waits = 0;
+ u_int data_retrying = 0;
+ int error_flag;
+ int xa_count;
+ int max_latency;
+ int success;
+ int wait;
+ int duration;
+
+ error_flag=0;
+ success=0;
#if LONG_TIMING
- for (timeout=jiffies+900; ; )
+ max_latency=900;
#else
- for (timeout=jiffies+100; ; )
+ if (D_S[d].f_multisession) max_latency=900;
+ else if (fam0L_drive) max_latency=300;
+ else if (famT_drive) max_latency=300;
+ else max_latency=100;
#endif
+ msg(DBG_TE2,"beginning to READ\n");
+ duration=jiffies;
+ for (frame=0;frame<D_S[d].sbp_read_frames&&!error_flag; frame++)
{
- for ( ; try!=0;try--)
- {
- j=inb(CDi_status);
- if (!(j&s_not_data_ready)) break;
- if (!(j&s_not_result_ready)) break;
- if (!new_drive) if (j&s_attention) break;
- }
- if (try != 0 || timeout <= jiffies) break;
- if (data_retrying == 0) data_waits++;
- data_retrying = 1;
- sbp_sleep(1);
- try = 1;
- }
- if (try==0)
- {
- DPRINTF((DBG_INF,"SBPCD: sbp_data: CDi_status timeout.\n"));
- error_flag++;
- break;
- }
+ SBPCD_CLI;
+
+ del_timer(&data_timer);
+ data_timer.expires=max_latency;
+ timed_out_data=0;
+ add_timer(&data_timer);
+ while (!timed_out_data)
+ {
+ if (D_S[d].f_multisession) try=maxtim_data*4;
+ else try=maxtim_data;
+ msg(DBG_000,"sbp_data: CDi_status loop: try=%d.\n",try);
+ for ( ; try!=0;try--)
+ {
+ j=inb(CDi_status);
+ if (!(j&s_not_data_ready)) break;;
+ if (!(j&s_not_result_ready)) break;
+ if (fam0L_drive) if (j&s_attention) break;
+ }
+ if (!(j&s_not_data_ready)) goto data_ready;
+ if (try==0)
+ {
+ if (data_retrying == 0) data_waits++;
+ data_retrying = 1;
+ msg(DBG_000,"sbp_data: CDi_status loop: sleeping.\n");
+ sbp_sleep(1);
+ try = 1;
+ }
+ }
+ msg(DBG_INF,"sbp_data: CDi_status loop expired.\n");
+ data_ready:
+ del_timer(&data_timer);
- if (j&s_not_data_ready)
+ if (timed_out_data)
+ {
+ msg(DBG_INF,"sbp_data: CDi_status timeout (timed_out_data) (%02X).\n", j);
+ error_flag++;
+ break;
+ }
+ if (try==0)
+ {
+ msg(DBG_INF,"sbp_data: CDi_status timeout (try=0) (%02X).\n", j);
+ error_flag++;
+ break;
+ }
+ if (!(j&s_not_result_ready))
+ {
+ msg(DBG_INF, "sbp_data: RESULT_READY where DATA_READY awaited (%02X).\n", j);
+ response_count=20;
+ j=ResponseInfo();
+ j=inb(CDi_status);
+ }
+ if (j&s_not_data_ready)
+ {
+ if ((D_S[d].ored_ctl_adr&0x40)==0)
+ msg(DBG_INF, "CD contains no data tracks.\n");
+ else msg(DBG_INF, "sbp_data: DATA_READY timeout (%02X).\n", j);
+ error_flag++;
+ break;
+ }
+ SBPCD_STI;
+ error_flag=0;
+ msg(DBG_000, "sbp_data: beginning to read.\n");
+ p = D_S[d].sbp_buf + frame * CD_FRAMESIZE;
+ if (sbpro_type==1) OUT(CDo_sel_i_d,1);
+ if (cmd_type==READ_M2) insb(CDi_data, xa_head_buf, CD_XA_HEAD);
+ insb(CDi_data, p, CD_FRAMESIZE);
+ if (cmd_type==READ_M2) insb(CDi_data, xa_tail_buf, CD_XA_TAIL);
+ if (famT_drive) msg(DBG_TE2, "================frame read=================.\n");
+ D_S[d].sbp_current++;
+ if (sbpro_type==1) OUT(CDo_sel_i_d,0);
+ if (cmd_type==READ_M2)
+ {
+ for (xa_count=0;xa_count<CD_XA_HEAD;xa_count++)
+ sprintf(&msgbuf[xa_count*3], " %02X", xa_head_buf[xa_count]);
+ msgbuf[xa_count*3]=0;
+ msg(DBG_XA1,"xa head:%s\n", msgbuf);
+ }
+ data_retrying = 0;
+ data_tries++;
+ if (data_tries >= 1000)
+ {
+ msg(DBG_INF,"sbp_data() statistics: %d waits in %d frames.\n", data_waits, data_tries);
+ data_waits = data_tries = 0;
+ }
+ }
+ duration=jiffies-duration;
+ msg(DBG_TE2,"time to read %d frames: %d jiffies .\n",frame,duration);
+ if (famT_drive)
{
- if ((DriveStruct[d].ored_ctl_adr&0x40)==0)
- printk("SBPCD: CD contains no data tracks.\n");
- else printk("SBPCD: sbp_data: DATA_READY timeout.\n");
- error_flag++;
- break;
+ wait=8;
+ do
+ {
+ sbp_sleep(1);
+ OUT(CDo_sel_i_d,0);
+ i=inb(CDi_status);
+ if (!(i&s_not_data_ready))
+ {
+ OUT(CDo_sel_i_d,1);
+ j=0;
+ do
+ {
+ i=inb(CDi_data);
+ j++;
+ i=inb(CDi_status);
+ }
+ while (!(i&s_not_data_ready));
+ msg(DBG_TEA, "=============too much data (%d bytes)=================.\n", j);
+ }
+ if (!(i&s_not_result_ready))
+ {
+ OUT(CDo_sel_i_d,0);
+ l=0;
+ do
+ {
+ infobuf[l++]=inb(CDi_info);
+ i=inb(CDi_status);
+ }
+ while (!(i&s_not_result_ready));
+ if (infobuf[0]==0x00) success=1;
+#if 1
+ for (j=0;j<l;j++) sprintf(&msgbuf[j*3], " %02X", infobuf[j]);
+ msgbuf[j*3]=0;
+ msg(DBG_TE2,"sbp_data info response:%s\n", msgbuf);
+#endif
+ if (infobuf[0]==0x02)
+ {
+ error_flag++;
+ do
+ {
+ ++recursion;
+ if (recursion>1) msg(DBG_TEA,"cmd_out_T READ_ERR recursion (sbp_data): %d !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n",recursion);
+ else msg(DBG_TEA,"sbp_data: CMDT_READ_ERR necessary.\n");
+ clr_cmdbuf();
+ drvcmd[0]=CMDT_READ_ERR;
+ j=cmd_out_T(); /* !!! recursive here !!! */
+ --recursion;
+ sbp_sleep(1);
+ }
+ while (j<0);
+ D_S[d].error_state=infobuf[2];
+ D_S[d].b3=infobuf[3];
+ D_S[d].b4=infobuf[4];
+ }
+ break;
+ }
+ else
+ {
+#if 0
+ msg(DBG_TEA, "============= waiting for result=================.\n");
+ sbp_sleep(1);
+#endif
+ }
+ }
+ while (wait--);
}
- SBPCD_STI;
- error_flag=0;
- p = DriveStruct[d].sbp_buf + frame * CD_FRAMESIZE;
-
- if (sbpro_type==1) OUT(CDo_sel_d_i,0x01);
- if (cmd_type==READ_M2) READ_DATA(CDi_data, xa_head_buf, CD_XA_HEAD);
- READ_DATA(CDi_data, p, CD_FRAMESIZE);
- if (cmd_type==READ_M2) READ_DATA(CDi_data, xa_tail_buf, CD_XA_TAIL);
- if (sbpro_type==1) OUT(CDo_sel_d_i,0x00);
- DriveStruct[d].sbp_current++;
- if (cmd_type==READ_M2)
- {
- DPRINTF((DBG_XA,"SBPCD: xa_head:"));
- for (xa_count=0;xa_count<CD_XA_HEAD;xa_count++)
- DPRINTF((DBG_XA," %02X", xa_head_buf[xa_count]));
- DPRINTF((DBG_XA,"\n"));
- }
- data_tries++;
- data_retrying = 0;
- if (data_tries >= 1000)
- {
- DPRINTF((DBG_INF,"SBPCD: info: %d waits in %d frames.\n",
- data_waits, data_tries));
- data_waits = data_tries = 0;
+ if (error_flag) /* must have been spurious D_RDY or (ATTN&&!D_RDY) */
+ {
+ msg(DBG_TEA, "================error flag: %d=================.\n", error_flag);
+ msg(DBG_INF,"sbp_data: read aborted by drive.\n");
+#if 1
+ i=cc_DriveReset(); /* ugly fix to prevent a hang */
+#else
+ i=cc_ReadError();
+#endif
+ return (0);
}
- }
- SBPCD_STI;
-
- if (error_flag) /* must have been spurious D_RDY or (ATTN&&!D_RDY) */
- {
- DPRINTF((DBG_INF,"SBPCD: read aborted by drive\n"));
- i=DriveReset(); /* ugly fix to prevent a hang */
- return (0);
- }
-
- if (!new_drive)
- {
- SBPCD_CLI;
- i=maxtim_data;
- for (timeout=jiffies+100; timeout > jiffies; timeout--)
- {
- for ( ;i!=0;i--)
- {
- j=inb(CDi_status);
- if (!(j&s_not_data_ready)) break;
- if (!(j&s_not_result_ready)) break;
- if (j&s_attention) break;
- }
- if (i != 0 || timeout <= jiffies) break;
- sbp_sleep(0);
- i = 1;
- }
- if (i==0) { DPRINTF((DBG_INF,"SBPCD: STATUS TIMEOUT AFTER READ")); }
- if (!(j&s_attention))
- {
- DPRINTF((DBG_INF,"SBPCD: sbp_data: timeout waiting DRV_ATTN - retrying\n"));
- i=DriveReset(); /* ugly fix to prevent a hang */
- SBPCD_STI;
- return (0);
- }
- SBPCD_STI;
- }
-
- do
- {
- if (!new_drive) xx_ReadStatus();
- i=ResponseStatus(); /* builds status_byte, returns orig. status (old) or faked p_success_old (new) */
- if (i<0) { DPRINTF((DBG_INF,"SBPCD: xx_ReadStatus error after read: %02X\n",
- DriveStruct[d].status_byte));
- return (0);
- }
- }
- while ((!new_drive)&&(!st_check)&&(!(i&p_success_old)));
- if (st_check)
- {
- i=xx_ReadError();
- DPRINTF((DBG_INF,"SBPCD: xx_ReadError was necessary after read: %02X\n",i));
- return (0);
- }
-
- DriveStruct[d].sbp_first_frame = CURRENT -> sector / 4;
- DriveStruct[d].sbp_last_frame = DriveStruct[d].sbp_first_frame + DriveStruct[d].sbp_read_frames - 1;
- sbp_transfer();
- return (1);
+
+ if (fam0L_drive)
+ {
+ SBPCD_CLI;
+ i=maxtim_data;
+ for (timeout=jiffies+100; timeout > jiffies; timeout--)
+ {
+ for ( ;i!=0;i--)
+ {
+ j=inb(CDi_status);
+ if (!(j&s_not_data_ready)) break;
+ if (!(j&s_not_result_ready)) break;
+ if (j&s_attention) break;
+ }
+ if (i != 0 || timeout <= jiffies) break;
+ sbp_sleep(0);
+ i = 1;
+ }
+ if (i==0) msg(DBG_INF,"status timeout after READ.\n");
+ if (!(j&s_attention))
+ {
+ msg(DBG_INF,"sbp_data: timeout waiting DRV_ATTN - retrying.\n");
+ i=cc_DriveReset(); /* ugly fix to prevent a hang */
+ SBPCD_STI;
+ return (0);
+ }
+ SBPCD_STI;
+ }
+
+#if 0
+ if (!success)
+#endif 0
+ do
+ {
+ if (fam0L_drive) cc_ReadStatus();
+#if 1
+ if (famT_drive) msg(DBG_TE2, "================before ResponseStatus=================.\n", i);
+#endif 1
+ i=ResponseStatus(); /* builds status_bits, returns orig. status (old) or faked p_success (new) */
+#if 1
+ if (famT_drive) msg(DBG_TE2, "================ResponseStatus: %d=================.\n", i);
+#endif 1
+ if (i<0)
+ {
+ msg(DBG_INF,"bad cc_ReadStatus after read: %02X\n", D_S[d].status_bits);
+ return (0);
+ }
+ }
+ while ((fam0L_drive)&&(!st_check)&&(!(i&p_success)));
+ if (st_check)
+ {
+ i=cc_ReadError();
+ msg(DBG_INF,"cc_ReadError was necessary after read: %d\n",i);
+ return (0);
+ }
+ if (fatal_err)
+ {
+ fatal_err=0;
+ D_S[d].sbp_first_frame=D_S[d].sbp_last_frame=-1; /* purge buffer */
+ D_S[d].sbp_current = 0;
+ msg(DBG_INF,"sbp_data: fatal_err - retrying.\n");
+ return (0);
+ }
+
+ D_S[d].sbp_first_frame = CURRENT -> sector / 4;
+ D_S[d].sbp_last_frame = D_S[d].sbp_first_frame + D_S[d].sbp_read_frames - 1;
+ sbp_transfer();
+#if 1
+ if (famT_drive) msg(DBG_TE2, "================sbp_transfer() done=================.\n");
+#endif 1
+ return (1);
}
/*==========================================================================*/
/*==========================================================================*/
@@ -3371,64 +4818,84 @@ static int sbp_data(void)
*/
static int sbpcd_open(struct inode *ip, struct file *fp)
{
- int i;
-
- if (ndrives==0) return (-ENXIO); /* no hardware */
-
- if (fp->f_mode & 2)
- return -EROFS;
-
- i = MINOR(ip->i_rdev);
- if ( (i<0) || (i>=NR_SBPCD) )
- {
- printk("SBPCD: open: bad device: %d\n", i);
- return (-ENODEV); /* no such drive */
- }
- switch_drive(i);
-
- flags_cmd_out |= f_respo2;
- xx_ReadStatus(); /* command: give 1-byte status */
- i=ResponseStatus();
- if (!st_door_closed) yy_CloseTray();
- if (!st_spinning) xx_SpinUp();
-
- flags_cmd_out |= f_respo2;
- xx_ReadStatus(); /* command: give 1-byte status */
- i=ResponseStatus();
- if (i<0)
- {
- DPRINTF((DBG_INF,"SBPCD: sbpcd_open: xx_ReadStatus timed out\n"));
- return (-EIO); /* drive doesn't respond */
- }
- DPRINTF((DBG_STA,"SBPCD: sbpcd_open: status %02X\n", DriveStruct[d].status_byte));
- if (!st_door_closed||!st_caddy_in)
- {
- printk("SBPCD: sbpcd_open: no disk in drive\n");
+ int i;
+
+ i = MINOR(ip->i_rdev);
+ if ((i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1))
+ {
+ msg(DBG_INF, "open: bad device: %04X\n", ip->i_rdev);
+ return (-ENXIO); /* no such drive */
+ }
+ if (fp->f_mode & 2)
+ return -EROFS;
+
+ switch_drive(i);
+
+ i=cc_ReadError();
+ flags_cmd_out |= f_respo2;
+ cc_ReadStatus(); /* command: give 1-byte status */
+ i=ResponseStatus();
+ if (famT_drive&&(i<0))
+ {
+ cc_DriveReset();
+ i=ResponseStatus();
+ i=ResponseStatus();
+ }
+ if (i<0)
+ {
+ msg(DBG_INF,"sbpcd_open: ResponseStatus timed out (%d).\n",i);
+ return (-EIO); /* drive doesn't respond */
+ }
+ if (famT_drive) msg(DBG_TE2,"sbpcd_open: ResponseStatus=%02X\n", i);
+ if (!st_door_closed)
+ {
+ if (famT_drive) msg(DBG_TE2,"sbpcd_open: !st_door_closed.\n");
+ cc_CloseTray();
+ flags_cmd_out |= f_respo2;
+ cc_ReadStatus();
+ i=ResponseStatus();
+ }
+ if (!(famT_drive))
+ if (!st_spinning)
+ {
+ if (famT_drive) msg(DBG_TE2,"sbpcd_open: !st_spinning.\n");
+ cc_SpinUp();
+ flags_cmd_out |= f_respo2;
+ cc_ReadStatus();
+ i=ResponseStatus();
+ }
+ if (famT_drive) msg(DBG_TE2,"sbpcd_open: status %02X\n", D_S[d].status_bits);
+ if (!st_door_closed||!st_caddy_in)
+ {
+ msg(DBG_INF, "sbpcd_open: no disk in drive.\n");
+ D_S[d].open_count=0;
#if JUKEBOX
- do
- i=yy_LockDoor(0);
- while (i!=0);
- if (new_drive) yy_SpinDown(); /* eject tray */
+ if (!fam0_drive)
+ {
+ i=UnLockDoor();
+ cc_SpinDown(); /* eject tray */
+ }
#endif
- return (-ENXIO);
- }
-/*
- * try to keep an "open" counter here and lock the door if 0->1.
- */
- DPRINTF((DBG_LCK,"SBPCD: open_count: %d -> %d\n",
- DriveStruct[d].open_count,DriveStruct[d].open_count+1));
- if (++DriveStruct[d].open_count==1)
- {
- do
- i=yy_LockDoor(1);
- while (i!=0);
- }
- if (!st_spinning) xx_SpinUp();
-
- i=DiskInfo();
- if ((DriveStruct[d].ored_ctl_adr&0x40)==0)
- DPRINTF((DBG_INF,"SBPCD: CD contains no data tracks.\n"));
- return (0);
+ return (-ENXIO);
+ }
+ /*
+ * try to keep an "open" counter here and lock the door if 0->1.
+ */
+ MOD_INC_USE_COUNT;
+ msg(DBG_LCK,"open_count: %d -> %d\n",
+ D_S[d].open_count,D_S[d].open_count+1);
+ if (++D_S[d].open_count<=1)
+ {
+ i=LockDoor();
+ D_S[d].open_count=1;
+ if (famT_drive) msg(DBG_TE2,"sbpcd_open: before i=DiskInfo();.\n");
+ i=DiskInfo();
+ if (famT_drive) msg(DBG_TE2,"sbpcd_open: after i=DiskInfo();.\n");
+ if ((D_S[d].ored_ctl_adr&0x40)==0)
+ msg(DBG_INF,"CD contains no data tracks.\n");
+ }
+ if (!st_spinning) cc_SpinUp();
+ return (0);
}
/*==========================================================================*/
/*
@@ -3436,36 +4903,37 @@ static int sbpcd_open(struct inode *ip, struct file *fp)
*/
static void sbpcd_release(struct inode * ip, struct file * file)
{
- int i;
-
- i = MINOR(ip->i_rdev);
- if ( (i<0) || (i>=NR_SBPCD) )
- {
- printk("SBPCD: release: bad device: %d\n", i);
- return;
- }
- switch_drive(i);
-
- DriveStruct[d].sbp_first_frame=DriveStruct[d].sbp_last_frame=-1;
- sync_dev(ip->i_rdev); /* nonsense if read only device? */
- invalidate_buffers(ip->i_rdev);
- DriveStruct[d].diskstate_flags &= ~cd_size_bit;
-
-/*
- * try to keep an "open" counter here and unlock the door if 1->0.
- */
- DPRINTF((DBG_LCK,"SBPCD: open_count: %d -> %d\n",
- DriveStruct[d].open_count,DriveStruct[d].open_count-1));
- if (DriveStruct[d].open_count!=0) /* CDROMEJECT may have been done */
- {
- if (--DriveStruct[d].open_count==0)
+ int i;
+
+ i = MINOR(ip->i_rdev);
+ if ((i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1))
{
- do
- i=yy_LockDoor(0);
- while (i!=0);
- if (DriveStruct[d].f_eject) yy_SpinDown();
+ msg(DBG_INF, "release: bad device: %04X\n", ip->i_rdev);
+ return;
+ }
+ switch_drive(i);
+
+ D_S[d].sbp_first_frame=D_S[d].sbp_last_frame=-1;
+ sync_dev(ip->i_rdev); /* nonsense if read only device? */
+ invalidate_buffers(ip->i_rdev);
+
+ /*
+ * try to keep an "open" counter here and unlock the door if 1->0.
+ */
+ MOD_DEC_USE_COUNT;
+ msg(DBG_LCK,"open_count: %d -> %d\n",
+ D_S[d].open_count,D_S[d].open_count-1);
+ if (D_S[d].open_count>-2) /* CDROMEJECT may have been done */
+ {
+ if (--D_S[d].open_count<=0)
+ {
+ i=UnLockDoor();
+ if (D_S[d].audio_state!=audio_playing)
+ if (D_S[d].f_eject) cc_SpinDown();
+ D_S[d].diskstate_flags &= ~cd_size_bit;
+ D_S[d].open_count=0;
+ }
}
- }
}
/*==========================================================================*/
/*
@@ -3473,19 +4941,19 @@ static void sbpcd_release(struct inode * ip, struct file * file)
*/
static struct file_operations sbpcd_fops =
{
- NULL, /* lseek - default */
- block_read, /* read - general block-dev read */
- block_write, /* write - general block-dev write */
- NULL, /* readdir - bad */
- NULL, /* select */
- sbpcd_ioctl, /* ioctl */
- NULL, /* mmap */
- sbpcd_open, /* open */
- sbpcd_release, /* release */
- NULL, /* fsync */
- NULL, /* fasync */
- check_media_change, /* media_change */
- NULL /* revalidate */
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ sbpcd_ioctl, /* ioctl */
+ NULL, /* mmap */
+ sbpcd_open, /* open */
+ sbpcd_release, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ sbpcd_chk_disk_change, /* media_change */
+ NULL /* revalidate */
};
/*==========================================================================*/
/*
@@ -3513,335 +4981,419 @@ static
#endif
void sbpcd_setup(char *s, int *p)
{
- DPRINTF((DBG_INI,"SBPCD: sbpcd_setup called with %04X,%s\n",p[1], s));
- sbpro_type=0;
- if (!strcmp(s,str_sb)) sbpro_type=1;
- else if (!strcmp(s,str_sp)) sbpro_type=2;
- if (p[0]>0) sbpcd_ioaddr=p[1];
-
- CDo_command=sbpcd_ioaddr;
- CDi_info=sbpcd_ioaddr;
- CDi_status=sbpcd_ioaddr+1;
- CDo_reset=sbpcd_ioaddr+2;
- CDo_enable=sbpcd_ioaddr+3;
- if (sbpro_type==1)
- {
- MIXER_addr=sbpcd_ioaddr-0x10+0x04;
- MIXER_data=sbpcd_ioaddr-0x10+0x05;
- CDo_sel_d_i=sbpcd_ioaddr+1;
- CDi_data=sbpcd_ioaddr;
- }
- else CDi_data=sbpcd_ioaddr+2;
+ setup_done++;
+ msg(DBG_INI,"sbpcd_setup called with %04X,%s\n",p[1], s);
+ sbpro_type=0;
+ if (!strcmp(s,str_sb)) sbpro_type=1;
+ else if (!strcmp(s,str_sb_l)) sbpro_type=1;
+ else if (!strcmp(s,str_sp)) sbpro_type=2;
+ else if (!strcmp(s,str_sp_l)) sbpro_type=2;
+ if (p[0]>0) sbpcd_ioaddr=p[1];
+
+ CDo_command=sbpcd_ioaddr;
+ CDi_info=sbpcd_ioaddr;
+ CDi_status=sbpcd_ioaddr+1;
+ CDo_sel_i_d=sbpcd_ioaddr+1;
+ CDo_reset=sbpcd_ioaddr+2;
+ CDo_enable=sbpcd_ioaddr+3;
+ if (sbpro_type==1)
+ {
+ MIXER_addr=sbpcd_ioaddr-0x10+0x04;
+ MIXER_data=sbpcd_ioaddr-0x10+0x05;
+ CDi_data=sbpcd_ioaddr;
+ }
+ else CDi_data=sbpcd_ioaddr+2;
}
/*==========================================================================*/
/*
* Sequoia S-1000 CD-ROM Interface Configuration
* as used within SPEA Media FX card
- * The SPEA soundcard has to get jumpered for
+ * The SPEA soundcard has to get configured for
* -> interface type "Matsushita/Panasonic" (not Sony or Mitsumi)
* -> I/O base address (0x320, 0x330, 0x340, 0x350)
*/
static int config_spea(void)
{
- int n_ports=0x10; /* 2:0x00, 8:0x10, 16:0x20, 32:0x30 */
- int irq_number=0; /* 2:0x01, 7:0x03, 12:0x05, 15:0x07, OFF:0x00 */
- int dma_channel=0; /* 0:0x08, 1:0x18, 3:0x38, 5:0x58, 6:0x68, 7:0x78, OFF: 0x00 */
- int dack_polarity=0; /* L:0x00, H:0x80 */
- int drq_polarity=0x40; /* L:0x00, H:0x40 */
-
- int i;
-
+ int n_ports=0x10; /* 2:0x00, 8:0x10, 16:0x20, 32:0x30 */
+ /* What is n_ports? Number of addresses or base address offset? */
+ int irq_number=0; /* 2:0x01, 7:0x03, 12:0x05, 15:0x07, OFF:0x00 */
+ int dma_channel=0; /* 0:0x08, 1:0x18, 3:0x38, 5:0x58, 6:0x68, 7:0x78, OFF: 0x00 */
+ int dack_polarity=0; /* L:0x00, H:0x80 */
+ int drq_polarity=0x40; /* L:0x00, H:0x40 */
+
+ int i;
+
#define SPEA_REG_1 sbpcd_ioaddr+4
#define SPEA_REG_2 sbpcd_ioaddr+5
-
- OUT(SPEA_REG_1,0xFF);
- i=inb(SPEA_REG_1);
- if (i!=0x0F)
- {
- DPRINTF((DBG_SEQ,"SBPCD: no SPEA interface at %04X present.\n",
- sbpcd_ioaddr));
- return (-1); /* no interface found */
- }
- OUT(SPEA_REG_1,0x04);
- OUT(SPEA_REG_2,0xC0);
-
- OUT(SPEA_REG_1,0x05);
- OUT(SPEA_REG_2,0x10|drq_polarity|dack_polarity);
-
+
+ OUT(SPEA_REG_1,0xFF);
+ i=inb(SPEA_REG_1);
+ if (i!=0x0F)
+ {
+ msg(DBG_SEQ,"no SPEA interface at %04X present.\n", sbpcd_ioaddr);
+ return (-1); /* no interface found */
+ }
+ OUT(SPEA_REG_1,0x04);
+ OUT(SPEA_REG_2,0xC0);
+
+ OUT(SPEA_REG_1,0x05);
+ OUT(SPEA_REG_2,0x10|drq_polarity|dack_polarity);
+
#if 1
#define SPEA_PATTERN 0x80
#else
#define SPEA_PATTERN 0x00
#endif
- OUT(SPEA_REG_1,0x06);
- OUT(SPEA_REG_2,dma_channel|irq_number|SPEA_PATTERN);
- OUT(SPEA_REG_2,dma_channel|irq_number|SPEA_PATTERN);
-
- OUT(SPEA_REG_1,0x09);
- i=(inb(SPEA_REG_2)&0xCF)|n_ports;
- OUT(SPEA_REG_2,i);
-
- sbpro_type = 0; /* acts like a LaserMate interface now */
- DPRINTF((DBG_SEQ,"SBPCD: found SPEA interface at %04X.\n",
- sbpcd_ioaddr));
- return (0);
+ OUT(SPEA_REG_1,0x06);
+ OUT(SPEA_REG_2,dma_channel|irq_number|SPEA_PATTERN);
+ OUT(SPEA_REG_2,dma_channel|irq_number|SPEA_PATTERN);
+
+ OUT(SPEA_REG_1,0x09);
+ i=(inb(SPEA_REG_2)&0xCF)|n_ports;
+ OUT(SPEA_REG_2,i);
+
+ sbpro_type = 0; /* acts like a LaserMate interface now */
+ msg(DBG_SEQ,"found SPEA interface at %04X.\n", sbpcd_ioaddr);
+ return (0);
}
/*==========================================================================*/
/*
* Test for presence of drive and initialize it. Called at boot time.
*/
-unsigned long SBPCD_INIT(u_long mem_start, u_long mem_end)
+#ifdef MODULE
+int init_module(void)
+#else
+ unsigned long SBPCD_INIT(u_long mem_start, u_long mem_end)
+#endif MODULE
{
- int i=0, j=0;
- int addr[2]={1, CDROM_PORT};
- int port_index;
-
- sti(); /* necessary, has consequences for other drivers' init routines */
-
- DPRINTF((DBG_INF,"SBPCD version %s\n", VERSION));
-
- DPRINTF((DBG_INF,"SBPCD: Looking for a SoundBlaster/Matsushita CD-ROM drive\n"));
- DPRINTF((DBG_WRN,"SBPCD: \n"));
- DPRINTF((DBG_WRN,"SBPCD: = = = = = = = = = = W A R N I N G = = = = = = = = = =\n"));
- DPRINTF((DBG_WRN,"SBPCD: Auto-Probing can cause a hang (f.e. touching an ethernet card).\n"));
- DPRINTF((DBG_WRN,"SBPCD: If that happens, you have to reboot and use the\n"));
- DPRINTF((DBG_WRN,"SBPCD: LILO (kernel) command line feature like:\n"));
- DPRINTF((DBG_WRN,"SBPCD: \n"));
- DPRINTF((DBG_WRN,"SBPCD: LILO boot: linux sbpcd=0x230,SoundBlaster\n"));
- DPRINTF((DBG_WRN,"SBPCD: or like:\n"));
- DPRINTF((DBG_WRN,"SBPCD: LILO boot: linux sbpcd=0x300,LaserMate\n"));
- DPRINTF((DBG_WRN,"SBPCD: or like:\n"));
- DPRINTF((DBG_WRN,"SBPCD: LILO boot: linux sbpcd=0x330,SPEA\n"));
- DPRINTF((DBG_WRN,"SBPCD: \n"));
- DPRINTF((DBG_WRN,"SBPCD: with your REAL address.\n"));
- DPRINTF((DBG_WRN,"SBPCD: = = = = = = = = = = END of WARNING = = = = = = = = = =\n"));
- DPRINTF((DBG_WRN,"SBPCD: \n"));
-
- autoprobe[0]=sbpcd_ioaddr; /* possibly changed by kernel command line */
- autoprobe[1]=sbpro_type; /* possibly changed by kernel command line */
-
- for (port_index=0;port_index<NUM_AUTOPROBE;port_index+=2)
- {
- addr[1]=autoprobe[port_index];
- if (check_region(addr[1],4)) continue;
- DPRINTF((DBG_INI,"SBPCD: check_region: free.\n"));
- if (autoprobe[port_index+1]==0) type=str_lm;
- else if (autoprobe[port_index+1]==1) type=str_sb;
- else type=str_sp;
- sbpcd_setup(type, addr);
- DPRINTF((DBG_INF,"SBPCD: Trying to detect a %s CD-ROM drive at 0x%X.\n",
- type, CDo_command));
-
- DPRINTF((DBG_INF,"SBPCD: - "));
- if (autoprobe[port_index+1]==2)
- {
- i=config_spea();
- if (i<0)
- {
- DPRINTF((DBG_INF,"\n"));
- continue;
- }
- }
- i=check_drives();
- DPRINTF((DBG_INI,"SBPCD: check_drives done.\n"));
- if (i>=0) break; /* drive found */
- DPRINTF((DBG_INF,"\n"));
- } /* end of cycling through the set of possible I/O port addresses */
-
- if (ndrives==0)
- {
- printk("SBPCD: No drive found.\n");
-#if PRINTK_BUG
- sti(); /* to avoid possible "printk" bug */
-#endif
- goto init_done;
- }
-
- if (port_index>0)
- {
- printk("SBPCD: You should configure sbpcd.h for your hardware.\n");
-#if PRINTK_BUG
- sti(); /* to avoid possible "printk" bug */
-#endif
- }
-
- printk("SBPCD: %d %s CD-ROM drive(s) at 0x%04X.\n",
- ndrives, type, CDo_command);
-#if PRINTK_BUG
- sti(); /* to avoid possible "printk" bug */
-#endif
- check_datarate();
- DPRINTF((DBG_INI,"SBPCD: check_datarate done.\n"));
-
- OUT(CDo_reset,0);
- sbp_sleep(100);
+ int i=0, j=0;
+ int addr[2]={1, CDROM_PORT};
+ int port_index;
+
+ sti();
+
+ msg(DBG_INF,"sbpcd.c %s\n", VERSION);
+#ifndef MODULE
+#if DISTRIBUTION
+ if (!setup_done)
+ {
+ msg(DBG_INF,"Looking for Matsushita/Panasonic, CreativeLabs, IBM, Longshine, TEAC CD-ROM drives\n");
+ msg(DBG_INF,"\n= = = = = = = = = = W A R N I N G = = = = = = = = = =\n");
+ msg(DBG_INF,"Auto-Probing can cause a hang (f.e. touching an ethernet card).\n");
+ msg(DBG_INF,"If that happens, you have to reboot and use the\n");
+ msg(DBG_INF,"LILO (kernel) command line feature like:\n");
+ msg(DBG_INF," LILO boot: ... sbpcd=0x230,SoundBlaster\n");
+ msg(DBG_INF,"or like:\n");
+ msg(DBG_INF," LILO boot: ... sbpcd=0x300,LaserMate\n");
+ msg(DBG_INF,"or like:\n");
+ msg(DBG_INF," LILO boot: ... sbpcd=0x330,SPEA\n");
+ msg(DBG_INF,"with your REAL address.\n");
+ msg(DBG_INF,"= = = = = = = = = = END of WARNING = = = = = = = = = =\n\n");
+ }
+#endif DISTRIBUTION
+ sbpcd[0]=sbpcd_ioaddr; /* possibly changed by kernel command line */
+ sbpcd[1]=sbpro_type; /* possibly changed by kernel command line */
+#endif MODULE
+
+ for (port_index=0;port_index<NUM_PROBE;port_index+=2)
+ {
+ addr[1]=sbpcd[port_index];
+ if (addr[1]==0) break;
+ if (check_region(addr[1],4))
+ {
+ msg(DBG_INF,"check_region: %03X is not free.\n",addr[1]);
+ continue;
+ }
+ if (sbpcd[port_index+1]==2) type=str_sp;
+ else if (sbpcd[port_index+1]==1) type=str_sb;
+ else type=str_lm;
+ sbpcd_setup(type, addr);
+#if DISTRIBUTION
+ msg(DBG_INF,"Scanning 0x%X (%s)...\n", CDo_command, type);
+#endif DISTRIBUTION
+ if (sbpcd[port_index+1]==2)
+ {
+ i=config_spea();
+ if (i<0) continue;
+ }
+#ifdef PATH_CHECK
+ if (check_card(addr[1])) continue;
+#endif PATH_CHECK
+ i=check_drives();
+ msg(DBG_INI,"check_drives done.\n");
+ if (i>=0) break; /* drive found */
+ } /* end of cycling through the set of possible I/O port addresses */
+
+ if (ndrives==0)
+ {
+ msg(DBG_INF, "No drive found.\n");
+#ifdef MODULE
+ return -EIO;
+#else
+ goto init_done;
+#endif MODULE
+ }
+
+ if (port_index>0)
+ msg(DBG_INF, "You should configure sbpcd.h for your hardware.\n");
+ check_datarate();
+ msg(DBG_INI,"check_datarate done.\n");
+
+#if 0
+ if (!famL_drive)
+ {
+ OUT(CDo_reset,0);
+ sbp_sleep(100);
+ }
+#endif 0
- for (j=0;j<NR_SBPCD;j++)
- {
- if (DriveStruct[j].drv_minor==-1) continue;
- switch_drive(j);
- xy_DriveReset();
- if (!st_spinning) xx_SpinUp();
- DriveStruct[d].sbp_first_frame = -1; /* First frame in buffer */
- DriveStruct[d].sbp_last_frame = -1; /* Last frame in buffer */
- DriveStruct[d].sbp_read_frames = 0; /* Number of frames being read to buffer */
- DriveStruct[d].sbp_current = 0; /* Frame being currently read */
- DriveStruct[d].CD_changed=1;
- DriveStruct[d].frame_size=CD_FRAMESIZE;
+ for (j=0;j<NR_SBPCD;j++)
+ {
+ if (D_S[j].drv_id==-1) continue;
+ switch_drive(j);
+#if 1
+ if (!famL_drive) cc_DriveReset();
+#endif 0
+ if (!st_spinning) cc_SpinUp();
+ D_S[d].sbp_first_frame = -1; /* First frame in buffer */
+ D_S[d].sbp_last_frame = -1; /* Last frame in buffer */
+ D_S[d].sbp_read_frames = 0; /* Number of frames being read to buffer */
+ D_S[d].sbp_current = 0; /* Frame being currently read */
+ D_S[d].CD_changed=1;
+ D_S[d].frame_size=CD_FRAMESIZE;
#if EJECT
- if (new_drive) DriveStruct[d].f_eject=1;
- else DriveStruct[d].f_eject=0;
+ if (!fam0_drive) D_S[d].f_eject=1;
+ else D_S[d].f_eject=0;
#else
- DriveStruct[d].f_eject=0;
+ D_S[d].f_eject=0;
#endif
-
- xx_ReadStatus();
- i=ResponseStatus(); /* returns orig. status or p_busy_new */
- if (i<0)
- DPRINTF((DBG_INF,"SBPCD: init: ResponseStatus returns %02X\n",i));
- else
- {
- if (st_check)
- {
- i=xx_ReadError();
- DPRINTF((DBG_INI,"SBPCD: init: xx_ReadError returns %d\n",i));
- }
- }
- DPRINTF((DBG_INI,"SBPCD: init: first GetStatus: %d\n",i));
- if (DriveStruct[d].error_byte==aud_12)
- {
- do { i=GetStatus();
- DPRINTF((DBG_INI,"SBPCD: init: second GetStatus: %02X\n",i));
- if (i<0) break;
- if (!st_caddy_in) break;
- }
- while (!st_diskok);
- }
- i=SetSpeed();
- if (i>=0) DriveStruct[d].CD_changed=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))
- {
- if (sbpro_type!=1)
+
+ cc_ReadStatus();
+ i=ResponseStatus(); /* returns orig. status or p_busy_new */
+ if (famT_drive) i=ResponseStatus(); /* returns orig. status or p_busy_new */
+ if (i<0)
+ if (i!=-402)
+ msg(DBG_INF,"init: ResponseStatus returns %d.\n",i);
+ else
+ {
+ if (st_check)
+ {
+ i=cc_ReadError();
+ msg(DBG_INI,"init: cc_ReadError returns %d\n",i);
+ }
+ }
+ msg(DBG_INI,"init: first GetStatus: %d\n",i);
+ msg(DBG_LCS,"init: first GetStatus: error_byte=%d\n",
+ D_S[d].error_byte);
+ if (D_S[d].error_byte==aud_12)
+ {
+ timeout=jiffies+200;
+ do
+ {
+ i=GetStatus();
+ msg(DBG_INI,"init: second GetStatus: %02X\n",i);
+ msg(DBG_LCS,
+ "init: second GetStatus: error_byte=%d\n",
+ D_S[d].error_byte);
+ if (i<0) break;
+ if (!st_caddy_in) break;
+ }
+ while ((!st_diskok)||(timeout<jiffies));
+ }
+ i=SetSpeed();
+ if (i>=0) D_S[d].CD_changed=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))
{
- MIXER_addr=SOUND_BASE+0x04; /* sound card's address register */
- MIXER_data=SOUND_BASE+0x05; /* sound card's data register */
+ 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 */
}
- 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);
-#if PRINTK_BUG
- sti(); /* to avoid possible "printk" bug */
-#endif
- goto init_done;
- }
- blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
- read_ahead[MAJOR_NR] = SBP_BUFFER_FRAMES * (CD_FRAMESIZE / 512);
-
- snarf_region(CDo_command,4);
-
- for (j=0;j<NR_SBPCD;j++)
- {
- if (DriveStruct[j].drv_minor==-1) continue;
-/*
- * allocate memory for the frame buffers
- */
- DriveStruct[j].sbp_buf=(u_char *)mem_start;
- mem_start += SBP_BUFFER_FRAMES*CD_FRAMESIZE;
- if (new_drive)
+
+ if (register_blkdev(MAJOR_NR, major_name, &sbpcd_fops) != 0)
{
- DriveStruct[j].aud_buf=(u_char *)mem_start;
- mem_start += SBP_BUFFER_AUDIO_FRAMES*CD_FRAMESIZE_RAW;
+ msg(DBG_INF, "Can't get MAJOR %d for Matsushita CDROM\n", MAJOR_NR);
+#ifdef MODULE
+ return -EIO;
+#else
+ goto init_done;
+#endif MODULE
}
- else DriveStruct[j].aud_buf=NULL;
-/*
- * set the block size
- */
- sbpcd_blocksizes[j]=CD_FRAMESIZE;
- }
- blksize_size[MAJOR_NR]=sbpcd_blocksizes;
-
-init_done:
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ read_ahead[MAJOR_NR] = SBP_BUFFER_FRAMES * (CD_FRAMESIZE / 512);
+
+ request_region(CDo_command,4,major_name);
+
+ for (j=0;j<NR_SBPCD;j++)
+ {
+ if (D_S[j].drv_id==-1) continue;
+ switch_drive(j);
+ /*
+ * allocate memory for the frame buffers
+ */
+ D_S[j].aud_buf=NULL;
+ D_S[j].sbp_audsiz=0;
+ D_S[j].sbp_bufsiz=SBP_BUFFER_FRAMES;
+ if (D_S[j].drv_type&drv_fam1)
+ if (READ_AUDIO>0) D_S[j].sbp_audsiz=READ_AUDIO;
+#ifdef MODULE
+ D_S[j].sbp_buf=(u_char *) vmalloc(D_S[j].sbp_bufsiz*CD_FRAMESIZE);
+ if (D_S[j].sbp_buf==NULL)
+ {
+ msg(DBG_INF,"data buffer (%d frames) not available.\n",D_S[j].sbp_bufsiz);
+ return -EIO;
+ }
+ msg(DBG_INF,"data buffer size: %d frames.\n",SBP_BUFFER_FRAMES);
+ if (D_S[j].sbp_audsiz>0)
+ {
+ D_S[j].aud_buf=(u_char *) vmalloc(D_S[j].sbp_audsiz*CD_FRAMESIZE_RAW);
+ if (D_S[j].aud_buf==NULL) msg(DBG_INF,"audio buffer (%d frames) not available.\n",D_S[j].sbp_audsiz);
+ else msg(DBG_INF,"audio buffer size: %d frames.\n",D_S[j].sbp_audsiz);
+ }
+#else
+ D_S[j].sbp_buf=(u_char *)mem_start;
+ mem_start += D_S[j].sbp_bufsiz*CD_FRAMESIZE;
+ if (D_S[j].sbp_audsiz>0)
+ {
+ D_S[j].aud_buf=(u_char *)mem_start;
+ mem_start += D_S[j].sbp_audsiz*CD_FRAMESIZE_RAW;
+ }
+#endif MODULE
+ /*
+ * set the block size
+ */
+ sbpcd_blocksizes[j]=CD_FRAMESIZE;
+ }
+ blksize_size[MAJOR_NR]=sbpcd_blocksizes;
+
+#ifdef MODULE
+ return (0);
+#else
+ init_done:
#if !(SBPCD_ISSUE-1)
#ifdef CONFIG_SBPCD2
- mem_start=sbpcd2_init(mem_start, mem_end);
+ mem_start=sbpcd2_init(mem_start, mem_end);
#endif
#ifdef CONFIG_SBPCD3
- mem_start=sbpcd3_init(mem_start, mem_end);
+ mem_start=sbpcd3_init(mem_start, mem_end);
#endif
#ifdef CONFIG_SBPCD4
- mem_start=sbpcd4_init(mem_start, mem_end);
-#endif
+ mem_start=sbpcd4_init(mem_start, mem_end);
#endif
-#if !(SBPCD_ISSUE-1)
- DPRINTF((DBG_INF,"SBPCD: init done.\n"));
#endif
- return (mem_start);
+ return (mem_start);
+#endif MODULE
}
/*==========================================================================*/
+#ifdef MODULE
+void cleanup_module(void)
+{
+ int j;
+
+ if (MOD_IN_USE)
+ {
+ msg(DBG_INF, "%s module in use - can't remove it.\n", major_name);
+ return;
+ }
+ if ((unregister_blkdev(MAJOR_NR, major_name) == -EINVAL))
+ {
+ msg(DBG_INF, "What's that: can't unregister %s.\n", major_name);
+ return;
+ }
+ release_region(CDo_command,4);
+
+ for (j=0;j<NR_SBPCD;j++)
+ {
+ if (D_S[j].drv_id==-1) continue;
+ vfree(D_S[j].sbp_buf);
+ if (D_S[j].sbp_audsiz>0)
+ vfree(D_S[j].aud_buf);
+ }
+ msg(DBG_INF, "%s module released.\n", major_name);
+}
+#endif MODULE
+/*==========================================================================*/
/*
* Check if the media has changed in the CD-ROM drive.
* used externally (isofs/inode.c, fs/buffer.c)
* Currently disabled (has to get "synchronized").
*/
-static int check_media_change(dev_t full_dev)
+static int sbpcd_chk_disk_change(dev_t full_dev)
{
- int st;
-
- DPRINTF((DBG_CHK,"SBPCD: media_check (%d) called\n", MINOR(full_dev)));
- return (0); /* "busy" test necessary before we really can check */
-
- if ((MAJOR(full_dev)!=MAJOR_NR)||(MINOR(full_dev)>=NR_SBPCD))
- {
- printk("SBPCD: media_check: invalid device %04X.\n", full_dev);
- return (-1);
- }
-
- switch_drive(MINOR(full_dev));
-
- xx_ReadStatus(); /* command: give 1-byte status */
- st=ResponseStatus();
- DPRINTF((DBG_CHK,"SBPCD: media_check: %02X\n",DriveStruct[d].status_byte));
- if (st<0)
- {
- DPRINTF((DBG_INF,"SBPCD: media_check: ResponseStatus error.\n"));
- return (1); /* status not obtainable */
- }
- if (DriveStruct[d].CD_changed==0xFF) DPRINTF((DBG_CHK,"SBPCD: media_check: \"changed\" assumed.\n"));
- if (!st_spinning) DPRINTF((DBG_CHK,"SBPCD: media_check: motor off.\n"));
- if (!st_door_closed)
- {
- DPRINTF((DBG_CHK,"SBPCD: media_check: door open.\n"));
- DriveStruct[d].CD_changed=0xFF;
- }
- if (!st_caddy_in)
- {
- DPRINTF((DBG_CHK,"SBPCD: media_check: no disk in drive.\n"));
- DriveStruct[d].CD_changed=0xFF;
- }
- if (!st_diskok) DPRINTF((DBG_CHK,"SBPCD: media_check: !st_diskok.\n"));
-
+ int i, st;
+
+ msg(DBG_CHK,"media_check (%d) called\n", MINOR(full_dev));
+ return (0); /* "busy" test necessary before we really can check */
+
+ i=MINOR(full_dev);
+ if ( (i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1) )
+ {
+ msg(DBG_INF, "media_check: invalid device %04X.\n", full_dev);
+ return (-1);
+ }
+
+ switch_drive(i);
+
+ cc_ReadStatus(); /* command: give 1-byte status */
+ st=ResponseStatus();
+ msg(DBG_CHK,"media_check: %02X\n",D_S[d].status_bits);
+ if (st<0)
+ {
+ msg(DBG_INF,"media_check: ResponseStatus error.\n");
+ return (1); /* status not obtainable */
+ }
+ if (D_S[d].CD_changed==0xFF) msg(DBG_CHK,"media_check: \"changed\" assumed.\n");
+ if (!st_spinning) msg(DBG_CHK,"media_check: motor off.\n");
+ if (!st_door_closed)
+ {
+ msg(DBG_CHK,"media_check: door open.\n");
+ D_S[d].CD_changed=0xFF;
+ }
+ if (!st_caddy_in)
+ {
+ msg(DBG_CHK,"media_check: no disk in drive.\n");
+ D_S[d].open_count=0;
+ D_S[d].CD_changed=0xFF;
+ }
+ if (!st_diskok) msg(DBG_CHK,"media_check: !st_diskok.\n");
+
#if 0000
- if (DriveStruct[d].CD_changed==0xFF)
- {
- DriveStruct[d].CD_changed=1;
- return (1); /* driver had a change detected before */
- }
+ if (D_S[d].CD_changed==0xFF)
+ {
+ D_S[d].CD_changed=1;
+ return (1); /* driver had a change detected before */
+ }
#endif 0000 /* seems to give additional errors at the moment */
-
- if (!st_diskok) return (1); /* disk not o.k. */
- if (!st_caddy_in) return (1); /* disk removed */
- if (!st_door_closed) return (1); /* door open */
- return (0);
+
+ if (!st_diskok) return (1); /* disk not o.k. */
+ if (!st_caddy_in) return (1); /* disk removed */
+ if (!st_door_closed) return (1); /* door open */
+ return (0);
}
/*==========================================================================*/
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/drivers/block/sbpcd2.c b/drivers/block/sbpcd2.c
new file mode 100644
index 000000000..82e2c68ed
--- /dev/null
+++ b/drivers/block/sbpcd2.c
@@ -0,0 +1,5 @@
+/*
+ * duplication of sbpcd.c for multiple interfaces
+ */
+#define SBPCD_ISSUE 2
+#include "sbpcd.c"
diff --git a/drivers/block/sbpcd3.c b/drivers/block/sbpcd3.c
new file mode 100644
index 000000000..0e79c4155
--- /dev/null
+++ b/drivers/block/sbpcd3.c
@@ -0,0 +1,5 @@
+/*
+ * duplication of sbpcd.c for multiple interfaces
+ */
+#define SBPCD_ISSUE 3
+#include "sbpcd.c"
diff --git a/drivers/block/sbpcd4.c b/drivers/block/sbpcd4.c
new file mode 100644
index 000000000..f4b34cc29
--- /dev/null
+++ b/drivers/block/sbpcd4.c
@@ -0,0 +1,5 @@
+/*
+ * duplication of sbpcd.c for multiple interfaces
+ */
+#define SBPCD_ISSUE 4
+#include "sbpcd.c"
diff --git a/drivers/block/sonycd535.c b/drivers/block/sonycd535.c
new file mode 100644
index 000000000..0ab6f13aa
--- /dev/null
+++ b/drivers/block/sonycd535.c
@@ -0,0 +1,1743 @@
+/*
+ * Sony CDU-535 interface device driver
+ *
+ * This is a modified version of the CDU-31A device driver (see below).
+ * Changes were made using documentation for the CDU-531 (which Sony
+ * assures me is very similar to the 535) and partial disassembly of the
+ * DOS driver. I used Minyard's driver and replaced the the CDU-31A
+ * commands with the CDU-531 commands. This was complicated by a different
+ * interface protocol with the drive. The driver is still polled.
+ *
+ * Data transfer rate is about 110 Kb/sec, theoretical maximum is 150 Kb/sec.
+ * I tried polling without the sony_sleep during the data transfers but
+ * it did not speed things up any.
+ *
+ * 1993-05-23 (rgj) changed the major number to 21 to get rid of conflict
+ * with CDU-31A driver. This is the also the number from the Linux
+ * Device Driver Registry for the Sony Drive. Hope nobody else is using it.
+ *
+ * 1993-08-29 (rgj) remove the configuring of the interface board address
+ * from the top level configuration, you have to modify it in this file.
+ *
+ * 1995-01-26 Made module-capable (Joel Katz <Stimpson@Panix.COM>)
+ *
+ * 1995-05-20
+ * Modified to support CDU-510/515 series
+ * (Claudio Porfiri<C.Porfiri@nisms.tei.ericsson.se>)
+ * Fixed to report verify_area() failures
+ * (Heiko Eissfeldt <heiko@colossus.escape.de>)
+ *
+ * 1995-06-01
+ * More chages to support CDU-510/515 series
+ * (Claudio Porfiri<C.Porfiri@nisms.tei.ericsson.se>)
+ *
+ * Things to do:
+ * - handle errors and status better, put everything into a single word
+ * - use interrupts (code mostly there, but a big hole still missing)
+ * - handle multi-session CDs?
+ * - use DMA?
+ *
+ * Known Bugs:
+ * -
+ *
+ * Ken Pizzini (ken@halcyon.com)
+ *
+ * Original by:
+ * Ron Jeppesen (ronj.an@site007.saic.com)
+ *
+ *
+ *------------------------------------------------------------------------
+ * Sony CDROM interface device driver.
+ *
+ * Corey Minyard (minyard@wf-rch.cirr.com) (CDU-535 complaints to Ken above)
+ *
+ * Colossians 3:17
+ *
+ * The Sony interface device driver handles Sony interface CDROM
+ * drives and provides a complete block-level interface as well as an
+ * ioctl() interface compatible with the Sun (as specified in
+ * include/linux/cdrom.h). With this interface, CDROMs can be
+ * accessed and standard audio CDs can be played back normally.
+ *
+ * This interface is (unfortunately) a polled interface. This is
+ * because most Sony interfaces are set up with DMA and interrupts
+ * disables. Some (like mine) do not even have the capability to
+ * handle interrupts or DMA. For this reason you will see a lot of
+ * the following:
+ *
+ * retry_count = jiffies+ SONY_JIFFIES_TIMEOUT;
+ * while ((retry_count > jiffies) && (! <some condition to wait for))
+ * {
+ * while (handle_sony_cd_attention())
+ * ;
+ *
+ * sony_sleep();
+ * }
+ * if (the condition not met)
+ * {
+ * return an error;
+ * }
+ *
+ * This ugly hack waits for something to happen, sleeping a little
+ * between every try. it also handles attentions, which are
+ * asynchronous events from the drive informing the driver that a disk
+ * has been inserted, removed, etc.
+ *
+ * One thing about these drives: They talk in MSF (Minute Second Frame) format.
+ * There are 75 frames a second, 60 seconds a minute, and up to 75 minutes on a
+ * disk. The funny thing is that these are sent to the drive in BCD, but the
+ * interface wants to see them in decimal. A lot of conversion goes on.
+ *
+ * Copyright (C) 1993 Corey Minyard
+ *
+ * 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.
+ *
+ */
+
+
+#include <linux/config.h>
+#if defined(CONFIG_CDU535) || defined(MODULE)
+
+#ifdef MODULE
+# include <linux/module.h>
+# include <linux/malloc.h>
+# include <linux/version.h>
+# ifndef CONFIG_MODVERSIONS
+ char kernel_version[]= UTS_RELEASE;
+# endif
+#endif
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+#include <linux/mm.h>
+
+#define REALLY_SLOW_IO
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#include <linux/cdrom.h>
+#include <linux/sonycd535.h>
+
+#define MAJOR_NR CDU535_CDROM_MAJOR
+
+#ifdef MODULE
+# include "blk.h"
+#else
+# include "blk.h"
+# define MOD_INC_USE_COUNT
+# define MOD_DEC_USE_COUNT
+#endif
+
+/*
+ * this is the base address of the interface card for the Sony CDU-535
+ * CDROM drive. If your jumpers are set for an address other than
+ * this one (the default), change the following line to the
+ * proper address.
+ */
+#ifndef CDU535_ADDRESS
+# define CDU535_ADDRESS 0x340
+#endif
+#ifndef CDU535_INTERRUPT
+# define CDU535_INTERRUPT 0
+#endif
+#ifndef CDU535_HANDLE
+# define CDU535_HANDLE "cdu535"
+#endif
+#ifndef CDU535_MESSAGE_NAME
+# define CDU535_MESSAGE_NAME "Sony CDU-535"
+#endif
+
+#ifndef MAX_SPINUP_RETRY
+# define MAX_SPINUP_RETRY 3 /* 1 is sufficient for most drives... */
+#endif
+#ifndef RETRY_FOR_BAD_STATUS
+# define RETRY_FOR_BAD_STATUS 100 /* in 10th of second */
+#endif
+
+#ifndef DEBUG
+# define DEBUG 1
+#endif
+
+/*
+ * SONY535_BUFFER_SIZE determines the size of internal buffer used
+ * by the drive. It must be at least 2K and the larger the buffer
+ * the better the transfer rate. It does however take system memory.
+ * On my system I get the following transfer rates using dd to read
+ * 10 Mb off /dev/cdrom.
+ *
+ * 8K buffer 43 Kb/sec
+ * 16K buffer 66 Kb/sec
+ * 32K buffer 91 Kb/sec
+ * 64K buffer 111 Kb/sec
+ * 128K buffer 123 Kb/sec
+ * 512K buffer 123 Kb/sec
+ */
+#define SONY535_BUFFER_SIZE (64*1024)
+
+/*
+ * if LOCK_DOORS is defined then the eject button is disabled while
+ * the device is open.
+ */
+#ifndef NO_LOCK_DOORS
+# define LOCK_DOORS
+#endif
+
+static int read_subcode(void);
+static void sony_get_toc(void);
+static int cdu_open(struct inode *inode, struct file *filp);
+static inline unsigned int int_to_bcd(unsigned int val);
+static unsigned int bcd_to_int(unsigned int bcd);
+static int do_sony_cmd(Byte * cmd, int nCmd, Byte status[2],
+ Byte * response, int n_response, int ignoreStatusBit7);
+
+/* The base I/O address of the Sony Interface. This is a variable (not a
+ #define) so it can be easily changed via some future ioctl() */
+#ifndef MODULE
+static
+#endif
+unsigned short sony535_cd_base_io = CDU535_ADDRESS;
+
+/*
+ * The following are I/O addresses of the various registers for the drive. The
+ * comment for the base address also applies here.
+ */
+static unsigned short select_unit_reg;
+static unsigned short result_reg;
+static unsigned short command_reg;
+static unsigned short read_status_reg;
+static unsigned short data_reg;
+
+static int initialized = 0; /* Has the drive been initialized? */
+static int sony_disc_changed = 1; /* Has the disk been changed
+ since the last check? */
+static int sony_toc_read = 0; /* Has the table of contents been
+ read? */
+static unsigned int sony_buffer_size; /* Size in bytes of the read-ahead
+ buffer. */
+static unsigned int sony_buffer_sectors; /* Size (in 2048 byte records) of
+ the read-ahead buffer. */
+static unsigned int sony_usage = 0; /* How many processes have the
+ drive open. */
+
+static int sony_first_block = -1; /* First OS block (512 byte) in
+ the read-ahead buffer */
+static int sony_last_block = -1; /* Last OS block (512 byte) in
+ the read-ahead buffer */
+
+static struct s535_sony_toc *sony_toc; /* Points to the table of
+ contents. */
+static struct s535_sony_subcode *last_sony_subcode; /* Points to the last
+ subcode address read */
+#ifndef MODULE
+static Byte *sony_buffer; /* Points to the read-ahead buffer */
+#else
+static Byte **sony_buffer; /* Points to the pointers
+ to the sector buffers */
+#endif
+static int sony_inuse = 0; /* is the drive in use? Only one
+ open at a time allowed */
+
+/*
+ * The audio status uses the values from read subchannel data as specified
+ * in include/linux/cdrom.h.
+ */
+static int sony_audio_status = CDROM_AUDIO_NO_STATUS;
+
+/*
+ * The following are a hack for pausing and resuming audio play. The drive
+ * does not work as I would expect it, if you stop it then start it again,
+ * the drive seeks back to the beginning and starts over. This holds the
+ * position during a pause so a resume can restart it. It uses the
+ * audio status variable above to tell if it is paused.
+ * I just kept the CDU-31A driver behavior rather than using the PAUSE
+ * command on the CDU-535.
+ */
+static Byte cur_pos_msf[3] = {0, 0, 0};
+static Byte final_pos_msf[3] = {0, 0, 0};
+
+/* What IRQ is the drive using? 0 if none. */
+#ifndef MODULE
+static
+#endif
+int sony535_irq_used = CDU535_INTERRUPT;
+
+/* The interrupt handler will wake this queue up when it gets an interrupt. */
+static struct wait_queue *cdu535_irq_wait = NULL;
+
+
+/*
+ * This routine returns 1 if the disk has been changed since the last
+ * check or 0 if it hasn't. Setting flag to 0 resets the changed flag.
+ */
+static int
+cdu535_check_media_change(dev_t full_dev)
+{
+ int retval;
+
+ if (MINOR(full_dev) != 0) {
+ printk(CDU535_MESSAGE_NAME " request error: invalid device.\n");
+ return 0;
+ }
+
+ /* if driver is not initialized, always return 0 */
+ retval = initialized ? sony_disc_changed : 0;
+ sony_disc_changed = 0;
+ return retval;
+}
+
+static inline void
+enable_interrupts(void)
+{
+#ifdef USE_IRQ
+ /* this code snarfed from cdu31a.c; it will not
+ * directly work for the cdu535 as written...
+ */
+ curr_control_reg |= ( SONY_ATTN_INT_EN_BIT
+ | SONY_RES_RDY_INT_EN_BIT
+ | SONY_DATA_RDY_INT_EN_BIT);
+ outb(curr_control_reg, sony_cd_control_reg);
+#endif
+}
+
+static inline void
+disable_interrupts(void)
+{
+#ifdef USE_IRQ
+ /* this code snarfed from cdu31a.c; it will not
+ * directly work for the cdu535 as written...
+ */
+ curr_control_reg &= ~(SONY_ATTN_INT_EN_BIT
+ | SONY_RES_RDY_INT_EN_BIT
+ | SONY_DATA_RDY_INT_EN_BIT);
+ outb(curr_control_reg, sony_cd_control_reg);
+#endif
+}
+
+static void
+cdu535_interrupt(int irq, struct pt_regs *regs)
+{
+ disable_interrupts();
+ if (cdu535_irq_wait != NULL)
+ wake_up(&cdu535_irq_wait);
+ else
+ printk(CDU535_MESSAGE_NAME
+ ": Got an interrupt but nothing was waiting\n");
+}
+
+
+/*
+ * Wait a little while (used for polling the drive). If in initialization,
+ * setting a timeout doesn't work, so just loop for a while. (We trust
+ * that the sony_sleep() call is protected by a test for proper jiffies count.)
+ */
+static inline void
+sony_sleep(void)
+{
+ if (sony535_irq_used <= 0) { /* poll */
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies;
+ schedule();
+ } else { /* Interrupt driven */
+ cli();
+ enable_interrupts();
+ interruptible_sleep_on(&cdu535_irq_wait);
+ sti();
+ }
+}
+
+/*------------------start of SONY CDU535 very specific ---------------------*/
+
+/****************************************************************************
+ * void select_unit( int unit_no )
+ *
+ * Select the specified unit (0-3) so that subsequent commands reference it
+ ****************************************************************************/
+static void
+select_unit(int unit_no)
+{
+ unsigned int select_mask = ~(1 << unit_no);
+ outb(select_mask, select_unit_reg);
+}
+
+/***************************************************************************
+ * int read_result_reg( Byte *data_ptr )
+ *
+ * Read a result byte from the Sony CDU controller, store in location pointed
+ * to by data_ptr. Return zero on success, TIME_OUT if we did not receive
+ * data.
+ ***************************************************************************/
+static int
+read_result_reg(Byte *data_ptr)
+{
+ int retry_count;
+ int read_status;
+
+ retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+ while (jiffies < retry_count) {
+ if (((read_status = inb(read_status_reg)) & SONY535_RESULT_NOT_READY_BIT) == 0) {
+#if DEBUG > 1
+ printk(CDU535_MESSAGE_NAME
+ ": read_result_reg(): readStatReg = 0x%x\n", read_status);
+#endif
+ *data_ptr = inb(result_reg);
+ return 0;
+ } else {
+ sony_sleep();
+ }
+ }
+ printk(CDU535_MESSAGE_NAME " read_result_reg: TIME OUT!\n");
+ return TIME_OUT;
+}
+
+/****************************************************************************
+ * int read_exec_status( Byte status[2] )
+ *
+ * Read the execution status of the last command and put into status.
+ * Handles reading second status word if available. Returns 0 on success,
+ * TIME_OUT on failure.
+ ****************************************************************************/
+static int
+read_exec_status(Byte status[2])
+{
+ status[1] = 0;
+ if (read_result_reg(&(status[0])) != 0)
+ return TIME_OUT;
+ if ((status[0] & 0x80) != 0) { /* byte two follows */
+ if (read_result_reg(&(status[1])) != 0)
+ return TIME_OUT;
+ }
+#if DEBUG > 1
+ printk(CDU535_MESSAGE_NAME ": read_exec_status: read 0x%x 0x%x\n",
+ status[0], status[1]);
+#endif
+ return 0;
+}
+
+/****************************************************************************
+ * int check_drive_status( void )
+ *
+ * Check the current drive status. Using this before executing a command
+ * takes care of the problem of unsolicited drive status-2 messages.
+ * Add a check of the audio status if we think the disk is playing.
+ ****************************************************************************/
+static int
+check_drive_status(void)
+{
+ Byte status, e_status[2];
+ int CDD, ATN;
+ Byte cmd;
+
+ select_unit(0);
+ if (sony_audio_status == CDROM_AUDIO_PLAY) { /* check status */
+ outb(SONY535_REQUEST_AUDIO_STATUS, command_reg);
+ if (read_result_reg(&status) == 0) {
+ switch (status) {
+ case 0x0:
+ break; /* play in progress */
+ case 0x1:
+ break; /* paused */
+ case 0x3: /* audio play completed */
+ case 0x5: /* play not requested */
+ sony_audio_status = CDROM_AUDIO_COMPLETED;
+ read_subcode();
+ break;
+ case 0x4: /* error during play */
+ sony_audio_status = CDROM_AUDIO_ERROR;
+ break;
+ }
+ }
+ }
+ /* now check drive status */
+ outb(SONY535_REQUEST_DRIVE_STATUS_2, command_reg);
+ if (read_result_reg(&status) != 0)
+ return TIME_OUT;
+
+#if DEBUG > 1
+ printk(CDU535_MESSAGE_NAME ": check_drive_status() got 0x%x\n", status);
+#endif
+
+ if (status == 0)
+ return 0;
+
+ ATN = status & 0xf;
+ CDD = (status >> 4) & 0xf;
+
+ switch (ATN) {
+ case 0x0:
+ break; /* go on to CDD stuff */
+ case SONY535_ATN_BUSY:
+ if (initialized)
+ printk(CDU535_MESSAGE_NAME " error: drive busy\n");
+ return CD_BUSY;
+ case SONY535_ATN_EJECT_IN_PROGRESS:
+ printk(CDU535_MESSAGE_NAME " error: eject in progress\n");
+ sony_audio_status = CDROM_AUDIO_INVALID;
+ return CD_BUSY;
+ case SONY535_ATN_RESET_OCCURRED:
+ case SONY535_ATN_DISC_CHANGED:
+ case SONY535_ATN_RESET_AND_DISC_CHANGED:
+#if DEBUG > 0
+ printk(CDU535_MESSAGE_NAME " notice: reset occurred or disc changed\n");
+#endif
+ sony_disc_changed = 1;
+ sony_toc_read = 0;
+ sony_audio_status = CDROM_AUDIO_NO_STATUS;
+ sony_first_block = -1;
+ sony_last_block = -1;
+ if (initialized) {
+ cmd = SONY535_SPIN_UP;
+ do_sony_cmd(&cmd, 1, e_status, NULL, 0, 0);
+ sony_get_toc();
+ }
+ return 0;
+ default:
+ printk(CDU535_MESSAGE_NAME " error: drive busy (ATN=0x%x)\n", ATN);
+ return CD_BUSY;
+ }
+ switch (CDD) { /* the 531 docs are not helpful in decoding this */
+ case 0x0: /* just use the values from the DOS driver */
+ case 0x2:
+ case 0xa:
+ break; /* no error */
+ case 0xc:
+ printk(CDU535_MESSAGE_NAME
+ ": check_drive_status(): CDD = 0xc! Not properly handled!\n");
+ return CD_BUSY; /* ? */
+ default:
+ return CD_BUSY;
+ }
+ return 0;
+} /* check_drive_status() */
+
+/*****************************************************************************
+ * int do_sony_cmd( Byte *cmd, int n_cmd, Byte status[2],
+ * Byte *response, int n_response, int ignore_status_bit7 )
+ *
+ * Generic routine for executing commands. The command and its parameters
+ * should be placed in the cmd[] array, number of bytes in the command is
+ * stored in nCmd. The response from the command will be stored in the
+ * response array. The number of bytes you expect back (excluding status)
+ * should be passed in n_response. Finally, some
+ * commands set bit 7 of the return status even when there is no second
+ * status byte, on these commands set ignoreStatusBit7 TRUE.
+ * If the command was sent and data received back, then we return 0,
+ * else we return TIME_OUT. You still have to check the status yourself.
+ * You should call check_drive_status() before calling this routine
+ * so that you do not lose notifications of disk changes, etc.
+ ****************************************************************************/
+static int
+do_sony_cmd(Byte * cmd, int n_cmd, Byte status[2],
+ Byte * response, int n_response, int ignore_status_bit7)
+{
+ int i;
+
+ /* write out the command */
+ for (i = 0; i < n_cmd; i++)
+ outb(cmd[i], command_reg);
+
+ /* read back the status */
+ if (read_result_reg(status) != 0)
+ return TIME_OUT;
+ if (!ignore_status_bit7 && ((status[0] & 0x80) != 0)) {
+ /* get second status byte */
+ if (read_result_reg(status + 1) != 0)
+ return TIME_OUT;
+ } else {
+ status[1] = 0;
+ }
+#if DEBUG > 2
+ printk(CDU535_MESSAGE_NAME ": do_sony_cmd %x: %x %x\n",
+ *cmd, status[0], status[1]);
+#endif
+
+ /* do not know about when I should read set of data and when not to */
+ if ((status[0] & ((ignore_status_bit7 ? 0x7f : 0xff) & 0x8f)) != 0)
+ return 0;
+
+ /* else, read in rest of data */
+ for (i = 0; 0 < n_response; n_response--, i++)
+ if (read_result_reg(response + i) != 0)
+ return TIME_OUT;
+ return 0;
+} /* do_sony_cmd() */
+
+/**************************************************************************
+ * int set_drive_mode( int mode, Byte status[2] )
+ *
+ * Set the drive mode to the specified value (mode=0 is audio, mode=e0
+ * is mode-1 CDROM
+ **************************************************************************/
+static int
+set_drive_mode(int mode, Byte status[2])
+{
+ Byte cmd_buff[2];
+ Byte ret_buff[1];
+
+ cmd_buff[0] = SONY535_SET_DRIVE_MODE;
+ cmd_buff[1] = mode;
+ return do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1);
+}
+
+/***************************************************************************
+ * int seek_and_read_N_blocks( Byte params[], int n_blocks, Byte status[2],
+ * Byte *data_buff, int buff_size )
+ *
+ * Read n_blocks of data from the CDROM starting at position params[0:2],
+ * number of blocks in stored in params[3:5] -- both these are already
+ * int bcd format.
+ * Transfer the data into the buffer pointed at by data_buff. buff_size
+ * gives the number of bytes available in the buffer.
+ * The routine returns number of bytes read in if successful, otherwise
+ * it returns one of the standard error returns.
+ ***************************************************************************/
+static int
+#ifndef MODULE
+seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2],
+ Byte * data_buff, int buf_size)
+#else
+seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2],
+ Byte **buff, int buf_size)
+#endif
+{
+ const int block_size = 2048;
+ Byte cmd_buff[7];
+ int i;
+ int read_status;
+ int retry_count;
+#ifndef MODULE
+ Byte *start_pos = data_buff;
+#else
+ Byte *data_buff;
+ int sector_count = 0;
+#endif
+
+ if (buf_size < ((long)block_size) * n_blocks)
+ return NO_ROOM;
+
+ set_drive_mode(SONY535_CDROM_DRIVE_MODE, status);
+
+ /* send command to read the data */
+ cmd_buff[0] = SONY535_SEEK_AND_READ_N_BLOCKS_1;
+ for (i = 0; i < 6; i++)
+ cmd_buff[i + 1] = params[i];
+ for (i = 0; i < 7; i++)
+ outb(cmd_buff[i], command_reg);
+
+ /* read back the data one block at a time */
+ while (0 < n_blocks--) {
+ /* wait for data to be ready */
+ retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+ while (jiffies < retry_count) {
+ read_status = inb(read_status_reg);
+ if ((read_status & SONY535_RESULT_NOT_READY_BIT) == 0) {
+ read_exec_status(status);
+ return BAD_STATUS;
+ }
+ if ((read_status & SONY535_DATA_NOT_READY_BIT) == 0) {
+ /* data is ready, read it */
+#ifdef MODULE
+ data_buff = buff[sector_count++];
+#endif
+ for (i = 0; i < block_size; i++)
+ *data_buff++ = inb(data_reg); /* unrolling this loop does not seem to help */
+ break; /* exit the timeout loop */
+ }
+ sony_sleep(); /* data not ready, sleep a while */
+ }
+ if (retry_count <= jiffies)
+ return TIME_OUT; /* if we reach this stage */
+ }
+
+ /* read all the data, now read the status */
+ if ((i = read_exec_status(status)) != 0)
+ return i;
+#ifndef MODULE
+ return data_buff - start_pos;
+#else
+ return block_size * sector_count;
+#endif
+} /* seek_and_read_N_blocks() */
+
+/****************************************************************************
+ * int request_toc_data( Byte status[2], struct s535_sony_toc *toc )
+ *
+ * Read in the table of contents data. Converts all the bcd data
+ * into integers in the toc structure.
+ ****************************************************************************/
+static int
+request_toc_data(Byte status[2], struct s535_sony_toc *toc)
+{
+ int to_status;
+ int i, j, n_tracks, track_no;
+ int first_track_num, last_track_num;
+ Byte cmd_no = 0xb2;
+ Byte track_address_buffer[5];
+
+ /* read the fixed portion of the table of contents */
+ if ((to_status = do_sony_cmd(&cmd_no, 1, status, (Byte *) toc, 15, 1)) != 0)
+ return to_status;
+
+ /* convert the data into integers so we can use them */
+ first_track_num = bcd_to_int(toc->first_track_num);
+ last_track_num = bcd_to_int(toc->last_track_num);
+ n_tracks = last_track_num - first_track_num + 1;
+
+ /* read each of the track address descriptors */
+ for (i = 0; i < n_tracks; i++) {
+ /* read the descriptor into a temporary buffer */
+ for (j = 0; j < 5; j++) {
+ if (read_result_reg(track_address_buffer + j) != 0)
+ return TIME_OUT;
+ if (j == 1) /* need to convert from bcd */
+ track_no = bcd_to_int(track_address_buffer[j]);
+ }
+ /* copy the descriptor to proper location - sonycd.c just fills */
+ memcpy(toc->tracks + i, track_address_buffer, 5);
+ }
+ return 0;
+} /* request_toc_data() */
+
+/***************************************************************************
+ * int spin_up_drive( Byte status[2] )
+ *
+ * Spin up the drive (unless it is already spinning).
+ ***************************************************************************/
+static int
+spin_up_drive(Byte status[2])
+{
+ Byte cmd;
+
+ /* first see if the drive is already spinning */
+ cmd = SONY535_REQUEST_DRIVE_STATUS_1;
+ if (do_sony_cmd(&cmd, 1, status, NULL, 0, 0) != 0)
+ return TIME_OUT;
+ if ((status[0] & SONY535_STATUS1_NOT_SPINNING) == 0)
+ return 0; /* it's already spinning */
+
+ /* otherwise, give the spin-up command */
+ cmd = SONY535_SPIN_UP;
+ return do_sony_cmd(&cmd, 1, status, NULL, 0, 0);
+}
+
+/*--------------------end of SONY CDU535 very specific ---------------------*/
+
+/* Convert from an integer 0-99 to BCD */
+static inline unsigned int
+int_to_bcd(unsigned int val)
+{
+ int retval;
+
+ retval = (val / 10) << 4;
+ retval = retval | val % 10;
+ return retval;
+}
+
+
+/* Convert from BCD to an integer from 0-99 */
+static unsigned int
+bcd_to_int(unsigned int bcd)
+{
+ return (((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f);
+}
+
+
+/*
+ * Convert a logical sector value (like the OS would want to use for
+ * a block device) to an MSF format.
+ */
+static void
+log_to_msf(unsigned int log, Byte *msf)
+{
+ log = log + LOG_START_OFFSET;
+ msf[0] = int_to_bcd(log / 4500);
+ log = log % 4500;
+ msf[1] = int_to_bcd(log / 75);
+ msf[2] = int_to_bcd(log % 75);
+}
+
+
+/*
+ * Convert an MSF format to a logical sector.
+ */
+static unsigned int
+msf_to_log(Byte *msf)
+{
+ unsigned int log;
+
+
+ log = bcd_to_int(msf[2]);
+ log += bcd_to_int(msf[1]) * 75;
+ log += bcd_to_int(msf[0]) * 4500;
+ log = log - LOG_START_OFFSET;
+
+ return log;
+}
+
+
+/*
+ * Take in integer size value and put it into a buffer like
+ * the drive would want to see a number-of-sector value.
+ */
+static void
+size_to_buf(unsigned int size, Byte *buf)
+{
+ buf[0] = size / 65536;
+ size = size % 65536;
+ buf[1] = size / 256;
+ buf[2] = size % 256;
+}
+
+
+/*
+ * The OS calls this to perform a read or write operation to the drive.
+ * Write obviously fail. Reads to a read ahead of sony_buffer_size
+ * bytes to help speed operations. This especially helps since the OS
+ * uses 1024 byte blocks and the drive uses 2048 byte blocks. Since most
+ * data access on a CD is done sequentially, this saves a lot of operations.
+ */
+static void
+do_cdu535_request(void)
+{
+ unsigned int dev;
+ unsigned int read_size;
+ int block;
+ int nsect;
+ int copyoff;
+ int spin_up_retry;
+ Byte params[10];
+ Byte status[2];
+ Byte cmd[2];
+
+ if (!sony_inuse) {
+ cdu_open(NULL, NULL);
+ }
+ while (1) {
+ /*
+ * The beginning here is stolen from the hard disk driver. I hope
+ * it's right.
+ */
+ if (!(CURRENT) || CURRENT->dev < 0) {
+ return;
+ }
+ INIT_REQUEST;
+ dev = MINOR(CURRENT->dev);
+ block = CURRENT->sector;
+ nsect = CURRENT->nr_sectors;
+ if (dev != 0) {
+ end_request(0);
+ continue;
+ }
+ switch (CURRENT->cmd) {
+ case READ:
+ /*
+ * If the block address is invalid or the request goes beyond the end of
+ * the media, return an error.
+ */
+
+ if (sony_toc->lead_out_start_lba <= (block / 4)) {
+ end_request(0);
+ return;
+ }
+ if (sony_toc->lead_out_start_lba <= ((block + nsect) / 4)) {
+ end_request(0);
+ return;
+ }
+ while (0 < nsect) {
+ /*
+ * If the requested sector is not currently in the read-ahead buffer,
+ * it must be read in.
+ */
+ if ((block < sony_first_block) || (sony_last_block < block)) {
+ sony_first_block = (block / 4) * 4;
+ log_to_msf(block / 4, params);
+
+ /*
+ * If the full read-ahead would go beyond the end of the media, trim
+ * it back to read just till the end of the media.
+ */
+ if (sony_toc->lead_out_start_lba <= ((block / 4) + sony_buffer_sectors)) {
+ sony_last_block = (sony_toc->lead_out_start_lba * 4) - 1;
+ read_size = sony_toc->lead_out_start_lba - (block / 4);
+ } else {
+ sony_last_block = sony_first_block + (sony_buffer_sectors * 4) - 1;
+ read_size = sony_buffer_sectors;
+ }
+ size_to_buf(read_size, &params[3]);
+
+ /*
+ * Read the data. If the drive was not spinning,
+ * spin it up and try some more.
+ */
+ for (spin_up_retry=0 ;; ++spin_up_retry) {
+ /* This loop has been modified to support the Sony
+ * CDU-510/515 series, thanks to Claudio Porfiri
+ * <C.Porfiri@nisms.tei.ericsson.se>.
+ */
+ /*
+ * This part is to deal with very slow hardware. We
+ * try at most MAX_SPINUP_RETRY times to read the same
+ * block. A check for seek_and_read_N_blocks' result is
+ * performed; if the result is wrong, the CDROM's engine
+ * is restarted and the operation is tried again.
+ */
+ /*
+ * 1995-06-01: The system got problems when downloading
+ * from Slackware CDROM, the problem seems to be:
+ * seek_and_read_N_blocks returns BAD_STATUS and we
+ * should wait for a while before retrying, so a new
+ * part was added to discriminate the return value from
+ * seek_and_read_N_blocks for the various cases.
+ */
+ int readStatus = seek_and_read_N_blocks(params, read_size,
+ status, sony_buffer, (read_size * 2048));
+ if (0 <= readStatus) /* Good data; common case, placed first */
+ break;
+ if (readStatus == NO_ROOM || spin_up_retry == MAX_SPINUP_RETRY) {
+ /* give up */
+ if (readStatus == NO_ROOM)
+ printk(CDU535_MESSAGE_NAME " No room to read from CD\n");
+ else
+ printk(CDU535_MESSAGE_NAME " Read error: 0x%.2x\n",
+ status[0]);
+ sony_first_block = -1;
+ sony_last_block = -1;
+ end_request(0);
+ return;
+ }
+ if (readStatus == BAD_STATUS) {
+ /* Sleep for a while, then retry */
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + RETRY_FOR_BAD_STATUS;
+ schedule();
+ }
+#if DEBUG > 0
+ printk(CDU535_MESSAGE_NAME
+ " debug: calling spin up when reading data!\n");
+#endif
+ cmd[0] = SONY535_SPIN_UP;
+ do_sony_cmd(cmd, 1, status, NULL, 0, 0);
+ }
+ }
+ /*
+ * The data is in memory now, copy it to the buffer and advance to the
+ * next block to read.
+ */
+#ifndef MODULE
+ copyoff = (block - sony_first_block) * 512;
+ memcpy(CURRENT->buffer, sony_buffer + copyoff, 512);
+#else
+ copyoff = block - sony_first_block;
+ memcpy(CURRENT->buffer,
+ sony_buffer[copyoff / 4] + 512 * (copyoff % 4), 512);
+#endif
+
+ block += 1;
+ nsect -= 1;
+ CURRENT->buffer += 512;
+ }
+
+ end_request(1);
+ break;
+
+ case WRITE:
+ end_request(0);
+ break;
+
+ default:
+ panic("Unknown SONY CD cmd");
+ }
+ }
+}
+
+
+/*
+ * Read the table of contents from the drive and set sony_toc_read if
+ * successful.
+ */
+static void
+sony_get_toc(void)
+{
+ Byte status[2];
+ if (!sony_toc_read) {
+ /* do not call check_drive_status() from here since it can call this routine */
+ if (request_toc_data(status, sony_toc) < 0)
+ return;
+ sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf);
+ sony_toc_read = 1;
+ }
+}
+
+
+/*
+ * Search for a specific track in the table of contents. track is
+ * passed in bcd format
+ */
+static int
+find_track(int track)
+{
+ int i;
+ int num_tracks;
+
+
+ num_tracks = bcd_to_int(sony_toc->last_track_num) -
+ bcd_to_int(sony_toc->first_track_num) + 1;
+ for (i = 0; i < num_tracks; i++) {
+ if (sony_toc->tracks[i].track == track) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * Read the subcode and put it int last_sony_subcode for future use.
+ */
+static int
+read_subcode(void)
+{
+ Byte cmd = SONY535_REQUEST_SUB_Q_DATA;
+ Byte status[2];
+ int dsc_status;
+
+ if (check_drive_status() != 0)
+ return -EIO;
+
+ if ((dsc_status = do_sony_cmd(&cmd, 1, status, (Byte *) last_sony_subcode,
+ sizeof(struct s535_sony_subcode), 1)) != 0) {
+ printk(CDU535_MESSAGE_NAME " error 0x%.2x, %d (read_subcode)\n",
+ status[0], dsc_status);
+ return -EIO;
+ }
+ return 0;
+}
+
+
+/*
+ * Get the subchannel info like the CDROMSUBCHNL command wants to see it. If
+ * the drive is playing, the subchannel needs to be read (since it would be
+ * changing). If the drive is paused or completed, the subcode information has
+ * already been stored, just use that. The ioctl call wants things in decimal
+ * (not BCD), so all the conversions are done.
+ */
+static int
+sony_get_subchnl_info(long arg)
+{
+ struct cdrom_subchnl schi;
+ int err;
+
+ /* Get attention stuff */
+ if (check_drive_status() != 0)
+ return -EIO;
+
+ sony_get_toc();
+ if (!sony_toc_read) {
+ return -EIO;
+ }
+ err = verify_area(VERIFY_WRITE /* and read */ , (char *)arg, sizeof schi);
+ if (err)
+ return err;
+
+ memcpy_fromfs(&schi, (char *)arg, sizeof schi);
+
+ switch (sony_audio_status) {
+ case CDROM_AUDIO_PLAY:
+ if (read_subcode() < 0) {
+ return -EIO;
+ }
+ break;
+
+ case CDROM_AUDIO_PAUSED:
+ case CDROM_AUDIO_COMPLETED:
+ break;
+
+ case CDROM_AUDIO_NO_STATUS:
+ schi.cdsc_audiostatus = sony_audio_status;
+ memcpy_tofs((char *)arg, &schi, sizeof schi);
+ return 0;
+ break;
+
+ case CDROM_AUDIO_INVALID:
+ case CDROM_AUDIO_ERROR:
+ default:
+ return -EIO;
+ }
+
+ schi.cdsc_audiostatus = sony_audio_status;
+ schi.cdsc_adr = last_sony_subcode->address;
+ schi.cdsc_ctrl = last_sony_subcode->control;
+ schi.cdsc_trk = bcd_to_int(last_sony_subcode->track_num);
+ schi.cdsc_ind = bcd_to_int(last_sony_subcode->index_num);
+ if (schi.cdsc_format == CDROM_MSF) {
+ schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode->abs_msf[0]);
+ schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode->abs_msf[1]);
+ schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode->abs_msf[2]);
+
+ schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode->rel_msf[0]);
+ schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode->rel_msf[1]);
+ schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode->rel_msf[2]);
+ } else if (schi.cdsc_format == CDROM_LBA) {
+ schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode->abs_msf);
+ schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode->rel_msf);
+ }
+ memcpy_tofs((char *)arg, &schi, sizeof schi);
+ return 0;
+}
+
+
+/*
+ * The big ugly ioctl handler.
+ */
+static int
+cdu_ioctl(struct inode *inode,
+ struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ unsigned int dev;
+ Byte status[2];
+ Byte cmd_buff[10], params[10];
+ int i;
+ int dsc_status;
+ int err;
+
+ if (!inode) {
+ return -EINVAL;
+ }
+ dev = MINOR(inode->i_rdev) >> 6;
+ if (dev != 0) {
+ return -EINVAL;
+ }
+ if (check_drive_status() != 0)
+ return -EIO;
+
+ switch (cmd) {
+ case CDROMSTART: /* Spin up the drive */
+ if (spin_up_drive(status) < 0) {
+ printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMSTART)\n",
+ status[0]);
+ return -EIO;
+ }
+ return 0;
+ break;
+
+ case CDROMSTOP: /* Spin down the drive */
+ cmd_buff[0] = SONY535_HOLD;
+ do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
+
+ /*
+ * Spin the drive down, ignoring the error if the disk was
+ * already not spinning.
+ */
+ sony_audio_status = CDROM_AUDIO_NO_STATUS;
+ cmd_buff[0] = SONY535_SPIN_DOWN;
+ dsc_status = do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
+ if (((dsc_status < 0) && (dsc_status != BAD_STATUS)) ||
+ ((status[0] & ~(SONY535_STATUS1_NOT_SPINNING)) != 0)) {
+ printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMSTOP)\n",
+ status[0]);
+ return -EIO;
+ }
+ return 0;
+ break;
+
+ case CDROMPAUSE: /* Pause the drive */
+ cmd_buff[0] = SONY535_HOLD; /* CDU-31 driver uses AUDIO_STOP, not pause */
+ if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0) {
+ printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPAUSE)\n",
+ status[0]);
+ return -EIO;
+ }
+ /* Get the current position and save it for resuming */
+ if (read_subcode() < 0) {
+ return -EIO;
+ }
+ cur_pos_msf[0] = last_sony_subcode->abs_msf[0];
+ cur_pos_msf[1] = last_sony_subcode->abs_msf[1];
+ cur_pos_msf[2] = last_sony_subcode->abs_msf[2];
+ sony_audio_status = CDROM_AUDIO_PAUSED;
+ return 0;
+ break;
+
+ case CDROMRESUME: /* Start the drive after being paused */
+ set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status);
+
+ if (sony_audio_status != CDROM_AUDIO_PAUSED) {
+ return -EINVAL;
+ }
+ spin_up_drive(status);
+
+ /* Start the drive at the saved position. */
+ cmd_buff[0] = SONY535_PLAY_AUDIO;
+ cmd_buff[1] = 0; /* play back starting at this address */
+ cmd_buff[2] = cur_pos_msf[0];
+ cmd_buff[3] = cur_pos_msf[1];
+ cmd_buff[4] = cur_pos_msf[2];
+ cmd_buff[5] = SONY535_PLAY_AUDIO;
+ cmd_buff[6] = 2; /* set ending address */
+ cmd_buff[7] = final_pos_msf[0];
+ cmd_buff[8] = final_pos_msf[1];
+ cmd_buff[9] = final_pos_msf[2];
+ if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) ||
+ (do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) {
+ printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMRESUME)\n",
+ status[0]);
+ return -EIO;
+ }
+ sony_audio_status = CDROM_AUDIO_PLAY;
+ return 0;
+ break;
+
+ case CDROMPLAYMSF: /* Play starting at the given MSF address. */
+ err = verify_area(VERIFY_READ, (char *)arg, 6);
+ if (err)
+ return err;
+ spin_up_drive(status);
+ set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status);
+ memcpy_fromfs(params, (void *)arg, 6);
+
+ /* The parameters are given in int, must be converted */
+ for (i = 0; i < 3; i++) {
+ cmd_buff[2 + i] = int_to_bcd(params[i]);
+ cmd_buff[7 + i] = int_to_bcd(params[i + 3]);
+ }
+ cmd_buff[0] = SONY535_PLAY_AUDIO;
+ cmd_buff[1] = 0; /* play back starting at this address */
+ /* cmd_buff[2-4] are filled in for loop above */
+ cmd_buff[5] = SONY535_PLAY_AUDIO;
+ cmd_buff[6] = 2; /* set ending address */
+ /* cmd_buff[7-9] are filled in for loop above */
+ if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) ||
+ (do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) {
+ printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPLAYMSF)\n",
+ status[0]);
+ return -EIO;
+ }
+ /* Save the final position for pauses and resumes */
+ final_pos_msf[0] = cmd_buff[7];
+ final_pos_msf[1] = cmd_buff[8];
+ final_pos_msf[2] = cmd_buff[9];
+ sony_audio_status = CDROM_AUDIO_PLAY;
+ return 0;
+ break;
+
+ case CDROMREADTOCHDR: /* Read the table of contents header */
+ {
+ struct cdrom_tochdr *hdr;
+ struct cdrom_tochdr loc_hdr;
+
+ sony_get_toc();
+ if (!sony_toc_read)
+ return -EIO;
+ hdr = (struct cdrom_tochdr *)arg;
+ err = verify_area(VERIFY_WRITE, hdr, sizeof *hdr);
+ if (err)
+ return err;
+ loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num);
+ loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num);
+ memcpy_tofs(hdr, &loc_hdr, sizeof *hdr);
+ }
+ return 0;
+ break;
+
+ case CDROMREADTOCENTRY: /* Read a given table of contents entry */
+ {
+ struct cdrom_tocentry *entry;
+ struct cdrom_tocentry loc_entry;
+ int track_idx;
+ Byte *msf_val = NULL;
+
+ sony_get_toc();
+ if (!sony_toc_read) {
+ return -EIO;
+ }
+ entry = (struct cdrom_tocentry *)arg;
+ err = verify_area(VERIFY_WRITE /* and read */ , entry, sizeof *entry);
+ if (err)
+ return err;
+
+ memcpy_fromfs(&loc_entry, entry, sizeof loc_entry);
+
+ /* Lead out is handled separately since it is special. */
+ if (loc_entry.cdte_track == CDROM_LEADOUT) {
+ loc_entry.cdte_adr = 0 /*sony_toc->address2 */ ;
+ loc_entry.cdte_ctrl = sony_toc->control2;
+ msf_val = sony_toc->lead_out_start_msf;
+ } else {
+ track_idx = find_track(int_to_bcd(loc_entry.cdte_track));
+ if (track_idx < 0)
+ return -EINVAL;
+ loc_entry.cdte_adr = 0 /*sony_toc->tracks[track_idx].address */ ;
+ loc_entry.cdte_ctrl = sony_toc->tracks[track_idx].control;
+ msf_val = sony_toc->tracks[track_idx].track_start_msf;
+ }
+
+ /* Logical buffer address or MSF format requested? */
+ if (loc_entry.cdte_format == CDROM_LBA) {
+ loc_entry.cdte_addr.lba = msf_to_log(msf_val);
+ } else if (loc_entry.cdte_format == CDROM_MSF) {
+ loc_entry.cdte_addr.msf.minute = bcd_to_int(*msf_val);
+ loc_entry.cdte_addr.msf.second = bcd_to_int(*(msf_val + 1));
+ loc_entry.cdte_addr.msf.frame = bcd_to_int(*(msf_val + 2));
+ }
+ memcpy_tofs(entry, &loc_entry, sizeof *entry);
+ }
+ return 0;
+ break;
+
+ case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */
+ {
+ struct cdrom_ti ti;
+ int track_idx;
+
+ sony_get_toc();
+ if (!sony_toc_read)
+ return -EIO;
+ err = verify_area(VERIFY_READ, (char *)arg, sizeof ti);
+ if (err)
+ return err;
+
+ memcpy_fromfs(&ti, (char *)arg, sizeof ti);
+ if ((ti.cdti_trk0 < sony_toc->first_track_num)
+ || (sony_toc->last_track_num < ti.cdti_trk0)
+ || (ti.cdti_trk1 < ti.cdti_trk0)) {
+ return -EINVAL;
+ }
+ track_idx = find_track(int_to_bcd(ti.cdti_trk0));
+ if (track_idx < 0)
+ return -EINVAL;
+ params[1] = sony_toc->tracks[track_idx].track_start_msf[0];
+ params[2] = sony_toc->tracks[track_idx].track_start_msf[1];
+ params[3] = sony_toc->tracks[track_idx].track_start_msf[2];
+ /*
+ * If we want to stop after the last track, use the lead-out
+ * MSF to do that.
+ */
+ if (bcd_to_int(sony_toc->last_track_num) <= ti.cdti_trk1) {
+ log_to_msf(msf_to_log(sony_toc->lead_out_start_msf) - 1,
+ &(params[4]));
+ } else {
+ track_idx = find_track(int_to_bcd(ti.cdti_trk1 + 1));
+ if (track_idx < 0)
+ return -EINVAL;
+ log_to_msf(msf_to_log(sony_toc->tracks[track_idx].track_start_msf) - 1,
+ &(params[4]));
+ }
+ params[0] = 0x03;
+
+ spin_up_drive(status);
+
+ set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status);
+
+ /* Start the drive at the saved position. */
+ cmd_buff[0] = SONY535_PLAY_AUDIO;
+ cmd_buff[1] = 0; /* play back starting at this address */
+ cmd_buff[2] = params[1];
+ cmd_buff[3] = params[2];
+ cmd_buff[4] = params[3];
+ cmd_buff[5] = SONY535_PLAY_AUDIO;
+ cmd_buff[6] = 2; /* set ending address */
+ cmd_buff[7] = params[4];
+ cmd_buff[8] = params[5];
+ cmd_buff[9] = params[6];
+ if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) ||
+ (do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) {
+ printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPLAYTRKIND)\n",
+ status[0]);
+ printk("... Params: %x %x %x %x %x %x %x\n",
+ params[0], params[1], params[2],
+ params[3], params[4], params[5], params[6]);
+ return -EIO;
+ }
+ /* Save the final position for pauses and resumes */
+ final_pos_msf[0] = params[4];
+ final_pos_msf[1] = params[5];
+ final_pos_msf[2] = params[6];
+ sony_audio_status = CDROM_AUDIO_PLAY;
+ return 0;
+ }
+
+ case CDROMSUBCHNL: /* Get subchannel info */
+ return sony_get_subchnl_info(arg);
+
+ case CDROMVOLCTRL: /* Volume control. What volume does this change, anyway? */
+ {
+ struct cdrom_volctrl volctrl;
+
+ err = verify_area(VERIFY_READ, (char *)arg, sizeof volctrl);
+ if (err)
+ return err;
+
+ memcpy_fromfs(&volctrl, (char *)arg, sizeof volctrl);
+ cmd_buff[0] = SONY535_SET_VOLUME;
+ cmd_buff[1] = volctrl.channel0;
+ cmd_buff[2] = volctrl.channel1;
+ if (do_sony_cmd(cmd_buff, 3, status, NULL, 0, 0) != 0) {
+ printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMVOLCTRL)\n",
+ status[0]);
+ return -EIO;
+ }
+ }
+ return 0;
+
+ case CDROMEJECT: /* Eject the drive */
+ cmd_buff[0] = SONY535_STOP;
+ do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
+ cmd_buff[0] = SONY535_SPIN_DOWN;
+ do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
+
+ sony_audio_status = CDROM_AUDIO_INVALID;
+ cmd_buff[0] = SONY535_EJECT_CADDY;
+ if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0) {
+ printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMEJECT)\n",
+ status[0]);
+ return -EIO;
+ }
+ return 0;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+
+/*
+ * Open the drive for operations. Spin the drive up and read the table of
+ * contents if these have not already been done.
+ */
+static int
+cdu_open(struct inode *inode,
+ struct file *filp)
+{
+ Byte status[2], cmd_buff[2];
+
+
+ if (sony_inuse)
+ return -EBUSY;
+ if (check_drive_status() != 0)
+ return -EIO;
+ sony_inuse = 1;
+ MOD_INC_USE_COUNT;
+
+ if (spin_up_drive(status) != 0) {
+ printk(CDU535_MESSAGE_NAME " error 0x%.2x (cdu_open, spin up)\n",
+ status[0]);
+ sony_inuse = 0;
+ MOD_DEC_USE_COUNT;
+ return -EIO;
+ }
+ sony_get_toc();
+ if (!sony_toc_read) {
+ cmd_buff[0] = SONY535_SPIN_DOWN;
+ do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
+ sony_inuse = 0;
+ MOD_DEC_USE_COUNT;
+ return -EIO;
+ }
+ if (inode) {
+ check_disk_change(inode->i_rdev);
+ }
+ sony_usage++;
+
+#ifdef LOCK_DOORS
+ /* disable the eject button while mounted */
+ cmd_buff[0] = SONY535_DISABLE_EJECT_BUTTON;
+ do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
+#endif
+
+ return 0;
+}
+
+
+/*
+ * Close the drive. Spin it down if no task is using it. The spin
+ * down will fail if playing audio, so audio play is OK.
+ */
+static void
+cdu_release(struct inode *inode,
+ struct file *filp)
+{
+ Byte status[2], cmd_no;
+
+ sony_inuse = 0;
+ MOD_DEC_USE_COUNT;
+
+ if (0 < sony_usage) {
+ sony_usage--;
+ }
+ if (sony_usage == 0) {
+ sync_dev(inode->i_rdev);
+ check_drive_status();
+
+ if (sony_audio_status != CDROM_AUDIO_PLAY) {
+ cmd_no = SONY535_SPIN_DOWN;
+ do_sony_cmd(&cmd_no, 1, status, NULL, 0, 0);
+ }
+#ifdef LOCK_DOORS
+ /* enable the eject button after umount */
+ cmd_no = SONY535_ENABLE_EJECT_BUTTON;
+ do_sony_cmd(&cmd_no, 1, status, NULL, 0, 0);
+#endif
+ }
+}
+
+
+static struct file_operations cdu_fops =
+{
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ cdu_ioctl, /* ioctl */
+ NULL, /* mmap */
+ cdu_open, /* open */
+ cdu_release, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ cdu535_check_media_change, /* check media change */
+ NULL /* revalidate */
+};
+
+/*
+ * Initialize the driver.
+ */
+#ifndef MODULE
+unsigned long
+sony535_init(unsigned long mem_start, unsigned long mem_end)
+#else
+int
+init_module(void)
+#endif
+{
+ struct s535_sony_drive_config drive_config;
+ Byte cmd_buff[3];
+ Byte ret_buff[2];
+ Byte status[2];
+ int retry_count;
+ int tmp_irq;
+#ifdef MODULE
+ int i;
+#endif
+
+ /* Setting the base I/O address to 0xffff will disable it. */
+ if (sony535_cd_base_io == 0xffff)
+ goto bail;
+
+ /* Set up all the register locations */
+ result_reg = sony535_cd_base_io;
+ command_reg = sony535_cd_base_io;
+ data_reg = sony535_cd_base_io + 1;
+ read_status_reg = sony535_cd_base_io + 2;
+ select_unit_reg = sony535_cd_base_io + 3;
+
+#ifndef USE_IRQ
+ sony535_irq_used = 0; /* polling only until this is ready... */
+#endif
+ /* we need to poll until things get initialized */
+ tmp_irq = sony535_irq_used;
+ sony535_irq_used = 0;
+
+#if DEBUG > 0
+ printk(CDU535_MESSAGE_NAME ": probing base address %03X\n",
+ sony535_cd_base_io);
+#endif
+ if (check_region(sony535_cd_base_io,4)) {
+ printk(CDU535_MESSAGE_NAME ": my base address is not free!\n");
+#ifndef MODULE
+ return mem_start;
+#else
+ return -EIO;
+#endif
+ }
+ /* look for the CD-ROM, follows the procedure in the DOS driver */
+ inb(select_unit_reg);
+ retry_count = jiffies + 2 * HZ;
+ while (jiffies < retry_count)
+ sony_sleep(); /* wait for 40 18 Hz ticks (from DOS driver) */
+ inb(result_reg);
+
+ outb(0, read_status_reg); /* does a reset? */
+ retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
+ while (jiffies < retry_count) {
+ select_unit(0);
+ if (inb(result_reg) != 0xff)
+ break;
+ sony_sleep();
+ }
+
+ if ((jiffies < retry_count) && (check_drive_status() != TIME_OUT)) {
+ /* CD-ROM drive responded -- get the drive configuration */
+ cmd_buff[0] = SONY535_INQUIRY;
+ if (do_sony_cmd(cmd_buff, 1, status,
+ (Byte *)&drive_config, 28, 1) == 0) {
+ /* was able to get the configuration,
+ * set drive mode as rest of init
+ */
+#if DEBUG > 0
+ /* 0x50 == CADDY_NOT_INSERTED | NOT_SPINNING */
+ if ( (status[0] & 0x7f) != 0 && (status[0] & 0x7f) != 0x50 )
+ printk(CDU535_MESSAGE_NAME
+ "Inquiry command returned status = 0x%x\n", status[0]);
+#endif
+ /* now ready to use interrupts, if available */
+ sony535_irq_used = tmp_irq;
+#ifndef MODULE
+/* This code is not in MODULEs by default, since the autoirq stuff might
+ * not be in the module-accessible symbol table.
+ */
+ /* A negative sony535_irq_used will attempt an autoirq. */
+ if (sony535_irq_used < 0) {
+ autoirq_setup(0);
+ enable_interrupts();
+ outb(0, read_status_reg); /* does a reset? */
+ sony535_irq_used = autoirq_report(10);
+ disable_interrupts();
+ }
+#endif
+ if (sony535_irq_used > 0) {
+ if (request_irq(sony535_irq_used, cdu535_interrupt,
+ SA_INTERRUPT, CDU535_HANDLE)) {
+ printk("Unable to grab IRQ%d for the " CDU535_MESSAGE_NAME
+ " driver; polling instead.\n", sony535_irq_used);
+ sony535_irq_used = 0;
+ }
+ }
+ cmd_buff[0] = SONY535_SET_DRIVE_MODE;
+ cmd_buff[1] = 0x0; /* default audio */
+ if (do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1) == 0) {
+ /* set the drive mode successful, we are set! */
+ sony_buffer_size = SONY535_BUFFER_SIZE;
+ sony_buffer_sectors = sony_buffer_size / 2048;
+
+ printk(CDU535_MESSAGE_NAME " I/F CDROM : %8.8s %16.16s %4.4s",
+ drive_config.vendor_id,
+ drive_config.product_id,
+ drive_config.product_rev_level);
+ printk(" base address %03X, ", sony535_cd_base_io);
+ if (tmp_irq > 0)
+ printk("IRQ%d, ", tmp_irq);
+ printk("using %d byte buffer\n", sony_buffer_size);
+
+ if (register_blkdev(MAJOR_NR, CDU535_HANDLE, &cdu_fops)) {
+ printk("Unable to get major %d for %s\n",
+ MAJOR_NR, CDU535_MESSAGE_NAME);
+#ifndef MODULE
+ return mem_start;
+#else
+ return -EIO;
+#endif
+ }
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */
+
+#ifndef MODULE
+ sony_toc = (struct s535_sony_toc *)mem_start;
+ mem_start += sizeof *sony_toc;
+ last_sony_subcode = (struct s535_sony_subcode *)mem_start;
+ mem_start += sizeof *last_sony_subcode;
+ sony_buffer = (Byte *)mem_start;
+ mem_start += sony_buffer_size;
+
+#else /* MODULE */
+ sony_toc = (struct s535_sony_toc *)
+ kmalloc(sizeof *sony_toc, GFP_KERNEL);
+ last_sony_subcode = (struct s535_sony_subcode *)
+ kmalloc(sizeof *last_sony_subcode, GFP_KERNEL);
+ sony_buffer = (Byte **)
+ kmalloc(4 * sony_buffer_sectors, GFP_KERNEL);
+ for (i = 0; i < sony_buffer_sectors; i++)
+ sony_buffer[i] = (Byte *)kmalloc(2048, GFP_KERNEL);
+#endif /* MODULE */
+ initialized = 1;
+ }
+ }
+ }
+
+ if (!initialized) {
+ printk("Did not find a " CDU535_MESSAGE_NAME " drive\n");
+#ifdef MODULE
+ return -EIO;
+#endif
+ } else {
+ request_region(sony535_cd_base_io, 4, CDU535_HANDLE);
+ }
+bail:
+#ifndef MODULE
+ return mem_start;
+#else
+ return 0;
+#endif
+}
+
+#ifndef MODULE
+/*
+ * accept "kernel command line" parameters
+ * (added by emoenke@gwdg.de)
+ *
+ * use: tell LILO:
+ * sonycd535=0x320
+ *
+ * the address value has to be the existing CDROM port address.
+ */
+void
+sonycd535_setup(char *strings, int *ints)
+{
+ /* if IRQ change and default io base desired,
+ * then call with io base of 0
+ */
+ if (ints[0] > 0)
+ if (ints[0] != 0)
+ sony535_cd_base_io = ints[1];
+ if (ints[0] > 1)
+ sony535_irq_used = ints[2];
+ if ((strings != NULL) && (*strings != '\0'))
+ printk(CDU535_MESSAGE_NAME
+ ": Warning: Unknown interface type: %s\n", strings);
+}
+
+#else /* MODULE */
+
+void
+cleanup_module(void)
+{
+ int i;
+ if (MOD_IN_USE) {
+ printk(CDU535_HANDLE " module in use, cannot remove\n");
+ return;
+ }
+ release_region(sony535_cd_base_io, 4);
+ for (i = 0; i < sony_buffer_sectors; i++)
+ kfree_s(sony_buffer[i], 2048);
+ kfree_s(sony_buffer, 4 * sony_buffer_sectors);
+ kfree_s(last_sony_subcode, sizeof *last_sony_subcode);
+ kfree_s(sony_toc, sizeof *sony_toc);
+ if (unregister_blkdev(MAJOR_NR, CDU535_HANDLE) == -EINVAL)
+ printk("Uh oh, couldn't unregister " CDU535_HANDLE "\n");
+ else
+ printk(CDU535_HANDLE " module released\n");
+}
+#endif /* MODULE */
+
+#endif /* CONFIG_CDU535 */
diff --git a/drivers/block/xd.c b/drivers/block/xd.c
index bbe7d3332..46cb09957 100644
--- a/drivers/block/xd.c
+++ b/drivers/block/xd.c
@@ -22,6 +22,7 @@
#include <linux/errno.h>
#include <linux/sched.h>
+#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/genhd.h>
@@ -380,7 +381,7 @@ static void xd_recalibrate (u_char drive)
}
/* xd_interrupt_handler: interrupt service routine */
-static void xd_interrupt_handler (int unused)
+static void xd_interrupt_handler(int irq, struct pt_regs * regs)
{
if (inb(XD_STATUS) & STAT_INTERRUPT) { /* check if it was our device */
#ifdef DEBUG_OTHER
diff --git a/drivers/char/8x16.fnt b/drivers/char/8x16.fnt
new file mode 100644
index 000000000..88fb566ef
--- /dev/null
+++ b/drivers/char/8x16.fnt
@@ -0,0 +1,4097 @@
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x04,0x0f,0x0f,0x0f,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x0f,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x0f,0x04,0x0f,0x04,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x0f,0x04,0x0f,0x0f,0x0f,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x04,0x0f,0x0f,0x0f,0x04,0x0f,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x0f,0x04,0x0f,0x0f,0x0f,0x04,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x04,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,
+0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,0x0f,
+0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,0x0f,
+0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,0x0f,
+0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,0x0f,
+0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,0x0f,
+0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x0f,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x0f,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x0f,0x04,0x04,0x0f,0x04,0x04,0x04,0x0f,
+0x0f,0x04,0x04,0x0f,0x04,0x04,0x04,0x0f,
+0x0f,0x04,0x04,0x0f,0x04,0x04,0x04,0x0f,
+0x0f,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x0f,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x0f,0x04,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x0f,0x04,0x0f,0x04,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x0f,0x04,0x0f,0x04,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x0f,0x04,0x0f,0x04,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x0f,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x0f,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x0f,
+0x0f,0x04,0x0f,0x04,0x04,0x0f,0x04,0x0f,
+0x0f,0x04,0x0f,0x04,0x04,0x0f,0x04,0x0f,
+0x0f,0x04,0x0f,0x04,0x04,0x0f,0x04,0x0f,
+0x0f,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x0f,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,0x0f,
+0x0f,0x04,0x0f,0x04,0x04,0x0f,0x04,0x0f,
+0x0f,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x0f,
+0x0f,0x04,0x0f,0x04,0x04,0x0f,0x04,0x0f,
+0x0f,0x04,0x0f,0x04,0x04,0x0f,0x04,0x0f,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x0f,
+0x0f,0x04,0x04,0x0f,0x0f,0x04,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x04,0x0f,0x0f,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x04,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x0f,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x0f,
+0x04,0x0f,0x0f,0x0f,0x04,0x0f,0x0f,0x0f,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x0f,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x0f,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x0f,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x04,0x0f,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x0f,0x0f,
+0x04,0x04,0x04,0x0f,0x04,0x0f,0x04,0x0f,
+0x0f,0x0f,0x0f,0x04,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x04,0x0f,0x04,0x0f,
+0x0f,0x0f,0x0f,0x04,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x04,0x0f,0x04,0x0f,
+0x0f,0x0f,0x0f,0x04,0x0f,0x04,0x0f,0x0f,
+0x04,0x04,0x04,0x0f,0x04,0x0f,0x04,0x0f,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x04,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x0f,0x04,0x0f,0x04,0x0f,0x04,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x0f,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x0f,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x04,0x04,0x0f,0x04,0x0f,0x04,0x04,
+0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x04,0x0f,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x0f,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x0f,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x0f,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x0f,
+0x0f,0x04,0x0f,0x04,0x04,0x0f,0x04,0x0f,
+0x0f,0x04,0x0f,0x04,0x04,0x0f,0x04,0x0f,
+0x0f,0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x04,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x04,0x0f,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x04,0x04,0x0f,0x04,0x04,0x0f,0x0f,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,
+0x0f,0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x0f,0x04,
+0x0f,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x04,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x0f,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,
+0x0f,0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x0f,0x04,0x04,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x04,0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x04,0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x0f,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,0x0f,
+0x0f,0x04,0x0f,0x04,0x04,0x0f,0x04,0x0f,
+0x0f,0x04,0x0f,0x04,0x04,0x04,0x04,0x0f,
+0x0f,0x04,0x0f,0x04,0x04,0x0f,0x04,0x0f,
+0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,0x0f,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x0f,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x0f,
+0x0f,0x04,0x0f,0x04,0x04,0x0f,0x04,0x0f,
+0x0f,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x0f,
+0x0f,0x04,0x0f,0x04,0x0f,0x04,0x04,0x0f,
+0x0f,0x04,0x0f,0x04,0x04,0x0f,0x04,0x0f,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x0f,
+0x04,0x0f,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x04,0x0f,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x0f,0x04,0x0f,0x04,0x0f,0x04,0x0f,
+0x04,0x0f,0x04,0x0f,0x04,0x04,0x04,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x04,0x0f,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x04,0x0f,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x0f,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x0f,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x04,0x0f,0x0f,0x0f,
+0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,0x0f,
+0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,0x0f,
+0x0f,0x0f,0x0f,0x04,0x0f,0x0f,0x0f,0x0f,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x04,0x0f,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x0f,0x04,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x0f,0x04,0x04,0x04,
+0x0f,0x04,0x0f,0x04,0x0f,0x04,0x0f,0x04,
+0x0f,0x0f,0x0f,0x04,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x04,0x0f,0x0f,0x04,
+0x0f,0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,
+0x0f,0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,
+0x0f,0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,
+0x0f,0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,
+0x0f,0x0f,0x04,0x0f,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x04,0x0f,0x0f,0x04,
+0x0f,0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,
+0x0f,0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,
+0x0f,0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,
+0x0f,0x04,0x04,0x0f,0x04,0x04,0x0f,0x04,
+0x0f,0x0f,0x04,0x0f,0x04,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x0f,0x0f,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x0f,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x0f,0x0f,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x0f,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x0f,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x04,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x0f,0x0f,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,
+0x04,0x0f,0x04,0x04,0x0f,0x04,0x04,0x04,
+0x04,0x04,0x0f,0x0f,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,
+0
diff --git a/drivers/char/ChangeLog b/drivers/char/ChangeLog
index 78f1fb332..da6f8befc 100644
--- a/drivers/char/ChangeLog
+++ b/drivers/char/ChangeLog
@@ -1,3 +1,193 @@
+Tue May 2 00:53:25 1995 <tytso@rsx-11.mit.edu>
+
+ * tty_io.c (tty_set_ldisc): Wait until the output buffer is
+ drained before closing the old line discipline --- needed
+ in only one case: XON/XOFF processing.
+
+ * n_tty.c (n_tty_close): Don't bother waiting until the output
+ driver is closed; in general, the line discipline
+ shouldn't care if the hardware is finished
+ transmitting before the line discipline terminates.
+
+ * tty_io.c (release_dev): Shutdown the line discipline after
+ decrementing the tty count variable; but set the
+ TTY_CLOSING flag so that we know that this tty structure
+ isn't long for this world.
+
+ * tty_io.c (init_dev): Add sanity code to check to see if
+ TTY_CLOSING is set on a tty structure; if so, something
+ bad has happened (probably a line discipline close blocked
+ when it shouldn't have; so do a kernel printk and then
+ return an error).
+
+Wed Apr 26 10:23:44 1995 Theodore Y. Ts'o <tytso@localhost>
+
+ * tty_io.c (release_dev): Try to shutdown the line discipline
+ *before* decrementing the tty count variable; this removes
+ a potential race condition which occurs when the line
+ discipline close blocks, and another process then tries
+ open the same serial port.
+
+ * serial.c (rs_hangup): When hanging up, flush the output buffer
+ before shutting down the UART. Otherwise the line
+ discipline close blocks waiting for the characters to get
+ flushed, which never happens until the serial port gets reused.
+
+Wed Apr 12 08:06:16 1995 Theodore Y. Ts'o <tytso@localhost>
+
+ * serial.c (do_serial_hangup, do_softint, check_modem_status,
+ rs_init): Hangups are now scheduled via a separate tqueue
+ structure in the async_struct structure, tqueue_hangup.
+ This task is pushed on to the tq_schedule queue, so that
+ it is processed synchronously by the scheduler.
+
+Sat Feb 18 12:13:51 1995 Theodore Y. Ts'o (tytso@rt-11)
+
+ * tty_io.c (disassociate_ctty, tty_open, tty_ioctl): Clear
+ current->tty_old_pgrp field when a session leader
+ acquires a controlling tty, and after a session leader
+ has disassociated from a controlling tty.
+
+Fri Feb 17 09:34:09 1995 Theodore Y. Ts'o (tytso@rt-11)
+
+ * serial.c (rs_interrupt_single, rs_interrupt, rs_interrupt_multi):
+ Change the the number of passes made from 64 to be 256,
+ configurable with the #define RS_ISR_PASS_LIMIT.
+
+ * serial.c (rs_init, set_serial_info, get_serial_info, rs_close):
+ Remove support for closing_wait2. Instead, set
+ tty->closing and rely on the line discipline to prevent
+ echo wars.
+
+ * n_tty.c (n_tty_receive_char): IEXTEN does not need to be
+ enabled in order for IXANY to be active.
+
+ If tty->closing is set, then only process XON and XOFF
+ characters.
+
+Sun Feb 12 23:57:48 1995 Theodore Y. Ts'o (tytso@rt-11)
+
+ * serial.c (rs_timer): Change the interrupt poll time from 60
+ seconds to 10 seconds, configurable with the #define
+ RS_STROBE_TIME.
+
+ * serial.c (rs_interrupt_multi, startup, shutdown, rs_ioctl,
+ set_multiport_struct, get_multiport_struct): Add
+ provisions for a new type of interrupt service routine,
+ which better supports multiple serial ports on a single
+ IRQ.
+
+Sun Feb 5 19:35:11 1995 Theodore Y. Ts'o (tytso@rt-11)
+
+ * tty_ioctl.c (n_tty_ioctl, set_termios, tty_wait_until_sent):
+ * serial.c (rs_ioctl, rs_close):
+ * cyclades.c (cy_ioctl, cy_close):
+ * n_tty.c (n_tty_close): Rename wait_until_sent to
+ tty_wait_until_sent, so that it's a better name to export
+ in ksyms.c.
+
+Sat Feb 4 23:36:20 1995 Theodore Y. Ts'o (tytso@rt-11)
+
+ * serial.c (rs_close): Added missing check for closing_wait2 being
+ ASYNC_CLOSING_WAIT_NONE.
+
+Thu Jan 26 09:02:49 1995 Theodore Y. Ts'o (tytso@rt-11)
+
+ * serial.c (rs_init, set_serial_info, get_serial_info,
+ rs_close): Support close_wait in the serial driver.
+ This is helpful for slow devices (like serial
+ plotters) so that their outputs don't get flushed upon
+ device close. This has to be configurable because
+ normally we don't want ports to be hung up for long
+ periods of time during a close when they are not
+ connected to a device, or the device is powered off.
+
+ The default is to wait 30 seconds; in the case of a
+ very slow device, the close_wait timeout should be
+ lengthened. If it is set to 0, the kernel will wait
+ forever for all of the data to be transmitted.
+
+Thu Jan 17 01:17:20 1995 Theodore Y. Ts'o (tytso@rt-11)
+
+ * serial.c (startup, change_speed, rs_init): Add support to detect
+ the StarTech 16650 chip. Treat it as a 16450 for now,
+ because of its FIFO bugs.
+
+Thu Jan 5 21:21:57 1995 <dhinds@allegro.stanford.edu>
+
+ * serial.c: (receive_char): Added counter to prevent infinite loop
+ when a PCMCIA serial device is ejected.
+
+Thu Dec 29 17:53:48 1994 <tytso@rsx-11.mit.edu>
+
+ * tty_io.c (check_tty_count): New procedure which checks
+ tty->count to make sure that it matches with the number of
+ open file descriptors which point at the structure. If
+ the number doesn't match, it prints a warning message.
+
+Wed Dec 28 15:41:51 1994 <tytso@rsx-11.mit.edu>
+
+ * tty_io.c (do_tty_hangup, disassociate_ctty): At hangup time,
+ save the tty's current foreground process group in the
+ session leader's task structure. When the session leader
+ terminates, send a SIGHUP, SIGCONT to that process group.
+ This is not required by POSIX, but it's not prohibited
+ either, and it appears to be the least intrusive way
+ to fix a problem that dialup servers have with
+ orphaned process groups caused by modem hangups.
+
+Thu Dec 8 14:52:11 1994 <tytso@rsx-11.mit.edu>
+
+ * serial.c (rs_ioctl): Don't allow most ioctl's if the serial port
+ isn't initialized.
+
+ * serial.c (rs_close): Don't clear the IER if the serial port
+ isn't initialized.
+
+ * serial.c (block_til_ready): Don't try to block on the dialin
+ port if the serial port isn't initialized.
+
+Wed Dec 7 10:48:30 1994 Si Park (si@wimpol.demon.co.uk)
+ * tty_io.c (tty_register_driver): Fix bug when linking onto
+ the tty_drivers list. We now test that there are elements
+ already on the list before setting the back link from the
+ first element to the new driver.
+
+ * tty_io.c (tty_unregister_driver): Fix bug in unlinking the
+ specified driver from the tty_drivers list. We were not
+ setting the back link correctly. This used to result in
+ a dangling back link pointer and cause panics on the next
+ call to get_tty_driver().
+
+Tue Nov 29 10:21:09 1994 Theodore Y. Ts'o (tytso@rt-11)
+
+ * tty_io.c (tty_unregister_driver): Fix bug in
+ tty_unregister_driver where the pointer to the refcount is
+ tested, instead of the refcount itself. This caused
+ tty_unregister_driver to always return EBUSY.
+
+Sat Nov 26 11:59:24 1994 Theodore Y. Ts'o (tytso@rt-11)
+
+ * tty_io.c (tty_ioctl): Add support for the new ioctl
+ TIOCTTYGSTRUCT, which allow a kernel debugging program
+ direct read access to the tty and tty_driver structures.
+
+Fri Nov 25 17:26:22 1994 Theodore Y. Ts'o (tytso@rt-11)
+
+ * serial.c (rs_set_termios): Don't wake up processes blocked in
+ open when the CLOCAL flag changes, since a blocking
+ open only samples the CLOCAL flag once when it blocks,
+ and doesn't check it again. (n.b. FreeBSD has a
+ different behavior for blocking opens; it's not clear
+ whether Linux or FreeBSD's interpretation is correct.
+ POSIX doesn't give clear guidance on this issue, so
+ this may change in the future....)
+
+ * serial.c (block_til_ready): Use the correct termios structure to
+ check the CLOCAL flag. If the cuaXX device is active,
+ then check the saved termios for the ttySXX device.
+ Otherwise, use the currently active termios structure.
+
Sun Nov 6 21:05:44 1994 Theodore Y. Ts'o (tytso@rt-11)
* serial.c (change_speed): Add support for direct access of
@@ -130,7 +320,7 @@ Fri Sep 16 08:13:25 1994 Theodore Y. Ts'o (tytso@rt-11)
until after block_til_ready has returned successfully.
Modify block_til_ready to check the normal_termios
structure directly, so it doesn't rely on termios being
- set before its called.
+ set before it's called.
Thu Sep 15 23:34:01 1994 Theodore Y. Ts'o (tytso@rt-11)
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 6045cc7fc..217907a85 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -17,14 +17,24 @@
$(CC) $(CFLAGS) -c $<
OBJS = tty_io.o n_tty.o console.o keyboard.o serial.o \
- tty_ioctl.o pty.o vt.o mem.o \
- defkeymap.o uni_to_437.o
+ tty_ioctl.o pty.o vt.o mem.o vc_screen.o \
+ defkeymap.o consolemap.o vesa_blank.o selection.o
SRCS = tty_io.c n_tty.c console.c keyboard.c serial.c \
- tty_ioctl.c pty.c vt.c mem.c \
- defkeymap.c uni_to_437.c
+ tty_ioctl.c pty.c vt.c mem.c vc_screen.c \
+ defkeymap.c consolemap.c vesa_blank.c selection.c
+ifdef CONFIG_MIPS_MAGNUM_4000
+OBJS := $(OBJS) fnt8x16.o
+SRCS := $(SRCS) fnt8x16.c
+endif
+
+ifdef CONFIG_CYCLADES
+OBJS := $(OBJS) cyclades.o
+SRCS := $(SRCS) cyclades.c
+endif
+
ifdef CONFIG_ATIXL_BUSMOUSE
M = y
OBJS := $(OBJS) atixlmouse.o
@@ -40,6 +50,8 @@ endif
ifdef CONFIG_PRINTER
OBJS := $(OBJS) lp.o
SRCS := $(SRCS) lp.c
+else
+MODULES := $(MODULES) lp.o
endif
ifdef CONFIG_MS_BUSMOUSE
@@ -67,6 +79,12 @@ ifdef M
OBJS := $(OBJS) mouse.o
SRCS := $(SRCS) mouse.c
endif
+
+ifdef CONFIG_SCC
+OBJS := $(OBJS) scc.o
+SRCS := $(SRCS) scc.c
+endif
+
all: char.a
@@ -74,8 +92,22 @@ char.a: $(OBJS)
$(AR) rcs char.a $(OBJS)
sync
+ifdef MODULES
+
+modules: $(MODULES)
+ (cd ../../modules;for i in $(MODULES); do ln -sf ../drivers/char/$$i .; done)
+
+else
+
+modules:
+
+endif
+
dep:
$(CPP) -M $(SRCS) > .depend
+ifdef MODULES
+ $(CPP) -M -DMODULE $(MODULES:.o=.c) >> .depend
+endif
dummy:
diff --git a/drivers/char/README.scc b/drivers/char/README.scc
new file mode 100644
index 000000000..c48c33c23
--- /dev/null
+++ b/drivers/char/README.scc
@@ -0,0 +1,933 @@
+This is a subset of the documentation. To use this driver you MUST have the
+full package from:
+
+Internet:
+=========
+
+ftp.ucsd.edu:/hamradio/packet/tcpip/incoming/z8530drv-1.8.dl1bke.tar.gz
+
+and various mirrors (i.e. nic.switch.ch)
+
+AX.25 BBS
+=========
+
+UNIX @ DB0ACH.#NRW.DEU.EU, subject: Z8530D18.Pxx/Pyy
+
+(AX.25 call: DB0ACH-8)
+
+and various BBS that received the file through AUTO7P or 7PSERV
+with the filename Z8530D18.TGZ
+
+
+---------------------------------------------------------------------------
+
+!! Version 1.8
+!!
+!! Deutscher Text siehe scc_ger.doc
+!!
+!! perhaps somebody could correct the English documentation (grammar,
+!! spelling)?
+!!
+!! BTW: REAL programmers don't document...
+!!
+
+
+ SCC.C - Linux driver for Z8530 based HDLC cards for AX.25
+
+ ********************************************************************
+
+ (c) 1994 by Joerg Reuter DL1BKE
+
+ portions (c) 1994 Hans Alblas PE1AYX
+ and (c) 1993 Guido ten Dolle PE1NNZ
+
+ for the complete copyright notice see >> Copying.Z8530DRV <<
+
+ ********************************************************************
+
+
+0. Installation of the package
+==============================
+
+Run SCC-Install. If one (or more) of the patches fails PLEASE consult
+chapter 2 (and READ IT of course!)
+
+
+
+1. Initialization and attachment of the channels
+================================================
+
+To use the driver, 3 steps must be performed:
+
+ 1. Global initialization of the driver in the kernel
+ 2. Setup of parameters with sccinit
+ 2. Attachment of each channel in the packet software
+
+The global initialization is needed to reset all SCCs and to
+install a common interrupt handler. Also, the hardware addresses
+of the chips are defined in this step. In the second step, each
+channel is set up for the intended use.
+
+
+
+1.1. Initialization
+===================
+
+Initialization of the hardware is performed by setting the defines and
+variables in the file "/linux/drivers/char/scc_config.h". You can change
+a number of parameters.
+
+
+
+################################################################################################
+# For OptoSCC card e.g:
+#
+
+int Nchips = 2 ; /* number of chips */
+io_port Vector_Latch = 0x168 ; /* addr. of INTACK-Latch (0 for poll mode)
+*/
+int Ivec = 9 ; /* interrupt vector */
+long Clock = 4915200 ; /* frequency of the scc clock */
+char Pclk = 1 ; /* use PCLK (1) or RTxC (0) */
+char Board = PA0HZP ; /* what type of SCC card do you use? */
+int Option = 0 ; /* command for extra hardware */
+io_port Special_Port = 0 ; /* port address for special hardware */
+ /* (for EAGLE, PC100, PRIMUS, DRSI) */
+
+ /* ^ never remove the semicolon !! */
+
+
+/* Channel A B Chip */
+/* ============ ======== */
+/* Control ports: */
+
+io_port SCC_ctrl[MAXSCC * 2] = {0x152, 0x150, /* ...one... */
+ 0x156, 0x154, /* ...two... */
+ 0, 0, /* ...three... */
+ 0, 0}; /* ...four... */
+
+
+/* Data ports: */
+
+io_port SCC_data[MAXSCC * 2] = {0x153, 0x151, /* ...one... */
+ 0x157, 0x155, /* ...two... */
+ 0, 0, /* ...three... */
+ 0, 0}; /* ...four... */
+
+
+/* set to '1' if you have and want ESCC chip (8580/85180/85280) support */
+
+/* Chip */
+/* ======== */
+int SCC_Enhanced[MAXSCC] = {0, /* ...one... */
+ 0, /* ...two... */
+ 0, /* ...three... */
+ 0}; /* ...four... */
+
+/* some useful #defines. You might need them or not */
+
+#define VERBOSE_BOOTMSG 1
+#undef SCC_DELAY /* perhaps a 486DX2 is a *bit* too fast */
+#undef SCC_LDELAY /* slow it even a bit more down */
+#undef DONT_CHECK /* don't look if the SCCs you specified are available */
+
+
+/*********** END OF CONFIGURATION PARAMETERS ********************************************/
+
+
+
+
+################################################################################################
+# For Baycom (U)SCC card e.g:
+#
+
+int Nchips = 2 ; /* number of chips */
+io_port Vector_Latch = 0 ; /* addr. of INTACK-Latch (0 for poll mode) */
+int Ivec = 7 ; /* interrupt vector */
+long Clock = 4915200 ; /* frequency of the scc clock */
+char Board = BAYCOM ; /* what type of SCC card do you use? */
+int Option = 0 ; /* command for extra hardware */
+io_port Special_Port = 0 ; /* port address for special hardware */
+ /* (for EAGLE, PC100, PRIMUS, DRSI) */
+
+ /* ^ never remove the semicolon !! */
+
+
+
+/* Channel A B Chip */
+/* ============ ======== */
+/* Control ports: */
+
+io_port SCC_ctrl[MAXSCC * 2] = {0x304, 0x305, /* ...one... */
+ 0x306, 0x307, /* ...two... */
+ 0, 0, /* ...three... */
+ 0, 0}; /* ...four... */
+
+/* Data ports: */
+
+io_port SCC_data[MAXSCC * 2] = {0x300, 0x301, /* ...one... */
+ 0x302, 0x303, /* ...two... */
+ 0, 0, /* ...three... */
+ 0, 0}; /* ...four... */
+
+
+/* set to '1' if you have and want ESCC chip (8580/85180/85280) support */
+
+/* Chip */
+/* ======== */
+int SCC_Enhanced[MAXSCC] = {0, /* ...one... */
+ 0, /* ...two... */
+ 0, /* ...three... */
+ 0}; /* ...four... */
+
+/* some useful #defines. You might need them or not */
+
+#define VERBOSE_BOOTMSG 1
+#undef SCC_DELAY /* perhaps a 486DX2 is a *bit* too fast */
+#undef SCC_LDELAY /* slow it even a bit more down */
+#undef DONT_CHECK /* don't look if the SCCs you specified are available */
+
+After you changed a parameter you have to recompile a new kernel image file.
+
+The channel number ranges from 0 to (2 * Nchips) - 1,
+where Nchips is the number of chips.
+
+The crystal clock is specified as 4.9152 MHz. Other frequencies
+can be used, and this parameter should be adjusted accordingly.
+
+
+You can define your scc type with Board
+
+ SCC type value
+ ---------------------------------
+ PA0HZP SCC card PA0HZP
+ EAGLE card EAGLE
+ PC100 card PC100
+ PRIMUS-PC (DG9BL) card PRIMUS
+ BayCom (U)SCC card BAYCOM
+
+
+NOTE:
+=====
+
+If you only know the parameters for the PE1CHL driver for DOS,
+run gencfg. It will generate the correct port addresses (I hope).
+Its parameters are exactly the same as the ones you use with
+the "attach scc" command in net, except that the string "init" must
+not appear. Example:
+
+gencfg 2 0x150 4 2 0 1 0x168 9 4915200
+
+will print a short form of scc_config.h for the OptoSCC to stdout.
+("short" <=> few comments).
+
+gencfg 2 0x300 2 4 5 -4 0 7 4915200 0x10
+
+does the same for the BayCom USCC card. I my opinion it is much easier
+to edit scc_config.h...
+
+
+1.2 initializing the driver on bootup
+=====================================
+
+
+To setup a number parameters you must run /sbin/sccinit from one
+of your rc.*-files. This has to be done BEFORE the start of
+NET or the ax25attach. Sccinit reads the file /etc/z8530drv.rc
+and sets the MODEM and KISS parameters. A sample file is
+delivered with this package. Change it to your needs:
+
+Each channel definition is divided into three sections. An
+example for /dev/sc1:
+
+# DEVICE
+
+device /dev/sc1 # the device for the following params
+
+# MODEM
+
+speed 1200 # the default baudrate
+clock dpll # clock source:
+ # dpll = normal halfduplex operation
+ # external = MODEM provides own Rx/Tx clock
+ # divider = use fullduplex divider if
+ # installed (1)
+mode nrzi # HDLC encoding mode
+ # nrzi = 1k2 MODEM, G3RUH 9k6 MODEM
+ # nrz = DF9IC 9k6 MODEM
+# KISS (Layer 1)
+
+txdelay 36 # (see chapter 1.4)
+persist 64
+slot 8
+tail 8
+fulldup 0
+wait 12
+min 3
+maxkey 7
+idle 3
+maxdef 120
+group 0
+txoff off
+softdcd on
+slip off
+
+The order WITHIN these sections is unimportant. The order OF these
+sections IS important. The MODEM parameters are set with the first
+recognized KISS paramer...
+
+Please note that you can initialize the board only once after boot.
+You can change all paramters but "mode" and "clock" later with the
+Sccparam program or through KISS. Just to avoid securety holes...
+
+(1) this divider is usually mounted on the SCC-PBC (PA0HZP) or not
+ present at all (BayCom). It feeds back the output of the DPLL
+ (digital pll) as transmit clock. Using this mode without a divider
+ installed will normally result in keying the transceiver until
+ maxkey expires --- of course without sending anything (useful).
+
+
+1.3. Attach commands
+====================
+
+When the linux has startup, the SCC driver has been initialized,
+you can attach the channels in your packet software. This is done
+by open the scc devices by using the attach asy command.
+The SCC-drivers emulates the scc devices as serial asy ports,
+this means e.g. that the baudrate can be set in the attach command.
+
+
+Example Wampes:
+
+#############################################################################################
+# Wampes device attach
+# NOTE: Interfacename and the device must be the same!!
+# Usage: attach asy 0 0 slip|vjslip|ax25ui|ax25i|nrs|kissui <label> 0 <mtu> <speed> [ip_addr]
+#
+attach asy 0 0 kissi sc1 256 256 1200 # Attach SCC channel 1 in 1200 baud
+attach asy 0 0 kissi sc2 256 256 1200 # Attach SCC channel 2 in 1200 baud
+attach asy 0 0 kissui sc3 256 256 38400 # Attach SCC channel 3 in 38400 baud
+attach asy 0 0 kissui sc4 256 256 9600 # Attach SCC channel 4 in 9600 baud
+# ^
+# for WAMPES 921229 use here: ax25
+#
+
+Example JNOS:
+
+############################################
+# JNOS device attach
+#
+#attach asy sc1 0 ax25 sc1 256 256 1200
+#attach asy sc2 0 ax25 sc2 256 256 1200
+#attach asy sc3 0 ax25 sc3 256 256 300
+#attach asy sc4 0 ax25 sc4 256 256 4800
+#
+#
+
+
+It allows AX.25 communication without a TNC. Only a MODEM is
+needed. The parameters have the same meaning as in KISS mode.
+In fact, the AX.25 mode is emulating an extended KISS TNC, so
+the same commands can be used to set the parameters of the
+interface (see below).
+
+To be able to run fullduplex using an SCC in AX.25 mode, an
+external divider must be available, that divides the baudrate
+generator clock available on the TRxC pin by 32, and puts the
+resulting signal on the RTxC pint of the same channel of the SCC.
+Such a divider is not necessary for normal CSMA packet radio
+operation, but interrupt overhead is slightly reduced if you
+still install it.
+
+
+
+1.4. Displaying SCC Parameters:
+===============================
+
+Once a SCC channel has been attached, the parameter settings and
+some statistic information can be shown using the param program:
+
+dl1bke-u:~$ sccstat /dev/sc1
+
+Parameters:
+
+speed : 1200 baud
+txdelay : 36
+persist : 255
+slottime : 0
+txtail : 8
+fulldup : 1
+waittime : 12
+mintime : 3 sec
+maxkeyup : 7 sec
+idletime : 3 sec
+maxdefer : 120 sec
+group : 0x00
+txoff : off
+softdcd : on
+SLIP : off
+
+Status:
+
+HDLC Z8530 Interrupts Queues
+-----------------------------------------------------------------------
+Sent : 273 RxOver : 0 RxInts : 125074 RxQueue : 0
+Received : 1095 TxUnder: 0 TxInts : 4684 TxQueue : 0
+RxErrors : 1591 ExInts : 11776
+KissErrors : 0 SpInts : 1503 NoSpace : 0
+Tx State : idle
+
+Memory allocated:
+
+Total : 1
+RxAlloc: 0
+TxAlloc: 1
+
+
+The status info shown is:
+
+Sent - number of frames transmitted
+Received - number of frames received
+RxErrors - number of receive errors (CRC, ABORT)
+KissErrors - number of KISS errors (should be zero...)
+Tx State - status of the Tx interrupt handler: idle/busy/active/tail (2)
+RxOver - number of receiver overruns
+TxUnder - number of transmitter underruns
+RxInts - number of receiver interrupts
+TxInts - number of transmitter interrupts
+EpInts - number of receiver special condition interrupts
+SpInts - number of external/status interrupts
+RxQueue - number of received packets enqueued for this channel
+TxQueue - number of packets enqueued for Tx
+NoSpace - number of times the receiver buffer pool was found empty
+
+
+An overrun is abnormal. If lots of these occur, the product of
+baudrate and number of interfaces is too high for the processing
+power of you computer. If "Space" errors occur, specify a higher
+number of buffers in the "scc.h" file.
+
+
+1.5 Setting Parameters
+======================
+
+
+The setting of parameters of the emulated KISS TNC is done in the
+same way in the SCC driver. You can change parameters by using
+the command param in NET or NOS
+
+ param <iface> <paramname> <value>
+
+or use the program "sccparam":
+
+ sccparam <device> <paramname> <decimal-|hexadecimal value>
+
+You can change the following parameters:
+
+param : value
+------------------------
+speed : 1200
+txdelay : 36
+persist : 255
+slottime : 0
+txtail : 8
+fulldup : 1
+waittime : 12
+mintime : 3
+maxkeyup : 7
+idletime : 3
+maxdefer : 120
+group : 0x00
+txoff : off
+softdcd : on
+SLIP : off
+
+
+The parameters have the following meaning:
+
+speed:
+ The baudrate on this channel in bits/sec
+
+ Example: sccparam /dev/sc4 speed 9600
+
+txdelay:
+ The delay (in units of 10ms) after keying of the
+ transmitter, until the first byte is sent. This is usually
+ called "TXDELAY" in a TNC. When 0 is specified, the driver
+ will just wait until the CTS signal is asserted. This
+ assumes the presence of a timer or other circuitry in the
+ MODEM and/or transmitter, that asserts CTS when the
+ transmitter is ready for data.
+ A normal value of this parameter is 30-36.
+
+ Example: sccparam /dev/sc1 txd 20
+
+persist:
+ This is the probability that the transmitter will be keyed
+ when the channel is found to be free. It is a value from 0
+ to 255, and the probability is (value+1)/256. The value
+ should be somewhere near 50-60, and should be lowered when
+ the channel is used more heavily.
+
+ Example: sccparam /dev/sc3 persist 20
+
+slottime:
+ This is the time between samples of the channel. It is
+ expressed in units of 10ms. About 200-300 ms (value 20-30)
+ seems to be a good value.
+
+ Example: sccparam /dev/sc1 slot 20
+
+tail:
+ The time the transmitter will remain keyed after the last
+ byte of a packet has been transferred to the SCC. This is
+ necessary because the CRC and a flag still have to leave the
+ SCC before the transmitter is keyed down. The value depends
+ on the baudrate selected. A few character times should be
+ sufficient, e.g. 40ms at 1200 baud. (value 4)
+ The value of this parameter is in 10ms units.
+
+ Example: sccparam /dev/sc3 4
+
+full:
+ The full-duplex mode switch. This can be one of the folowing
+ values:
+
+ 0: The interface will operate in CSMA mode (the normal
+ half-duplex packet radio operation)
+ 1: Fullduplex mode, i.e. the transmitter will be keyed at
+ any time, without checking the received carrier. It
+ will be unkeyed when there are no packets to be sent.
+ 2: Like 1, but the transmitter will remain keyed, also
+ when there are no packets to be sent. Flags will be
+ sent in that case, until a timeout (parameter 10)
+ occurs.
+
+ Example: sccparam /dev/sc1 fulldup off
+
+wait:
+ The initial waittime before any transmit attempt, after the
+ frame has been queue for transmit. This is the length of
+ the first slot in CSMA mode. In fullduplex modes it is
+ set to 0 for maximum performance.
+ The value of this parameter is in 10ms units.
+
+ Example: sccparam /dev/sc2 wait 4
+
+maxkey:
+ The maximal time the transmitter will be keyed to send
+ packets, in seconds. This can be useful on busy CSMA
+ channels, to avoid "getting a bad reputation" when you are
+ generating a lot of traffic. After the specified time has
+ elapsed, no new frame will be started. Instead, the trans-
+ mitter will be switched off for a specified time (parameter
+ min), and then the selected algorithm for keyup will be
+ started again.
+ The value 0 as well as "off" will disable this feature,
+ and allow infinite transmission time.
+
+ Example: sccparam /dev/sc1 maxk 20
+
+min:
+ This is the time the transmitter will be switched off when
+ the maximum transmission time is exceeded.
+
+ Example: sccparam /dev/sc4 min 10
+
+idle
+ This parameter specifies the maximum idle time in fullduplex
+ 2 mode, in seconds. When no frames have been sent for this
+ time, the transmitter will be keyed down. A value of 0 is
+ has same result as the fullduplex mode 1. This parameter
+ can be disabled.
+
+ Example: sccparam /dev/sc3 idle off # transmit forever
+
+maxdefer
+ This is the maximum time (in seconds) to wait for a free channel
+ to send. When this timer expires the transmitter will be keyed
+ IMMEDIATLY. If you love to get trouble with other users you
+ should set this to a very low value ;-)
+
+ Example: sccparam /dev/sc1 maxdefer 240 # 2 minutes
+
+
+txoff:
+ When this parameter has the value 0, the transmission of packets
+ is enable. Otherwise it is disabled.
+
+ Example: sccparam /dev/sc3 txoff on
+
+group:
+ It is possible to build special radio equipment to use more than
+ one frequency on the same bad, e.g. using several receivers and
+ only one transmitter that can be switched between frequencies.
+ Also, you can connect several radios that are active on the same
+ band. In these cases, it is not possible, or not a good idea, to
+ transmit on more than one frequency. The SCC driver provides a
+ method to lock transmitters on different interfaces, using the
+ "param <interface> group <x>" command. This will only work when
+ you are using CSMA mode (parameter full = 0).
+ The number <x> must be 0 if you want no group restrictions, and
+ can be computed as follows to create restricted groups:
+ <x> is the sum of some OCTAL numbers:
+
+ 200 This transmitter will only be keyed when all other
+ transmitters in the group are off.
+ 100 This transmitter will only be keyed when the carrier
+ detect of all other interfaces in the group is off.
+ 0xx A byte that can be used to define different groups.
+ Interfaces are in the same group, when the logical AND
+ between their xx values is nonzero.
+
+ Examples:
+ When 2 interfaces use group 201, their transmitters will never be
+ keyed at the same time.
+ When 2 interfaces use group 101, the transmitters will only key
+ when both channels are clear at the same time. When group 301,
+ the transmitters will not be keyed at the same time.
+
+ Don't forget to convert the octal numbers into decimal before
+ you set the parameter.
+
+ Example: (to be written)
+
+softdcd:
+ use a software dcd instead of the real one... Useful for a very
+ slow squelch.
+
+ Example: sccparam /dev/sc1 soft on
+
+
+slip:
+ use slip encoding instead of kiss
+
+ Example: sccparam /dev/sc2 slip on
+
+
+
+2. Problems
+===========
+
+We are poking around in somebody else's code, so everything may change
+from one patchlevel to another... If the patches fail, try the
+following:
+
+2.1 /linux/drivers/char/Makefile
+================================
+
+Add "scc.o" to the definition of OBJS and "scc.c" to SRCS
+
+
+2.2 /linux/include/linux/tty_driver.h
+=====================================
+
+add the following DEFINE:
+
+#define TTY_DRIVER_TYPE_SCC 0x0005
+
+
+2.3 /linux/drivers/char/tty_io.c
+================================
+
+in tty_init() add the line
+
+ kmem_start=scc_init(kmem_start);
+
+just before "return kmem_start".
+
+2.4 /linux/arch/i386/config.in
+==============================
+
+somewhere in that file add:
+
+ comment 'Z8530 SCC driver for Amateur Packet Radio'
+ bool 'KISS emulator for Z8530 based HDLC cards' CONFIG_SCC y
+ comment ''
+
+
+
+2.5 Other problems
+==================
+
+If you have tx-problems with your BayCom USCC card please check
+the manufacturer of the 8530. SGS chips have a slightly
+different timing. Try Zilog... I have no information if this
+driver works with baudrates higher than 1200 baud. A solution is
+to write to register 8 instead to the data port, but this won't
+work with the ESCC chips *SIGH!*
+
+I got reports that the driver has problems on some 386-based systems.
+(i.e. Amstrad) Those systems have a bogus AT bus timing which will
+lead to delayed answers on interrupts. You can recognize these
+problems by looking at the output of Sccstat for the suspected
+port. See if it shows under- and overruns you own such a system.
+Perhaps it will help if you simplify the scc_isr() function a bit.
+You'll find a slightly faster version in the files scc_isr_intack
+or scc_isr_novec.
+
+
+Delayed processing of received data: This depends on
+
+- the kernel version
+
+- kernel profiling compiled or not
+
+- the rather slow receiver in tty_io.c
+
+- a high interrupt load
+
+- a high load of the maching --- running X, Xmorph, XV and Povray,
+ while compiling the kernel... hmm ... even with 32 MB RAM ... ;-)
+
+- NET's speed itself.
+
+
+Kernel panics (based on excerpts from /linux/README)
+
+
+- if a bug results in a message like
+
+ unable to handle kernel paging request at address C0000010
+ Oops: 0002
+ EIP: 0010:XXXXXXXX
+ eax: xxxxxxxx ebx: xxxxxxxx ecx: xxxxxxxx edx: xxxxxxxx
+ esi: xxxxxxxx edi: xxxxxxxx ebp: xxxxxxxx
+ ds: xxxx es: xxxx fs: xxxx gs: xxxx
+ Pid: xx, process nr: xx
+ xx xx xx xx xx xx xx xx xx xx
+
+ or similar kernel debugging information on your screen or in your
+ system log, please duplicate it *exactly*. The dump may look
+ incomprehensible to you, but it does contain information that may
+ help debugging the problem. The text above the dump is also
+ important: it tells something about why the kernel dumped code (in
+ the above example it's due to a bad kernel pointer)
+
+- in debugging dumps like the above, please look up what the EIP value
+ means. The hex value as such doesn't help me or anybody else very much:
+ it will depend on your particular kernel setup. What you should do is
+ take the hex value from the EIP line (ignore the "0010:"), and look it up
+ in the kernel namelist to see which kernel function contains the offending
+ address.
+
+ To find out the kernel function name, you'll need to
+
+ less /linux/System.map
+
+ This will give you a list of kernel addresses sorted in ascending
+ order, from which it is simple to find the function that contains the
+ offending address. Note that the address given by the kernel
+ debugging messages will not necessarily match exactly with the
+ function addresses (in fact, that is very unlikely), so you can't
+ just 'grep' the list: the list will, however, give you the starting
+ point of each kernel function, so by looking for the function that
+ has a starting address lower than the one you are searching for but
+ is followed by a function with a higher address you will find the one
+ you want. In fact, it may be [IS!] a good idea to include a bit of
+ "context" in your problem report, giving a few lines around the
+ interesting one.
+
+ I included a small program which does this for you. Just call
+
+ grep_eip /linux/System.map address
+
+ for example: grep_eip /linux/System.map 182f98
+
+- alternately, you can use gdb on a running kernel. (read-only; i.e. you
+ cannot change values or set break points.) To do this, first compile the
+ kernel with -g; edit arch/i386/Makefile appropriately, then do a "make
+ clean". You'll also need to enable CONFIG_PROC_FS (via "make config").
+
+ After you've rebooted with the new kernel, do "gdb vmlinux /proc/kcore".
+ You can now use all the usual gdb commands. The command to look up the
+ point where your system crashed is "l *0xXXXXXXXX". (Replace the XXXes
+ with the EIP value.)
+
+ gdb'ing a non-running kernel currently fails because gdb (wrongly)
+ disregards the starting offset for which the kernel is compiled.
+
+
+
+If you can't solve a problem, send me
+
+- a description of the problem,
+- information on your hardware (computer system, scc board, modem)
+- your kernel version
+- the output of sccstat /dev/sc# ("#" is the No. of the channel)
+- the settings of "speed", "clock" and "mode" for that channel
+ in /etc/z8530drv.rc
+- your scc_config.h
+
+
+And always remember:
+The 1.1.* kernel series is for alpha tests -- use at your own risk ;-)
+The 1.2.* series should run reliable. This driver perhaps NOT!
+
+------------
+
+Example scc_config.h
+
+#include <linux/scc.h>
+
+/********* CONFIGURATION PARAMATERES; PLEASE CHANGE THIS TO YOUR OWN SITUATION **********/
+
+/* SCC hardware parameters */
+
+/* use the following board types:
+ *
+ * PA0HZP OptoSCC (PA0HZP)
+ * EAGLE EAGLE
+ * PC100 PC100
+ * PRIMUS PRIMUS-PC (DG9BL)
+ * DRSI DRSI PC*Packet
+ * BAYCOM BayCom (U)SCC
+ *
+ */
+
+int Nchips = 2 ; /* number of chips */
+io_port Vector_Latch = 0 ; /* addr. of INTACK-Latch (0 for poll mode) */
+int Ivec = 7 ; /* interrupt vector */
+long Clock = 4915200 ; /* frequency of the scc clock */
+char Board = BAYCOM ; /* what type of SCC card do you use? */
+int Option = 0 ; /* command for extra hardware */
+io_port Special_Port = 0 ; /* port address for special hardware */
+ /* (for EAGLE, PC100, PRIMUS, DRSI) */
+
+ /* ^ never remove the semicolon !! */
+
+
+
+/* Channel A B Chip */
+/* ============ ======== */
+/* Control ports: */
+
+io_port SCC_ctrl[MAXSCC * 2] = {0x304, 0x305, /* ...one... */
+ 0x306, 0x307, /* ...two... */
+ 0, 0, /* ...three... */
+ 0, 0}; /* ...four... */
+
+/* Data ports: */
+
+io_port SCC_data[MAXSCC * 2] = {0x300, 0x301, /* ...one... */
+ 0x302, 0x303, /* ...two... */
+ 0, 0, /* ...three... */
+ 0, 0}; /* ...four... */
+
+
+/* set to '1' if you have and want ESCC chip (8580/85180/85280) support */
+
+/* Chip */
+/* ======== */
+int SCC_Enhanced[MAXSCC] = {0, /* ...one... */
+ 0, /* ...two... */
+ 0, /* ...three... */
+ 0}; /* ...four... */
+
+/* some useful #defines. You might need them or not */
+
+#define VERBOSE_BOOTMSG 1
+#undef SCC_DELAY /* perhaps a 486DX2 is a *bit* too fast */
+#undef SCC_LDELAY /* slow it even a bit more down */
+#undef DONT_CHECK /* don't look if the SCCs you specified are available */
+
+
+/* The external clocking, nrz and fullduplex divider configuration is gone */
+/* you can set these parameters in /etc/z8530drv.rc and initialize the */
+/* driver with sccinit */
+
+---------
+
+I still can't test the DRSI board, but this configuration derived from
+the PE1CHL SCC driver configuration should work:
+
+An example of scc_config.h for
+
+One DRSI board installed:
+=========================
+
+/* gencfg 1 0x300 0x10 2 0 1 0 7 4915200 */
+
+/* file generated by $Id: gencfg.c,v 1.2 1994/11/29 21:42:24 JReuter Exp JReuter $ */
+
+#include <linux/scc.h>
+
+int Nchips = 1;
+io_port Vector_Latch = 0x0;
+int Ivec = 7;
+long Clock = 4915200;
+char Board = PA0HZP;
+int Option = 0;
+io_port Special_Port = 0x0;
+
+io_port SCC_ctrl[MAXSCC * 2] =
+{0x302, 0x300, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+
+io_port SCC_data[MAXSCC * 2] =
+{0x303, 0x301, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+
+/* set to '1' if you have and want ESCC chip (8580/85180/85280) support */
+
+/* Chip */
+/* ======== */
+int SCC_Enhanced[MAXSCC] = {0, /* ...one... */
+ 0, /* ...two... */
+ 0, /* ...three... */
+ 0}; /* ...four... */
+
+#define VERBOSE_BOOTMSG 1
+#undef SCC_DELAY /* perhaps a 486DX2 is a *bit* too fast */
+#undef SCC_LDELAY /* slow it even a bit more down */
+#undef DONT_CHECK /* don't look if the SCCs you specified are available */
+
+
+
+Two boards installed:
+=====================
+
+/* file generated by $Id: gencfg.c,v 1.2 1994/11/29 21:42:24 JReuter Exp JReuter $ */
+
+#include <linux/scc.h>
+
+int Nchips = 2;
+io_port Vector_Latch = 0x0;
+int Ivec = 7;
+long Clock = 4915200;
+char Board = PA0HZP;
+int Option = 0;
+io_port Special_Port = 0x0;
+
+io_port SCC_ctrl[MAXSCC * 2] =
+{0x302, 0x300, 0x312, 0x310, 0x0, 0x0, 0x0, 0x0};
+
+io_port SCC_data[MAXSCC * 2] =
+{0x303, 0x301, 0x313, 0x311, 0x0, 0x0, 0x0, 0x0};
+
+/* set to '1' if you have and want ESCC chip (8580/85180/85280) support */
+
+/* Chip */
+/* ======== */
+int SCC_Enhanced[MAXSCC] = {0, /* ...one... */
+ 0, /* ...two... */
+ 0, /* ...three... */
+ 0}; /* ...four... */
+
+#define VERBOSE_BOOTMSG 1
+#undef SCC_DELAY /* perhaps a 486DX2 is a *bit* too fast */
+#undef SCC_LDELAY /* slow it even a bit more down */
+#undef DONT_CHECK /* don't look if the SCCs you specified are available */
+
+
+
+
+
+*****************
+
+You m u s t use "clock dpll" in /etc/z8530drv.rc for operation,
+the on-board baudrate generator is not supported.
+
+*****************
+
+
+(mni tnx to Mike Bilow)
+
diff --git a/drivers/char/atixlmouse.c b/drivers/char/atixlmouse.c
index 649ce6d38..e88597718 100644
--- a/drivers/char/atixlmouse.c
+++ b/drivers/char/atixlmouse.c
@@ -63,7 +63,7 @@ static struct mouse_status {
struct wait_queue *wait;
} mouse;
-void mouse_interrupt(int unused)
+void mouse_interrupt(int irq, struct pt_regs * regs)
{
char dx, dy, buttons;
diff --git a/drivers/char/busmouse.c b/drivers/char/busmouse.c
index 7c2ce9910..7bbec7e40 100644
--- a/drivers/char/busmouse.c
+++ b/drivers/char/busmouse.c
@@ -28,6 +28,7 @@
#include <linux/busmouse.h>
#include <linux/signal.h>
#include <linux/errno.h>
+#include <linux/mm.h>
#include <asm/io.h>
#include <asm/segment.h>
@@ -43,7 +44,7 @@ void bmouse_setup(char *str, int *ints)
mouse_irq=ints[1];
}
-static void mouse_interrupt(int unused)
+static void mouse_interrupt(int irq, struct pt_regs *regs)
{
char dx, dy;
unsigned char buttons;
diff --git a/drivers/char/console.c b/drivers/char/console.c
index 1c5d4eb90..027551dfa 100644
--- a/drivers/char/console.c
+++ b/drivers/char/console.c
@@ -2,6 +2,7 @@
* linux/drivers/char/console.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
+ * MIPS support by Ralf Baechle and Andreas Busse
*/
/*
@@ -22,23 +23,23 @@
* 'void console_print(const char * b)'
* 'void update_screen(int new_console)'
*
- * 'void blank_screen(void)'
- * 'void unblank_screen(void)'
+ * 'void do_blank_screen(int)'
+ * 'void do_unblank_screen(void)'
* 'void poke_blanked_console(void)'
+ *
+ * 'unsigned short *screen_pos(int currcons, int w_offset, int viewed)'
+ * 'void complement_pos(int currcons, int offset)'
+ * 'void invert_screen(int currcons, int offset, int count, int shift)'
+ *
* 'void scrollback(int lines)'
* 'void scrollfront(int lines)'
- * 'int do_screendump(int arg, int mode)'
*
* 'int con_get_font(char *)'
- * 'int con_set_font(char *)'
- * 'int con_get_trans(char *)'
- * 'int con_set_trans(char *)'
+ * 'int con_set_font(char *)'
*
- * 'int set_selection(const int arg)'
- * 'int paste_selection(struct tty_struct *tty)'
- * 'int sel_loadlut(const int arg)'
+ * 'void mouse_report(struct tty_struct * tty, int butt, int mrx, int mry)'
* 'int mouse_reporting(void)'
- *
+ *
* Hopefully this will be a rather complete VT102 implementation.
*
* Beeping thanks to John T Kohl.
@@ -69,6 +70,13 @@
#define BLANK 0x0020
#define CAN_LOAD_EGA_FONTS /* undefine if the user must not do this */
+/* A bitmap for codes <32. A bit of 1 indicates that the code
+ * corresponding to that bit number invokes some special action
+ * (such as cursor movement) and should not be displayed as a
+ * glyph unless the disp_ctrl mode is explicitly enabled.
+ */
+#define CTRL_ACTION 0xd00ff80
+
/*
* NOTE!!! We sometimes disable and enable interrupts for a short while
* (to put a word in video IO), but this will work even for keyboard
@@ -88,7 +96,19 @@
#include <linux/kd.h>
#include <linux/malloc.h>
#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#if defined(CONFIG_ACER_PICA_61) || defined(CONFIG_MIPS_MAGNUM_4000)
+#include <asm/bootinfo.h>
+/*
+ * The video control ports are mapped at virtual address
+ * 0xe0200000 for the onboard S3 card
+ */
+#if 0
+#define PORT_BASE 0xe0200000
+#endif
+#endif
#include <asm/io.h>
#include <asm/slots.h>
#include <asm/system.h>
@@ -97,6 +117,9 @@
#include "kbd_kern.h"
#include "vt_kern.h"
+#include "consolemap.h"
+#include "selection.h"
+
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
@@ -108,27 +131,6 @@ static struct tty_struct *console_table[MAX_NR_CONSOLES];
static struct termios *console_termios[MAX_NR_CONSOLES];
static struct termios *console_termios_locked[MAX_NR_CONSOLES];
-#ifdef CONFIG_SELECTION
-#include <linux/ctype.h>
-
-/* Routines for selection control. */
-int set_selection(const int arg, struct tty_struct *tty);
-int paste_selection(struct tty_struct *tty);
-static void clear_selection(void);
-static void highlight_pointer(const int currcons, const int where);
-
-/* Variables for selection control. */
-#define SEL_BUFFER_SIZE 4096
-static int sel_cons = 0;
-static int sel_start = -1;
-static int sel_end;
-static char sel_buffer[SEL_BUFFER_SIZE] = { '\0' };
-#endif /* CONFIG_SELECTION */
-
-#ifdef __mips__
-static unsigned int dummy;
-#endif
-
#define NPAR 16
static void con_setsize(unsigned long rows, unsigned long cols);
@@ -137,6 +139,9 @@ static void vc_init(unsigned int console, unsigned long rows, unsigned long cols
static void get_scrmem(int currcons);
static void set_scrmem(int currcons, long offset);
static void set_origin(int currcons);
+static void blank_screen(void);
+static void unblank_screen(void);
+void poke_blanked_console(void);
static void gotoxy(int currcons, int new_x, int new_y);
static void save_cur(int currcons);
static inline void set_cursor(int currcons);
@@ -144,6 +149,8 @@ static void reset_terminal(int currcons, int do_clear);
extern void reset_vc(unsigned int new_console);
extern void vt_init(void);
extern void register_console(void (*proc)(const char *));
+extern void vesa_blank(void);
+extern void vesa_unblank(void);
extern void compute_shiftstate(void);
extern int conv_uni_to_pc(unsigned long ucs);
@@ -152,11 +159,13 @@ static unsigned char video_type; /* Type of display being used */
static unsigned long video_mem_base; /* Base of video memory */
static unsigned long video_mem_term; /* End of video memory */
static unsigned char video_page; /* Initial video page (unused) */
-static unsigned short video_port_reg; /* Video register select port */
-static unsigned short video_port_val; /* Video register value port */
-static unsigned long video_num_columns; /* Number of text columns */
-static unsigned long video_num_lines; /* Number of text lines */
-static unsigned long video_size_row;
+ /* these two also used in vesa_blank.c */
+ unsigned short video_port_reg; /* Video register select port */
+ unsigned short video_port_val; /* Video register value port */
+ /* these three also used in selection.c */
+ unsigned long video_num_columns; /* Number of text columns */
+ unsigned long video_num_lines; /* Number of text lines */
+ unsigned long video_size_row;
static unsigned long video_screen_size;
static int can_do_color = 0;
static int printable = 0; /* Is console ready for printing? */
@@ -218,10 +227,10 @@ struct vc_data {
unsigned long vc_utf_char;
unsigned long vc_tab_stop[5]; /* Tab stops. 160 columns. */
unsigned char * vc_translate;
- unsigned char * vc_G0_charset;
- unsigned char * vc_G1_charset;
- unsigned char * vc_saved_G0;
- unsigned char * vc_saved_G1;
+ unsigned char vc_G0_charset;
+ unsigned char vc_G1_charset;
+ unsigned char vc_saved_G0;
+ unsigned char vc_saved_G1;
/* additional information is in vt_kern.h */
};
@@ -233,6 +242,39 @@ static struct vc {
would be that vc_cons etc can no longer be static */
} vc_cons [MAX_NR_CONSOLES];
+/*
+ * Fontsize for graphic console
+ */
+#define FONTSIZE_X 8 /* 8 pixels wide */
+#define FONTSIZE_Y 16 /* 16 pixels high */
+
+#ifdef CONFIG_MIPS_MAGNUM_4000
+extern unsigned char font[];
+static unsigned video_res_x;
+static int graph_mode = 0; /* true for graphic consoles */
+
+/*
+ * print a character to a graphics console.
+ * FIXME: slooooow
+ */
+static inline void
+gscr_pchar(unsigned char c, unsigned char *ptr)
+{
+ unsigned char *faddr;
+ int i,j;
+
+ faddr = font + (c<<7); /* 128 bytes/char */
+ for (i=0; i<FONTSIZE_Y; i++) {
+ for (j=0; j<FONTSIZE_X; j++) {
+ *(ptr+j) = *(faddr++);
+ }
+ ptr += video_res_x;
+ }
+}
+#else
+const int graph_mode = 0;
+#endif /* CONFIG_MIPS_MAGNUM_4000 */
+
#define screenbuf_size (vc_cons[currcons].d->vc_screenbuf_size)
#define origin (vc_cons[currcons].d->vc_origin)
#define scr_end (vc_cons[currcons].d->vc_scr_end)
@@ -293,27 +335,25 @@ static struct vc {
#define vcmode (vt_cons[currcons]->vc_mode)
#define structsize (sizeof(struct vc_data) + sizeof(struct vt_struct))
-static void * memsetw(void * s, unsigned short c, unsigned int count)
-{
-#if defined (__i386__)
-__asm__("cld\n\t"
- "rep\n\t"
- "stosw"
- : /* no output */
- :"a" (c),"D" (s),"c" (count/2)
- :"cx","di");
-#elif defined (__mips_)
-__asm__ __volatile__(
- ".set\tnoreorder\n"
- "1:sh\t%2,(%0)\n\t"
- "subu\t%1,%1,1\n\t"
- "bne\t$0,%1,1b\n\t"
- "addiu\t%0,%0,2\n\t"
- ".set\treorder"
- :"=r" (dummy),"=r" (dummy)
- :"r" (c),"0" (s),"1" (count/2));
-#endif
-return s;
+static void memsetw(void * s, unsigned short c, unsigned int count)
+{
+ unsigned short * addr = (unsigned short *) s;
+
+ count /= 2;
+ while (count) {
+ count--;
+ scr_writew(c, addr++);
+ }
+}
+
+static inline void memcpyw(unsigned short *to, unsigned short *from,
+ unsigned int count)
+{
+ count /= 2;
+ while (count) {
+ count--;
+ scr_writew(scr_readw(from++), to++);
+ }
}
int vc_cons_allocated(unsigned int i)
@@ -422,7 +462,7 @@ int vc_resize(unsigned long lines, unsigned long cols)
ol += (oll - ll) * osr;
while (ol < scr_end) {
- memcpy((void *) nl, (void *) ol, rlth);
+ memcpyw((unsigned short *) nl, (unsigned short *) ol, rlth);
if (rrem)
memsetw((void *)(nl + rlth), video_erase_char, rrem);
ol += osr;
@@ -482,81 +522,6 @@ void vc_disallocate(unsigned int currcons)
#define VT100ID "\033[?1;2c"
#define VT102ID "\033[?6c"
-static unsigned char * translations[] = {
-/* 8-bit Latin-1 mapped to the PC character set: '\0' means non-printable */
-(unsigned char *)
- "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- "\0\0\0\0\0\0\0\0\0\0\376\0\0\0\0\0"
- " !\"#$%&'()*+,-./0123456789:;<=>?"
- "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
- "`abcdefghijklmnopqrstuvwxyz{|}~\0"
- "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- "\377\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376"
- "\370\361\375\376\376\346\024\371\376\376\247\257\254\253\376\250"
- "\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376"
- "\376\245\376\376\376\376\231\376\350\376\376\376\232\376\376\341"
- "\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213"
- "\376\244\225\242\223\376\224\366\355\227\243\226\201\376\376\230",
-/* vt100 graphics */
-(unsigned char *)
- "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- "\0\0\0\0\0\0\0\0\0\0\376\0\0\0\0\0"
- " !\"#$%&'()*+,-./0123456789:;<=>?"
- "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^ "
- "\004\261\007\007\007\007\370\361\007\007\331\277\332\300\305\304"
- "\304\304\137\137\303\264\301\302\263\363\362\343\330\234\007\0"
- "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
- "\377\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376"
- "\370\361\375\376\376\346\024\371\376\376\247\257\254\253\376\250"
- "\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376"
- "\376\245\376\376\376\376\231\376\376\376\376\376\232\376\376\341"
- "\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213"
- "\376\244\225\242\223\376\224\366\376\227\243\226\201\376\376\230",
-/* IBM graphics: minimal translations (BS, CR, LF, LL, SO, SI and ESC) */
-(unsigned char *)
- "\000\001\002\003\004\005\006\007\000\011\000\013\000\000\000\000"
- "\020\021\022\023\024\025\026\027\030\031\032\000\034\035\036\037"
- "\040\041\042\043\044\045\046\047\050\051\052\053\054\055\056\057"
- "\060\061\062\063\064\065\066\067\070\071\072\073\074\075\076\077"
- "\100\101\102\103\104\105\106\107\110\111\112\113\114\115\116\117"
- "\120\121\122\123\124\125\126\127\130\131\132\133\134\135\136\137"
- "\140\141\142\143\144\145\146\147\150\151\152\153\154\155\156\157"
- "\160\161\162\163\164\165\166\167\170\171\172\173\174\175\176\177"
- "\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217"
- "\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237"
- "\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257"
- "\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277"
- "\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317"
- "\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"
- "\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357"
- "\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377",
- /* USER: customizable mappings, initialized as the previous one (IBM) */
-(unsigned char *)
- "\000\001\002\003\004\005\006\007\010\011\000\013\000\000\016\017"
- "\020\021\022\023\024\025\026\027\030\031\032\000\034\035\036\037"
- "\040\041\042\043\044\045\046\047\050\051\052\053\054\055\056\057"
- "\060\061\062\063\064\065\066\067\070\071\072\073\074\075\076\077"
- "\100\101\102\103\104\105\106\107\110\111\112\113\114\115\116\117"
- "\120\121\122\123\124\125\126\127\130\131\132\133\134\135\136\137"
- "\140\141\142\143\144\145\146\147\150\151\152\153\154\155\156\157"
- "\160\161\162\163\164\165\166\167\170\171\172\173\174\175\176\177"
- "\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217"
- "\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237"
- "\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257"
- "\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277"
- "\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317"
- "\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"
- "\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357"
- "\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377"
-};
-
-#define NORM_TRANS (translations[0])
-#define GRAF_TRANS (translations[1])
-#define NULL_TRANS (translations[2])
-#define USER_TRANS (translations[3])
-
static unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7,
8,12,10,14, 9,13,11,15 };
@@ -588,7 +553,10 @@ static void gotoxy(int currcons, int new_x, int new_y)
y = max_y - 1;
else
y = new_y;
- pos = origin + y*video_size_row + (x<<1);
+ if (graph_mode)
+ pos = origin + y*video_size_row + x*FONTSIZE_X;
+ else
+ pos = origin + y*video_size_row + (x<<1);
need_wrap = 0;
}
@@ -601,9 +569,9 @@ static unsigned short __origin; /* offset of currently displayed screen */
static inline void __set_origin(unsigned short offset)
{
unsigned long flags;
-#ifdef CONFIG_SELECTION
+
clear_selection();
-#endif /* CONFIG_SELECTION */
+
save_flags(flags); cli();
__origin = offset;
outb_p(12, video_port_reg);
@@ -681,7 +649,7 @@ static inline void set_cursor(int currcons)
static void scrup(int currcons, unsigned int t, unsigned int b)
{
int hardscroll = 1;
-
+
if (b > video_num_lines || t >= b)
return;
if (video_type != VIDEO_TYPE_EGAC && video_type != VIDEO_TYPE_EGAM)
@@ -693,157 +661,87 @@ static void scrup(int currcons, unsigned int t, unsigned int b)
pos += video_size_row;
scr_end += video_size_row;
if (scr_end > video_mem_end) {
-#if defined (__i386__)
- __asm__("cld\n\t"
- "rep\n\t"
- "movsl\n\t"
- "movl _video_num_columns,%1\n\t"
- "rep\n\t"
- "stosw"
- : /* no output */
- :"a" (video_erase_char),
- "c" ((video_num_lines-1)*video_num_columns>>1),
- "D" (video_mem_start),
- "S" (origin)
- :"cx","di","si");
-#elif defined (__mips__)
- __asm__ __volatile__(
- ".set\tnoreorder\n\t"
- ".set\tnoat\n"
- "1:\tlwu\t$1,(%2)\n\t"
- "subu\t%0,%0,1\n\t"
- "addiu\t%2,%2,4\n\t"
- "sw\t$1,(%1)\n\t"
- "bne\t$0,%0,1b\n\t"
- "addiu\t%1,%1,4\n"
- "1:\tsh\t%4,(%1)\n\t"
- "subu\t%3,%3,1\n\t"
- "bne\t$0,%3,1b\n\t"
- "addiu\t%1,%1,2\n\t"
- ".set\tat\n\t"
- ".set\treorder"
- :"=r" (dummy),"=r" (dummy),
- "=r" (dummy),"=r" (dummy)
- :"r" (video_erase_char),
- "0" ((video_num_lines-1)*video_num_columns>>1),
- "1" (video_mem_start),
- "2" (origin),
- "3" (video_num_columns)
- :"$1");
-#endif
+ unsigned short * d = (unsigned short *) video_mem_start;
+ unsigned short * s = (unsigned short *) origin;
+ unsigned int count;
+
+ count = (video_num_lines-1)*video_num_columns;
+ while (count) {
+ count--;
+ scr_writew(scr_readw(s++),d++);
+ }
+ count = video_num_columns;
+ while (count) {
+ count--;
+ scr_writew(video_erase_char, d++);
+ }
scr_end -= origin-video_mem_start;
pos -= origin-video_mem_start;
origin = video_mem_start;
has_scrolled = 1;
- } else {
-#if defined (__i386__)
- __asm__("cld\n\t"
- "rep\n\t"
- "stosw"
- : /* no output */
- :"a" (video_erase_char),
- "c" (video_num_columns),
- "D" (scr_end-video_size_row)
- :"cx","di");
-#elif defined (__mips__)
- __asm__ __volatile__(
- ".set\tnoreorder\n"
- "1:sh\t%2,(%1)\n\t"
- "subu\t%0,%0,1\n\t"
- "bne\t$0,%0,1b\n\t"
- "addiu\t%1,%1,2\n\t"
- ".set\treorder\n\t"
- :"=r" (dummy),"=r" (dummy)
- :"r" (video_erase_char),
- "0" (video_num_columns),
- "1" (scr_end-video_size_row));
-#endif
+ } else {
+ unsigned short * d;
+ unsigned int count;
+
+ d = (unsigned short *) (scr_end - video_size_row);
+ count = video_num_columns;
+ while (count) {
+ count--;
+ scr_writew(video_erase_char, d++);
+ }
}
set_origin(currcons);
- } else {
-#if defined (__i386__)
- __asm__("cld\n\t"
- "rep\n\t"
- "movsl\n\t"
- "movl _video_num_columns,%%ecx\n\t"
- "rep\n\t"
- "stosw"
- : /* no output */
- :"a" (video_erase_char),
- "c" ((b-t-1)*video_num_columns>>1),
- "D" (origin+video_size_row*t),
- "S" (origin+video_size_row*(t+1))
- :"cx","di","si");
-#elif defined (__mips__)
- __asm__ __volatile__(
- ".set\tnoreorder\n\t"
- ".set\tnoat\n"
- "1:\tlwu\t$1,(%2)\n\t"
- "subu\t%0,%0,1\n\t"
- "sw\t$1,(%1)\n\t"
- "addiu\t%2,%2,4\n\t"
- "bne\t$0,%1,1b\n\t"
- "addiu\t%1,%1,4\n"
- "1:\tsh\t%4,(%1)\n\t"
- "subu\t%3,%3,1\n\t"
- "bne\t$0,%3,1b\n\t"
- "addiu\t%1,%1,2\n\t"
- ".set\tat\n\t"
- ".set\treorder\n\t"
- :"=r" (dummy),"=r" (dummy),"=r" (dummy),"=r" (dummy)
- :"r" (video_erase_char),
- "0" ((b-t-1)*video_num_columns>>1),
- "1" (origin+video_size_row*t),
- "2" (origin+video_size_row*(t+1)),
- "3" (video_num_columns)
- :"$1");
-#endif
+ } else if (graph_mode) {
+ unsigned int * d = (unsigned int *) (origin+video_size_row*t);
+ unsigned int * s = (unsigned int *) (origin+video_size_row*(t+1));
+ unsigned int count = (b-t-1) * (video_size_row >> 2);
+
+ while (count) {
+ count--;
+ *(d++) = *(s++);
+ }
+ count = video_size_row >> 2;
+ while (count) {
+ count--;
+ *(d++) = (video_erase_char << 16) | video_erase_char;
+ }
+ }
+ else {
+ unsigned short * d = (unsigned short *) (origin+video_size_row*t);
+ unsigned short * s = (unsigned short *) (origin+video_size_row*(t+1));
+ unsigned int count = (b-t-1) * video_num_columns;
+
+ while (count) {
+ count--;
+ scr_writew(scr_readw(s++), d++);
+ }
+ count = video_num_columns;
+ while (count) {
+ count--;
+ scr_writew(video_erase_char, d++);
+ }
}
}
-
+
static void scrdown(int currcons, unsigned int t, unsigned int b)
{
+ unsigned short *d, *s;
+ unsigned int count;
+
if (b > video_num_lines || t >= b)
return;
-#if defined (__i386__)
- __asm__("std\n\t"
- "rep\n\t"
- "movsl\n\t"
- "addl $2,%%edi\n\t" /* %edi has been decremented by 4 */
- "movl _video_num_columns,%%ecx\n\t"
- "rep\n\t"
- "stosw\n\t"
- "cld"
- : /* no output */
- :"a" (video_erase_char),
- "c" ((b-t-1)*video_num_columns>>1),
- "D" (origin+video_size_row*b-4),
- "S" (origin+video_size_row*(b-1)-4)
- :"ax","cx","di","si");
-#elif defined (__mips__)
- __asm__ __volatile__(
- ".set\tnoreorder\n\t"
- ".set\tnoat\n"
- "1:\tlw\t$1,(%2)\n\t"
- "subu\t%0,%0,1\n\t"
- "sw\t$1,(%1)\n\t"
- "subu\t%2,%2,4\n\t"
- "bne\t$0,%0,1b\n\t"
- "subu\t%1,%1,4\n"
- "1:\tsh\t%4,(%1)\n\t"
- "subu\t%3,%3,1\n\t"
- "bne\t$0,%3,1b\n\t"
- "subu\t%1,%1,2\n\t"
- ".set\tat\n\t"
- ".set\treorder"
- :"=r" (dummy),"=r" (dummy),"=r" (dummy),"=r" (dummy)
- :"r" (video_erase_char),
- "0" ((b-t-1)*video_num_columns>>1),
- "1" (origin+video_size_row*b-4),
- "2" (origin+video_size_row*(b-1)-4),
- "3" (video_num_columns)
- :"$1");
-#endif
+ d = (unsigned short *) (origin+video_size_row*b);
+ s = (unsigned short *) (origin+video_size_row*(b-1));
+ count = (b-t-1)*video_num_columns;
+ while (count) {
+ count--;
+ scr_writew(scr_readw(--s), --d);
+ }
+ count = video_num_columns;
+ while (count) {
+ count--;
+ scr_writew(video_erase_char, --d);
+ }
has_scrolled = 1;
}
@@ -877,14 +775,20 @@ static void ri(int currcons)
static inline void cr(int currcons)
{
- pos -= x<<1;
+ if (graph_mode)
+ pos -= x*FONTSIZE_X;
+ else
+ pos -= x<<1;
need_wrap = x = 0;
}
static inline void bs(int currcons)
{
if (x) {
- pos -= 2;
+ if (graph_mode)
+ pos -= FONTSIZE_X;
+ else
+ pos -= 2;
x--;
need_wrap = 0;
}
@@ -898,122 +802,77 @@ static inline void del(int currcons)
static void csi_J(int currcons, int vpar)
{
unsigned long count;
- unsigned long start;
+ unsigned short * start;
switch (vpar) {
case 0: /* erase from cursor to end of display */
count = (scr_end-pos)>>1;
- start = pos;
+ start = (unsigned short *) pos;
break;
case 1: /* erase from start to cursor */
count = ((pos-origin)>>1)+1;
- start = origin;
+ start = (unsigned short *) origin;
break;
case 2: /* erase whole display */
count = video_num_columns * video_num_lines;
- start = origin;
+ start = (unsigned short *) origin;
break;
default:
return;
}
-#if defined (__i386__)
- __asm__("cld\n\t"
- "rep\n\t"
- "stosw\n\t"
- : /* no output */
- :"c" (count),
- "D" (start),"a" (video_erase_char)
- :"cx","di");
-#elif defined (__mips__)
- __asm__ __volatile__(
- ".set\tnoreorder\n"
- "1:\tsh\t%4,(%1)\n\t"
- "subu\t%0,%0,1\n\t"
- "bne\t$0,%0,1b\n\t"
- "addiu\t%1,%1,2\n\t"
- ".set\treorder"
- :"=r" (dummy),"=r" (dummy)
- :"0" (count),"1" (start),"r" (video_erase_char));
-#endif
+ while (count) {
+ count--;
+ scr_writew(video_erase_char, start++);
+ }
need_wrap = 0;
}
static void csi_K(int currcons, int vpar)
{
- long count;
- long start;
+ unsigned long count;
+ unsigned short * start;
switch (vpar) {
case 0: /* erase from cursor to end of line */
count = video_num_columns-x;
- start = pos;
+ start = (unsigned short *) pos;
break;
case 1: /* erase from start of line to cursor */
- start = pos - (x<<1);
+ start = (unsigned short *) (pos - (x<<1));
count = x+1;
break;
case 2: /* erase whole line */
- start = pos - (x<<1);
+ start = (unsigned short *) (pos - (x<<1));
count = video_num_columns;
break;
default:
return;
}
-#if defined (__i386__)
- __asm__("cld\n\t"
- "rep\n\t"
- "stosw\n\t"
- : /* no output */
- :"c" (count),
- "D" (start),"a" (video_erase_char)
- :"cx","di");
-#elif defined (__mips__)
- __asm__ __volatile__(
- ".set\tnoreorder\n"
- "1:\tsh\t%2,(%1)\n\t"
- "subu\t%0,%0,1\n\t"
- "bne\t$0,%0,1b\n\t"
- "addiu\t%1,%1,2\n\t"
- ".set\treorder\n\t"
- :"=r" (dummy),"=r" (dummy)
- :"0" (count),"1" (start),"r" (video_erase_char));
-#endif
+ while (count) {
+ count--;
+ scr_writew(video_erase_char, start++);
+ }
need_wrap = 0;
}
-
+
static void csi_X(int currcons, int vpar) /* erase the following vpar positions */
{ /* not vt100? */
- long count;
- long start;
+ unsigned long count;
+ unsigned short * start;
if (!vpar)
vpar++;
- start=pos;
- count=(vpar > video_num_columns-x) ? (video_num_columns-x) : vpar;
-
-#if defined (__i386__)
- __asm__("cld\n\t"
- "rep\n\t"
- "stosw\n\t"
- : /* no output */
- :"c" (count),
- "D" (start),"a" (video_erase_char)
- :"cx","di");
-#elif defined (__mips__)
- __asm__ __volatile__(
- ".set\tnoreorder\n"
- "1:\tsh\t%4,(%1)\n\t"
- "subu\t%0,%0,1\n\t"
- "bne\t$0,%0,1b\n\t"
- "addiu\t%1,%1,2\n\t"
- ".set\treorder"
- :"=r" (dummy),"=r" (dummy)
- :"0" (count),"1" (start),"r" (video_erase_char));
-#endif
+ start = (unsigned short *) pos;
+ count = (vpar > video_num_columns-x) ? (video_num_columns-x) : vpar;
+
+ while (count) {
+ count--;
+ scr_writew(video_erase_char, start++);
+ }
need_wrap = 0;
}
-
+
static void update_attr(int currcons)
{
attr = color;
@@ -1024,7 +883,7 @@ static void update_attr(int currcons)
attr = (attr & 0xf0) | halfcolor;
}
if (reverse ^ decscnm)
- attr = (attr & 0x88) | (((attr >> 4) | (attr << 4)) & 0x77);
+ attr = reverse_video_char(attr);
if (blink)
attr ^= 0x80;
if (intensity == 2)
@@ -1036,9 +895,13 @@ static void update_attr(int currcons)
attr = (attr & 0xf0) | 0x08;
}
if (decscnm)
- video_erase_char = (((color & 0x88) | (((color >> 4) | (color << 4)) & 0x77)) << 8) | ' ';
+ video_erase_char = (reverse_video_char(color) << 8) | ' ';
else
video_erase_char = (color << 8) | ' ';
+#ifdef CONFIG_MIPS_MAGNUM_4000
+ if (boot_info.machtype == MACH_MIPS_MAGNUM_4000)
+ video_erase_char = 0x0404;
+#endif
}
static void default_attr(int currcons)
@@ -1079,7 +942,7 @@ static void csi_m(int currcons)
* control chars if defined, don't set
* bit 8 on output.
*/
- translate = (charset == 0
+ translate = set_translate(charset == 0
? G0_charset
: G1_charset);
disp_ctrl = 0;
@@ -1089,7 +952,7 @@ static void csi_m(int currcons)
* Select first alternate font, let's
* chars < 32 be displayed as ROM chars.
*/
- translate = NULL_TRANS;
+ translate = set_translate(NULL_MAP);
disp_ctrl = 1;
toggle_meta = 0;
break;
@@ -1097,7 +960,7 @@ static void csi_m(int currcons)
* Select second alternate font, toggle
* high bit before displaying as ROM char.
*/
- translate = NULL_TRANS;
+ translate = set_translate(NULL_MAP);
disp_ctrl = 1;
toggle_meta = 1;
break;
@@ -1162,9 +1025,17 @@ static void cursor_report(int currcons, struct tty_struct * tty)
respond_string(buf, tty);
}
-#ifdef CONFIG_SELECTION
-static void mouse_report(int currcons, struct tty_struct * tty,
- int butt, int mrx, int mry)
+static inline void status_report(struct tty_struct * tty)
+{
+ respond_string("\033[0n", tty); /* Terminal ok */
+}
+
+static inline void respond_ID(struct tty_struct * tty)
+{
+ respond_string(VT102ID, tty);
+}
+
+void mouse_report(struct tty_struct * tty, int butt, int mrx, int mry)
{
char buf[8];
@@ -1172,27 +1043,84 @@ static void mouse_report(int currcons, struct tty_struct * tty,
(char)('!' + mry));
respond_string(buf, tty);
}
-#endif
-static inline void status_report(int currcons, struct tty_struct * tty)
+/* invoked via ioctl(TIOCLINUX) */
+int mouse_reporting(void)
{
- respond_string("\033[0n", tty); /* Terminal ok */
+ int currcons = fg_console;
+
+ return report_mouse;
}
-static inline void respond_ID(int currcons, struct tty_struct * tty)
+static inline unsigned short *screenpos(int currcons, int offset, int viewed)
{
- respond_string(VT102ID, tty);
+ unsigned short *p = (unsigned short *)(origin + offset);
+ if (viewed && currcons == fg_console)
+ p -= (__real_origin - __origin);
+ return p;
}
-static void invert_screen(int currcons) {
- unsigned char *p;
+/* Note: inverting the screen twice should revert to the original state */
+void invert_screen(int currcons, int offset, int count, int viewed)
+{
+ unsigned short *p;
+ count /= 2;
+ p = screenpos(currcons, offset, viewed);
if (can_do_color)
- for (p = (unsigned char *)origin+1; p < (unsigned char *)scr_end; p+=2)
- *p = (*p & 0x88) | (((*p >> 4) | (*p << 4)) & 0x77);
+ while (count--) {
+ unsigned short old = scr_readw(p);
+ scr_writew(reverse_video_short(old), p);
+ p++;
+ }
else
- for (p = (unsigned char *)origin+1; p < (unsigned char *)scr_end; p+=2)
- *p ^= *p & 0x07 == 1 ? 0x70 : 0x77;
+ while (count--) {
+ unsigned short old = scr_readw(p);
+ scr_writew(old ^ (((old & 0x0700) == 0x0100)
+ ? 0x7000 : 0x7700), p);
+ p++;
+ }
+}
+
+/* used by selection: complement pointer position */
+void complement_pos(int currcons, int offset)
+{
+ static unsigned short *p = NULL;
+ static unsigned short old = 0;
+
+ if (p)
+ scr_writew(old, p);
+ if (offset == -1)
+ p = NULL;
+ else {
+ p = screenpos(currcons, offset, 1);
+ old = scr_readw(p);
+ scr_writew(old ^ 0x7700, p);
+ }
+}
+
+/* used by selection */
+unsigned short screen_word(int currcons, int offset, int viewed)
+{
+ return scr_readw(screenpos(currcons, offset, viewed));
+}
+
+/* used by vcs - note the word offset */
+unsigned short *screen_pos(int currcons, int w_offset, int viewed)
+{
+ return screenpos(currcons, 2 * w_offset, viewed);
+}
+
+void getconsxy(int currcons, char *p)
+{
+ p[0] = x;
+ p[1] = y;
+}
+
+void putconsxy(int currcons, char *p)
+{
+ gotoxy(currcons, p[0], p[1]);
+ set_cursor(currcons);
}
static void set_mode(int currcons, int on_off)
@@ -1218,7 +1146,7 @@ static void set_mode(int currcons, int on_off)
case 5: /* Inverted screen on/off */
if (decscnm != on_off) {
decscnm = on_off;
- invert_screen(currcons);
+ invert_screen(currcons, 0, video_screen_size, 0);
update_attr(currcons);
}
break;
@@ -1246,6 +1174,9 @@ static void set_mode(int currcons, int on_off)
report_mouse = on_off ? 2 : 0;
break;
} else switch(par[i]) { /* ANSI modes set/reset */
+ case 3: /* Monitor (display ctrls) */
+ disp_ctrl = on_off;
+ break;
case 4: /* Insert Mode on/off */
decim = on_off;
break;
@@ -1282,6 +1213,7 @@ static void setterm_command(int currcons)
break;
case 9: /* set blanking interval */
blankinterval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ;
+ poke_blanked_console();
break;
}
}
@@ -1293,8 +1225,8 @@ static void insert_char(int currcons)
unsigned short * p = (unsigned short *) pos;
while (i++ < video_num_columns) {
- tmp = *p;
- *p = old;
+ tmp = scr_readw(p);
+ scr_writew(old, p);
old = tmp;
p++;
}
@@ -1313,10 +1245,10 @@ static void delete_char(int currcons)
unsigned short * p = (unsigned short *) pos;
while (++i < video_num_columns) {
- *p = *(p+1);
+ scr_writew(scr_readw(p+1), p);
p++;
}
- *p = video_erase_char;
+ scr_writew(video_erase_char, p);
need_wrap = 0;
}
@@ -1391,7 +1323,7 @@ static void restore_cur(int currcons)
color = s_color;
G0_charset = saved_G0;
G1_charset = saved_G1;
- translate = charset ? G1_charset : G0_charset;
+ translate = set_translate(charset ? G1_charset : G0_charset);
update_attr(currcons);
need_wrap = 0;
}
@@ -1405,9 +1337,9 @@ static void reset_terminal(int currcons, int do_clear)
bottom = video_num_lines;
vc_state = ESnormal;
ques = 0;
- translate = NORM_TRANS;
- G0_charset = NORM_TRANS;
- G1_charset = GRAF_TRANS;
+ translate = set_translate(NORM_MAP);
+ G0_charset = NORM_MAP;
+ G1_charset = GRAF_MAP;
charset = 0;
need_wrap = 0;
report_mouse = 0;
@@ -1423,7 +1355,6 @@ static void reset_terminal(int currcons, int do_clear)
deccm = 1;
decim = 0;
-#ifdef __i386__
set_kbd(decarm);
clr_kbd(decckm);
clr_kbd(kbdapplic);
@@ -1432,7 +1363,6 @@ static void reset_terminal(int currcons, int do_clear)
kbd_table[currcons].ledmode = LED_SHOW_FLAGS;
kbd_table[currcons].ledflagstate = kbd_table[currcons].default_ledflagstate;
set_leds();
-#endif
default_attr(currcons);
update_attr(currcons);
@@ -1496,21 +1426,19 @@ static int con_write(struct tty_struct * tty, int from_user,
}
return 0;
}
-#ifdef CONFIG_SELECTION
- /* clear the selection */
+
if (currcons == sel_cons)
clear_selection();
-#endif /* CONFIG_SELECTION */
+
disable_bh(KEYBOARD_BH);
while (!tty->stopped && count) {
- c = from_user ? get_fs_byte(buf) : *buf;
+ c = from_user ? get_user(buf) : *buf;
buf++; n++; count--;
if (utf) {
/* Combine UTF-8 into Unicode */
/* Incomplete characters silently ignored */
if(c > 0x7f) {
- /* UTF-8 to Latin-1 decoding */
if (utf_count > 0 && (c & 0xc0) == 0x80) {
utf_char = (utf_char << 6) | (c & 0x3f);
utf_count--;
@@ -1532,26 +1460,29 @@ static int con_write(struct tty_struct * tty, int from_user,
utf_count = 0;
/* Now try to find out how to display it */
- if (c > 0xff) {
- tc = conv_uni_to_pc(c);
- if (tc == -2)
- continue;
- vc_state = ESnormal;
- if (tc == -1)
- tc = 0376; /* small square: symbol not found */
- ok = 1;
- } else {
- tc = NORM_TRANS[c];
+ tc = conv_uni_to_pc(c);
+ if (tc == -1 || tc == -2)
+ continue;
+ if (tc == -3 || tc == -4) { /* hashtable not valid */
+ /* or symbol not found */
+ tc = (c <= 0xff) ? translate[c] : 040;
ok = 0;
- }
+ } else
+ ok = 1;
} else { /* no utf */
tc = translate[toggle_meta ? (c|0x80) : c];
ok = 0;
}
- /* Can print ibm (even if 0), and latin1 provided
- it is a printing char or control chars are printed ^@ */
- if (!ok && tc && (c >= 32 || (disp_ctrl && (c&0x7f) != 27)))
+ /* If the original code was < 32 we only allow a
+ * glyph to be displayed if the code is not normally
+ * used (such as for cursor movement) or if the
+ * disp_ctrl mode has been explicitly enabled.
+ * Note: ESC is *never* allowed to be displayed as
+ * that would disable all escape sequences!
+ */
+ if (!ok && tc && (c >= 32 || (disp_ctrl && c != 0x1b)
+ || !((CTRL_ACTION >> c) & 1)))
ok = 1;
if (vc_state == ESnormal && ok) {
@@ -1561,12 +1492,26 @@ static int con_write(struct tty_struct * tty, int from_user,
}
if (decim)
insert_char(currcons);
- *(unsigned short *) pos = (attr << 8) + tc;
- if (x == video_num_columns - 1)
- need_wrap = decawm;
- else {
- x++;
- pos+=2;
+#ifdef CONFIG_MIPS_MAGNUM_4000
+ if (graph_mode) {
+ gscr_pchar(tc, (unsigned char *)pos);
+ if (x == video_num_columns - 1)
+ need_wrap = 1;
+ else {
+ x++;
+ pos += FONTSIZE_X;
+ }
+ }
+ else /* graph_mode */
+#endif
+ {
+ scr_writew((attr << 8) + tc, (unsigned short *) pos);
+ if (x == video_num_columns - 1)
+ need_wrap = decawm;
+ else {
+ x++;
+ pos+=2;
+ }
}
continue;
}
@@ -1600,11 +1545,13 @@ static int con_write(struct tty_struct * tty, int from_user,
continue;
case 14:
charset = 1;
- translate = G1_charset;
+ translate = set_translate(G1_charset);
+ disp_ctrl = 1;
continue;
case 15:
charset = 0;
- translate = G0_charset;
+ translate = set_translate(G0_charset);
+ disp_ctrl = 0;
continue;
case 24: case 26:
vc_state = ESnormal;
@@ -1643,7 +1590,7 @@ static int con_write(struct tty_struct * tty, int from_user,
tab_stop[x >> 5] |= (1 << (x & 31));
continue;
case 'Z':
- respond_ID(currcons,tty);
+ respond_ID(tty);
continue;
case '7':
save_cur(currcons);
@@ -1704,7 +1651,7 @@ static int con_write(struct tty_struct * tty, int from_user,
case 'n':
if (!ques)
if (par[0] == 5)
- status_report(currcons,tty);
+ status_report(tty);
else if (par[0] == 6)
cursor_report(currcons,tty);
continue;
@@ -1768,7 +1715,7 @@ static int con_write(struct tty_struct * tty, int from_user,
continue;
case 'c':
if (!par[0])
- respond_ID(currcons,tty);
+ respond_ID(tty);
continue;
case 'g':
if (!par[0])
@@ -1826,11 +1773,8 @@ static int con_write(struct tty_struct * tty, int from_user,
case '@': /* defined in ISO 2022 */
utf = 0;
continue;
- case '8':
- /* ISO/ECMA hasn't yet registered an
- official ESC sequence for UTF-8,
- so this one (ESC %8) will likely
- change in the future. */
+ case 'G': /* prelim official escape code */
+ case '8': /* retained for compatibility */
utf = 1;
continue;
}
@@ -1851,28 +1795,28 @@ static int con_write(struct tty_struct * tty, int from_user,
continue;
case ESsetG0:
if (c == '0')
- G0_charset = GRAF_TRANS;
+ G0_charset = GRAF_MAP;
else if (c == 'B')
- G0_charset = NORM_TRANS;
+ G0_charset = NORM_MAP;
else if (c == 'U')
- G0_charset = NULL_TRANS;
+ G0_charset = NULL_MAP;
else if (c == 'K')
- G0_charset = USER_TRANS;
+ G0_charset = USER_MAP;
if (charset == 0)
- translate = G0_charset;
+ translate = set_translate(G0_charset);
vc_state = ESnormal;
continue;
case ESsetG1:
if (c == '0')
- G1_charset = GRAF_TRANS;
+ G1_charset = GRAF_MAP;
else if (c == 'B')
- G1_charset = NORM_TRANS;
+ G1_charset = NORM_MAP;
else if (c == 'U')
- G1_charset = NULL_TRANS;
+ G1_charset = NULL_MAP;
else if (c == 'K')
- G1_charset = USER_TRANS;
+ G1_charset = USER_MAP;
if (charset == 1)
- translate = G1_charset;
+ translate = set_translate(G1_charset);
vc_state = ESnormal;
continue;
default:
@@ -1915,9 +1859,11 @@ void console_print(const char * b)
{
int currcons = fg_console;
unsigned char c;
+ static int printing = 0;
- if (!printable)
+ if (!printable || printing)
return; /* console not yet initialized */
+ printing = 1;
if (!vc_cons_allocated(currcons)) {
/* impossible */
@@ -1933,25 +1879,32 @@ void console_print(const char * b)
if (c == 10 || c == 13)
continue;
}
- *(unsigned short *) pos = (attr << 8) + c;
- if (x == video_num_columns - 1) {
- need_wrap = 1;
- continue;
+#ifdef CONFIG_MIPS_MAGNUM_4000
+ if (graph_mode)
+ {
+ gscr_pchar(c, (unsigned char *) pos);
+ if (x == video_num_columns - 1) {
+ need_wrap = 1;
+ continue;
+ }
+ x++;
+ pos += FONTSIZE_X;
+ }
+ else
+#endif
+ {
+ scr_writew((attr << 8) + c, (unsigned short *) pos);
+ if (x == video_num_columns - 1) {
+ need_wrap = 1;
+ continue;
+ }
+ x++;
+ pos+=2;
}
- x++;
- pos+=2;
}
set_cursor(currcons);
- if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
- return;
- timer_active &= ~(1<<BLANK_TIMER);
- if (console_blanked) {
- timer_table[BLANK_TIMER].expires = 0;
- timer_active |= 1<<BLANK_TIMER;
- } else if (blankinterval) {
- timer_table[BLANK_TIMER].expires = jiffies + blankinterval;
- timer_active |= 1<<BLANK_TIMER;
- }
+ poke_blanked_console();
+ printing = 0;
}
/*
@@ -1976,7 +1929,10 @@ static void vc_init(unsigned int currcons, unsigned long rows, unsigned long col
video_num_columns = cols;
video_num_lines = rows;
- video_size_row = cols<<1;
+ if (graph_mode)
+ video_size_row = cols * FONTSIZE_X * FONTSIZE_Y;
+ else
+ video_size_row = cols<<1;
video_screen_size = video_num_lines * video_size_row;
pos = origin = video_mem_start = base;
@@ -1994,10 +1950,12 @@ static void con_setsize(unsigned long rows, unsigned long cols)
{
video_num_lines = rows;
video_num_columns = cols;
- video_size_row = 2 * cols;
+ if (graph_mode)
+ video_size_row = cols * FONTSIZE_X * FONTSIZE_Y;
+ else
+ video_size_row = 2 * cols;
video_screen_size = video_num_lines * video_size_row;
}
-
/*
* long con_init(long);
*
@@ -2051,7 +2009,54 @@ long con_init(long kmem_start)
timer_table[BLANK_TIMER].expires = jiffies+blankinterval;
timer_active |= 1<<BLANK_TIMER;
}
-
+
+#ifdef CONFIG_ACER_PICA_61
+ /*
+ * This type of video is only available as 64bit on board display for
+ * MIPS machines based on the PICA chipset. If the loader has detected
+ * such a machine assume that we use that type of video.
+ */
+ if (boot_info.machtype == MACH_ACER_PICA_61)
+ {
+ can_do_color = 1;
+ video_port_reg = 0x3d4;
+ video_port_val = 0x3d5;
+ video_type = VIDEO_TYPE_PICA_S3;
+ video_mem_base = boot_info.vram_base;
+ video_mem_term = video_mem_base + 0x8000;
+ display_desc = "PICA-S3";
+ /*
+ * Don't request a region - the video ports are outside of
+ * the normal port address range.
+ */
+ }
+ else
+#endif
+#ifdef CONFIG_MIPS_MAGNUM_4000
+ /*
+ * This type of video is only available as 64bit on board display for
+ * MIPS Magnum 4000 machines. If the loader has detected such a machine
+ * assume that we use that type of video.
+ */
+ if (boot_info.machtype == MACH_MIPS_MAGNUM_4000)
+ {
+ can_do_color = 1; /* always in colour graphics mode */
+ graph_mode = 1; /* no text mode available */
+ video_port_reg = 0x3d4; /* the usual (senseless) assumption... */
+ video_port_val = 0x3d5;
+ video_type = VIDEO_TYPE_MIPS_G364;
+ video_mem_base = boot_info.vram_base;
+ video_mem_term = video_mem_base;
+ video_res_x = video_num_columns * FONTSIZE_X;
+ display_desc = "MIPS-G364";
+
+ /*
+ * Don't request a region - the video ports are outside of
+ * the normal port address range.
+ */
+ }
+ else
+#endif
if (ORIG_VIDEO_MODE == 7) /* Is this a monochrome display? */
{
video_mem_base = SLOTSPACE + 0xb0000;
@@ -2062,12 +2067,17 @@ long con_init(long kmem_start)
video_type = VIDEO_TYPE_EGAM;
video_mem_term = SLOTSPACE + 0xb8000;
display_desc = "EGA+";
+ request_region(0x3b4,2,"ega+");
}
else
{
video_type = VIDEO_TYPE_MDA;
video_mem_term = SLOTSPACE + 0xb2000;
display_desc = "*MDA";
+ request_region(0x3b4,2,"mda");
+ request_region(0x3b8,1,"mda");
+ request_region(0x3bf,1,"mda");
+
}
}
else /* If not, it is color. */
@@ -2081,12 +2091,14 @@ long con_init(long kmem_start)
video_type = VIDEO_TYPE_EGAC;
video_mem_term = SLOTSPACE + 0xc0000;
display_desc = "EGA+";
+ request_region(0x3d4,2,"ega+");
}
else
{
video_type = VIDEO_TYPE_CGA;
video_mem_term = SLOTSPACE + 0xba000;
display_desc = "*CGA";
+ request_region(0x3d4,2,"cga");
}
}
@@ -2113,6 +2125,8 @@ long con_init(long kmem_start)
origin = video_mem_start;
scr_end = video_mem_start + video_num_lines * video_size_row;
gotoxy(currcons,orig_x,orig_y);
+ set_origin(currcons);
+ csi_J(currcons, 0);
printable = 1;
printk("Console: %s %s %ldx%ld, %d virtual console%s (max %d)\n",
can_do_color ? "colour" : "mono",
@@ -2125,10 +2139,14 @@ long con_init(long kmem_start)
static void get_scrmem(int currcons)
{
- memcpy((void *)vc_scrbuf[currcons], (void *)origin, video_screen_size);
+ memcpyw((unsigned short *)vc_scrbuf[currcons],
+ (unsigned short *)origin, video_screen_size);
origin = video_mem_start = (unsigned long)vc_scrbuf[currcons];
scr_end = video_mem_end = video_mem_start + video_screen_size;
- pos = origin + y*video_size_row + (x<<1);
+ if (graph_mode)
+ pos = origin + y*video_size_row + x*FONTSIZE_X;
+ else
+ pos = origin + y*video_size_row + (x<<1);
}
static void set_scrmem(int currcons, long offset)
@@ -2166,25 +2184,26 @@ static void set_scrmem(int currcons, long offset)
if (video_mem_term - video_mem_base < offset + video_screen_size)
offset = 0; /* strange ... */
- memcpy((void *)(video_mem_base + offset), (void *) origin, video_screen_size);
+ memcpyw((unsigned short *)(video_mem_base + offset),
+ (unsigned short *) origin, video_screen_size);
video_mem_start = video_mem_base;
video_mem_end = video_mem_term;
origin = video_mem_base + offset;
scr_end = origin + video_screen_size;
- pos = origin + y*video_size_row + (x<<1);
+ if (graph_mode)
+ pos = origin + y*video_size_row + x*FONTSIZE_X;
+ else
+ pos = origin + y*video_size_row + (x<<1);
}
-void blank_screen(void)
+void do_blank_screen(int nopowersave)
{
int currcons;
if (console_blanked)
return;
- if (!vc_cons_allocated(fg_console)) {
- /* impossible */
- printk("blank_screen: tty %d not allocated ??\n", fg_console+1);
- return;
- }
+
+ timer_active &= ~(1<<BLANK_TIMER);
timer_table[BLANK_TIMER].fn = unblank_screen;
/* try not to lose information by blanking, and not to waste memory */
@@ -2198,9 +2217,12 @@ void blank_screen(void)
memsetw((void *)blank_origin, BLANK, video_mem_term-blank_origin);
hide_cursor();
console_blanked = fg_console + 1;
+
+ if(!nopowersave)
+ vesa_blank();
}
-void unblank_screen(void)
+void do_unblank_screen(void)
{
int currcons;
int resetorg;
@@ -2218,6 +2240,7 @@ void unblank_screen(void)
timer_table[BLANK_TIMER].expires = jiffies + blankinterval;
timer_active |= 1<<BLANK_TIMER;
}
+
currcons = fg_console;
offset = 0;
resetorg = 0;
@@ -2235,6 +2258,22 @@ void unblank_screen(void)
set_cursor(fg_console);
if (resetorg)
__set_origin(blank__origin);
+
+ vesa_unblank();
+}
+
+/*
+ * If a blank_screen is due to a timer, then a power save is allowed.
+ * If it is related to console_switching, then avoid vesa_blank().
+ */
+static void blank_screen(void)
+{
+ do_blank_screen(0);
+}
+
+static void unblank_screen(void)
+{
+ do_unblank_screen();
}
void update_screen(int new_console)
@@ -2249,9 +2288,9 @@ void update_screen(int new_console)
return;
}
lock = 1;
-#ifdef CONFIG_SELECTION
+
clear_selection();
-#endif /* CONFIG_SELECTION */
+
if (!console_blanked)
get_scrmem(fg_console);
else
@@ -2267,60 +2306,6 @@ void update_screen(int new_console)
}
/*
- * do_screendump is used for three tasks:
- * if (mode==0) is the old ioctl(TIOCLINUX,0)
- * if (mode==1) dumps wd,hg, cursor position, and all the char-attr pairs
- * if (mode==2) restores what mode1 got.
- * the new modes are needed for a fast and complete dump-restore cycle,
- * needed to implement root-window menus in text mode (A Rubini Nov 1994)
- */
-int do_screendump(int arg, int mode)
-{
- char *sptr, *buf = (char *)arg;
- int currcons, l, chcount;
-
- if (!suser())
- return -EPERM;
- l = verify_area(VERIFY_READ, buf, 2);
- if (l)
- return l;
- currcons = get_fs_byte(buf+1);
- currcons = (currcons ? currcons-1 : fg_console);
- if (!vc_cons_allocated(currcons))
- return -EIO;
-
- /* mode 0 needs 2+wd*ht, modes 1 and 2 need 4+2*wd*ht */
- chcount=video_num_columns*video_num_lines;
- l = verify_area(mode==2 ? VERIFY_READ :VERIFY_WRITE,
- buf, (2+chcount)*(mode ? 2 : 1));
- if (l)
- return l;
- if (mode<2) {
- put_fs_byte((char)(video_num_lines),buf++);
- put_fs_byte((char)(video_num_columns),buf++);
- }
- switch(mode) {
- case 0:
- sptr = (char *) origin;
- for (l=chcount; l>0 ; l--, sptr++)
- put_fs_byte(*sptr++,buf++);
- break;
- case 1:
-#ifdef CONFIG_SELECTION
- clear_selection();
-#endif
- put_fs_byte((char)x,buf++); put_fs_byte((char)y,buf++);
- memcpy_tofs(buf,(char *)origin,2*chcount);
- break;
- case 2:
- buf+=4; /* ioctl#, console#, x,y */
- memcpy_fromfs((char *)origin,buf,2*chcount);
- break;
- }
- return(0);
-}
-
-/*
* Allocate the console screen memory.
*/
int con_open(struct tty_struct *tty, struct file * filp)
@@ -2344,284 +2329,6 @@ int con_open(struct tty_struct *tty, struct file * filp)
return 0;
}
-#ifdef CONFIG_SELECTION
-/* correction factor for when screen is hardware-scrolled */
-#define hwscroll_offset (currcons == fg_console ? ((__real_origin - __origin) << 1) : 0)
-
-/* set reverse video on characters s-e of console with selection. */
-static void highlight(const int currcons, const int s, const int e)
-{
- unsigned char *p, *p1, *p2;
-
- p1 = (unsigned char *)origin - hwscroll_offset + s + 1;
- p2 = (unsigned char *)origin - hwscroll_offset + e + 1;
- if (p1 > p2)
- {
- p = p1;
- p1 = p2;
- p2 = p;
- }
- for (p = p1; p <= p2; p += 2)
- *p = (*p & 0x88) | ((*p << 4) & 0x70) | ((*p >> 4) & 0x07);
-}
-
-/* use complementary color to show the pointer */
-static void highlight_pointer(const int currcons, const int where)
-{
- unsigned char *p;
- static unsigned char *prev=NULL;
-
- if (where==-1) /* remove the pointer */
- {
- if (prev)
- {
- *prev ^= 0x77;
- prev=NULL;
- }
- }
- else
- {
- p = (unsigned char *)origin - hwscroll_offset + where + 1;
- *p ^= 0x77;
- if (prev) *prev ^= 0x77; /* remove the previous one */
- prev=p;
- }
-}
-
-
-/*
- * This function uses a 128-bit look up table
- */
-static unsigned long inwordLut[4]={
- 0x00000000, /* control chars */
- 0x03FF0000, /* digits */
- 0x87FFFFFE, /* uppercase and '_' */
- 0x07FFFFFE /* lowercase */
-};
-static inline int inword(const char c) {
- return ( inwordLut[(c>>5)&3] >> (c&0x1F) ) & 1;
-}
-
-/* set inwordLut contents. Invoked by ioctl(). */
-int sel_loadlut(const int arg)
-{
- memcpy_fromfs(inwordLut,(unsigned long *)(arg+4),16);
- return 0;
-}
-
-/* does screen address p correspond to character at LH/RH edge of screen? */
-static inline int atedge(const int p)
-{
- return (!(p % video_size_row) || !((p + 2) % video_size_row));
-}
-
-/* constrain v such that l <= v <= u */
-static inline short limit(const int v, const int l, const int u)
-{
- return (v < l) ? l : ((v > u) ? u : v);
-}
-
-/* invoked via ioctl(TIOCLINUX) */
-int mouse_reporting(void)
-{
- int currcons = fg_console;
-
- return report_mouse;
-}
-
-/* set the current selection. Invoked by ioctl(). */
-int set_selection(const int arg, struct tty_struct *tty)
-{
- unsigned short *args, xs, ys, xe, ye;
- int currcons = fg_console;
- int sel_mode, new_sel_start, new_sel_end, spc;
- char *bp, *obp, *spos;
- int i, ps, pe;
- char *off = (char *)origin - hwscroll_offset;
-
- unblank_screen();
- args = (unsigned short *)(arg + 1);
- xs = get_fs_word(args++) - 1;
- ys = get_fs_word(args++) - 1;
- xe = get_fs_word(args++) - 1;
- ye = get_fs_word(args++) - 1;
- sel_mode = get_fs_word(args);
-
- xs = limit(xs, 0, video_num_columns - 1);
- ys = limit(ys, 0, video_num_lines - 1);
- xe = limit(xe, 0, video_num_columns - 1);
- ye = limit(ye, 0, video_num_lines - 1);
- ps = ys * video_size_row + (xs << 1);
- pe = ye * video_size_row + (xe << 1);
-
- if (report_mouse && (sel_mode & 16)) {
- mouse_report(currcons, tty, sel_mode & 15, xs, ys);
- return 0;
- }
-
- if (ps > pe) /* make sel_start <= sel_end */
- {
- int tmp = ps;
- ps = pe;
- pe = tmp;
- }
-
- switch (sel_mode)
- {
- case 0: /* character-by-character selection */
- new_sel_start = ps;
- new_sel_end = pe;
- break;
- case 1: /* word-by-word selection */
- spc = isspace(*(off + ps));
- for (new_sel_start = ps; ; ps -= 2)
- {
- if ((spc && !isspace(*(off + ps))) ||
- (!spc && !inword(*(off + ps))))
- break;
- new_sel_start = ps;
- if (!(ps % video_size_row))
- break;
- }
- spc = isspace(*(off + pe));
- for (new_sel_end = pe; ; pe += 2)
- {
- if ((spc && !isspace(*(off + pe))) ||
- (!spc && !inword(*(off + pe))))
- break;
- new_sel_end = pe;
- if (!((pe + 2) % video_size_row))
- break;
- }
- break;
- case 2: /* line-by-line selection */
- new_sel_start = ps - ps % video_size_row;
- new_sel_end = pe + video_size_row
- - pe % video_size_row - 2;
- break;
- case 3: /* pointer highlight */
- if (sel_cons != currcons)
- {
- clear_selection();
- sel_cons = currcons;
- }
- highlight_pointer(sel_cons,pe);
- return 0; /* nothing more */
- default:
- return -EINVAL;
- }
- /* remove the pointer */
- highlight_pointer(sel_cons,-1);
- /* select to end of line if on trailing space */
- if (new_sel_end > new_sel_start &&
- !atedge(new_sel_end) && isspace(*(off + new_sel_end)))
- {
- for (pe = new_sel_end + 2; ; pe += 2)
- {
- if (!isspace(*(off + pe)) || atedge(pe))
- break;
- }
- if (isspace(*(off + pe)))
- new_sel_end = pe;
- }
- if (sel_cons != currcons)
- {
- clear_selection();
- sel_cons = currcons;
- }
- if (sel_start == -1) /* no current selection */
- highlight(sel_cons, new_sel_start, new_sel_end);
- else if (new_sel_start == sel_start)
- {
- if (new_sel_end == sel_end) /* no action required */
- return 0;
- else if (new_sel_end > sel_end) /* extend to right */
- highlight(sel_cons, sel_end + 2, new_sel_end);
- else /* contract from right */
- highlight(sel_cons, new_sel_end + 2, sel_end);
- }
- else if (new_sel_end == sel_end)
- {
- if (new_sel_start < sel_start) /* extend to left */
- highlight(sel_cons, new_sel_start, sel_start - 2);
- else /* contract from left */
- highlight(sel_cons, sel_start, new_sel_start - 2);
- }
- else /* some other case; start selection from scratch */
- {
- clear_selection();
- highlight(sel_cons, new_sel_start, new_sel_end);
- }
- sel_start = new_sel_start;
- sel_end = new_sel_end;
- obp = bp = sel_buffer;
- for (i = sel_start; i <= sel_end; i += 2)
- {
- spos = (char *)off + i;
- *bp++ = *spos;
- if (!isspace(*spos))
- obp = bp;
- if (! ((i + 2) % video_size_row))
- {
- /* strip trailing blanks from line and add newline,
- unless non-space at end of line. */
- if (obp != bp)
- {
- bp = obp;
- *bp++ = '\r';
- }
- obp = bp;
- }
- /* check for space, leaving room for next character, possible
- newline, and null at end. */
- if (bp - sel_buffer > SEL_BUFFER_SIZE - 3)
- break;
- }
- *bp = '\0';
- return 0;
-}
-
-/* insert the contents of the selection buffer into the queue of the
- tty associated with the current console. Invoked by ioctl(). */
-int paste_selection(struct tty_struct *tty)
-{
- struct wait_queue wait = { current, NULL };
- char *bp = sel_buffer;
- int c, l;
- struct vt_struct *vt = (struct vt_struct *) tty->driver_data;
-
- if (!sel_buffer[0])
- return 0;
- unblank_screen();
- c = strlen(sel_buffer);
- current->state = TASK_INTERRUPTIBLE;
- add_wait_queue(&vt->paste_wait, &wait);
- while (c) {
- if (test_bit(TTY_THROTTLED, &tty->flags)) {
- schedule();
- continue;
- }
- l = MIN(c, tty->ldisc.receive_room(tty));
- tty->ldisc.receive_buf(tty, bp, 0, l);
- c -= l;
- bp += l;
- }
- current->state = TASK_RUNNING;
- return 0;
-}
-
-/* remove the current selection highlight, if any, from the console holding
- the selection. */
-static void clear_selection()
-{
- highlight_pointer(sel_cons, -1); /* hide the pointer */
- if (sel_start != -1)
- {
- highlight(sel_cons, sel_start, sel_end);
- sel_start = -1;
- }
-}
-#endif /* CONFIG_SELECTION */
/*
* PIO_FONT support.
@@ -2687,10 +2394,10 @@ static int set_get_font(char * arg, int set)
if (set)
for (i=0; i<cmapsz ; i++)
- *(charmap+i) = get_fs_byte(arg+i);
+ scr_writeb(get_user(arg + i), charmap + i);
else
for (i=0; i<cmapsz ; i++)
- put_fs_byte(*(charmap+i), arg+i);
+ put_user(scr_readb(charmap + i), arg + i);
cli();
outb_p( 0x00, seq_port_reg ); /* First, the sequencer */
@@ -2724,6 +2431,7 @@ static int set_get_font(char * arg, int set)
int con_set_font (char *arg)
{
+ hashtable_contents_valid = 0;
return set_get_font (arg,1);
}
@@ -2731,36 +2439,3 @@ int con_get_font (char *arg)
{
return set_get_font (arg,0);
}
-
-/*
- * Load customizable translation table (USER_TRANS[]). All checks are here,
- * so we need only include 'return con_set_trans(arg)' in the ioctl handler
- * arg points to a 256 byte translation table.
- */
-int con_set_trans(char * arg)
-{
- int i;
-
- i = verify_area(VERIFY_READ, (void *)arg, E_TABSZ);
- if (i)
- return i;
-
- for (i=0; i<E_TABSZ ; i++) USER_TRANS[i] = get_fs_byte(arg+i);
- USER_TRANS[012]=0;
- USER_TRANS[014]=0;
- USER_TRANS[015]=0;
- USER_TRANS[033]=0;
- return 0;
-}
-
-int con_get_trans(char * arg)
-{
- int i;
-
- i = verify_area(VERIFY_WRITE, (void *)arg, E_TABSZ);
- if (i)
- return i;
-
- for (i=0; i<E_TABSZ ; i++) put_fs_byte(USER_TRANS[i],arg+i);
- return 0;
-}
diff --git a/drivers/char/consolemap.c b/drivers/char/consolemap.c
new file mode 100644
index 000000000..54c2d19a4
--- /dev/null
+++ b/drivers/char/consolemap.c
@@ -0,0 +1,286 @@
+/*
+ * consolemap.c
+ *
+ * Mapping from internal code (such as Latin-1 or Unicode or IBM PC code)
+ * to font positions.
+ *
+ * aeb, 950210
+ */
+
+#include <linux/kd.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <asm/segment.h>
+#include "consolemap.h"
+
+static unsigned char * translations[] = {
+/* 8-bit Latin-1 mapped to the PC character set: '\0' means non-printable */
+(unsigned char *)
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\376\0\0\0\0\0"
+ " !\"#$%&'()*+,-./0123456789:;<=>?"
+ "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
+ "`abcdefghijklmnopqrstuvwxyz{|}~\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\377\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376"
+ "\370\361\375\376\376\346\024\371\376\376\247\257\254\253\376\250"
+ "\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376"
+ "\376\245\376\376\376\376\231\376\350\376\376\376\232\376\376\341"
+ "\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213"
+ "\376\244\225\242\223\376\224\366\355\227\243\226\201\376\376\230",
+/* vt100 graphics */
+(unsigned char *)
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\376\0\0\0\0\0"
+ " !\"#$%&'()*+,-./0123456789:;<=>?"
+ "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^ "
+ "\004\261\007\007\007\007\370\361\007\007\331\277\332\300\305\304"
+ "\304\304\137\137\303\264\301\302\263\363\362\343\330\234\007\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\377\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376"
+ "\370\361\375\376\376\346\024\371\376\376\247\257\254\253\376\250"
+ "\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376"
+ "\376\245\376\376\376\376\231\376\376\376\376\376\232\376\376\341"
+ "\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213"
+ "\376\244\225\242\223\376\224\366\376\227\243\226\201\376\376\230",
+/* IBM graphics: minimal translations (BS, CR, LF, LL, SO, SI and ESC) */
+(unsigned char *)
+ "\000\001\002\003\004\005\006\007\000\011\000\013\000\000\000\000"
+ "\020\021\022\023\024\025\026\027\030\031\032\000\034\035\036\037"
+ "\040\041\042\043\044\045\046\047\050\051\052\053\054\055\056\057"
+ "\060\061\062\063\064\065\066\067\070\071\072\073\074\075\076\077"
+ "\100\101\102\103\104\105\106\107\110\111\112\113\114\115\116\117"
+ "\120\121\122\123\124\125\126\127\130\131\132\133\134\135\136\137"
+ "\140\141\142\143\144\145\146\147\150\151\152\153\154\155\156\157"
+ "\160\161\162\163\164\165\166\167\170\171\172\173\174\175\176\177"
+ "\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217"
+ "\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237"
+ "\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257"
+ "\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277"
+ "\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317"
+ "\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"
+ "\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357"
+ "\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377",
+ /* USER: customizable mappings, initialized as the previous one (IBM) */
+(unsigned char *)
+ "\000\001\002\003\004\005\006\007\010\011\000\013\000\000\016\017"
+ "\020\021\022\023\024\025\026\027\030\031\032\000\034\035\036\037"
+ "\040\041\042\043\044\045\046\047\050\051\052\053\054\055\056\057"
+ "\060\061\062\063\064\065\066\067\070\071\072\073\074\075\076\077"
+ "\100\101\102\103\104\105\106\107\110\111\112\113\114\115\116\117"
+ "\120\121\122\123\124\125\126\127\130\131\132\133\134\135\136\137"
+ "\140\141\142\143\144\145\146\147\150\151\152\153\154\155\156\157"
+ "\160\161\162\163\164\165\166\167\170\171\172\173\174\175\176\177"
+ "\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217"
+ "\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237"
+ "\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257"
+ "\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277"
+ "\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317"
+ "\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"
+ "\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357"
+ "\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377"
+};
+
+/* the above mappings are not invertible - this is just a best effort */
+static unsigned char * inv_translate = NULL;
+static unsigned char inv_norm_transl[E_TABSZ];
+static unsigned char * inverse_translations[4] = { NULL, NULL, NULL, NULL };
+
+static void set_inverse_transl(int i)
+{
+ int j;
+ unsigned char *p = translations[i];
+ unsigned char *q = inverse_translations[i];
+
+ if (!q) {
+ /* slightly messy to avoid calling kmalloc too early */
+ q = inverse_translations[i] = ((i == NORM_MAP)
+ ? inv_norm_transl
+ : (unsigned char *) kmalloc(E_TABSZ, GFP_KERNEL));
+ if (!q)
+ return;
+ }
+ for (j=0; j<E_TABSZ; j++)
+ q[j] = 0;
+ for (j=0; j<E_TABSZ; j++)
+ if (q[p[j]] < 32) /* prefer '-' above SHY etc. */
+ q[p[j]] = j;
+}
+
+unsigned char *set_translate(int m)
+{
+ if (!inverse_translations[m])
+ set_inverse_transl(m);
+ inv_translate = inverse_translations[m];
+ return translations[m];
+}
+
+/*
+ * Inverse translation is impossible for several reasons:
+ * 1. The translation maps are not 1-1
+ * 2. The text may have been written while a different translation map
+ * was active
+ * Still, it is now possible to a certain extent to cut and paste non-ASCII.
+ */
+unsigned char inverse_translate(unsigned char c) {
+ return ((inv_translate && inv_translate[c]) ? inv_translate[c] : c);
+}
+
+/*
+ * Load customizable translation table
+ * arg points to a 256 byte translation table.
+ */
+int con_set_trans(char * arg)
+{
+ int i;
+ unsigned char *p = translations[USER_MAP];
+
+ i = verify_area(VERIFY_READ, (void *)arg, E_TABSZ);
+ if (i)
+ return i;
+
+ for (i=0; i<E_TABSZ ; i++)
+ p[i] = get_fs_byte(arg+i);
+ p[012] = p[014] = p[015] = p[033] = 0;
+ set_inverse_transl(USER_MAP);
+ return 0;
+}
+
+int con_get_trans(char * arg)
+{
+ int i;
+ unsigned char *p = translations[USER_MAP];
+
+ i = verify_area(VERIFY_WRITE, (void *)arg, E_TABSZ);
+ if (i)
+ return i;
+
+ for (i=0; i<E_TABSZ ; i++) put_fs_byte(p[i],arg+i);
+ return 0;
+}
+
+/*
+ * Unicode -> current font conversion
+ *
+ * A font has at most 512 chars, usually 256.
+ * But one font position may represent several Unicode chars
+ * (and moreover, hashtables work best when they are not too full),
+ * so pick HASHSIZE somewhat larger than 512.
+ * Since there are likely to be long consecutive stretches
+ * (like U+0000 to U+00FF), HASHSTEP should not be too small.
+ * Searches longer than MAXHASHLEVEL steps are refused, unless
+ * requested explicitly.
+ *
+ * Note: no conversion tables are compiled in, so the user
+ * must supply an explicit mapping herself. See kbd-0.90 (or an
+ * earlier kernel version) for the default Unicode-to-PC mapping.
+ * Usually, the mapping will be loaded simultaneously with the font.
+ */
+
+#define HASHSIZE 641
+#define HASHSTEP 189 /* yields hashlevel = 3 initially */
+#define MAXHASHLEVEL 6
+static struct unipair hashtable[HASHSIZE];
+
+int hashtable_contents_valid = 0; /* cleared by setfont */
+
+static unsigned int hashsize;
+static unsigned int hashstep;
+static unsigned int hashlevel;
+static unsigned int maxhashlevel;
+
+void
+con_clear_unimap(struct unimapinit *ui) {
+ int i;
+
+ /* read advisory values for hash algorithm */
+ hashsize = ui->advised_hashsize;
+ if (hashsize < 256 || hashsize > HASHSIZE)
+ hashsize = HASHSIZE;
+ hashstep = (ui->advised_hashstep % hashsize);
+ if (hashstep < 64)
+ hashstep = HASHSTEP;
+ maxhashlevel = ui->advised_hashlevel;
+ if (!maxhashlevel)
+ maxhashlevel = MAXHASHLEVEL;
+ if (maxhashlevel > hashsize)
+ maxhashlevel = hashsize;
+
+ /* initialize */
+ hashlevel = 0;
+ for (i=0; i<hashsize; i++)
+ hashtable[i].unicode = 0xffff;
+ hashtable_contents_valid = 1;
+}
+
+int
+con_set_unimap(ushort ct, struct unipair *list){
+ int i, lct;
+ ushort u, hu;
+ struct unimapinit hashdefaults = { 0, 0, 0 };
+
+ if (!hashtable_contents_valid)
+ con_clear_unimap(&hashdefaults);
+ while(ct) {
+ u = get_fs_word(&list->unicode);
+ i = u % hashsize;
+ lct = 1;
+ while ((hu = hashtable[i].unicode) != 0xffff && hu != u) {
+ if (lct++ >= maxhashlevel)
+ return -ENOMEM;
+ i += hashstep;
+ if (i >= hashsize)
+ i -= hashsize;
+ }
+ if (lct > hashlevel)
+ hashlevel = lct;
+ hashtable[i].unicode = u;
+ hashtable[i].fontpos = get_fs_word(&list->fontpos);
+ list++;
+ ct--;
+ }
+ return 0;
+}
+
+int
+con_get_unimap(ushort ct, ushort *uct, struct unipair *list){
+ int i, ect;
+
+ ect = 0;
+ if (hashtable_contents_valid)
+ for (i = 0; i<hashsize; i++)
+ if (hashtable[i].unicode != 0xffff) {
+ if (ect++ < ct) {
+ put_fs_word(hashtable[i].unicode, &list->unicode);
+ put_fs_word(hashtable[i].fontpos, &list->fontpos);
+ list++;
+ }
+ }
+ put_fs_word(ect, uct);
+ return ((ect <= ct) ? 0 : -ENOMEM);
+}
+
+int
+conv_uni_to_pc(unsigned long ucs) {
+ int i, h;
+
+ if (!hashtable_contents_valid || ucs < 0x20)
+ return -3;
+ if (ucs == 0xffff || ucs == 0xfffe)
+ return -1;
+ if (ucs == 0xfeff || (ucs >= 0x200a && ucs <= 0x200f))
+ return -2;
+
+ h = ucs % hashsize;
+ for (i = 0; i < hashlevel; i++) {
+ if (hashtable[h].unicode == ucs)
+ return hashtable[h].fontpos;
+ if ((h += hashstep) >= hashsize)
+ h -= hashsize;
+ }
+
+ return -4; /* not found */
+}
diff --git a/drivers/char/consolemap.h b/drivers/char/consolemap.h
new file mode 100644
index 000000000..f0a2a9100
--- /dev/null
+++ b/drivers/char/consolemap.h
@@ -0,0 +1,13 @@
+/*
+ * consolemap.h
+ *
+ * Interface between console.c, selection.c and consolemap.c
+ */
+#define NORM_MAP 0
+#define GRAF_MAP 1
+#define NULL_MAP 2
+#define USER_MAP 3
+
+extern int hashtable_contents_valid;
+extern unsigned char inverse_translate(unsigned char c);
+extern unsigned char *set_translate(int m);
diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c
new file mode 100644
index 000000000..cbeb31ae0
--- /dev/null
+++ b/drivers/char/cyclades.c
@@ -0,0 +1,2958 @@
+static char rcsid[] =
+"$Revision: 1.36.1.4 $$Date: 1995/03/29 06:14:14 $";
+/*
+ * linux/kernel/cyclades.c
+ *
+ * Maintained by Marcio Saito (cyclades@netcom.com) and
+ * Randolph Bentson (bentson@grieg.seaslug.org)
+ *
+ * Much of the design and some of the code came from serial.c
+ * which was copyright (C) 1991, 1992 Linus Torvalds. It was
+ * extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92,
+ * and then fixed as suggested by Michael K. Johnson 12/12/92.
+ *
+ * This version does not support shared irq's.
+ *
+ * This module exports the following rs232 io functions:
+ * long cy_init(long);
+ * int cy_open(struct tty_struct *tty, struct file *filp);
+ *
+ * $Log: cyclades.c,v $
+ * Revision 1.36.1.4 1995/03/29 06:14:14 bentson
+ * disambiguate between Cyclom-16Y and Cyclom-32Ye;
+ *
+ * Revision 1.36.1.3 1995/03/23 22:15:35 bentson
+ * add missing break in modem control block in ioctl switch statement
+ * (discovered by Michael Edward Chastain <mec@jobe.shell.portal.com>);
+ *
+ * Revision 1.36.1.2 1995/03/22 19:16:22 bentson
+ * make sure CTS flow control is set as soon as possible (thanks
+ * to note from David Lambert <lambert@chesapeake.rps.slb.com>);
+ *
+ * Revision 1.36.1.1 1995/03/13 15:44:43 bentson
+ * initialize defaults for receive threshold and stale data timeout;
+ * cosmetic changes;
+ *
+ * Revision 1.36 1995/03/10 23:33:53 bentson
+ * added support of chips 4-7 in 32 port Cyclom-Ye;
+ * fix cy_interrupt pointer dereference problem
+ * (Joe Portman <baron@aa.net>);
+ * give better error response if open is attempted on non-existent port
+ * (Zachariah Vaum <jchryslr@netcom.com>);
+ * correct command timeout (Kenneth Lerman <lerman@@seltd.newnet.com>);
+ * conditional compilation for -16Y on systems with fast, noisy bus;
+ * comment out diagnostic print function;
+ * cleaned up table of base addresses;
+ * set receiver time-out period register to correct value,
+ * set receive threshold to better default values,
+ * set chip timer to more accurate 200 Hz ticking,
+ * add code to monitor and modify receive parameters
+ * (Rik Faith <faith@cs.unc.edu> Nick Simicich
+ * <njs@scifi.emi.net>);
+ *
+ * Revision 1.35 1994/12/16 13:54:18 steffen
+ * additional patch by Marcio Saito for board detection
+ * Accidently left out in 1.34
+ *
+ * Revision 1.34 1994/12/10 12:37:12 steffen
+ * This is the corrected version as suggested by Marcio Saito
+ *
+ * Revision 1.33 1994/12/01 22:41:18 bentson
+ * add hooks to support more high speeds directly; add tytso
+ * patch regarding CLOCAL wakeups
+ *
+ * Revision 1.32 1994/11/23 19:50:04 bentson
+ * allow direct kernel control of higher signalling rates;
+ * look for cards at additional locations
+ *
+ * Revision 1.31 1994/11/16 04:33:28 bentson
+ * ANOTHER fix from Corey Minyard, minyard@wf-rch.cirr.com--
+ * a problem in chars_in_buffer has been resolved by some
+ * small changes; this should yield smoother output
+ *
+ * Revision 1.30 1994/11/16 04:28:05 bentson
+ * Fix from Corey Minyard, Internet: minyard@metronet.com,
+ * UUCP: minyard@wf-rch.cirr.com, WORK: minyardbnr.ca, to
+ * cy_hangup that appears to clear up much (all?) of the
+ * DTR glitches; also he's added/cleaned-up diagnostic messages
+ *
+ * Revision 1.29 1994/11/16 04:16:07 bentson
+ * add change proposed by Ralph Sims, ralphs@halcyon.com, to
+ * operate higher speeds in same way as other serial ports;
+ * add more serial ports (for up to two 16-port muxes).
+ *
+ * Revision 1.28 1994/11/04 00:13:16 root
+ * turn off diagnostic messages
+ *
+ * Revision 1.27 1994/11/03 23:46:37 root
+ * bunch of changes to bring driver into greater conformance
+ * with the serial.c driver (looking for missed fixes)
+ *
+ * Revision 1.26 1994/11/03 22:40:36 root
+ * automatic interrupt probing fixed.
+ *
+ * Revision 1.25 1994/11/03 20:17:02 root
+ * start to implement auto-irq
+ *
+ * Revision 1.24 1994/11/03 18:01:55 root
+ * still working on modem signals--trying not to drop DTR
+ * during the getty/login processes
+ *
+ * Revision 1.23 1994/11/03 17:51:36 root
+ * extend baud rate support; set receive threshold as function
+ * of baud rate; fix some problems with RTS/CTS;
+ *
+ * Revision 1.22 1994/11/02 18:05:35 root
+ * changed arguments to udelay to type long to get
+ * delays to be of correct duration
+ *
+ * Revision 1.21 1994/11/02 17:37:30 root
+ * employ udelay (after calibrating loops_per_second earlier
+ * in init/main.c) instead of using home-grown delay routines
+ *
+ * Revision 1.20 1994/11/02 03:11:38 root
+ * cy_chars_in_buffer forces a return value of 0 to let
+ * login work (don't know why it does); some functions
+ * that were returning EFAULT, now executes the code;
+ * more work on deciding when to disable xmit interrupts;
+ *
+ * Revision 1.19 1994/11/01 20:10:14 root
+ * define routine to start transmission interrupts (by enabling
+ * transmit interrupts); directly enable/disable modem interrupts;
+ *
+ * Revision 1.18 1994/11/01 18:40:45 bentson
+ * Don't always enable transmit interrupts in startup; interrupt on
+ * TxMpty instead of TxRdy to help characters get out before shutdown;
+ * restructure xmit interrupt to check for chars first and quit if
+ * none are ready to go; modem status (MXVRx) is upright, _not_ inverted
+ * (to my view);
+ *
+ * Revision 1.17 1994/10/30 04:39:45 bentson
+ * rename serial_driver and callout_driver to cy_serial_driver and
+ * cy_callout_driver to avoid linkage interference; initialize
+ * info->type to PORT_CIRRUS; ruggedize paranoia test; elide ->port
+ * from cyclades_port structure; add paranoia check to cy_close;
+ *
+ * Revision 1.16 1994/10/30 01:14:33 bentson
+ * change major numbers; add some _early_ return statements;
+ *
+ * Revision 1.15 1994/10/29 06:43:15 bentson
+ * final tidying up for clean compile; enable some error reporting
+ *
+ * Revision 1.14 1994/10/28 20:30:22 Bentson
+ * lots of changes to drag the driver towards the new tty_io
+ * structures and operation. not expected to work, but may
+ * compile cleanly.
+ *
+ * Revision 1.13 1994/07/21 23:08:57 Bentson
+ * add some diagnostic cruft; support 24 lines (for testing
+ * both -8Y and -16Y cards; be more thorough in servicing all
+ * chips during interrupt; add "volatile" a few places to
+ * circumvent compiler optimizations; fix base & offset
+ * computations in block_til_ready (was causing chip 0 to
+ * stop operation)
+ *
+ * Revision 1.12 1994/07/19 16:42:11 Bentson
+ * add some hackery for kernel version 1.1.8; expand
+ * error messages; refine timing for delay loops and
+ * declare loop params volatile
+ *
+ * Revision 1.11 1994/06/11 21:53:10 bentson
+ * get use of save_car right in transmit interrupt service
+ *
+ * Revision 1.10.1.1 1994/06/11 21:31:18 bentson
+ * add some diagnostic printing; try to fix save_car stuff
+ *
+ * Revision 1.10 1994/06/11 20:36:08 bentson
+ * clean up compiler warnings
+ *
+ * Revision 1.9 1994/06/11 19:42:46 bentson
+ * added a bunch of code to support modem signalling
+ *
+ * Revision 1.8 1994/06/11 17:57:07 bentson
+ * recognize break & parity error
+ *
+ * Revision 1.7 1994/06/05 05:51:34 bentson
+ * Reorder baud table to be monotonic; add cli to CP; discard
+ * incoming characters and status if the line isn't open; start to
+ * fold code into cy_throttle; start to port get_serial_info,
+ * set_serial_info, get_modem_info, set_modem_info, and send_break
+ * from serial.c; expand cy_ioctl; relocate and expand config_setup;
+ * get flow control characters from tty struct; invalidate ports w/o
+ * hardware;
+ *
+ * Revision 1.6 1994/05/31 18:42:21 bentson
+ * add a loop-breaker in the interrupt service routine;
+ * note when port is initialized so that it can be shut
+ * down under the right conditions; receive works without
+ * any obvious errors
+ *
+ * Revision 1.5 1994/05/30 00:55:02 bentson
+ * transmit works without obvious errors
+ *
+ * Revision 1.4 1994/05/27 18:46:27 bentson
+ * incorporated more code from lib_y.c; can now print short
+ * strings under interrupt control to port zero; seems to
+ * select ports/channels/lines correctly
+ *
+ * Revision 1.3 1994/05/25 22:12:44 bentson
+ * shifting from multi-port on a card to proper multiplexor
+ * data structures; added skeletons of most routines
+ *
+ * Revision 1.2 1994/05/19 13:21:43 bentson
+ * start to crib from other sources
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/cyclades.h>
+#include <linux/delay.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+
+#define small_delay(x) for(j=0;j<x;j++)k++;
+
+
+#define SERIAL_PARANOIA_CHECK
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_THROTTLE
+#undef SERIAL_DEBUG_OTHER
+#undef SERIAL_DEBUG_IO
+#undef SERIAL_DEBUG_COUNT
+#undef SERIAL_DEBUG_DTR
+#undef CYCLOM_16Y_HACK
+#undef CYCLOM_ENABLE_MONITORING
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define WAKEUP_CHARS 256
+
+#define STD_COM_FLAGS (0)
+
+#define SERIAL_TYPE_NORMAL 1
+#define SERIAL_TYPE_CALLOUT 2
+
+
+DECLARE_TASK_QUEUE(tq_cyclades);
+
+struct tty_driver cy_serial_driver, cy_callout_driver;
+
+static volatile int cy_irq_triggered;
+static volatile int cy_triggered;
+static int cy_wild_int_mask;
+static unsigned char *intr_base_addr;
+
+/* This is the per-card data structure containing a card's base
+ address. Here are declarations for some common addresses.
+ Add entries to match your configuration (there are sixty-
+ four possible from 0x80000 to 0xFE000) */
+struct cyclades_card cy_card[] = {
+ /* BASE_ADDR */
+ {0xD0000},
+ {0xD2000},
+ {0xD4000},
+ {0xD6000},
+ {0xD8000},
+ {0xDA000},
+ {0xDC000},
+ {0xDE000}
+};
+
+#define NR_CARDS (sizeof(cy_card)/sizeof(struct cyclades_card))
+
+/* The Cyclom-Ye has placed the sequential chips in non-sequential
+ * address order. This look-up table overcomes that problem.
+ */
+int cy_chip_offset [] =
+ { 0x0000,
+ 0x0400,
+ 0x0800,
+ 0x0C00,
+ 0x0200,
+ 0x0600,
+ 0x0A00,
+ 0x0E00
+ };
+
+/* This is the per-port data structure */
+struct cyclades_port cy_port[] = {
+ /* CARD# */
+ {-1 }, /* ttyC0 */
+ {-1 }, /* ttyC1 */
+ {-1 }, /* ttyC2 */
+ {-1 }, /* ttyC3 */
+ {-1 }, /* ttyC4 */
+ {-1 }, /* ttyC5 */
+ {-1 }, /* ttyC6 */
+ {-1 }, /* ttyC7 */
+ {-1 }, /* ttyC8 */
+ {-1 }, /* ttyC9 */
+ {-1 }, /* ttyC10 */
+ {-1 }, /* ttyC11 */
+ {-1 }, /* ttyC12 */
+ {-1 }, /* ttyC13 */
+ {-1 }, /* ttyC14 */
+ {-1 }, /* ttyC15 */
+ {-1 }, /* ttyC16 */
+ {-1 }, /* ttyC17 */
+ {-1 }, /* ttyC18 */
+ {-1 }, /* ttyC19 */
+ {-1 }, /* ttyC20 */
+ {-1 }, /* ttyC21 */
+ {-1 }, /* ttyC22 */
+ {-1 }, /* ttyC23 */
+ {-1 }, /* ttyC24 */
+ {-1 }, /* ttyC25 */
+ {-1 }, /* ttyC26 */
+ {-1 }, /* ttyC27 */
+ {-1 }, /* ttyC28 */
+ {-1 }, /* ttyC29 */
+ {-1 }, /* ttyC30 */
+ {-1 } /* ttyC31 */
+};
+#define NR_PORTS (sizeof(cy_port)/sizeof(struct cyclades_port))
+
+static int serial_refcount;
+
+static struct tty_struct *serial_table[NR_PORTS];
+static struct termios *serial_termios[NR_PORTS];
+static struct termios *serial_termios_locked[NR_PORTS];
+
+
+/* This is the per-irq data structure,
+ it maps an irq to the corresponding card */
+struct cyclades_card * IRQ_cards[16];
+
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write. We need to
+ * lock it in case the memcpy_fromfs blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char *tmp_buf = 0;
+static struct semaphore tmp_buf_sem = MUTEX;
+
+/*
+ * This is used to look up the divisor speeds and the timeouts
+ * We're normally limited to 15 distinct baud rates. The extra
+ * are accessed via settings in info->flags.
+ * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ * HI VHI
+ */
+static int baud_table[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200,
+ 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800,115200,150000,
+ 0};
+
+static char baud_co[] = { /* 25 MHz clock option table */
+ /* value => 00 01 02 03 04 */
+ /* divide by 8 32 128 512 2048 */
+ 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02,
+ 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+static char baud_bpr[] = { /* 25 MHz baud rate period table */
+ 0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3,
+ 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15};
+
+static char baud_cor3[] = { /* receive threshold */
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07};
+
+
+
+static void shutdown(struct cyclades_port *);
+static int startup (struct cyclades_port *);
+static void cy_throttle(struct tty_struct *);
+static void cy_unthrottle(struct tty_struct *);
+static void config_setup(struct cyclades_port *);
+extern void console_print(const char *);
+#ifdef CYCLOM_SHOW_STATUS
+static void show_status(int);
+#endif
+
+
+
+static inline int
+serial_paranoia_check(struct cyclades_port *info,
+ dev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+ static const char *badmagic =
+ "Warning: bad magic number for serial struct (%d, %d) in %s\n";
+ static const char *badinfo =
+ "Warning: null cyclades_port for (%d, %d) in %s\n";
+ static const char *badrange =
+ "Warning: cyclades_port out of range for (%d, %d) in %s\n";
+
+ if (!info) {
+ printk(badinfo, MAJOR(device), MINOR(device), routine);
+ return 1;
+ }
+
+ if( (long)info < (long)(&cy_port[0])
+ || (long)(&cy_port[NR_PORTS]) < (long)info ){
+ printk(badrange, MAJOR(device), MINOR(device), routine);
+ return 1;
+ }
+
+ if (info->magic != CYCLADES_MAGIC) {
+ printk(badmagic, MAJOR(device), MINOR(device), routine);
+ return 1;
+ }
+#endif
+ return 0;
+} /* serial_paranoia_check */
+
+/* The following diagnostic routines allow the driver to spew
+ information on the screen, even (especially!) during interrupts.
+ */
+void
+SP(char *data){
+ unsigned long flags;
+ save_flags(flags); cli();
+ console_print(data);
+ restore_flags(flags);
+}
+char scrn[2];
+void
+CP(char data){
+ unsigned long flags;
+ save_flags(flags); cli();
+ scrn[0] = data;
+ console_print(scrn);
+ restore_flags(flags);
+}/* CP */
+
+void CP1(int data) { (data<10)? CP(data+'0'): CP(data+'A'-10); }/* CP1 */
+void CP2(int data) { CP1((data>>4) & 0x0f); CP1( data & 0x0f); }/* CP2 */
+void CP4(int data) { CP2((data>>8) & 0xff); CP2(data & 0xff); }/* CP4 */
+void CP8(long data) { CP4((data>>16) & 0xffff); CP4(data & 0xffff); }/* CP8 */
+
+
+/* This routine waits up to 1000 micro-seconds for the previous
+ command to the Cirrus chip to complete and then issues the
+ new command. An error is returned if the previous command
+ didn't finish within the time limit.
+ */
+u_short
+write_cy_cmd(u_char *base_addr, u_char cmd)
+{
+ unsigned long flags;
+ volatile int i;
+
+ save_flags(flags); cli();
+ /* Check to see that the previous command has completed */
+ for(i = 0 ; i < 100 ; i++){
+ if (base_addr[CyCCR] == 0){
+ break;
+ }
+ udelay(10L);
+ }
+ /* if the CCR never cleared, the previous command
+ didn't finish within the "reasonable time" */
+ if ( i == 10 ) {
+ restore_flags(flags);
+ return (-1);
+ }
+
+ /* Issue the new command */
+ base_addr[CyCCR] = cmd;
+ restore_flags(flags);
+ return(0);
+} /* write_cy_cmd */
+
+
+/* cy_start and cy_stop provide software output flow control as a
+ function of XON/XOFF, software CTS, and other such stuff. */
+
+static void
+cy_stop(struct tty_struct *tty)
+{
+ struct cyclades_card *cinfo;
+ struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+ unsigned char *base_addr;
+ int chip,channel;
+ unsigned long flags;
+
+#ifdef SERIAL_DEBUG_OTHER
+ printk("cy_stop ttyC%d\n", info->line); /* */
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "cy_stop"))
+ return;
+
+ cinfo = &cy_card[info->card];
+ channel = info->line - cinfo->first_line;
+ chip = channel>>2;
+ channel &= 0x03;
+ base_addr = (unsigned char*)
+ (cy_card[info->card].base_addr + cy_chip_offset[chip]);
+
+ save_flags(flags); cli();
+ base_addr[CyCAR] = (u_char)(channel & 0x0003); /* index channel */
+ base_addr[CySRER] &= ~CyTxMpty;
+ restore_flags(flags);
+
+ return;
+} /* cy_stop */
+
+static void
+cy_start(struct tty_struct *tty)
+{
+ struct cyclades_card *cinfo;
+ struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+ unsigned char *base_addr;
+ int chip,channel;
+ unsigned long flags;
+
+#ifdef SERIAL_DEBUG_OTHER
+ printk("cy_start ttyC%d\n", info->line); /* */
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "cy_start"))
+ return;
+
+ cinfo = &cy_card[info->card];
+ channel = info->line - cinfo->first_line;
+ chip = channel>>2;
+ channel &= 0x03;
+ base_addr = (unsigned char*)
+ (cy_card[info->card].base_addr + cy_chip_offset[chip]);
+
+ save_flags(flags); cli();
+ base_addr[CyCAR] = (u_char)(channel & 0x0003);
+ base_addr[CySRER] |= CyTxMpty;
+ restore_flags(flags);
+
+ return;
+} /* cy_start */
+
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver
+ * (also known as the "bottom half"). This can be called any
+ * number of times for any channel without harm.
+ */
+static inline void
+cy_sched_event(struct cyclades_port *info, int event)
+{
+ info->event |= 1 << event; /* remember what kind of event and who */
+ queue_task_irq_off(&info->tqueue, &tq_cyclades); /* it belongs to */
+ mark_bh(CYCLADES_BH); /* then trigger event */
+} /* cy_sched_event */
+
+
+
+/*
+ * This interrupt routine is used
+ * while we are probing for submarines.
+ */
+static void
+cy_probe(int irq, struct pt_regs *regs)
+{
+ cy_irq_triggered = irq;
+ cy_triggered |= 1 << irq;
+ return;
+} /* cy_probe */
+
+/* The real interrupt service routine is called
+ whenever the card wants its hand held--chars
+ received, out buffer empty, modem change, etc.
+ */
+static void
+cy_interrupt(int irq, struct pt_regs *regs)
+{
+ struct tty_struct *tty;
+ int status;
+ struct cyclades_card *cinfo;
+ struct cyclades_port *info;
+ volatile unsigned char *base_addr, *card_base_addr;
+ int chip;
+ int save_xir, channel, save_car;
+ char data;
+ volatile char vdata;
+ int char_count;
+ int outch;
+ int i,j;
+ int too_many;
+ int had_work;
+ int mdm_change;
+ int mdm_status;
+
+ if((cinfo = IRQ_cards[irq]) == 0){
+ return; /* spurious interrupt */
+ }
+
+ /* This loop checks all chips in the card. Make a note whenever
+ _any_ chip had some work to do, as this is considered an
+ indication that there will be more to do. Only when no chip
+ has any work does this outermost loop exit.
+ */
+ do{
+ had_work = 0;
+ for ( chip = 0 ; chip < cinfo->num_chips ; chip ++) {
+ base_addr = (unsigned char *)
+ (cinfo->base_addr + cy_chip_offset[chip]);
+ too_many = 0;
+ while ( (status = base_addr[CySVRR]) != 0x00) {
+ had_work++;
+ /* The purpose of the following test is to ensure that
+ no chip can monopolize the driver. This forces the
+ chips to be checked in a round-robin fashion (after
+ draining each of a bunch (1000) of characters).
+ */
+ if(1000<too_many++){
+ break;
+ }
+ if (status & CySRReceive) { /* reception interrupt */
+
+ /* determine the channel and change to that context */
+ save_xir = (u_char) base_addr[CyRIR];
+ channel = (u_short ) (save_xir & CyIRChannel);
+ i = channel + chip * 4 + cinfo->first_line;
+ info = &cy_port[i];
+ info->last_active = jiffies;
+ save_car = base_addr[CyCAR];
+ base_addr[CyCAR] = save_xir;
+
+ /* if there is nowhere to put the data, discard it */
+ if(info->tty == 0){
+ j = (base_addr[CyRIVR] & CyIVRMask);
+ if ( j == CyIVRRxEx ) { /* exception */
+ data = base_addr[CyRDSR];
+ } else { /* normal character reception */
+ char_count = base_addr[CyRDCR];
+ while(char_count--){
+ data = base_addr[CyRDSR];
+ }
+ }
+ }else{ /* there is an open port for this data */
+ tty = info->tty;
+ j = (base_addr[CyRIVR] & CyIVRMask);
+ if ( j == CyIVRRxEx ) { /* exception */
+ data = base_addr[CyRDSR];
+ if(data & info->ignore_status_mask){
+ continue;
+ }
+ if (tty->flip.count < TTY_FLIPBUF_SIZE){
+ tty->flip.count++;
+ if (data & info->read_status_mask){
+ if(data & CyBREAK){
+ *tty->flip.flag_buf_ptr++ =
+ TTY_BREAK;
+ *tty->flip.char_buf_ptr++ =
+ base_addr[CyRDSR];
+ if (info->flags & ASYNC_SAK){
+ do_SAK(tty);
+ }
+ }else if(data & CyFRAME){
+ *tty->flip.flag_buf_ptr++ =
+ TTY_FRAME;
+ *tty->flip.char_buf_ptr++ =
+ base_addr[CyRDSR];
+ }else if(data & CyPARITY){
+ *tty->flip.flag_buf_ptr++ =
+ TTY_PARITY;
+ *tty->flip.char_buf_ptr++ =
+ base_addr[CyRDSR];
+ }else if(data & CyOVERRUN){
+ *tty->flip.flag_buf_ptr++ =
+ TTY_OVERRUN;
+ *tty->flip.char_buf_ptr++ = 0;
+ /* If the flip buffer itself is
+ overflowing, we still loose
+ the next incoming character.
+ */
+ if(tty->flip.count < TTY_FLIPBUF_SIZE){
+ tty->flip.count++;
+ *tty->flip.flag_buf_ptr++ =
+ TTY_NORMAL;
+ *tty->flip.char_buf_ptr++ =
+ base_addr[CyRDSR];
+ }
+ /* These two conditions may imply */
+ /* a normal read should be done. */
+ /* }else if(data & CyTIMEOUT){ */
+ /* }else if(data & CySPECHAR){ */
+ }else{
+ *tty->flip.flag_buf_ptr++ = 0;
+ *tty->flip.char_buf_ptr++ = 0;
+ }
+ }else{
+ *tty->flip.flag_buf_ptr++ = 0;
+ *tty->flip.char_buf_ptr++ = 0;
+ }
+ }else{
+ /* there was a software buffer overrun
+ and nothing could be done about it!!! */
+ }
+ } else { /* normal character reception */
+ /* load # characters available from the chip */
+ char_count = base_addr[CyRDCR];
+
+#ifdef CYCLOM_ENABLE_MONITORING
+ ++info->mon.int_count;
+ info->mon.char_count += char_count;
+ if (char_count > info->mon.char_max)
+ info->mon.char_max = char_count;
+ info->mon.char_last = char_count;
+#endif
+ while(char_count--){
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE){
+ break;
+ }
+ tty->flip.count++;
+ data = base_addr[CyRDSR];
+ *tty->flip.flag_buf_ptr++ = TTY_NORMAL;
+ *tty->flip.char_buf_ptr++ = data;
+#ifdef CYCLOM_16Y_HACK
+ udelay(10L);
+#endif
+ }
+ }
+ queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+ }
+ /* end of service */
+ base_addr[CyRIR] = (save_xir & 0x3f);
+ base_addr[CyCAR] = (save_car);
+ }
+
+
+ if (status & CySRTransmit) { /* transmission interrupt */
+ /* Since we only get here when the transmit buffer is empty,
+ we know we can always stuff a dozen characters. */
+
+ /* determine the channel and change to that context */
+ save_xir = (u_char) base_addr[CyTIR];
+ channel = (u_short ) (save_xir & CyIRChannel);
+ i = channel + chip * 4 + cinfo->first_line;
+ save_car = base_addr[CyCAR];
+ base_addr[CyCAR] = save_xir;
+
+ /* validate the port number (as configured and open) */
+ if( (i < 0) || (NR_PORTS <= i) ){
+ base_addr[CySRER] &= ~CyTxMpty;
+ goto txend;
+ }
+ info = &cy_port[i];
+ info->last_active = jiffies;
+ if(info->tty == 0){
+ base_addr[CySRER] &= ~CyTxMpty;
+ goto txdone;
+ }
+
+ /* load the on-chip space available for outbound data */
+ char_count = info->xmit_fifo_size;
+
+
+ if(info->x_char) { /* send special char */
+ outch = info->x_char;
+ base_addr[CyTDR] = outch;
+ char_count--;
+ info->x_char = 0;
+ }
+
+ if (info->x_break){
+ /* The Cirrus chip requires the "Embedded Transmit
+ Commands" of start break, delay, and end break
+ sequences to be sent. The duration of the
+ break is given in TICs, which runs at HZ
+ (typically 100) and the PPR runs at 200 Hz,
+ so the delay is duration * 200/HZ, and thus a
+ break can run from 1/100 sec to about 5/4 sec.
+ */
+ base_addr[CyTDR] = 0; /* start break */
+ base_addr[CyTDR] = 0x81;
+ base_addr[CyTDR] = 0; /* delay a bit */
+ base_addr[CyTDR] = 0x82;
+ base_addr[CyTDR] = info->x_break*200/HZ;
+ base_addr[CyTDR] = 0; /* terminate break */
+ base_addr[CyTDR] = 0x83;
+ char_count -= 7;
+ info->x_break = 0;
+ }
+
+ while (char_count-- > 0){
+ if (!info->xmit_cnt){
+ base_addr[CySRER] &= ~CyTxMpty;
+ goto txdone;
+ }
+ if (info->xmit_buf == 0){
+ base_addr[CySRER] &= ~CyTxMpty;
+ goto txdone;
+ }
+ if (info->tty->stopped || info->tty->hw_stopped){
+ base_addr[CySRER] &= ~CyTxMpty;
+ goto txdone;
+ }
+ /* Because the Embedded Transmit Commands have been
+ enabled, we must check to see if the escape
+ character, NULL, is being sent. If it is, we
+ must ensure that there is room for it to be
+ doubled in the output stream. Therefore we
+ no longer advance the pointer when the character
+ is fetched, but rather wait until after the check
+ for a NULL output character. (This is necessary
+ because there may not be room for the two chars
+ needed to send a NULL.
+ */
+ outch = info->xmit_buf[info->xmit_tail];
+ if( outch ){
+ info->xmit_cnt--;
+ info->xmit_tail = (info->xmit_tail + 1)
+ & (PAGE_SIZE - 1);
+ base_addr[CyTDR] = outch;
+ }else{
+ if(char_count > 1){
+ info->xmit_cnt--;
+ info->xmit_tail = (info->xmit_tail + 1)
+ & (PAGE_SIZE - 1);
+ base_addr[CyTDR] = outch;
+ base_addr[CyTDR] = 0;
+ char_count--;
+ }else{
+ }
+ }
+ }
+
+ txdone:
+ if (info->xmit_cnt < WAKEUP_CHARS) {
+ cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
+ }
+
+ txend:
+ /* end of service */
+ base_addr[CyTIR] = (save_xir & 0x3f);
+ base_addr[CyCAR] = (save_car);
+ }
+
+ if (status & CySRModem) { /* modem interrupt */
+
+ /* determine the channel and change to that context */
+ save_xir = (u_char) base_addr[CyMIR];
+ channel = (u_short ) (save_xir & CyIRChannel);
+ info = &cy_port[channel + chip * 4 + cinfo->first_line];
+ info->last_active = jiffies;
+ save_car = base_addr[CyCAR];
+ base_addr[CyCAR] = save_xir;
+
+ mdm_change = base_addr[CyMISR];
+ mdm_status = base_addr[CyMSVR1];
+
+ if(info->tty == 0){ /* nowhere to put the data, ignore it */
+ ;
+ }else{
+ if((mdm_change & CyDCD)
+ && (info->flags & ASYNC_CHECK_CD)){
+ if(mdm_status & CyDCD){
+/* CP('!'); */
+ cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP);
+ }else if(!((info->flags & ASYNC_CALLOUT_ACTIVE)
+ &&(info->flags & ASYNC_CALLOUT_NOHUP))){
+/* CP('@'); */
+ cy_sched_event(info, Cy_EVENT_HANGUP);
+ }
+ }
+ if((mdm_change & CyCTS)
+ && (info->flags & ASYNC_CTS_FLOW)){
+ if(info->tty->stopped){
+ if(mdm_status & CyCTS){
+ /* !!! cy_start isn't used because... */
+ info->tty->stopped = 0;
+ base_addr[CySRER] |= CyTxMpty;
+ cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
+ }
+ }else{
+ if(!(mdm_status & CyCTS)){
+ /* !!! cy_stop isn't used because... */
+ info->tty->stopped = 1;
+ base_addr[CySRER] &= ~CyTxMpty;
+ }
+ }
+ }
+ if(mdm_status & CyDSR){
+ }
+ if(mdm_status & CyRI){
+ }
+ }
+ /* end of service */
+ base_addr[CyMIR] = (save_xir & 0x3f);
+ base_addr[CyCAR] = save_car;
+ }
+ } /* end while status != 0 */
+ } /* end loop for chips... */
+ } while(had_work);
+
+ /* clear interrupts */
+ card_base_addr = (unsigned char *)cinfo->base_addr;
+ vdata = *(card_base_addr + Cy_ClrIntr); /* Cy_ClrIntr is 0x1800 */
+
+} /* cy_interrupt */
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * cy_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using cy_sched_event(), and they get done here.
+ *
+ * This is done through one level of indirection--the task queue.
+ * When a hardware interrupt service routine wants service by the
+ * driver's bottom half, it enqueues the appropriate tq_struct (one
+ * per port) to the tq_cyclades work queue and sets a request flag
+ * via mark_bh for processing that queue. When the time is right,
+ * do_cyclades_bh is called (because of the mark_bh) and it requests
+ * that the work queue be processed.
+ *
+ * Although this may seem unwieldy, it gives the system a way to
+ * pass an argument (in this case the pointer to the cyclades_port
+ * structure) to the bottom half of the driver. Previous kernels
+ * had to poll every port to see if that port needed servicing.
+ */
+static void
+do_cyclades_bh(void *unused)
+{
+ run_task_queue(&tq_cyclades);
+} /* do_cyclades_bh */
+
+static void
+do_softint(void *private_)
+{
+ struct cyclades_port *info = (struct cyclades_port *) private_;
+ struct tty_struct *tty;
+
+ tty = info->tty;
+ if (!tty)
+ return;
+
+ if (clear_bit(Cy_EVENT_HANGUP, &info->event)) {
+ tty_hangup(info->tty);
+ wake_up_interruptible(&info->open_wait);
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|
+ ASYNC_CALLOUT_ACTIVE);
+ }
+ if (clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) {
+ wake_up_interruptible(&info->open_wait);
+ }
+ if (clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) {
+ if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP))
+ && tty->ldisc.write_wakeup){
+ (tty->ldisc.write_wakeup)(tty);
+ }
+ wake_up_interruptible(&tty->write_wait);
+ }
+} /* do_softint */
+
+
+/*
+ * Grab all interrupts in preparation for doing an automatic irq
+ * detection. dontgrab is a mask of irq's _not_ to grab. Returns a
+ * mask of irq's which were grabbed and should therefore be freed
+ * using free_all_interrupts().
+ */
+static int
+grab_all_interrupts(int dontgrab)
+{
+ int irq_lines = 0;
+ int i, mask;
+
+ for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
+ if (!(mask & dontgrab)
+ && !request_irq(i, cy_probe, SA_INTERRUPT, "serial probe")) {
+ irq_lines |= mask;
+ }
+ }
+ return irq_lines;
+} /* grab_all_interrupts */
+
+/*
+ * Release all interrupts grabbed by grab_all_interrupts
+ */
+static void
+free_all_interrupts(int irq_lines)
+{
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ if (irq_lines & (1 << i))
+ free_irq(i);
+ }
+} /* free_all_interrupts */
+
+/*
+ * This routine returns a bitfield of "wild interrupts". Basically,
+ * any unclaimed interrupts which is flapping around.
+ */
+static int
+check_wild_interrupts(void)
+{
+ int i, mask;
+ int wild_interrupts = 0;
+ int irq_lines;
+ unsigned long timeout;
+ unsigned long flags;
+
+ /*Turn on interrupts (they may be off) */
+ save_flags(flags); sti();
+
+ irq_lines = grab_all_interrupts(0);
+
+ /*
+ * Delay for 0.1 seconds -- we use a busy loop since this may
+ * occur during the bootup sequence
+ */
+ timeout = jiffies+10;
+ while (timeout >= jiffies)
+ ;
+
+ cy_triggered = 0; /* Reset after letting things settle */
+
+ timeout = jiffies+10;
+ while (timeout >= jiffies)
+ ;
+
+ for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
+ if ((cy_triggered & (1 << i)) &&
+ (irq_lines & (1 << i))) {
+ wild_interrupts |= mask;
+ }
+ }
+ free_all_interrupts(irq_lines);
+ restore_flags(flags);
+ return wild_interrupts;
+} /* check_wild_interrupts */
+
+/*
+ * This routine is called by do_auto_irq(); it attempts to determine
+ * which interrupt a serial port is configured to use. It is not
+ * fool-proof, but it works a large part of the time.
+ */
+static int
+get_auto_irq(int card)
+{
+ unsigned long timeout;
+ unsigned char *base_addr;
+ int save_xir, save_car;
+ volatile char vdata;
+
+ base_addr = (unsigned char*) (cy_card[card].base_addr);
+ intr_base_addr = base_addr;
+
+ /*
+ * Enable interrupts and see who answers
+ */
+ cy_irq_triggered = 0;
+ cli();
+ base_addr[CyCAR] = 0;
+ write_cy_cmd(base_addr,CyCHAN_CTL|CyENB_XMTR);
+ base_addr[CySRER] |= CyTxMpty;
+ sti();
+
+ timeout = jiffies+2;
+ while (timeout >= jiffies) {
+ if (cy_irq_triggered)
+ break;
+ }
+ /*
+ * Now check to see if we got any business, and clean up.
+ */
+ cli();
+ if(intr_base_addr[CySVRR] != 0){
+ save_xir = (u_char) intr_base_addr[CyTIR];
+ save_car = intr_base_addr[CyCAR];
+ if ((save_xir & 0x3) != 0){
+ printk("channel %x requesting unexpected interrupt\n",save_xir);
+ }
+ intr_base_addr[CyCAR] = (save_xir & 0x3);
+ intr_base_addr[CySRER] &= ~CyTxMpty;
+ intr_base_addr[CyTIR] = (save_xir & 0x3f);
+ intr_base_addr[CyCAR] = (save_car);
+ vdata = *(intr_base_addr + Cy_ClrIntr); /* Cy_ClrIntr is 0x1800 */
+ }
+ sti();
+
+ return(cy_irq_triggered);
+} /* get_auto_irq */
+
+/*
+ * Calls get_auto_irq() multiple times, to make sure we don't get
+ * faked out by random interrupts
+ */
+static int
+do_auto_irq(int card)
+{
+ int irq_lines = 0;
+ int irq_try_1 = 0, irq_try_2 = 0;
+ int retries;
+ unsigned long flags;
+
+ /* Turn on interrupts (they may be off) */
+ save_flags(flags); sti();
+
+ cy_wild_int_mask = check_wild_interrupts();
+
+ irq_lines = grab_all_interrupts(cy_wild_int_mask);
+
+ for (retries = 0; retries < 5; retries++) {
+ if (!irq_try_1)
+ irq_try_1 = get_auto_irq(card);
+ if (!irq_try_2)
+ irq_try_2 = get_auto_irq(card);
+ if (irq_try_1 && irq_try_2) {
+ if (irq_try_1 == irq_try_2)
+ break;
+ irq_try_1 = irq_try_2 = 0;
+ }
+ }
+ restore_flags(flags);
+ free_all_interrupts(irq_lines);
+ return (irq_try_1 == irq_try_2) ? irq_try_1 : 0;
+} /* do_auto_irq */
+
+
+/* This is called whenever a port becomes active;
+ interrupts are enabled and DTR & RTS are turned on.
+ */
+static int
+startup(struct cyclades_port * info)
+{
+ unsigned long flags;
+ unsigned char *base_addr;
+ int card,chip,channel;
+
+ if (info->flags & ASYNC_INITIALIZED){
+ return 0;
+ }
+
+ if (!info->type){
+ if (info->tty){
+ set_bit(TTY_IO_ERROR, &info->tty->flags);
+ }
+ return 0;
+ }
+ if (!info->xmit_buf){
+ info->xmit_buf = (unsigned char *) get_free_page (GFP_KERNEL);
+ if (!info->xmit_buf){
+ return -ENOMEM;
+ }
+ }
+
+ config_setup(info);
+
+ card = info->card;
+ channel = (info->line) - (cy_card[card].first_line);
+ chip = channel>>2;
+ channel &= 0x03;
+ base_addr = (unsigned char*)
+ (cy_card[card].base_addr + cy_chip_offset[chip]);
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("startup card %d, chip %d, channel %d, base_addr %lx",
+ card, chip, channel, (long)base_addr);/**/
+#endif
+
+ save_flags(flags); cli();
+ base_addr[CyCAR] = (u_char)channel;
+
+ base_addr[CyRTPR] = (info->default_timeout
+ ? info->default_timeout
+ : 0x02); /* 10ms rx timeout */
+
+ write_cy_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR);
+
+ base_addr[CyCAR] = (u_char)channel; /* !!! Is this needed? */
+ base_addr[CyMSVR1] = CyRTS;
+/* CP('S');CP('1'); */
+ base_addr[CyMSVR2] = CyDTR;
+
+#ifdef SERIAL_DEBUG_DTR
+ printk("cyc: %d: raising DTR\n", __LINE__);
+ printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+
+ base_addr[CySRER] |= CyRxData;
+ info->flags |= ASYNC_INITIALIZED;
+
+ if (info->tty){
+ clear_bit(TTY_IO_ERROR, &info->tty->flags);
+ }
+ info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+ restore_flags(flags);
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk(" done\n");
+#endif
+ return 0;
+} /* startup */
+
+void
+start_xmit( struct cyclades_port *info )
+{
+ unsigned long flags;
+ unsigned char *base_addr;
+ int card,chip,channel;
+
+ card = info->card;
+ channel = (info->line) - (cy_card[card].first_line);
+ chip = channel>>2;
+ channel &= 0x03;
+ base_addr = (unsigned char*)
+ (cy_card[card].base_addr + cy_chip_offset[chip]);
+ save_flags(flags); cli();
+ base_addr[CyCAR] = channel;
+ base_addr[CySRER] |= CyTxMpty;
+ restore_flags(flags);
+} /* start_xmit */
+
+/*
+ * This routine shuts down a serial port; interrupts are disabled,
+ * and DTR is dropped if the hangup on close termio flag is on.
+ */
+static void
+shutdown(struct cyclades_port * info)
+{
+ unsigned long flags;
+ unsigned char *base_addr;
+ int card,chip,channel;
+
+ if (!(info->flags & ASYNC_INITIALIZED)){
+/* CP('$'); */
+ return;
+ }
+
+ card = info->card;
+ channel = info->line - cy_card[card].first_line;
+ chip = channel>>2;
+ channel &= 0x03;
+ base_addr = (unsigned char*)
+ (cy_card[card].base_addr + cy_chip_offset[chip]);
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("shutdown card %d, chip %d, channel %d, base_addr %lx\n",
+ card, chip, channel, (long)base_addr);
+#endif
+
+ /* !!! REALLY MUST WAIT FOR LAST CHARACTER TO BE
+ SENT BEFORE DROPPING THE LINE !!! (Perhaps
+ set some flag that is read when XMTY happens.)
+ Other choices are to delay some fixed interval
+ or schedule some later processing.
+ */
+ save_flags(flags); cli();
+ if (info->xmit_buf){
+ free_page((unsigned long) info->xmit_buf);
+ info->xmit_buf = 0;
+ }
+
+ base_addr[CyCAR] = (u_char)channel;
+ if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+ base_addr[CyMSVR1] = ~CyRTS;
+/* CP('C');CP('1'); */
+ base_addr[CyMSVR2] = ~CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+ printk("cyc: %d: dropping DTR\n", __LINE__);
+ printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+ }
+ write_cy_cmd(base_addr,CyCHAN_CTL|CyDIS_RCVR);
+ /* it may be appropriate to clear _XMIT at
+ some later date (after testing)!!! */
+
+ if (info->tty){
+ set_bit(TTY_IO_ERROR, &info->tty->flags);
+ }
+ info->flags &= ~ASYNC_INITIALIZED;
+ restore_flags(flags);
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk(" done\n");
+#endif
+ return;
+} /* shutdown */
+
+/*
+ * This routine finds or computes the various line characteristics.
+ */
+static void
+config_setup(struct cyclades_port * info)
+{
+ unsigned long flags;
+ unsigned char *base_addr;
+ int card,chip,channel;
+ unsigned cflag;
+ int i;
+
+ if (!info->tty || !info->tty->termios){
+ return;
+ }
+ if (info->line == -1){
+ return;
+ }
+ cflag = info->tty->termios->c_cflag;
+
+ /* baud rate */
+ i = cflag & CBAUD;
+#ifdef CBAUDEX
+/* Starting with kernel 1.1.65, there is direct support for
+ higher baud rates. The following code supports those
+ changes. The conditional aspect allows this driver to be
+ used for earlier as well as later kernel versions. (The
+ mapping is slightly different from serial.c because there
+ is still the possibility of supporting 75 kbit/sec with
+ the Cyclades board.)
+ */
+ if (i & CBAUDEX) {
+ if (i == B57600)
+ i = 16;
+ else if(i == B115200)
+ i = 18;
+#ifdef B78600
+ else if(i == B78600)
+ i = 17;
+#endif
+ else
+ info->tty->termios->c_cflag &= ~CBAUDEX;
+ }
+#endif
+ if (i == 15) {
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ i += 1;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ i += 3;
+ }
+ info->tbpr = baud_bpr[i]; /* Tx BPR */
+ info->tco = baud_co[i]; /* Tx CO */
+ info->rbpr = baud_bpr[i]; /* Rx BPR */
+ info->rco = baud_co[i]; /* Rx CO */
+ if (baud_table[i] == 134) {
+ info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
+ /* get it right for 134.5 baud */
+ } else if (baud_table[i]) {
+ info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2;
+ /* this needs to be propagated into the card info */
+ } else {
+ info->timeout = 0;
+ }
+ /* By tradition (is it a standard?) a baud rate of zero
+ implies the line should be/has been closed. A bit
+ later in this routine such a test is performed. */
+
+ /* byte size and parity */
+ info->cor5 = 0;
+ info->cor4 = 0;
+ info->cor3 = (info->default_threshold
+ ? info->default_threshold
+ : baud_cor3[i]); /* receive threshold */
+ info->cor2 = CyETC;
+ switch(cflag & CSIZE){
+ case CS5:
+ info->cor1 = Cy_5_BITS;
+ break;
+ case CS6:
+ info->cor1 = Cy_6_BITS;
+ break;
+ case CS7:
+ info->cor1 = Cy_7_BITS;
+ break;
+ case CS8:
+ info->cor1 = Cy_8_BITS;
+ break;
+ }
+ if(cflag & CSTOPB){
+ info->cor1 |= Cy_2_STOP;
+ }
+ if (cflag & PARENB){
+ if (cflag & PARODD){
+ info->cor1 |= CyPARITY_O;
+ }else{
+ info->cor1 |= CyPARITY_E;
+ }
+ }else{
+ info->cor1 |= CyPARITY_NONE;
+ }
+
+ /* CTS flow control flag */
+ if (cflag & CRTSCTS){
+ info->flags |= ASYNC_CTS_FLOW;
+ info->cor2 |= CyCtsAE;
+ }else{
+ info->flags &= ~ASYNC_CTS_FLOW;
+ info->cor2 &= ~CyCtsAE;
+ }
+ if (cflag & CLOCAL)
+ info->flags &= ~ASYNC_CHECK_CD;
+ else
+ info->flags |= ASYNC_CHECK_CD;
+
+ /***********************************************
+ The hardware option, CyRtsAO, presents RTS when
+ the chip has characters to send. Since most modems
+ use RTS as reverse (inbound) flow control, this
+ option is not used. If inbound flow control is
+ necessary, DTR can be programmed to provide the
+ appropriate signals for use with a non-standard
+ cable. Contact Marcio Saito for details.
+ ***********************************************/
+
+ card = info->card;
+ channel = (info->line) - (cy_card[card].first_line);
+ chip = channel>>2;
+ channel &= 0x03;
+ base_addr = (unsigned char*)
+ (cy_card[card].base_addr + cy_chip_offset[chip]);
+
+ save_flags(flags); cli();
+ base_addr[CyCAR] = (u_char)channel;
+
+ /* tx and rx baud rate */
+
+ base_addr[CyTCOR] = info->tco;
+ base_addr[CyTBPR] = info->tbpr;
+ base_addr[CyRCOR] = info->rco;
+ base_addr[CyRBPR] = info->rbpr;
+
+ /* set line characteristics according configuration */
+
+ base_addr[CySCHR1] = START_CHAR(info->tty);
+ base_addr[CySCHR2] = STOP_CHAR(info->tty);
+ base_addr[CyCOR1] = info->cor1;
+ base_addr[CyCOR2] = info->cor2;
+ base_addr[CyCOR3] = info->cor3;
+ base_addr[CyCOR4] = info->cor4;
+ base_addr[CyCOR5] = info->cor5;
+
+ write_cy_cmd(base_addr,CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch);
+
+ base_addr[CyCAR] = (u_char)channel; /* !!! Is this needed? */
+
+ base_addr[CyRTPR] = (info->default_timeout
+ ? info->default_timeout
+ : 0x02); /* 10ms rx timeout */
+
+ if (C_CLOCAL(info->tty)) {
+ base_addr[CySRER] |= 0; /* without modem intr */
+ /* ignore 1->0 modem transitions */
+ base_addr[CyMCOR1] = 0x0;
+ /* ignore 0->1 modem transitions */
+ base_addr[CyMCOR2] = 0x0;
+ } else {
+ base_addr[CySRER] |= CyMdmCh; /* with modem intr */
+ /* act on 1->0 modem transitions */
+ base_addr[CyMCOR1] = CyDSR|CyCTS|CyRI|CyDCD;
+ /* act on 0->1 modem transitions */
+ base_addr[CyMCOR2] = CyDSR|CyCTS|CyRI|CyDCD;
+ }
+
+ if(i == 0){ /* baud rate is zero, turn off line */
+ base_addr[CyMSVR2] = ~CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+ printk("cyc: %d: dropping DTR\n", __LINE__);
+ printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+ }else{
+ base_addr[CyMSVR2] = CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+ printk("cyc: %d: raising DTR\n", __LINE__);
+ printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+ }
+
+ if (info->tty){
+ clear_bit(TTY_IO_ERROR, &info->tty->flags);
+ }
+
+ restore_flags(flags);
+
+} /* config_setup */
+
+
+static void
+cy_put_char(struct tty_struct *tty, unsigned char ch)
+{
+ struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+ unsigned long flags;
+
+#ifdef SERIAL_DEBUG_IO
+ printk("cy_put_char ttyC%d\n", info->line);
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "cy_put_char"))
+ return;
+
+ if (!tty || !info->xmit_buf)
+ return;
+
+ save_flags(flags); cli();
+ if (info->xmit_cnt >= PAGE_SIZE - 1) {
+ restore_flags(flags);
+ return;
+ }
+
+ info->xmit_buf[info->xmit_head++] = ch;
+ info->xmit_head &= PAGE_SIZE - 1;
+ info->xmit_cnt++;
+ restore_flags(flags);
+} /* cy_put_char */
+
+
+static void
+cy_flush_chars(struct tty_struct *tty)
+{
+ struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+ unsigned long flags;
+ unsigned char *base_addr;
+ int card,chip,channel;
+
+#ifdef SERIAL_DEBUG_IO
+ printk("cy_flush_chars ttyC%d\n", info->line); /* */
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "cy_flush_chars"))
+ return;
+
+ if (info->xmit_cnt <= 0 || tty->stopped
+ || tty->hw_stopped || !info->xmit_buf)
+ return;
+
+ card = info->card;
+ channel = info->line - cy_card[card].first_line;
+ chip = channel>>2;
+ channel &= 0x03;
+ base_addr = (unsigned char*)
+ (cy_card[card].base_addr + cy_chip_offset[chip]);
+
+ save_flags(flags); cli();
+ base_addr[CyCAR] = channel;
+ base_addr[CySRER] |= CyTxMpty;
+ restore_flags(flags);
+} /* cy_flush_chars */
+
+
+/* This routine gets called when tty_write has put something into
+ the write_queue. If the port is not already transmitting stuff,
+ start it off by enabling interrupts. The interrupt service
+ routine will then ensure that the characters are sent. If the
+ port is already active, there is no need to kick it.
+ */
+static int
+cy_write(struct tty_struct * tty, int from_user,
+ unsigned char *buf, int count)
+{
+ struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+ unsigned long flags;
+ int c, total = 0;
+
+#ifdef SERIAL_DEBUG_IO
+ printk("cy_write ttyC%d\n", info->line); /* */
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "cy_write")){
+ return 0;
+ }
+
+ if (!tty || !info->xmit_buf || !tmp_buf){
+ return 0;
+ }
+
+ while (1) {
+ save_flags(flags); cli();
+ c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - info->xmit_head));
+ if (c <= 0){
+ restore_flags(flags);
+ break;
+ }
+
+ if (from_user) {
+ down(&tmp_buf_sem);
+ memcpy_fromfs(tmp_buf, buf, c);
+ c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - info->xmit_head));
+ memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
+ up(&tmp_buf_sem);
+ } else
+ memcpy(info->xmit_buf + info->xmit_head, buf, c);
+ info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+ info->xmit_cnt += c;
+ restore_flags(flags);
+ buf += c;
+ count -= c;
+ total += c;
+ }
+
+
+ if (info->xmit_cnt
+ && !tty->stopped
+ && !tty->hw_stopped ) {
+ start_xmit(info);
+ }
+ return total;
+} /* cy_write */
+
+
+static int
+cy_write_room(struct tty_struct *tty)
+{
+ struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+ int ret;
+
+#ifdef SERIAL_DEBUG_IO
+ printk("cy_write_room ttyC%d\n", info->line); /* */
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "cy_write_room"))
+ return 0;
+ ret = PAGE_SIZE - info->xmit_cnt - 1;
+ if (ret < 0)
+ ret = 0;
+ return ret;
+} /* cy_write_room */
+
+
+static int
+cy_chars_in_buffer(struct tty_struct *tty)
+{
+ struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+
+#ifdef SERIAL_DEBUG_IO
+ printk("cy_chars_in_buffer ttyC%d %d\n", info->line, info->xmit_cnt); /* */
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "cy_chars_in_buffer"))
+ return 0;
+
+ return info->xmit_cnt;
+} /* cy_chars_in_buffer */
+
+
+static void
+cy_flush_buffer(struct tty_struct *tty)
+{
+ struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+ unsigned long flags;
+
+#ifdef SERIAL_DEBUG_IO
+ printk("cy_flush_buffer ttyC%d\n", info->line); /* */
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "cy_flush_buffer"))
+ return;
+ save_flags(flags); cli();
+ info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+ restore_flags(flags);
+ wake_up_interruptible(&tty->write_wait);
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+ && tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+} /* cy_flush_buffer */
+
+
+/* This routine is called by the upper-layer tty layer to signal
+ that incoming characters should be throttled or that the
+ throttle should be released.
+ */
+static void
+cy_throttle(struct tty_struct * tty)
+{
+ struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+ unsigned long flags;
+ unsigned char *base_addr;
+ int card,chip,channel;
+
+#ifdef SERIAL_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("throttle %s: %d....\n", _tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+ printk("cy_throttle ttyC%d\n", info->line);
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){
+ return;
+ }
+
+ if (I_IXOFF(tty)) {
+ info->x_char = STOP_CHAR(tty);
+ /* Should use the "Send Special Character" feature!!! */
+ }
+
+ card = info->card;
+ channel = info->line - cy_card[card].first_line;
+ chip = channel>>2;
+ channel &= 0x03;
+ base_addr = (unsigned char*)
+ (cy_card[card].base_addr + cy_chip_offset[chip]);
+
+ save_flags(flags); cli();
+ base_addr[CyCAR] = (u_char)channel;
+ base_addr[CyMSVR1] = ~CyRTS;
+ restore_flags(flags);
+
+ return;
+} /* cy_throttle */
+
+
+static void
+cy_unthrottle(struct tty_struct * tty)
+{
+ struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+ unsigned long flags;
+ unsigned char *base_addr;
+ int card,chip,channel;
+
+#ifdef SERIAL_DEBUG_THROTTLE
+ char buf[64];
+
+ printk("throttle %s: %d....\n", _tty_name(tty, buf),
+ tty->ldisc.chars_in_buffer(tty));
+ printk("cy_unthrottle ttyC%d\n", info->line);
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){
+ return;
+ }
+
+ if (I_IXOFF(tty)) {
+ info->x_char = START_CHAR(tty);
+ /* Should use the "Send Special Character" feature!!! */
+ }
+
+ card = info->card;
+ channel = info->line - cy_card[card].first_line;
+ chip = channel>>2;
+ channel &= 0x03;
+ base_addr = (unsigned char*)
+ (cy_card[card].base_addr + cy_chip_offset[chip]);
+
+ save_flags(flags); cli();
+ base_addr[CyCAR] = (u_char)channel;
+ base_addr[CyMSVR1] = CyRTS;
+ restore_flags(flags);
+
+ return;
+} /* cy_unthrottle */
+
+static int
+get_serial_info(struct cyclades_port * info,
+ struct serial_struct * retinfo)
+{
+ struct serial_struct tmp;
+ struct cyclades_card *cinfo = &cy_card[info->card];
+
+/* CP('g'); */
+ if (!retinfo)
+ return -EFAULT;
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.type = info->type;
+ tmp.line = info->line;
+ tmp.port = info->card * 0x100 + info->line - cinfo->first_line;
+ tmp.irq = cinfo->irq;
+ tmp.flags = info->flags;
+ tmp.baud_base = 0; /*!!!*/
+ tmp.close_delay = info->close_delay;
+ tmp.custom_divisor = 0; /*!!!*/
+ tmp.hub6 = 0; /*!!!*/
+ memcpy_tofs(retinfo,&tmp,sizeof(*retinfo));
+ return 0;
+} /* get_serial_info */
+
+static int
+set_serial_info(struct cyclades_port * info,
+ struct serial_struct * new_info)
+{
+ struct serial_struct new_serial;
+ struct cyclades_port old_info;
+
+/* CP('s'); */
+ if (!new_info)
+ return -EFAULT;
+ memcpy_fromfs(&new_serial,new_info,sizeof(new_serial));
+ old_info = *info;
+
+ if (!suser()) {
+ if ((new_serial.close_delay != info->close_delay) ||
+ ((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) !=
+ (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK)))
+ return -EPERM;
+ info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+ (new_serial.flags & ASYNC_USR_MASK));
+ goto check_and_exit;
+ }
+
+
+ /*
+ * OK, past this point, all the error checking has been done.
+ * At this point, we start making changes.....
+ */
+
+ info->flags = ((info->flags & ~ASYNC_FLAGS) |
+ (new_serial.flags & ASYNC_FLAGS));
+ info->close_delay = new_serial.close_delay;
+
+
+check_and_exit:
+ if (info->flags & ASYNC_INITIALIZED){
+ config_setup(info);
+ return 0;
+ }else{
+ return startup(info);
+ }
+} /* set_serial_info */
+
+static int
+get_modem_info(struct cyclades_port * info, unsigned int *value)
+{
+ int card,chip,channel;
+ unsigned char *base_addr;
+ unsigned long flags;
+ unsigned char status;
+ unsigned int result;
+
+ card = info->card;
+ channel = (info->line) - (cy_card[card].first_line);
+ chip = channel>>2;
+ channel &= 0x03;
+ base_addr = (unsigned char*)
+ (cy_card[card].base_addr + cy_chip_offset[chip]);
+
+ save_flags(flags); cli();
+ base_addr[CyCAR] = (u_char)channel;
+ status = base_addr[CyMSVR1] | base_addr[CyMSVR2];
+ restore_flags(flags);
+
+ result = ((status & CyRTS) ? TIOCM_RTS : 0)
+ | ((status & CyDTR) ? TIOCM_DTR : 0)
+ | ((status & CyDCD) ? TIOCM_CAR : 0)
+ | ((status & CyRI) ? TIOCM_RNG : 0)
+ | ((status & CyDSR) ? TIOCM_DSR : 0)
+ | ((status & CyCTS) ? TIOCM_CTS : 0);
+ put_fs_long(result,(unsigned long *) value);
+ return 0;
+} /* get_modem_info */
+
+static int
+set_modem_info(struct cyclades_port * info, unsigned int cmd,
+ unsigned int *value)
+{
+ int card,chip,channel;
+ unsigned char *base_addr;
+ unsigned long flags;
+ unsigned int arg = get_fs_long((unsigned long *) value);
+
+ card = info->card;
+ channel = (info->line) - (cy_card[card].first_line);
+ chip = channel>>2;
+ channel &= 0x03;
+ base_addr = (unsigned char*)
+ (cy_card[card].base_addr + cy_chip_offset[chip]);
+
+ switch (cmd) {
+ case TIOCMBIS:
+ if (arg & TIOCM_RTS){
+ save_flags(flags); cli();
+ base_addr[CyCAR] = (u_char)channel;
+ base_addr[CyMSVR1] = CyRTS;
+ restore_flags(flags);
+ }
+ if (arg & TIOCM_DTR){
+ save_flags(flags); cli();
+ base_addr[CyCAR] = (u_char)channel;
+/* CP('S');CP('2'); */
+ base_addr[CyMSVR2] = CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+ printk("cyc: %d: raising DTR\n", __LINE__);
+ printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+ restore_flags(flags);
+ }
+ break;
+ case TIOCMBIC:
+ if (arg & TIOCM_RTS){
+ save_flags(flags); cli();
+ base_addr[CyCAR] = (u_char)channel;
+ base_addr[CyMSVR1] = ~CyRTS;
+ restore_flags(flags);
+ }
+ if (arg & TIOCM_DTR){
+ save_flags(flags); cli();
+ base_addr[CyCAR] = (u_char)channel;
+/* CP('C');CP('2'); */
+ base_addr[CyMSVR2] = ~CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+ printk("cyc: %d: dropping DTR\n", __LINE__);
+ printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+ restore_flags(flags);
+ }
+ break;
+ case TIOCMSET:
+ if (arg & TIOCM_RTS){
+ save_flags(flags); cli();
+ base_addr[CyCAR] = (u_char)channel;
+ base_addr[CyMSVR1] = CyRTS;
+ restore_flags(flags);
+ }else{
+ save_flags(flags); cli();
+ base_addr[CyCAR] = (u_char)channel;
+ base_addr[CyMSVR1] = ~CyRTS;
+ restore_flags(flags);
+ }
+ if (arg & TIOCM_DTR){
+ save_flags(flags); cli();
+ base_addr[CyCAR] = (u_char)channel;
+/* CP('S');CP('3'); */
+ base_addr[CyMSVR2] = CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+ printk("cyc: %d: raising DTR\n", __LINE__);
+ printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+ restore_flags(flags);
+ }else{
+ save_flags(flags); cli();
+ base_addr[CyCAR] = (u_char)channel;
+/* CP('C');CP('3'); */
+ base_addr[CyMSVR2] = ~CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+ printk("cyc: %d: dropping DTR\n", __LINE__);
+ printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+ restore_flags(flags);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+} /* set_modem_info */
+
+static void
+send_break( struct cyclades_port * info, int duration)
+{ /* Let the transmit ISR take care of this (since it
+ requires stuffing characters into the output stream).
+ */
+ info->x_break = duration;
+ if (!info->xmit_cnt ) {
+ start_xmit(info);
+ }
+} /* send_break */
+
+static int
+get_mon_info(struct cyclades_port * info, struct cyclades_monitor * mon)
+{
+
+ memcpy_tofs(mon, &info->mon, sizeof(struct cyclades_monitor));
+ info->mon.int_count = 0;
+ info->mon.char_count = 0;
+ info->mon.char_max = 0;
+ info->mon.char_last = 0;
+ return 0;
+}
+
+static int
+set_threshold(struct cyclades_port * info, unsigned long value)
+{
+ unsigned char *base_addr;
+ int card,channel,chip;
+
+ card = info->card;
+ channel = info->line - cy_card[card].first_line;
+ chip = channel>>2;
+ channel &= 0x03;
+ base_addr = (unsigned char*)
+ (cy_card[card].base_addr + cy_chip_offset[chip]);
+
+ info->cor3 &= ~CyREC_FIFO;
+ info->cor3 |= value & CyREC_FIFO;
+ base_addr[CyCOR3] = info->cor3;
+ write_cy_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch);
+ return 0;
+}
+
+static int
+get_threshold(struct cyclades_port * info, unsigned long *value)
+{
+ unsigned char *base_addr;
+ int card,channel,chip;
+ unsigned long tmp;
+
+ card = info->card;
+ channel = info->line - cy_card[card].first_line;
+ chip = channel>>2;
+ channel &= 0x03;
+ base_addr = (unsigned char*)
+ (cy_card[card].base_addr + cy_chip_offset[chip]);
+
+ tmp = base_addr[CyCOR3] & CyREC_FIFO;
+ put_fs_long(tmp,value);
+ return 0;
+}
+
+static int
+set_default_threshold(struct cyclades_port * info, unsigned long value)
+{
+ info->default_threshold = value & 0x0f;
+ return 0;
+}
+
+static int
+get_default_threshold(struct cyclades_port * info, unsigned long *value)
+{
+ put_fs_long(info->default_threshold,value);
+ return 0;
+}
+
+static int
+set_timeout(struct cyclades_port * info, unsigned long value)
+{
+ unsigned char *base_addr;
+ int card,channel,chip;
+
+ card = info->card;
+ channel = info->line - cy_card[card].first_line;
+ chip = channel>>2;
+ channel &= 0x03;
+ base_addr = (unsigned char*)
+ (cy_card[card].base_addr + cy_chip_offset[chip]);
+
+ base_addr[CyRTPR] = value & 0xff;
+ return 0;
+}
+
+static int
+get_timeout(struct cyclades_port * info, unsigned long *value)
+{
+ unsigned char *base_addr;
+ int card,channel,chip;
+ unsigned long tmp;
+
+ card = info->card;
+ channel = info->line - cy_card[card].first_line;
+ chip = channel>>2;
+ channel &= 0x03;
+ base_addr = (unsigned char*)
+ (cy_card[card].base_addr + cy_chip_offset[chip]);
+
+ tmp = base_addr[CyRTPR];
+ put_fs_long(tmp,value);
+ return 0;
+}
+
+static int
+set_default_timeout(struct cyclades_port * info, unsigned long value)
+{
+ info->default_timeout = value & 0xff;
+ return 0;
+}
+
+static int
+get_default_timeout(struct cyclades_port * info, unsigned long *value)
+{
+ put_fs_long(info->default_timeout,value);
+ return 0;
+}
+
+static int
+cy_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ int error;
+ struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
+ int ret_val = 0;
+
+#ifdef SERIAL_DEBUG_OTHER
+ printk("cy_ioctl ttyC%d, cmd = %x arg = %lx\n", info->line, cmd, arg); /* */
+#endif
+
+ switch (cmd) {
+ case CYGETMON:
+ error = verify_area(VERIFY_WRITE, (void *) arg
+ ,sizeof(struct cyclades_monitor));
+ if (error){
+ ret_val = error;
+ break;
+ }
+ ret_val = get_mon_info(info, (struct cyclades_monitor *)arg);
+ break;
+ case CYGETTHRESH:
+ error = verify_area(VERIFY_WRITE, (void *) arg
+ ,sizeof(unsigned long));
+ if (error){
+ ret_val = error;
+ break;
+ }
+ ret_val = get_threshold(info, (unsigned long *)arg);
+ break;
+ case CYSETTHRESH:
+ ret_val = set_threshold(info, (unsigned long)arg);
+ break;
+ case CYGETDEFTHRESH:
+ error = verify_area(VERIFY_WRITE, (void *) arg
+ ,sizeof(unsigned long));
+ if (error){
+ ret_val = error;
+ break;
+ }
+ ret_val = get_default_threshold(info, (unsigned long *)arg);
+ break;
+ case CYSETDEFTHRESH:
+ ret_val = set_default_threshold(info, (unsigned long)arg);
+ break;
+ case CYGETTIMEOUT:
+ error = verify_area(VERIFY_WRITE, (void *) arg
+ ,sizeof(unsigned long));
+ if (error){
+ ret_val = error;
+ break;
+ }
+ ret_val = get_timeout(info, (unsigned long *)arg);
+ break;
+ case CYSETTIMEOUT:
+ ret_val = set_timeout(info, (unsigned long)arg);
+ break;
+ case CYGETDEFTIMEOUT:
+ error = verify_area(VERIFY_WRITE, (void *) arg
+ ,sizeof(unsigned long));
+ if (error){
+ ret_val = error;
+ break;
+ }
+ ret_val = get_default_timeout(info, (unsigned long *)arg);
+ break;
+ case CYSETDEFTIMEOUT:
+ ret_val = set_default_timeout(info, (unsigned long)arg);
+ break;
+ case TCSBRK: /* SVID version: non-zero arg --> no break */
+ ret_val = tty_check_change(tty);
+ if (ret_val)
+ return ret_val;
+ tty_wait_until_sent(tty,0);
+ if (!arg)
+ send_break(info, HZ/4); /* 1/4 second */
+ break;
+ case TCSBRKP: /* support for POSIX tcsendbreak() */
+ ret_val = tty_check_change(tty);
+ if (ret_val)
+ return ret_val;
+ tty_wait_until_sent(tty,0);
+ send_break(info, arg ? arg*(HZ/10) : HZ/4);
+ break;
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ ret_val = set_modem_info(info, cmd, (unsigned int *) arg);
+ break;
+
+/* The following commands are incompletely implemented!!! */
+ case TIOCGSOFTCAR:
+ error = verify_area(VERIFY_WRITE, (void *) arg
+ ,sizeof(unsigned int *));
+ if (error){
+ ret_val = error;
+ break;
+ }
+ put_fs_long(C_CLOCAL(tty) ? 1 : 0,
+ (unsigned long *) arg);
+ break;
+ case TIOCSSOFTCAR:
+ arg = get_fs_long((unsigned long *) arg);
+ tty->termios->c_cflag =
+ ((tty->termios->c_cflag & ~CLOCAL) |
+ (arg ? CLOCAL : 0));
+ break;
+ case TIOCMGET:
+ error = verify_area(VERIFY_WRITE, (void *) arg
+ ,sizeof(unsigned int *));
+ if (error){
+ ret_val = error;
+ break;
+ }
+ ret_val = get_modem_info(info, (unsigned int *) arg);
+ break;
+ case TIOCGSERIAL:
+ error = verify_area(VERIFY_WRITE, (void *) arg
+ ,sizeof(struct serial_struct));
+ if (error){
+ ret_val = error;
+ break;
+ }
+ ret_val = get_serial_info(info,
+ (struct serial_struct *) arg);
+ break;
+ case TIOCSSERIAL:
+ ret_val = set_serial_info(info,
+ (struct serial_struct *) arg);
+ break;
+ default:
+ ret_val = -ENOIOCTLCMD;
+ }
+
+#ifdef SERIAL_DEBUG_OTHER
+ printk("cy_ioctl done\n");
+#endif
+
+ return ret_val;
+} /* cy_ioctl */
+
+
+
+
+static void
+cy_set_termios(struct tty_struct *tty, struct termios * old_termios)
+{
+ struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+
+#ifdef SERIAL_DEBUG_OTHER
+ printk("cy_set_termios ttyC%d\n", info->line);
+#endif
+
+ if (tty->termios->c_cflag == old_termios->c_cflag)
+ return;
+ config_setup(info);
+
+ if ((old_termios->c_cflag & CRTSCTS) &&
+ !(tty->termios->c_cflag & CRTSCTS)) {
+ tty->stopped = 0;
+ cy_start(tty);
+ }
+#ifdef tytso_patch_94Nov25_1726
+ if (!(old_termios->c_cflag & CLOCAL) &&
+ (tty->termios->c_cflag & CLOCAL))
+ wake_up_interruptible(&info->open_wait);
+#endif
+
+ return;
+} /* cy_set_termios */
+
+
+static void
+cy_close(struct tty_struct * tty, struct file * filp)
+{
+ struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
+
+/* CP('C'); */
+#ifdef SERIAL_DEBUG_OTHER
+ printk("cy_close ttyC%d\n", info->line);
+#endif
+
+ if (!info
+ || serial_paranoia_check(info, tty->device, "cy_close")){
+ return;
+ }
+#ifdef SERIAL_DEBUG_OPEN
+ printk("cy_close ttyC%d, count = %d\n", info->line, info->count);
+#endif
+
+ if ((tty->count == 1) && (info->count != 1)) {
+ /*
+ * Uh, oh. tty->count is 1, which means that the tty
+ * structure will be freed. Info->count should always
+ * be one in these conditions. If it's greater than
+ * one, we've got real problems, since it means the
+ * serial port won't be shutdown.
+ */
+ printk("cy_close: bad serial port count; tty->count is 1, "
+ "info->count is %d\n", info->count);
+ info->count = 1;
+ }
+#ifdef SERIAL_DEBUG_COUNT
+ printk("cyc: %d: decrementing count to %d\n", __LINE__, info->count - 1);
+#endif
+ if (--info->count < 0) {
+ printk("cy_close: bad serial port count for ttys%d: %d\n",
+ info->line, info->count);
+#ifdef SERIAL_DEBUG_COUNT
+ printk("cyc: %d: setting count to 0\n", __LINE__);
+#endif
+ info->count = 0;
+ }
+ if (info->count)
+ return;
+ info->flags |= ASYNC_CLOSING;
+ /*
+ * Save the termios structure, since this port may have
+ * separate termios for callout and dialin.
+ */
+ if (info->flags & ASYNC_NORMAL_ACTIVE)
+ info->normal_termios = *tty->termios;
+ if (info->flags & ASYNC_CALLOUT_ACTIVE)
+ info->callout_termios = *tty->termios;
+ if (info->flags & ASYNC_INITIALIZED)
+ tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */
+ shutdown(info);
+ if (tty->driver.flush_buffer)
+ tty->driver.flush_buffer(tty);
+ if (tty->ldisc.flush_buffer)
+ tty->ldisc.flush_buffer(tty);
+ info->event = 0;
+ info->tty = 0;
+ if (tty->ldisc.num != ldiscs[N_TTY].num) {
+ if (tty->ldisc.close)
+ (tty->ldisc.close)(tty);
+ tty->ldisc = ldiscs[N_TTY];
+ tty->termios->c_line = N_TTY;
+ if (tty->ldisc.open)
+ (tty->ldisc.open)(tty);
+ }
+ if (info->blocked_open) {
+ if (info->close_delay) {
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + info->close_delay;
+ schedule();
+ }
+ wake_up_interruptible(&info->open_wait);
+ }
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+ ASYNC_CLOSING);
+ wake_up_interruptible(&info->close_wait);
+
+#ifdef SERIAL_DEBUG_OTHER
+ printk("cy_close done\n");
+#endif
+
+ return;
+} /* cy_close */
+
+/*
+ * cy_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+void
+cy_hangup(struct tty_struct *tty)
+{
+ struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
+
+#ifdef SERIAL_DEBUG_OTHER
+ printk("cy_hangup ttyC%d\n", info->line); /* */
+#endif
+
+ if (serial_paranoia_check(info, tty->device, "cy_hangup"))
+ return;
+
+ shutdown(info);
+#if 0
+ info->event = 0;
+ info->count = 0;
+#ifdef SERIAL_DEBUG_COUNT
+ printk("cyc: %d: setting count to 0\n", __LINE__);
+#endif
+ info->tty = 0;
+#endif
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+ wake_up_interruptible(&info->open_wait);
+} /* cy_hangup */
+
+
+
+/*
+ * ------------------------------------------------------------
+ * cy_open() and friends
+ * ------------------------------------------------------------
+ */
+
+static int
+block_til_ready(struct tty_struct *tty, struct file * filp,
+ struct cyclades_port *info)
+{
+ struct wait_queue wait = { current, NULL };
+ struct cyclades_card *cinfo;
+ unsigned long flags;
+ int chip, channel;
+ int retval;
+ char *base_addr;
+
+ /*
+ * If the device is in the middle of being closed, then block
+ * until it's done, and then try again.
+ */
+ if (info->flags & ASYNC_CLOSING) {
+ interruptible_sleep_on(&info->close_wait);
+ if (info->flags & ASYNC_HUP_NOTIFY){
+ return -EAGAIN;
+ }else{
+ return -ERESTARTSYS;
+ }
+ }
+
+ /*
+ * If this is a callout device, then just make sure the normal
+ * device isn't being used.
+ */
+ if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+ if (info->flags & ASYNC_NORMAL_ACTIVE){
+ return -EBUSY;
+ }
+ if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_SESSION_LOCKOUT) &&
+ (info->session != current->session)){
+ return -EBUSY;
+ }
+ if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_PGRP_LOCKOUT) &&
+ (info->pgrp != current->pgrp)){
+ return -EBUSY;
+ }
+ info->flags |= ASYNC_CALLOUT_ACTIVE;
+ return 0;
+ }
+
+ /*
+ * If non-blocking mode is set, then make the check up front
+ * and then exit.
+ */
+ if (filp->f_flags & O_NONBLOCK) {
+ if (info->flags & ASYNC_CALLOUT_ACTIVE){
+ return -EBUSY;
+ }
+ info->flags |= ASYNC_NORMAL_ACTIVE;
+ return 0;
+ }
+
+ /*
+ * Block waiting for the carrier detect and the line to become
+ * free (i.e., not in use by the callout). While we are in
+ * this loop, info->count is dropped by one, so that
+ * cy_close() knows when to free things. We restore it upon
+ * exit, either normal or abnormal.
+ */
+ retval = 0;
+ add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready before block: ttyC%d, count = %d\n",
+ info->line, info->count);/**/
+#endif
+ info->count--;
+#ifdef SERIAL_DEBUG_COUNT
+ printk("cyc: %d: decrementing count to %d\n", __LINE__, info->count);
+#endif
+ info->blocked_open++;
+
+ cinfo = &cy_card[info->card];
+ channel = info->line - cinfo->first_line;
+ chip = channel>>2;
+ channel &= 0x03;
+ base_addr = (char *) (cinfo->base_addr + cy_chip_offset[chip]);
+
+ while (1) {
+ save_flags(flags); cli();
+ if (!(info->flags & ASYNC_CALLOUT_ACTIVE)){
+ base_addr[CyCAR] = (u_char)channel;
+ base_addr[CyMSVR1] = CyRTS;
+/* CP('S');CP('4'); */
+ base_addr[CyMSVR2] = CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+ printk("cyc: %d: raising DTR\n", __LINE__);
+ printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+ }
+ restore_flags(flags);
+ current->state = TASK_INTERRUPTIBLE;
+ if (tty_hung_up_p(filp)
+ || !(info->flags & ASYNC_INITIALIZED) ){
+ if (info->flags & ASYNC_HUP_NOTIFY) {
+ retval = -EAGAIN;
+ }else{
+ retval = -ERESTARTSYS;
+ }
+ break;
+ }
+ save_flags(flags); cli();
+ base_addr[CyCAR] = (u_char)channel;
+/* CP('L');CP1(1 && C_CLOCAL(tty)); CP1(1 && (base_addr[CyMSVR1] & CyDCD) ); */
+ if (!(info->flags & ASYNC_CALLOUT_ACTIVE)
+ && !(info->flags & ASYNC_CLOSING)
+ && (C_CLOCAL(tty)
+ || (base_addr[CyMSVR1] & CyDCD))) {
+ restore_flags(flags);
+ break;
+ }
+ restore_flags(flags);
+ if (current->signal & ~current->blocked) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready blocking: ttyC%d, count = %d\n",
+ info->line, info->count);/**/
+#endif
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&info->open_wait, &wait);
+ if (!tty_hung_up_p(filp)){
+ info->count++;
+#ifdef SERIAL_DEBUG_COUNT
+ printk("cyc: %d: incrementing count to %d\n", __LINE__, info->count);
+#endif
+ }
+ info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+ printk("block_til_ready after blocking: ttyC%d, count = %d\n",
+ info->line, info->count);/**/
+#endif
+ if (retval)
+ return retval;
+ info->flags |= ASYNC_NORMAL_ACTIVE;
+ return 0;
+} /* block_til_ready */
+
+/*
+ * This routine is called whenever a serial port is opened. It
+ * performs the serial-specific initialization for the tty structure.
+ */
+int
+cy_open(struct tty_struct *tty, struct file * filp)
+{
+ struct cyclades_port *info;
+ int retval, line;
+
+/* CP('O'); */
+ line = MINOR(tty->device) - tty->driver.minor_start;
+ if ((line < 0) || (NR_PORTS <= line)){
+ return -ENODEV;
+ }
+ info = &cy_port[line];
+ if (info->line < 0){
+ return -ENODEV;
+ }
+#ifdef SERIAL_DEBUG_OTHER
+ printk("cy_open ttyC%d\n", info->line); /* */
+#endif
+ if (serial_paranoia_check(info, tty->device, "cy_open")){
+ return -ENODEV;
+ }
+#ifdef SERIAL_DEBUG_OPEN
+ printk("cy_open ttyC%d, count = %d\n", info->line, info->count);/**/
+#endif
+ info->count++;
+#ifdef SERIAL_DEBUG_COUNT
+ printk("cyc: %d: incrementing count to %d\n", __LINE__, info->count);
+#endif
+ tty->driver_data = info;
+ info->tty = tty;
+
+ if (!tmp_buf) {
+ tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL);
+ if (!tmp_buf){
+ return -ENOMEM;
+ }
+ }
+
+ if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
+ if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+ *tty->termios = info->normal_termios;
+ else
+ *tty->termios = info->callout_termios;
+ }
+ /*
+ * Start up serial port
+ */
+ retval = startup(info);
+ if (retval){
+ return retval;
+ }
+
+ retval = block_til_ready(tty, filp, info);
+ if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+ printk("cy_open returning after block_til_ready with %d\n",
+ retval);
+#endif
+ return retval;
+ }
+
+ info->session = current->session;
+ info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("cy_open done\n");/**/
+#endif
+ return 0;
+} /* cy_open */
+
+
+
+/*
+ * ---------------------------------------------------------------------
+ * cy_init() and friends
+ *
+ * cy_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+
+/*
+ * This routine prints out the appropriate serial driver version
+ * number, and identifies which options were configured into this
+ * driver.
+ */
+static void
+show_version(void)
+{
+ printk("Cyclom driver %s\n",rcsid);
+} /* show_version */
+
+/* initialize chips on card -- return number of valid
+ chips (which is number of ports/4) */
+int
+cy_init_card(unsigned char *true_base_addr)
+{
+ volatile unsigned short discard;
+ unsigned int chip_number;
+ unsigned char* base_addr;
+
+ discard = true_base_addr[Cy_HwReset]; /* Cy_HwReset is 0x1400 */
+ discard = true_base_addr[Cy_ClrIntr]; /* Cy_ClrIntr is 0x1800 */
+ udelay(500L);
+
+ for(chip_number=0; chip_number<CyMaxChipsPerCard; chip_number++){
+ base_addr = true_base_addr + cy_chip_offset[chip_number];
+ udelay(1000L);
+ if(base_addr[CyCCR] != 0x00){
+ /*************
+ printk(" chip #%d at %#6lx is never idle (CCR != 0)\n",
+ chip_number, (unsigned long)base_addr);
+ *************/
+ return chip_number;
+ }
+
+ base_addr[CyGFRCR] = 0;
+ udelay(10L);
+
+ /* The Cyclom-16Y does not decode address bit 9 and therefore
+ cannot distinguish between references to chip 0 and a non-
+ existent chip 4. If the preceding clearing of the supposed
+ chip 4 GFRCR register appears at chip 0, there is no chip 4
+ and this must be a Cyclom-16Y, not a Cyclom-32Ye.
+ */
+ if (chip_number == 4
+ && *(true_base_addr + cy_chip_offset[0] + CyGFRCR) == 0){
+ return chip_number;
+ }
+
+ base_addr[CyCCR] = CyCHIP_RESET;
+ udelay(1000L);
+
+ if(base_addr[CyGFRCR] == 0x00){
+ printk(" chip #%d at %#6lx is not responding (GFRCR stayed 0)\n",
+ chip_number, (unsigned long)base_addr);
+ return chip_number;
+ }
+ if((0xf0 & base_addr[CyGFRCR]) != 0x40){
+ printk(" chip #%d at %#6lx is not valid (GFRCR == %#2x)\n",
+ chip_number, (unsigned long)base_addr, base_addr[CyGFRCR]);
+ return chip_number;
+ }
+ base_addr[CyGCR] = CyCH0_SERIAL;
+ base_addr[CyPPR] = 244; /* better value than CyCLOCK_25_1MS * 5
+ to run clock at 200 Hz */
+
+ printk(" chip #%d at %#6lx is rev 0x%2x\n",
+ chip_number, (unsigned long)base_addr, base_addr[CyGFRCR]);
+ }
+
+ return chip_number;
+} /* cy_init_card */
+
+/* The serial driver boot-time initialization code!
+ Hardware I/O ports are mapped to character special devices on a
+ first found, first allocated manner. That is, this code searches
+ for Cyclom cards in the system. As each is found, it is probed
+ to discover how many chips (and thus how many ports) are present.
+ These ports are mapped to the tty ports 32 and upward in monotonic
+ fashion. If an 8-port card is replaced with a 16-port card, the
+ port mapping on a following card will shift.
+
+ This approach is different from what is used in the other serial
+ device driver because the Cyclom is more properly a multiplexer,
+ not just an aggregation of serial ports on one card.
+
+ If there are more cards with more ports than have been statically
+ allocated above, a warning is printed and the extra ports are ignored.
+ */
+long
+cy_init(long kmem_start)
+{
+ struct cyclades_port *info;
+ struct cyclades_card *cinfo;
+ int good_ports = 0;
+ int port_num = 0;
+ int index;
+#ifdef notyet
+ struct sigaction sa;
+#endif
+ int retval;
+
+scrn[1] = '\0';
+ show_version();
+
+ /* Initialize the tty_driver structure */
+
+ memset(&cy_serial_driver, 0, sizeof(struct tty_driver));
+ cy_serial_driver.magic = TTY_DRIVER_MAGIC;
+ cy_serial_driver.name = "ttyC";
+ cy_serial_driver.major = CYCLADES_MAJOR;
+ cy_serial_driver.minor_start = 32;
+ cy_serial_driver.num = NR_PORTS;
+ cy_serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ cy_serial_driver.subtype = SERIAL_TYPE_NORMAL;
+ cy_serial_driver.init_termios = tty_std_termios;
+ cy_serial_driver.init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ cy_serial_driver.flags = TTY_DRIVER_REAL_RAW;
+ cy_serial_driver.refcount = &serial_refcount;
+ cy_serial_driver.table = serial_table;
+ cy_serial_driver.termios = serial_termios;
+ cy_serial_driver.termios_locked = serial_termios_locked;
+ cy_serial_driver.open = cy_open;
+ cy_serial_driver.close = cy_close;
+ cy_serial_driver.write = cy_write;
+ cy_serial_driver.put_char = cy_put_char;
+ cy_serial_driver.flush_chars = cy_flush_chars;
+ cy_serial_driver.write_room = cy_write_room;
+ cy_serial_driver.chars_in_buffer = cy_chars_in_buffer;
+ cy_serial_driver.flush_buffer = cy_flush_buffer;
+ cy_serial_driver.ioctl = cy_ioctl;
+ cy_serial_driver.throttle = cy_throttle;
+ cy_serial_driver.unthrottle = cy_unthrottle;
+ cy_serial_driver.set_termios = cy_set_termios;
+ cy_serial_driver.stop = cy_stop;
+ cy_serial_driver.start = cy_start;
+ cy_serial_driver.hangup = cy_hangup;
+
+ /*
+ * The callout device is just like normal device except for
+ * major number and the subtype code.
+ */
+ cy_callout_driver = cy_serial_driver;
+ cy_callout_driver.name = "cub";
+ cy_callout_driver.major = CYCLADESAUX_MAJOR;
+ cy_callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+
+ if (tty_register_driver(&cy_serial_driver))
+ panic("Couldn't register Cyclom serial driver\n");
+ if (tty_register_driver(&cy_callout_driver))
+ panic("Couldn't register Cyclom callout driver\n");
+
+ bh_base[CYCLADES_BH].routine = do_cyclades_bh;
+ enable_bh(CYCLADES_BH);
+
+ for (index = 0; index < 16; index++) {
+ IRQ_cards[index] = 0;
+ }
+
+ port_num = 0;
+ info = cy_port;
+ for (index = 0, cinfo = cy_card; index < NR_CARDS; index++,cinfo++) {
+ /*** initialize card ***/
+ if(0 == (cinfo->num_chips =
+ cy_init_card((unsigned char *)cinfo->base_addr))){
+ /* this card is not present */
+ continue;
+ }
+
+#ifndef CY_DONT_PROBE
+ /* find out the board's irq by probing */
+ cinfo->irq = do_auto_irq(index);
+#endif
+
+ /** bind IRQ to card **/
+ if (cinfo->irq) {
+ retval = request_irq(cinfo->irq, cy_interrupt,
+ SA_INTERRUPT, "cyclades");
+ if (retval){
+ printk("request_irq returned %d\n",retval);
+ /* return retval; */
+ }
+ IRQ_cards[cinfo->irq] = cinfo;
+ }else{
+ printk("couldn't get board's irq\n");
+ continue;
+ }
+
+ printk(" share IRQ %d: ", cinfo->irq);
+ good_ports = 4 * cinfo->num_chips;
+
+ if(port_num < NR_PORTS){
+ cinfo->first_line = port_num;
+ while( good_ports-- && port_num < NR_PORTS){
+ /*** initialize port ***/
+ info->magic = CYCLADES_MAGIC;
+ info->type = PORT_CIRRUS;
+ info->card = index;
+ info->line = port_num;
+ info->flags = STD_COM_FLAGS;
+ info->tty = 0;
+ info->xmit_fifo_size = 12;
+ info->cor1 = CyPARITY_NONE|Cy_1_STOP|Cy_8_BITS;
+ info->cor2 = CyETC;
+ info->cor3 = 0x08; /* _very_ small receive threshold */
+ info->cor4 = 0;
+ info->cor5 = 0;
+ info->tbpr = baud_bpr[13]; /* Tx BPR */
+ info->tco = baud_co[13]; /* Tx CO */
+ info->rbpr = baud_bpr[13]; /* Rx BPR */
+ info->rco = baud_co[13]; /* Rx CO */
+ info->close_delay = 0;
+ info->x_char = 0;
+ info->event = 0;
+ info->count = 0;
+#ifdef SERIAL_DEBUG_COUNT
+ printk("cyc: %d: setting count to 0\n", __LINE__);
+#endif
+ info->blocked_open = 0;
+ info->default_threshold = 0;
+ info->default_timeout = 0;
+ info->tqueue.routine = do_softint;
+ info->tqueue.data = info;
+ info->callout_termios =cy_callout_driver.init_termios;
+ info->normal_termios = cy_serial_driver.init_termios;
+ info->open_wait = 0;
+ info->close_wait = 0;
+ /* info->session */
+ /* info->pgrp */
+/*** !!!!!!!! this may expose new bugs !!!!!!!!! *********/
+ info->read_status_mask = CyTIMEOUT| CySPECHAR| CyBREAK
+ | CyPARITY| CyFRAME| CyOVERRUN;
+ /* info->timeout */
+
+ printk("ttyC%1d ", info->line);
+ port_num++;info++;
+ if(!(port_num & 7)){
+ printk("\n ");
+ }
+ }
+ }else{
+ cinfo->first_line = -1;
+ }
+ printk("\n");
+ }
+ while( port_num < NR_PORTS){
+ info->line = -1;
+ port_num++;info++;
+ }
+ return kmem_start;
+
+} /* cy_init */
+
+#ifdef CYCLOM_SHOW_STATUS
+static void
+show_status(int line_num)
+{
+ unsigned char *base_addr;
+ int card,chip,channel;
+ struct cyclades_port * info;
+ unsigned long flags;
+
+ info = &cy_port[line_num];
+ card = info->card;
+ channel = (info->line) - (cy_card[card].first_line);
+ chip = channel>>2;
+ channel &= 0x03;
+ printk(" card %d, chip %d, channel %d\n", card, chip, channel);/**/
+
+ printk(" cy_card\n");
+ printk(" irq base_addr num_chips first_line = %d %lx %d %d\n",
+ cy_card[card].irq, (long)cy_card[card].base_addr,
+ cy_card[card].num_chips, cy_card[card].first_line);
+
+ printk(" cy_port\n");
+ printk(" card line flags = %d %d %x\n",
+ info->card, info->line, info->flags);
+ printk(" *tty read_status_mask timeout xmit_fifo_size = %lx %x %x %x\n",
+ (long)info->tty, info->read_status_mask,
+ info->timeout, info->xmit_fifo_size);
+ printk(" cor1,cor2,cor3,cor4,cor5 = %x %x %x %x %x\n",
+ info->cor1, info->cor2, info->cor3, info->cor4, info->cor5);
+ printk(" tbpr,tco,rbpr,rco = %d %d %d %d\n",
+ info->tbpr, info->tco, info->rbpr, info->rco);
+ printk(" close_delay event count = %d %d %d\n",
+ info->close_delay, info->event, info->count);
+ printk(" x_char blocked_open = %x %x\n",
+ info->x_char, info->blocked_open);
+ printk(" session pgrp open_wait = %lx %lx %lx\n",
+ info->session, info->pgrp, (long)info->open_wait);
+
+
+ save_flags(flags); cli();
+
+ base_addr = (unsigned char*)
+ (cy_card[card].base_addr + cy_chip_offset[chip]);
+
+/* Global Registers */
+
+ printk(" CyGFRCR %x\n", base_addr[CyGFRCR]);
+ printk(" CyCAR %x\n", base_addr[CyCAR]);
+ printk(" CyGCR %x\n", base_addr[CyGCR]);
+ printk(" CySVRR %x\n", base_addr[CySVRR]);
+ printk(" CyRICR %x\n", base_addr[CyRICR]);
+ printk(" CyTICR %x\n", base_addr[CyTICR]);
+ printk(" CyMICR %x\n", base_addr[CyMICR]);
+ printk(" CyRIR %x\n", base_addr[CyRIR]);
+ printk(" CyTIR %x\n", base_addr[CyTIR]);
+ printk(" CyMIR %x\n", base_addr[CyMIR]);
+ printk(" CyPPR %x\n", base_addr[CyPPR]);
+
+ base_addr[CyCAR] = (u_char)channel;
+
+/* Virtual Registers */
+
+ printk(" CyRIVR %x\n", base_addr[CyRIVR]);
+ printk(" CyTIVR %x\n", base_addr[CyTIVR]);
+ printk(" CyMIVR %x\n", base_addr[CyMIVR]);
+ printk(" CyMISR %x\n", base_addr[CyMISR]);
+
+/* Channel Registers */
+
+ printk(" CyCCR %x\n", base_addr[CyCCR]);
+ printk(" CySRER %x\n", base_addr[CySRER]);
+ printk(" CyCOR1 %x\n", base_addr[CyCOR1]);
+ printk(" CyCOR2 %x\n", base_addr[CyCOR2]);
+ printk(" CyCOR3 %x\n", base_addr[CyCOR3]);
+ printk(" CyCOR4 %x\n", base_addr[CyCOR4]);
+ printk(" CyCOR5 %x\n", base_addr[CyCOR5]);
+ printk(" CyCCSR %x\n", base_addr[CyCCSR]);
+ printk(" CyRDCR %x\n", base_addr[CyRDCR]);
+ printk(" CySCHR1 %x\n", base_addr[CySCHR1]);
+ printk(" CySCHR2 %x\n", base_addr[CySCHR2]);
+ printk(" CySCHR3 %x\n", base_addr[CySCHR3]);
+ printk(" CySCHR4 %x\n", base_addr[CySCHR4]);
+ printk(" CySCRL %x\n", base_addr[CySCRL]);
+ printk(" CySCRH %x\n", base_addr[CySCRH]);
+ printk(" CyLNC %x\n", base_addr[CyLNC]);
+ printk(" CyMCOR1 %x\n", base_addr[CyMCOR1]);
+ printk(" CyMCOR2 %x\n", base_addr[CyMCOR2]);
+ printk(" CyRTPR %x\n", base_addr[CyRTPR]);
+ printk(" CyMSVR1 %x\n", base_addr[CyMSVR1]);
+ printk(" CyMSVR2 %x\n", base_addr[CyMSVR2]);
+ printk(" CyRBPR %x\n", base_addr[CyRBPR]);
+ printk(" CyRCOR %x\n", base_addr[CyRCOR]);
+ printk(" CyTBPR %x\n", base_addr[CyTBPR]);
+ printk(" CyTCOR %x\n", base_addr[CyTCOR]);
+
+ restore_flags(flags);
+} /* show_status */
+#endif
+
diff --git a/drivers/char/defkeymap.c b/drivers/char/defkeymap.c
index e7a4b7b5c..d8ac176c2 100644
--- a/drivers/char/defkeymap.c
+++ b/drivers/char/defkeymap.c
@@ -33,7 +33,7 @@ u_short shift_map[NR_KEYS] = {
0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56,
0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c,
0xf703, 0xf020, 0xf207, 0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf10e,
- 0xf10f, 0xf110, 0xf111, 0xf112, 0xf113, 0xf208, 0xf203, 0xf307,
+ 0xf10f, 0xf110, 0xf111, 0xf112, 0xf113, 0xf213, 0xf203, 0xf307,
0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03e, 0xf10a,
0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
diff --git a/drivers/char/defkeymap.map b/drivers/char/defkeymap.map
index 5e0726557..50b30cace 100644
--- a/drivers/char/defkeymap.map
+++ b/drivers/char/defkeymap.map
@@ -160,7 +160,8 @@ keycode 68 = F10 F20 Console_22
control keycode 68 = F10
alt keycode 68 = Console_10
control alt keycode 68 = Console_10
-keycode 69 = Num_Lock
+keycode 69 = Num_Lock
+ shift keycode 69 = Bare_Num_Lock
keycode 70 = Scroll_Lock Show_Memory Show_Registers
control keycode 70 = Show_State
alt keycode 70 = Scroll_Lock
diff --git a/drivers/char/fnt8x16.c b/drivers/char/fnt8x16.c
new file mode 100644
index 000000000..8e1b95477
--- /dev/null
+++ b/drivers/char/fnt8x16.c
@@ -0,0 +1,7 @@
+/*
+ * Font for Mips Magnum 4000 graphic console
+ */
+
+unsigned char font[] = {
+#include "8x16.fnt"
+};
diff --git a/drivers/char/kbd_kern.h b/drivers/char/kbd_kern.h
index 440b0d5bb..b23fa1ed8 100644
--- a/drivers/char/kbd_kern.h
+++ b/drivers/char/kbd_kern.h
@@ -2,10 +2,10 @@
#define _KBD_KERN_H
#include <linux/interrupt.h>
-#define set_leds() mark_bh(KEYBOARD_BH)
-
#include <linux/keyboard.h>
+extern int shift_state;
+
extern char *func_table[MAX_NR_FUNC];
extern char func_buf[];
extern char *funcbufptr;
@@ -66,6 +66,11 @@ extern unsigned long kbd_init(unsigned long);
extern unsigned char getledstate(void);
extern void setledstate(struct kbd_struct *kbd, unsigned int led);
+extern inline void set_leds(void)
+{
+ mark_bh(KEYBOARD_BH);
+}
+
extern inline int vc_kbd_mode(struct kbd_struct * kbd, int flag)
{
return ((kbd->modeflags >> flag) & 1);
diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c
index 1f8b86b73..f5c3e8568 100644
--- a/drivers/char/keyboard.c
+++ b/drivers/char/keyboard.c
@@ -18,17 +18,22 @@
#define KEYBOARD_IRQ 1
+#include <linux/autoconf.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/mm.h>
#include <linux/ptrace.h>
-#include <linux/config.h>
#include <linux/signal.h>
#include <linux/string.h>
+#include <linux/ioport.h>
#include <asm/bitops.h>
+#ifdef __mips__
+#include <asm/bootinfo.h>
+#include <asm/jazz.h>
+#endif
#include "kbd_kern.h"
#include "diacr.h"
@@ -75,28 +80,115 @@ extern void scrollback(int);
extern void scrollfront(int);
extern int vc_cons_allocated(unsigned int);
-#if defined (__i386__)
-#define fake_keyboard_interrupt() \
-__asm__ __volatile__("int $0x21")
-#elif defined (__mips__)
-extern void fake_keyboard_interrupt(void);
+#ifdef __i386__
+#define fake_keyboard_interrupt() __asm__ __volatile__("int $0x21")
+#define kbd_inb_p(port) inb_p(port)
+#define kbd_inb(port) inb(port)
+#define kbd_outb_p(data,port) outb_p(data,port)
+#define kbd_outb(data,port) outb(data,port)
+
+#elif defined (CONFIG_MIPS_JAZZ)
+
+static volatile keyboard_hardware *kh = (void *) JAZZ_KEYBOARD_ADDRESS;
+
+extern __inline__ void
+fake_keyboard_interrupt(void)
+{
+printk("fake_keyboard_interrupt()\n");
+__asm__ __volatile__(
+ ".set\tnoat\n\t"
+ "mfc0\t$1,$13\n\t"
+ "ori\t$1,0x0100\n\t"
+ "mtc0\t$1,$13\n\t"
+ ".set\tat"
+ :::"$1");
+}
+
+#if 1
+extern __inline__ int
+kbd_inb_p(unsigned short port)
+{
+ int result;
+
+ if(port == 0x60)
+ result = kh->data;
+ else if(port == 0x64)
+ result = kh->command;
+ else printk("Eeeeks - bad port in kbd_inb_p()\n");
+ inb(0x80);
+
+ return result;
+}
+
+extern __inline__ int
+kbd_inb(unsigned short port)
+{
+ int result;
+
+ if(port == 0x60)
+ result = kh->data;
+ else if(port == 0x64)
+ result = kh->command;
+ else printk("Eeeeks - bad port in kbd_inb()\n");
+
+ return result;
+}
+
+extern __inline__ void
+kbd_outb_p(unsigned char data, unsigned short port)
+{
+ if(port == 0x60)
+ kh->data = data;
+ else if(port == 0x64)
+ kh->command = data;
+ else printk("Eeeeks - bad port in kbd_outb_p()\n");
+ inb(0x80);
+}
+
+extern __inline__ void
+kbd_outb(unsigned char data, unsigned short port)
+{
+ if(port == 0x60)
+ kh->data = data;
+ else if(port == 0x64)
+ kh->command = data;
+ else printk("Eeeeks - bad port in kbd_outb()\n");
+}
+#else /* !1 */
+
+#define kbd_inb_p(port) inb_p(port)
+#define kbd_inb(port) inb(port)
+#define kbd_outb_p(data,port) outb_p(data,port)
+#define kbd_outb(data,port) outb(data,port)
+
+#endif /* !1 */
+
+#else /* defined (__alpha__) || defined (CONFIG_DESKSTATION_TYNE) */
+
+#define fake_keyboard_interrupt() do ; while (0)
+#define kbd_inb_p(port) inb_p(port)
+#define kbd_inb(port) inb(port)
+#define kbd_outb_p(data,port) outb_p(data,port)
+#define kbd_outb(data,port) outb(data,port)
+
#endif
unsigned char kbd_read_mask = 0x01; /* modified by psaux.c */
/*
* global state includes the following, and various static variables
- * in this module: prev_scancode, shift_state, diacr, npadch,
- * dead_key_next, last_console
+ * in this module: prev_scancode, shift_state, diacr, npadch, dead_key_next.
+ * (last_console is now a global variable)
*/
/* shift state counters.. */
static unsigned char k_down[NR_SHIFT] = {0, };
/* keyboard key bitmap */
-static unsigned long key_down[8] = { 0, };
+#define BITS_PER_LONG (8*sizeof(unsigned long))
+static unsigned long key_down[256/BITS_PER_LONG] = { 0, };
+extern int last_console;
static int want_console = -1;
-static int last_console = 0; /* last used VC */
static int dead_key_next = 0;
/*
* In order to retrieve the shift_state (for the mouse server), either
@@ -137,14 +229,14 @@ typedef void (void_fn)(void);
static void_fn do_null, enter, show_ptregs, send_intr, lastcons, caps_toggle,
num, hold, scroll_forw, scroll_back, boot_it, caps_on, compose,
- SAK, decr_console, incr_console;
+ SAK, decr_console, incr_console, spawn_console, bare_num;
static void_fnp spec_fn_table[] = {
do_null, enter, show_ptregs, show_mem,
show_state, send_intr, lastcons, caps_toggle,
num, hold, scroll_forw, scroll_back,
boot_it, caps_on, compose, SAK,
- decr_console, incr_console
+ decr_console, incr_console, spawn_console, bare_num
};
/* maximum values each key_handler can handle */
@@ -166,15 +258,16 @@ static inline void kb_wait(void)
{
int i;
- for (i=0; i<0x10000; i++)
- if ((inb_p(0x64) & 0x02) == 0)
- break;
+ for (i=0; i<0x100000; i++)
+ if ((kbd_inb_p(0x64) & 0x02) == 0)
+ return;
+ printk("Keyboard timed out\n");
}
static inline void send_cmd(unsigned char c)
{
kb_wait();
- outb(c,0x64);
+ kbd_outb(c,0x64);
}
/*
@@ -194,7 +287,7 @@ void to_utf8(ushort c) {
put_queue(0x80 | ((c >> 6) & 0x3f));
put_queue(0x80 | (c & 0x3f));
}
- /* utf-8 is defined for words of up to 36 bits,
+ /* UTF-8 is defined for words of up to 31 bits,
but we need only 16 bits here */
}
@@ -332,19 +425,19 @@ int getkeycode(unsigned int scancode)
e0_keys[scancode - 128];
}
-static void keyboard_interrupt(int int_pt_regs)
+static void keyboard_interrupt(int irq, struct pt_regs *regs)
{
unsigned char scancode, keycode;
static unsigned int prev_scancode = 0; /* remember E0, E1 */
char up_flag; /* 0 or 0200 */
char raw_mode;
- pt_regs = (struct pt_regs *) int_pt_regs;
+ pt_regs = regs;
send_cmd(0xAD); /* disable keyboard */
kb_wait();
- if ((inb_p(0x64) & kbd_read_mask) != 0x01)
+ if ((kbd_inb_p(0x64) & kbd_read_mask) != 0x01)
goto end_kbd_intr;
- scancode = inb(0x60);
+ scancode = kbd_inb(0x60);
mark_bh(KEYBOARD_BH);
if (reply_expected) {
/* 0xfa, 0xfe only mean "acknowledge", "resend" for most keyboards */
@@ -370,25 +463,29 @@ static void keyboard_interrupt(int int_pt_regs)
prev_scancode = 0;
goto end_kbd_intr;
}
+
+ tty = ttytab[fg_console];
+ kbd = kbd_table + fg_console;
+ if ((raw_mode = (kbd->kbdmode == VC_RAW))) {
+ put_queue(scancode);
+ /* we do not return yet, because we want to maintain
+ the key_down array, so that we have the correct
+ values when finishing RAW mode or when changing VT's */
+ }
+
if (scancode == 0xff) {
+ /* in scancode mode 1, my ESC key generates 0xff */
/* the calculator keys on a FOCUS 9000 generate 0xff */
#ifndef KBD_IS_FOCUS_9000
#ifdef KBD_REPORT_ERR
- printk("keyboard error\n");
+ if (!raw_mode)
+ printk("keyboard error\n");
#endif
#endif
prev_scancode = 0;
goto end_kbd_intr;
}
- tty = ttytab[fg_console];
- kbd = kbd_table + fg_console;
- if ((raw_mode = (kbd->kbdmode == VC_RAW))) {
- put_queue(scancode);
- /* we do not return yet, because we want to maintain
- the key_down array, so that we have the correct
- values when finishing RAW mode or when changing VT's */
- }
if (scancode == 0xe0 || scancode == 0xe1) {
prev_scancode = scancode;
goto end_kbd_intr;
@@ -414,7 +511,8 @@ static void keyboard_interrupt(int int_pt_regs)
prev_scancode = 0;
} else {
#ifdef KBD_REPORT_UNKN
- printk("keyboard: unknown e1 escape sequence\n");
+ if (!raw_mode)
+ printk("keyboard: unknown e1 escape sequence\n");
#endif
prev_scancode = 0;
goto end_kbd_intr;
@@ -616,26 +714,8 @@ static void caps_on(void)
static void show_ptregs(void)
{
- if (!pt_regs)
- return;
-#if defined (__i386__)
- printk("\n");
- printk("EIP: %04x:%08lx",0xffff & pt_regs->cs,pt_regs->eip);
- if (pt_regs->cs & 3)
- printk(" ESP: %04x:%08lx",0xffff & pt_regs->ss,pt_regs->esp);
- printk(" EFLAGS: %08lx\n",pt_regs->eflags);
- printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
- pt_regs->orig_eax,pt_regs->ebx,pt_regs->ecx,pt_regs->edx);
- printk("ESI: %08lx EDI: %08lx EBP: %08lx",
- pt_regs->esi, pt_regs->edi, pt_regs->ebp);
- printk(" DS: %04x ES: %04x FS: %04x GS: %04x\n",
- 0xffff & pt_regs->ds,0xffff & pt_regs->es,
- 0xffff & pt_regs->fs,0xffff & pt_regs->gs);
-#elif defined (__mips__)
- /*
- * FIXME...
- */
-#endif
+ if (pt_regs)
+ show_regs(pt_regs);
}
static void hold(void)
@@ -656,11 +736,21 @@ static void hold(void)
static void num(void)
{
- if (vc_kbd_mode(kbd,VC_APPLIC)) {
+ if (vc_kbd_mode(kbd,VC_APPLIC))
applkey('P', 1);
- return;
- }
- if (!rep) /* no autorepeat for numlock, ChN */
+ else
+ bare_num();
+}
+
+/*
+ * Bind this to Shift-NumLock if you work in application keypad mode
+ * but want to be able to change the NumLock flag.
+ * Bind this to NumLock if you prefer that the NumLock key always
+ * changes the NumLock flag.
+ */
+static void bare_num(void)
+{
+ if (!rep)
chg_vc_kbd_led(kbd,VC_NUMLOCK);
}
@@ -724,6 +814,15 @@ static void compose(void)
dead_key_next = 1;
}
+int spawnpid, spawnsig;
+
+static void spawn_console(void)
+{
+ if (spawnpid)
+ if(kill_proc(spawnpid, spawnsig, 1))
+ spawnpid = 0;
+}
+
static void SAK(void)
{
do_SAK(tty);
@@ -738,7 +837,7 @@ static void SAK(void)
* work.
*/
reset_vc(fg_console);
- unblank_screen(); /* not in interrupt routine? */
+ do_unblank_screen(); /* not in interrupt routine? */
#endif
}
@@ -964,8 +1063,8 @@ void compute_shiftstate(void)
for(i=0; i < SIZE(key_down); i++)
if(key_down[i]) { /* skip this word if not a single bit on */
- k = (i<<5);
- for(j=0; j<32; j++,k++)
+ k = i*BITS_PER_LONG;
+ for(j=0; j<BITS_PER_LONG; j++,k++)
if(test_bit(k, key_down)) {
sym = U(plain_map[k]);
if(KTYP(sym) == KT_SHIFT) {
@@ -1033,9 +1132,9 @@ static int send_data(unsigned char data)
acknowledge = 0;
resend = 0;
reply_expected = 1;
- outb_p(data, 0x60);
- for(i=0; i<0x20000; i++) {
- inb_p(0x64); /* just as a delay */
+ kbd_outb_p(data, 0x60);
+ for(i=0; i<0x200000; i++) {
+ kbd_inb_p(0x64); /* just as a delay */
if (acknowledge)
return 1;
if (resend)
@@ -1141,7 +1240,6 @@ static void kbd_bh(void * unused)
}
if (want_console >= 0) {
if (want_console != fg_console) {
- last_console = fg_console;
change_console(want_console);
/* we only changed when the console had already
been allocated - a new console is not created
@@ -1151,46 +1249,11 @@ static void kbd_bh(void * unused)
}
poke_blanked_console();
cli();
- if ((inb_p(0x64) & kbd_read_mask) == 0x01)
+ if ((kbd_inb_p(0x64) & kbd_read_mask) == 0x01)
fake_keyboard_interrupt();
sti();
}
-#ifdef __i386__
-long no_idt[2] = {0, 0};
-#endif
-
-/*
- * This routine reboots the machine by asking the keyboard
- * controller to pulse the reset-line low. We try that for a while,
- * and if it doesn't work, we do some other stupid things.
- */
-void hard_reset_now(void)
-{
- int i, j;
-#ifdef __i386__
- extern unsigned long pg0[1024];
-#endif
-
- sti();
-/* rebooting needs to touch the page at absolute addr 0 */
-#ifdef __i386__
- pg0[0] = 7;
- *((unsigned short *)0x472) = 0x1234;
-#endif
- for (;;) {
- for (i=0; i<100; i++) {
- kb_wait();
- for(j = 0; j < 100000 ; j++)
- /* nothing */;
- outb(0xfe,0x64); /* pulse reset low */
- }
-#ifdef __i386__
- __asm__("\tlidt _no_idt");
-#endif
- }
-}
-
unsigned long kbd_init(unsigned long kmem_start)
{
int i;
@@ -1210,6 +1273,37 @@ unsigned long kbd_init(unsigned long kmem_start)
bh_base[KEYBOARD_BH].routine = kbd_bh;
request_irq(KEYBOARD_IRQ, keyboard_interrupt, 0, "keyboard");
+ request_region(0x60,1,"kbd");
+ request_region(0x64,1,"kbd");
+#ifdef __alpha__
+ /* enable keyboard interrupts, PC/AT mode */
+ kb_wait();
+ kbd_outb(0x60,0x64); /* write PS/2 Mode Register */
+ kb_wait();
+ kbd_outb(0x65,0x60); /* KCC | DMS | SYS | EKI */
+ kb_wait();
+ if (!send_data(0xf0) || !send_data(0x02))
+ printk("Scanmode 2 change failed\n");
+#elif defined (__mips__)
+ /*
+ * No special setup required for Deskstation Tyne
+ */
+ if (boot_info.machtype == MACH_ACER_PICA_61 ||
+ boot_info.machtype == MACH_MIPS_MAGNUM_4000) {
+ *((volatile u16 *)JAZZ_IO_IRQ_ENABLE) |= JAZZ_IE_KEYBOARD;
+ set_cp0_cause(IE_SW0, 0);
+ set_cp0_status(IE_SW0|IE_IRQ1, IE_SW0|IE_IRQ1);
+ /* enable keyboard interrupts, PC/AT mode */
+ kb_wait();
+ kbd_outb(0x60,0x64); /* write PS/2 Mode Register */
+ kb_wait();
+ kbd_outb(0x41,0x60); /* KCC | EKI */
+ kb_wait();
+ if (!send_data(0xf0) || !send_data(0x02))
+ printk("Scanmode 2 change failed\n");
+ }
+#endif
mark_bh(KEYBOARD_BH);
+ enable_bh(KEYBOARD_BH);
return kmem_start;
}
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index ea65813f1..f2cdc06e8 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -5,18 +5,34 @@
* checking ought to be.
* Copyright (C) 1993 by Nigel Gamble (added interrupt code)
* Copyright (C) 1994 by Alan Cox (Modularised it)
+ * LPCAREFUL, LPABORT, LPGETSTATUS added by Chris Metcalf, metcalf@lcs.mit.edu
+ * Mips JAZZ support by Andreas Busse, andy@waldorf-gmbh.de
*/
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/config.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/sched.h>
#include <linux/lp.h>
#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
+#ifdef CONFIG_MIPS_JAZZ
+# include <asm/jazz.h>
+#endif
/* the BIOS manuals say there can be up to 4 lpt devices
* but I have not seen a board where the 4th address is listed
@@ -25,16 +41,34 @@
* if you have more than 3 printers, remember to increase LP_NO
*/
struct lp_struct lp_table[] = {
+#ifdef CONFIG_MIPS_JAZZ
+ { JAZZ_PARALLEL_BASE,
+ 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
+#else
{ 0x3bc, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
+#endif
{ 0x378, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
{ 0x278, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
};
#define LP_NO 3
-#ifdef MODULE
-#include <linux/module.h>
-#include "../../tools/version.h"
-#endif
+/* Test if printer is ready (and optionally has no error conditions) */
+#define LP_READY(minor, status) \
+ ((LP_F(minor) & LP_CAREFUL) ? _LP_CAREFUL_READY(status) : (status & LP_PBUSY))
+#define LP_CAREFUL_READY(minor, status) \
+ ((LP_F(minor) & LP_CAREFUL) ? _LP_CAREFUL_READY(status) : 1)
+#define _LP_CAREFUL_READY(status) \
+ (status & (LP_PBUSY|LP_POUTPA|LP_PSELECD|LP_PERRORP)) == \
+ (LP_PBUSY|LP_PSELECD|LP_PERRORP)
+
+/* Allow old versions of tunelp to continue to work */
+#define OLD_LPCHAR 0x0001
+#define OLD_LPTIME 0x0002
+#define OLD_LPABORT 0x0004
+#define OLD_LPSETIRQ 0x0005
+#define OLD_LPGETIRQ 0x0006
+#define OLD_LPWAIT 0x0008
+#define OLD_IOCTL_MAX 8
/*
* All my debugging code assumes that you debug with only one printer at
@@ -43,6 +77,31 @@ struct lp_struct lp_table[] = {
#undef LP_DEBUG
+#ifdef CONFIG_MIPS_JAZZ
+static inline unsigned int lp_in(unsigned int port)
+{
+/* printk("lp_in: port>>24 = %08x, JAZZ_LOCAL_IO_SPACE >> 24 = %08x\n",
+ port >> 24,JAZZ_LOCAL_IO_SPACE >> 24); */
+ if (port >= JAZZ_LOCAL_IO_SPACE)
+ return (*(volatile unsigned char *)port);
+ else
+ return inb_p(port);
+}
+
+static inline void lp_out(unsigned char value, unsigned int port)
+{
+/* printk("lp_out: port>>24 = %08x, JAZZ_LOCAL_IO_SPACE >> 24 = %08x\n",
+ port >> 24,JAZZ_LOCAL_IO_SPACE >> 24); */
+ if (port >= JAZZ_LOCAL_IO_SPACE)
+ *(volatile unsigned char *)port = value;
+ else
+ outb(value, port);
+}
+#else
+#define lp_in(port) inb_p(port)
+#define lp_out(port,value) outb_p(port,value)
+#endif
+
static int lp_reset(int minor)
{
int testvalue;
@@ -51,10 +110,10 @@ static int lp_reset(int minor)
command = LP_PSELECP | LP_PINITP;
/* reset value */
- outb_p(0, LP_C(minor));
+ lp_out(0, LP_C(minor));
for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
;
- outb_p(command, LP_C(minor));
+ lp_out(command, LP_C(minor));
return LP_S(minor);
}
@@ -72,7 +131,7 @@ static int lp_char_polled(char lpchar, int minor)
count ++;
if(need_resched)
schedule();
- } while(!(status & LP_PBUSY) && count < LP_CHAR(minor));
+ } while(!LP_READY(minor,status) && count < LP_CHAR(minor));
if (count == LP_CHAR(minor)) {
return 0;
@@ -84,15 +143,15 @@ static int lp_char_polled(char lpchar, int minor)
lp_max_count=count;
}
#endif
- outb_p(lpchar, LP_B(minor));
+ lp_out(lpchar, LP_B(minor));
/* must wait before taking strobe high, and after taking strobe
low, according spec. Some printers need it, others don't. */
while(wait != LP_WAIT(minor)) wait++;
/* control port takes strobe high */
- outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
+ lp_out(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
while(wait) wait--;
/* take strobe low */
- outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
+ lp_out(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
return 1;
}
@@ -107,15 +166,17 @@ static int lp_char_interrupt(char lpchar, int minor)
|| !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)
|| !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)) {
- outb_p(lpchar, LP_B(minor));
+ if (!LP_CAREFUL_READY(minor, status))
+ return 0;
+ lp_out(lpchar, LP_B(minor));
/* must wait before taking strobe high, and after taking strobe
low, according spec. Some printers need it, others don't. */
while(wait != LP_WAIT(minor)) wait++;
/* control port takes strobe high */
- outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
+ lp_out(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
while(wait) wait--;
/* take strobe low */
- outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
+ lp_out(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
return 1;
}
@@ -127,7 +188,7 @@ static int lp_char_interrupt(char lpchar, int minor)
unsigned int lp_last_call = 0;
#endif
-static void lp_interrupt(int irq)
+static void lp_interrupt(int irq, struct pt_regs *regs)
{
struct lp_struct *lp = &lp_table[0];
struct lp_struct *lp_end = &lp_table[LP_NO];
@@ -159,36 +220,33 @@ static int lp_write_interrupt(struct inode * inode, struct file * file, char * b
--copy_size;
++bytes_written;
} else {
- if (!((status = LP_S(minor)) & LP_PERRORP)) {
- int rc = total_bytes_written + bytes_written;
-
- if ((status & LP_POUTPA)) {
- printk("lp%d out of paper\n", minor);
- if (!rc)
- rc = -ENOSPC;
- } else if (!(status & LP_PSELECD)) {
- printk("lp%d off-line\n", minor);
- if (!rc)
- rc = -EIO;
- } else {
- printk("lp%d printer error\n", minor);
- if (!rc)
- rc = -EIO;
- }
- if(LP_F(minor) & LP_ABORT)
- return rc;
+ int rc = total_bytes_written + bytes_written;
+ status = LP_S(minor);
+ if ((status & LP_POUTPA)) {
+ printk(KERN_INFO "lp%d out of paper\n", minor);
+ if (LP_F(minor) & LP_ABORT)
+ return rc?rc:-ENOSPC;
+ } else if (!(status & LP_PSELECD)) {
+ printk(KERN_INFO "lp%d off-line\n", minor);
+ if (LP_F(minor) & LP_ABORT)
+ return rc?rc:-EIO;
+ } else if (!(status & LP_PERRORP)) {
+ printk(KERN_ERR "lp%d printer error\n", minor);
+ if (LP_F(minor) & LP_ABORT)
+ return rc?rc:-EIO;
}
cli();
- outb_p((LP_PSELECP|LP_PINITP|LP_PINTEN), (LP_C(minor)));
+ lp_out((LP_PSELECP|LP_PINITP|LP_PINTEN), (LP_C(minor)));
status = LP_S(minor);
- if (!(status & LP_PACK) || (status & LP_PBUSY)) {
- outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
+ if ((!(status & LP_PACK) || (status & LP_PBUSY))
+ && LP_CAREFUL_READY(minor, status)) {
+ lp_out((LP_PSELECP|LP_PINITP), (LP_C(minor)));
sti();
continue;
}
current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
interruptible_sleep_on(&lp->lp_wait_q);
- outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
+ lp_out((LP_PSELECP|LP_PINITP), (LP_C(minor)));
sti();
if (current->signal & ~current->blocked) {
if (total_bytes_written + bytes_written)
@@ -237,7 +295,7 @@ static int lp_write_polled(struct inode * inode, struct file * file,
int status = LP_S(minor);
if (status & LP_POUTPA) {
- printk("lp%d out of paper\n", minor);
+ printk(KERN_INFO "lp%d out of paper\n", minor);
if(LP_F(minor) & LP_ABORT)
return temp-buf?temp-buf:-ENOSPC;
current->state = TASK_INTERRUPTIBLE;
@@ -245,7 +303,7 @@ static int lp_write_polled(struct inode * inode, struct file * file,
schedule();
} else
if (!(status & LP_PSELECD)) {
- printk("lp%d off-line\n", minor);
+ printk(KERN_INFO "lp%d off-line\n", minor);
if(LP_F(minor) & LP_ABORT)
return temp-buf?temp-buf:-EIO;
current->state = TASK_INTERRUPTIBLE;
@@ -254,9 +312,9 @@ static int lp_write_polled(struct inode * inode, struct file * file,
} else
/* not offline or out of paper. on fire? */
if (!(status & LP_PERRORP)) {
- printk("lp%d reported invalid error status (on fire, eh?)\n", minor);
+ printk(KERN_ERR "lp%d reported invalid error status (on fire, eh?)\n", minor);
if(LP_F(minor) & LP_ABORT)
- return temp-buf?temp-buf:-EFAULT;
+ return temp-buf?temp-buf:-EIO;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + LP_TIMEOUT_POLLED;
schedule();
@@ -309,24 +367,48 @@ static int lp_open(struct inode * inode, struct file * file)
if (LP_F(minor) & LP_BUSY)
return -EBUSY;
+ MOD_INC_USE_COUNT;
+
+ /* If ABORTOPEN is set and the printer is offline or out of paper,
+ we may still want to open it to perform ioctl()s. Therefore we
+ have commandeered O_NONBLOCK, even though it is being used in
+ a non-standard manner. This is strictly a Linux hack, and
+ should most likely only ever be used by the tunelp application. */
+ if ((LP_F(minor) & LP_ABORTOPEN) && !(file->f_flags & O_NONBLOCK)) {
+ int status = LP_S(minor);
+ if (status & LP_POUTPA) {
+ printk(KERN_INFO "lp%d out of paper\n", minor);
+ MOD_DEC_USE_COUNT;
+ return -ENOSPC;
+ } else if (!(status & LP_PSELECD)) {
+ printk(KERN_INFO "lp%d off-line\n", minor);
+ MOD_DEC_USE_COUNT;
+ return -EIO;
+ } else if (!(status & LP_PERRORP)) {
+ printk(KERN_ERR "lp%d printer error\n", minor);
+ MOD_DEC_USE_COUNT;
+ return -EIO;
+ }
+ }
+
if ((irq = LP_IRQ(minor))) {
lp_table[minor].lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
- if (!lp_table[minor].lp_buffer)
+ if (!lp_table[minor].lp_buffer) {
+ MOD_DEC_USE_COUNT;
return -ENOMEM;
+ }
ret = request_irq(irq, lp_interrupt, SA_INTERRUPT, "printer");
if (ret) {
kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
lp_table[minor].lp_buffer = NULL;
printk("lp%d unable to use interrupt %d, error %d\n", minor, irq, ret);
+ MOD_DEC_USE_COUNT;
return ret;
}
}
LP_F(minor) |= LP_BUSY;
-#ifdef MODULE
- MOD_INC_USE_COUNT;
-#endif
return 0;
}
@@ -342,9 +424,7 @@ static void lp_release(struct inode * inode, struct file * file)
}
LP_F(minor) &= ~LP_BUSY;
-#ifdef MODULE
MOD_DEC_USE_COUNT;
-#endif
}
@@ -361,22 +441,42 @@ static int lp_ioctl(struct inode *inode, struct file *file,
return -ENODEV;
if ((LP_F(minor) & LP_EXIST) == 0)
return -ENODEV;
+ if (cmd <= OLD_IOCTL_MAX)
+ printk(KERN_NOTICE "lp%d: warning: obsolete ioctl %#x (perhaps you need a new tunelp)\n",
+ minor, cmd);
switch ( cmd ) {
+ case OLD_LPTIME:
case LPTIME:
LP_TIME(minor) = arg;
break;
+ case OLD_LPCHAR:
case LPCHAR:
LP_CHAR(minor) = arg;
break;
+ case OLD_LPABORT:
case LPABORT:
if (arg)
LP_F(minor) |= LP_ABORT;
else
LP_F(minor) &= ~LP_ABORT;
break;
+ case LPABORTOPEN:
+ if (arg)
+ LP_F(minor) |= LP_ABORTOPEN;
+ else
+ LP_F(minor) &= ~LP_ABORTOPEN;
+ break;
+ case LPCAREFUL:
+ if (arg)
+ LP_F(minor) |= LP_CAREFUL;
+ else
+ LP_F(minor) &= ~LP_CAREFUL;
+ break;
+ case OLD_LPWAIT:
case LPWAIT:
LP_WAIT(minor) = arg;
break;
+ case OLD_LPSETIRQ:
case LPSETIRQ: {
int oldirq;
int newirq = arg;
@@ -420,9 +520,29 @@ static int lp_ioctl(struct inode *inode, struct file *file,
lp_reset(minor);
break;
}
- case LPGETIRQ:
+ case OLD_LPGETIRQ:
retval = LP_IRQ(minor);
break;
+ case LPGETIRQ:
+ retval = verify_area(VERIFY_WRITE, (void *) arg,
+ sizeof(int));
+ if (retval)
+ return retval;
+ memcpy_tofs((int *) arg, &LP_IRQ(minor), sizeof(int));
+ break;
+ case LPGETSTATUS:
+ retval = verify_area(VERIFY_WRITE, (void *) arg,
+ sizeof(int));
+ if (retval)
+ return retval;
+ else {
+ int status = LP_S(minor);
+ memcpy_tofs((int *) arg, &status, sizeof(int));
+ }
+ break;
+ case LPRESET:
+ lp_reset(minor);
+ break;
default:
retval = -EINVAL;
}
@@ -456,18 +576,25 @@ long lp_init(long kmem_start)
}
/* take on all known port values */
for (offset = 0; offset < LP_NO; offset++) {
+/* printk("lp_init: checking region at %08x\n",LP_B(offset)); */
if (check_region(LP_B(offset), 3))
continue;
/* write to port & read back to check */
- outb_p( LP_DUMMY, LP_B(offset));
+/* printk("lp_init: writing to %08x...\n",LP_B(offset)); */
+ lp_out( LP_DUMMY, LP_B(offset));
for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
;
- testvalue = inb_p(LP_B(offset));
+/* printk("lp_init: reading from %08x...\n",LP_B(offset)); */
+ testvalue = lp_in(LP_B(offset));
if (testvalue == LP_DUMMY) {
LP_F(offset) |= LP_EXIST;
lp_reset(offset);
- printk("lp_init: lp%d exists, ", offset);
- snarf_region(LP_B(offset), 3);
+#ifndef CONFIG_MIPS_JAZZ
+ printk("lp%d at 0x%04x, ", offset,LP_B(offset));
+#else
+ printk("lp%d at 0x%08x, ", offset,LP_B(offset));
+#endif
+ request_region(LP_B(offset), 3, "lp");
if (LP_IRQ(offset))
printk("using IRQ%d\n", LP_IRQ(offset));
else
@@ -497,14 +624,15 @@ int init_module(void)
/* take on all known port values */
for (offset = 0; offset < LP_NO; offset++) {
/* write to port & read back to check */
- outb_p( LP_DUMMY, LP_B(offset));
+ lp_out( LP_DUMMY, LP_B(offset));
for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
;
- testvalue = inb_p(LP_B(offset));
+ testvalue = lp_in(LP_B(offset));
if (testvalue == LP_DUMMY) {
LP_F(offset) |= LP_EXIST;
lp_reset(offset);
- printk("lp_init: lp%d exists, ", offset);
+ printk("lp%d at 0x%04x, ", offset,LP_B(offset));
+ request_region(LP_B(offset),3,"lp");
if (LP_IRQ(offset))
printk("using IRQ%d\n", LP_IRQ(offset));
else
@@ -519,10 +647,14 @@ int init_module(void)
void cleanup_module(void)
{
- if(MOD_IN_USE)
+ int offset;
+ if(MOD_IN_USE)
printk("lp: busy - remove delayed\n");
- else
+ else
unregister_chrdev(LP_MAJOR,"lp");
+ for (offset = 0; offset < LP_NO; offset++)
+ if(LP_F(offset) && LP_EXIST)
+ release_region(LP_B(offset),3);
}
#endif
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 3c1274030..c3ff674a7 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -15,9 +15,11 @@
#include <linux/tpqic02.h>
#include <linux/malloc.h>
#include <linux/mman.h>
+#include <linux/mm.h>
#include <asm/segment.h>
#include <asm/io.h>
+#include <asm/pgtable.h>
#ifdef CONFIG_SOUND
extern long soundcard_init(long mem_start);
@@ -87,16 +89,17 @@ static int mmap_mem(struct inode * inode, struct file * file, struct vm_area_str
{
if (vma->vm_offset & ~PAGE_MASK)
return -ENXIO;
-#if defined (__i386__)
+#if defined(__i386__)
+ /*
+ * hmm.. This disables high-memory caching, as the XFree86 team
+ * wondered about that at one time.
+ * The surround logic should disable caching for the high device
+ * addresses anyway, but right now this seems still needed.
+ */
if (x86 > 3 && vma->vm_offset >= high_memory)
- vma->vm_page_prot |= PAGE_PCD;
-#elif defined (__mips__)
- if (vma->vm_offset >= high_memory)
- vma->vm_page_prot = vma->vm_page_prot & ~CACHE_MASK | CACHE_UNCACHED;
+ pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
#endif
-
- if (remap_page_range(vma->vm_start, vma->vm_offset,
- vma->vm_end - vma->vm_start, vma->vm_page_prot))
+ if (remap_page_range(vma->vm_start, vma->vm_offset, vma->vm_end - vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
vma->vm_inode = inode;
inode->i_count++;
@@ -168,10 +171,13 @@ static int read_zero(struct inode * node,struct file * file,char * buf,int count
static int mmap_zero(struct inode * inode, struct file * file, struct vm_area_struct * vma)
{
- if (vma->vm_page_prot & PAGE_RW)
+ if (vma->vm_flags & VM_SHARED)
return -EINVAL;
+printk("mmap_zero() #1\n");
if (zeromap_page_range(vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot))
+{printk("mmap_zero() #2\n");while(1);
return -EAGAIN;
+}printk("mmap_zero() #3\n");while(1);
return 0;
}
@@ -186,9 +192,8 @@ static int write_full(struct inode * inode,struct file * file,char * buf, int co
}
/*
- * Special lseek() function for /dev/null and /dev/zero.
- * Most notably, you can fopen() both devices with "a" now.
- * This was previously impossible. SRB.
+ * Special lseek() function for /dev/null and /dev/zero. Most notably, you can fopen()
+ * both devices with "a" now. This was previously impossible. SRB.
*/
static int null_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
diff --git a/drivers/char/msbusmouse.c b/drivers/char/msbusmouse.c
index c5be7f034..6c4954ae5 100644
--- a/drivers/char/msbusmouse.c
+++ b/drivers/char/msbusmouse.c
@@ -41,7 +41,7 @@
static struct mouse_status mouse;
-static void ms_mouse_interrupt(int unused)
+static void ms_mouse_interrupt(int irq, struct pt_regs * regs)
{
char dx, dy;
unsigned char buttons;
diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c
index f8111121f..28964d27e 100644
--- a/drivers/char/n_tty.c
+++ b/drivers/char/n_tty.c
@@ -345,7 +345,7 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
return;
}
- if (tty->stopped && I_IXON(tty) && I_IXANY(tty) && L_IEXTEN(tty)) {
+ if (tty->stopped && I_IXON(tty) && I_IXANY(tty)) {
start_tty(tty);
return;
}
@@ -355,6 +355,16 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
if (I_IUCLC(tty) && L_IEXTEN(tty))
c=tolower(c);
+ if (tty->closing) {
+ if (I_IXON(tty)) {
+ if (c == START_CHAR(tty))
+ start_tty(tty);
+ else if (c == STOP_CHAR(tty))
+ stop_tty(tty);
+ }
+ return;
+ }
+
/*
* If the previous character was LNEXT, or we know that this
* character is not one of the characters that we'll have to
@@ -615,7 +625,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct termios * old)
I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) ||
I_PARMRK(tty)) {
cli();
- memset(tty->process_char_map, 0, 256/32);
+ memset(tty->process_char_map, 0, 256/8);
if (I_IGNCR(tty) || I_ICRNL(tty))
set_bit('\r', &tty->process_char_map);
@@ -666,7 +676,6 @@ static void n_tty_set_termios(struct tty_struct *tty, struct termios * old)
static void n_tty_close(struct tty_struct *tty)
{
- wait_until_sent(tty, 0);
n_tty_flush_buffer(tty);
if (tty->read_buf) {
free_page((unsigned long) tty->read_buf);
@@ -690,6 +699,7 @@ static int n_tty_open(struct tty_struct *tty)
memset(tty->read_flags, 0, sizeof(tty->read_flags));
n_tty_set_termios(tty, 0);
tty->minimum_to_wake = 1;
+ tty->closing = 0;
return 0;
}
diff --git a/drivers/char/psaux.c b/drivers/char/psaux.c
index edc4c2baa..774a6669a 100644
--- a/drivers/char/psaux.c
+++ b/drivers/char/psaux.c
@@ -192,7 +192,7 @@ static inline int queue_empty(void)
* is waiting in the keyboard/aux controller.
*/
-static void aux_interrupt(int cpl)
+static void aux_interrupt(int cpl, struct pt_regs * regs)
{
int head = queue->head;
int maxhead = (queue->tail-1) & (AUX_BUF_SIZE-1);
@@ -213,7 +213,7 @@ static void aux_interrupt(int cpl)
*/
#ifdef CONFIG_82C710_MOUSE
-static void qp_interrupt(int cpl)
+static void qp_interrupt(int cpl, struct pt_regs * regs)
{
int head = queue->head;
int maxhead = (queue->tail-1) & (AUX_BUF_SIZE-1);
diff --git a/drivers/char/scc.c b/drivers/char/scc.c
new file mode 100644
index 000000000..81cbd9e5e
--- /dev/null
+++ b/drivers/char/scc.c
@@ -0,0 +1,2305 @@
+#include <linux/autoconf.h> /* fastest method */
+#ifdef CONFIG_SCC
+
+#define RCS_ID "$Id: scc.c,v 1.17 1995/03/15 23:28:12 JReuter Exp JReuter $"
+
+#define BANNER "Z8530 SCC driver v1.8.dl1bke (beta) by dl1bke\n"
+
+/* ******************************************************************** */
+/* * SCC.C - Linux driver for Z8530 based HDLC cards for AX.25 * */
+/* ******************************************************************** */
+
+/* ********************************************************************
+
+ (c) 1995 by Joerg Reuter DL1BKE
+
+ portions (c) 1994 Hans Alblas PE1AYX
+ and (c) 1993 Guido ten Dolle PE1NNZ
+
+ for the complete copyright notice see >> Copying.Z8530DRV <<
+
+ ******************************************************************** */
+
+/*
+
+ 940913 - started to rewrite the drive
+ 950131 - changed copyright notice to GPL without limitations.
+ 950228 - (hopefully) fixed the reason for kernel panics in
+ chk_rcv_queue() [stupid error]
+ 950304 - fixed underrun/zcount handling
+ 950305 - the driver registers port addresses now
+ 950314 - fixed underrun interrupt handling again
+
+ Thanks to:
+
+ PE1CHL Rob - for a lot of good ideas from his SCC driver for DOS
+ PE1NNZ Guido - for his port of the original driver to Linux
+ KA9Q Phil - from whom we stole the mbuf-structure
+ PA3AYX Hans - who rewrote the memory management and some minor,
+ but nevertheless useful changes
+ DL8MBT Flori - for support
+ DG0FT Rene - for the BayCom USCC support
+ PA3AOU Harry - for ESCC testing, information supply and support
+
+ PE1KOX Rob, DG1RTF Thomas, ON5QK Roland,
+
+ and all who sent me bug reports and ideas...
+
+
+ NB -- if you find errors, change something, please let me know
+ first before you distribute it... And please don't touch
+ the version number. Just replace my callsign in
+ "v1.8.dl1bke" with your own. Just to avoid confusion...
+
+ Jörg Reuter DL1BKE
+
+*/
+
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/tqueue.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/termios.h>
+#include <linux/serial.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/scc.h>
+#include <linux/delay.h>
+#include "scc_config.h"
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <time.h>
+#include <linux/kernel.h>
+
+
+long scc_init(long kmem_start);
+
+int scc_open(struct tty_struct *tty, struct file *filp);
+static void scc_close(struct tty_struct *tty, struct file *filp);
+int scc_write(struct tty_struct *tty, int from_user, unsigned char *buf, int count);
+static void scc_put_char(struct tty_struct *tty, unsigned char ch);
+static void scc_flush_chars(struct tty_struct *tty);
+static int scc_write_room(struct tty_struct *tty);
+static int scc_chars_in_buffer(struct tty_struct *tty);
+static void scc_flush_buffer(struct tty_struct *tty);
+static int scc_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg);
+static void scc_set_termios(struct tty_struct *tty, struct termios *old_termios);
+static void scc_throttle(struct tty_struct *tty);
+static void scc_unthrottle(struct tty_struct *tty);
+static void scc_start(struct tty_struct *tty);
+static void scc_stop(struct tty_struct *tty);
+
+static void z8530_init(void);
+
+static void scc_change_speed(struct scc_channel *scc);
+static void kiss_encode(struct scc_channel *scc);
+
+static void init_channel(struct scc_channel *scc);
+static void scc_key_trx (struct scc_channel *scc, char tx);
+static void scc_txint(register struct scc_channel *scc);
+static void scc_exint(register struct scc_channel *scc);
+static void scc_rxint(register struct scc_channel *scc);
+static void scc_spint(register struct scc_channel *scc);
+static void scc_isr(int irq, struct pt_regs *regs);
+static void scc_timer(void);
+static void scc_init_timer(struct scc_channel *scc);
+
+/* from serial.c */
+
+static int baud_table[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+ 9600, 19200, 38400, 57600, 115200, 0 };
+
+
+struct tty_driver scc_driver; /* new in 1.1.xx */
+static int scc_refcount;
+static struct tty_struct *scc_table[2*MAXSCC];
+static struct termios scc_termios[2 * MAXSCC];
+static struct termios scc_termios_locked[2 * MAXSCC];
+
+struct scc_channel SCC_Info[2 * MAXSCC]; /* information per channel */
+
+unsigned char Random = 0; /* random number for p-persist */
+unsigned char Driver_Initialized = 0;
+static struct sccbuf *sccfreelist[MAX_IBUFS] = {0};
+static int allocated_ibufs = 0;
+
+
+/* ******************************************************************** */
+/* * Port Access Functions * */
+/* ******************************************************************** */
+
+static inline unsigned char
+InReg(register io_port port, register unsigned char reg)
+{
+#ifdef SCC_LDELAY
+ register unsigned char r;
+ Outb(port, reg);
+ udelay(5);
+ r=Inb(port);
+ udelay(5);
+ return r;
+#else
+ Outb(port, reg);
+ return Inb(port);
+#endif
+}
+
+static inline void
+OutReg(register io_port port, register unsigned char reg, register unsigned char val)
+{
+#ifdef SCC_LDELAY
+ Outb(port, reg); udelay(5);
+ Outb(port, val); udelay(5);
+#else
+ Outb(port, reg);
+ Outb(port, val);
+#endif
+}
+
+static inline void
+wr(register struct scc_channel *scc, register unsigned char reg, register unsigned char val)
+{
+ OutReg(scc->ctrl, reg, (scc->wreg[reg] = val));
+}
+
+static inline void
+or(register struct scc_channel *scc, register unsigned char reg, register unsigned char val)
+{
+ OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val));
+}
+
+static inline void
+cl(register struct scc_channel *scc, register unsigned char reg, register unsigned char val)
+{
+ OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val));
+}
+
+/* ******************************************************************** */
+/* * Memory Buffer Management */
+/* ******************************************************************** */
+
+/* mbuf concept lent from KA9Q. Tnx PE1AYX for the buffer pool concept */
+/* (sorry, do you have any better ideas?) */
+
+
+/* allocate memory for the interrupt buffer pool */
+
+void scc_alloc_buffer_pool(void)
+{
+ int i;
+ struct sccbuf *sccb;
+ struct mbuf *bp;
+
+ for (i = 0 ; i < MAX_IBUFS ; i++)
+ {
+ sccb = (struct sccbuf *)kmalloc(sizeof(struct sccbuf), GFP_ATOMIC);
+ bp = (struct mbuf *)kmalloc(sizeof(struct mbuf), GFP_ATOMIC);
+
+ if ( !(sccb && bp) )
+ {
+ allocated_ibufs = --i;
+
+ if (allocated_ibufs < 0)
+ panic("scc_alloc_buffer_pool() - can't get buffer space");
+ else
+ printk("Warning: scc_alloc_buffer_pool() - allocated only %i buffers\n",i);
+
+ return;
+ }
+
+ sccfreelist[i] = sccb;
+ sccfreelist[i]->bp = bp;
+ memset(sccfreelist[i]->bp ,0,sizeof(struct mbuf));
+ sccfreelist[i]->inuse = 0;
+ sccfreelist[i]->bp->type = 0;
+ sccfreelist[i]->bp->refcnt = 0;
+ sccfreelist[i]->bp->size = BUFSIZE;
+ }
+ allocated_ibufs = MAX_IBUFS;
+}
+
+unsigned int scc_count_used_buffers(unsigned int * rx, unsigned int * tx)
+{
+ unsigned int i, used = 0;
+
+ if (rx) *rx = 0;
+ if (tx) *tx = 0;
+
+ for (i = 0 ; i < allocated_ibufs ; i++)
+ {
+ if (sccfreelist[i]->inuse)
+ {
+ switch (sccfreelist[i]->bp->type)
+ {
+ case BT_RECEIVE:
+ if (rx) (*rx)++; break;
+ case BT_TRANSMIT:
+ if (tx) (*tx)++; break;
+ }
+
+ used++;
+ }
+ }
+
+ return used;
+}
+
+
+/* Allocate mbuf */
+struct mbuf *
+scc_get_buffer(char type)
+{
+ int i;
+ unsigned long flags;
+
+ save_flags(flags); cli(); /* just to be sure */
+
+ for (i = 0 ; i < allocated_ibufs ; i++)
+ {
+ if(sccfreelist[i]->inuse == 0)
+ {
+ sccfreelist[i]->inuse = 1;
+ sccfreelist[i]->bp->type = type;
+ sccfreelist[i]->bp->next = NULLBUF;
+ sccfreelist[i]->bp->anext = NULLBUF;
+ sccfreelist[i]->bp->dup = NULLBUF;
+ sccfreelist[i]->bp->size = BUFSIZE;
+ sccfreelist[i]->bp->refcnt = 1;
+ sccfreelist[i]->bp->cnt = 0;
+ sccfreelist[i]->bp->in_use = 0;
+
+ restore_flags(flags);
+ return sccfreelist[i]->bp;
+ }
+ }
+
+ printk("\nSCC scc_get_buffer(): buffer pool empty\n"); /* should never happen */
+ restore_flags(flags);
+ return NULLBUF;
+}
+
+
+/* Decrement the reference pointer in an mbuf. If it goes to zero,
+ * free all resources associated with mbuf.
+ * Return pointer to next mbuf in packet chain
+ */
+struct mbuf *
+scc_return_buffer(register struct mbuf *bp, char type)
+{
+ struct mbuf *bpnext;
+ int i;
+ unsigned long flags;
+
+ if(!bp)
+ return NULLBUF;
+
+ save_flags(flags); cli();
+ bpnext = bp->next;
+
+ if (bp->dup)
+ {
+ for(i = 0 ; i < allocated_ibufs ; i++)
+ {
+ if(sccfreelist[i]->bp == bp->dup)
+ {
+ if (sccfreelist[i]->bp->type != type)
+ {
+ printk("scc_return_buffer(bp->dup, %i): wrong buffer type %i",
+ type,sccfreelist[i]->bp->type);
+ }
+
+ sccfreelist[i]->bp->cnt = 0;
+ sccfreelist[i]->bp->refcnt = 0;
+ sccfreelist[i]->bp->in_use = 0;
+ sccfreelist[i]->inuse = 0;
+ bp->dup = NULLBUF;
+ }
+ }
+ }
+
+ /* Decrement reference count. If it has gone to zero, free it. */
+ if(--bp->refcnt <= 0)
+ {
+ for(i = 0 ; i < allocated_ibufs ; i++)
+ {
+ if(sccfreelist[i]->bp == bp)
+ {
+ if (sccfreelist[i]->bp->type != type)
+ {
+ printk("scc_return_buffer(bp, %i): wrong buffer type %i",
+ type,sccfreelist[i]->bp->type);
+ }
+
+ sccfreelist[i]->bp->cnt = 0;
+ sccfreelist[i]->bp->refcnt = 0;
+ sccfreelist[i]->inuse = 0;
+ restore_flags(flags);
+ return bpnext;
+ }
+ }
+ }
+
+ printk("\nscc_return_buffer(): bogus pointer %p\n",bp);
+ restore_flags(flags);
+ return bpnext;
+}
+
+
+/* Free packet (a chain of mbufs). Return pointer to next packet on queue,
+ * if any
+ */
+struct mbuf *
+scc_free_chain(register struct mbuf *bp, char type)
+{
+ register struct mbuf *abp;
+ unsigned long flags;
+
+ if(!bp)
+ return NULLBUF;
+
+ save_flags(flags); cli();
+
+ abp = bp->anext;
+ while (bp) bp = scc_return_buffer(bp, type);
+
+ restore_flags(flags); cli();
+ return abp;
+}
+
+
+/* Append mbuf to end of mbuf chain */
+void
+scc_append_to_chain(struct mbuf **bph,struct mbuf *bp)
+{
+ register struct mbuf *p;
+ unsigned long flags;
+
+ if(bph == NULLBUFP || bp == NULLBUF)
+ return;
+
+ save_flags(flags); cli();
+
+ if(*bph == NULLBUF)
+ {
+ /* First one on chain */
+ *bph = bp;
+ } else {
+ for(p = *bph ; p->next != NULLBUF ; p = p->next)
+ ;
+ p->next = bp;
+ }
+
+ restore_flags(flags);
+}
+
+
+/* Append packet (chain of mbufs) to end of packet queue */
+void
+scc_enqueue(struct mbuf **queue,struct mbuf *bp)
+{
+ register struct mbuf *p;
+ unsigned long flags;
+
+ if(queue == NULLBUFP || bp == NULLBUF)
+ return;
+
+ save_flags(flags); cli();
+
+ if(*queue == NULLBUF)
+ {
+ /* List is empty, stick at front */
+ *queue = bp;
+ } else {
+ for(p = *queue ; p->anext != NULLBUF ; p = p->anext)
+ ;
+ p->anext = bp;
+ }
+ restore_flags(flags);
+}
+
+
+/* ******************************************************************** */
+/* * Interrupt Service Routines * */
+/* ******************************************************************** */
+
+/* ----> interrupt service routine for the 8530 <---- */
+
+/* it's recommendet to keep this function "inline" ;-) */
+
+static inline void
+scc_isr_dispatch(register struct scc_channel *scc, register int vector)
+{
+ switch (vector & VECTOR_MASK)
+ {
+ case TXINT: scc_txint(scc); break;
+ case EXINT: scc_exint(scc); break;
+ case RXINT: scc_rxint(scc); break;
+ case SPINT: scc_spint(scc); break;
+ default : printk("scc_isr(): unknown interrupt status (addr %4.4x, state %2.2x)\n",scc->ctrl,vector);
+ }
+}
+
+
+
+
+
+/* If the card has a latch for the interrupt vector (like the PA0HZP card)
+ use it to get the number of the chip that generated the int.
+ If not: poll all defined chips.
+ */
+
+static void
+scc_isr(int irq, struct pt_regs *regs)
+{
+ register unsigned char vector;
+ register struct scc_channel *scc;
+ register io_port q;
+ register io_port *p;
+ register int k;
+
+
+ cli();
+
+ if (Vector_Latch)
+ {
+ while(1) /* forever...? */
+ {
+ Outb(Vector_Latch, 0); /* Generate INTACK */
+
+ /* Read the vector */
+ if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break;
+ /* ...not forever! */
+
+ /* Extract channel number and status from vector. */
+ /* Isolate channel nummer */
+ /* Call handler */
+
+ if (vector & 0x01) break;
+
+ scc=&SCC_Info[(((vector>>1)&0x7c)^0x04) >> 2];
+
+ if (!scc->tty) break;
+
+ scc_isr_dispatch(scc, vector);
+
+ Outb(scc->ctrl,0x38); /* Reset Highest IUS" opcode to WR0 */
+ }
+
+ sti();
+ return;
+ }
+
+
+ /* Find the SCC generating the interrupt by polling all attached SCCs
+ * reading RR3A (the interrupt pending register)
+ */
+
+ k = 0;
+ p = SCC_ctrl;
+
+ while (k++ < Nchips)
+ {
+ if (!(q=*p++)) break;
+
+ Outb(q,3);
+
+ if (Inb(q))
+ {
+ if (!(q=*p++)) break;
+
+ Outb(q,2);
+ vector=Inb(q); /* Read the vector */
+
+ /* Extract channel number and status from vector. */
+ /* Isolate channel nummer */
+ /* Call handler */
+
+
+ if (vector & 1) break;
+
+ scc = &SCC_Info[(((vector >> 1) & 0x7c) ^ 0x04) >> 2];
+
+ if (!scc->tty) break;
+
+ /* Isolate status info from vector, call handler */
+
+ scc_isr_dispatch(scc, vector);
+
+ k = 0;
+ p = SCC_ctrl;
+
+ } else p++;
+ }
+
+ sti();
+}
+
+
+
+/* ----> four different interrupt handlers for Tx, Rx, changing of */
+/* DCD/CTS and Rx/Tx errors */
+
+
+static inline void prepare_next_txframe(register struct scc_channel *scc)
+{
+ if ((scc->tbp = scc->sndq))
+ {
+ scc->sndq = scc->sndq->anext;
+ scc->stat.tx_state = TXS_NEWFRAME;
+
+ } else {
+ scc->stat.tx_state = TXS_BUSY;
+ scc->t_tail = scc->kiss.tailtime;
+ }
+}
+
+
+/* Transmitter interrupt handler */
+static void
+scc_txint(register struct scc_channel *scc)
+{
+ register struct mbuf *bp;
+
+ scc->stat.txints++;
+
+ bp = scc->tbp;
+
+ while (bp && !bp->cnt) /* find next buffer */
+ bp = scc_return_buffer(bp, BT_TRANSMIT);
+
+ if (bp == NULLBUF) /* no more buffers in this frame */
+ {
+ if (--scc->stat.tx_queued < 0)
+ scc->stat.tx_queued = 0;
+
+ Outb(scc->ctrl,RES_Tx_P); /* reset pending int */
+ cl(scc,R10,ABUNDER); /* frame complete, allow CRC transmit */
+ prepare_next_txframe(scc);
+
+ } else { /* okay, send byte */
+
+ if (scc->stat.tx_state == TXS_NEWFRAME)
+ { /* first byte ? */
+ Outb(scc->ctrl, RES_Tx_CRC); /* reset CRC generator */
+ or(scc,R10,ABUNDER); /* re-install underrun protection */
+ Outb(scc->data,bp->data[bp->in_use++]);
+ /* send byte */
+ if (!scc->enhanced) /* reset EOM latch */
+ Outb(scc->ctrl, RES_EOM_L);
+
+ scc->stat.tx_state = TXS_ACTIVE;/* next byte... */
+ } else {
+ Outb(scc->data,bp->data[bp->in_use++]);
+ }
+
+ bp->cnt--; /* decrease byte count */
+ scc->tbp=bp; /* store buffer address */
+ }
+}
+
+/* Throw away received mbuf(s) when an error occurred */
+
+static inline void
+scc_toss_buffer(register struct scc_channel *scc)
+{
+ register struct mbuf *bp;
+
+ if((bp = scc->rbp) != NULLBUF)
+ {
+ scc_free_chain(bp->next, BT_RECEIVE);
+ bp->next = NULLBUF;
+ scc->rbp1 = bp; /* Don't throw this one away */
+ bp->cnt = 0; /* Simply rewind it */
+ bp->in_use = 0;
+ }
+}
+
+static inline void
+flush_FIFO(register struct scc_channel *scc)
+{
+ register int k;
+
+ for (k=0; k<3; k++)
+ Inb(scc->data);
+
+ if(scc->rbp != NULLBUF) /* did we receive something? */
+ {
+ if(scc->rbp->next != NULLBUF || scc->rbp->cnt > 0)
+ scc->stat.rxerrs++; /* then count it as an error */
+
+ scc_toss_buffer(scc); /* throw away buffer */
+ }
+}
+
+
+
+/* External/Status interrupt handler */
+static void
+scc_exint(register struct scc_channel *scc)
+{
+ register unsigned char status,changes,chg_and_stat;
+
+ scc->stat.exints++;
+
+ status = InReg(scc->ctrl,R0);
+ changes = status ^ scc->status;
+ chg_and_stat = changes & status;
+
+ /* ABORT: generated whenever DCD drops while receiving */
+
+ if (chg_and_stat & BRK_ABRT) /* Received an ABORT */
+ flush_FIFO(scc);
+
+
+ /* DCD: on = start to receive packet, off = ABORT condition */
+ /* (a successfully received packet generates a special condition int) */
+
+ if(changes & DCD) /* DCD input changed state */
+ {
+ if(status & DCD) /* DCD is now ON */
+ {
+ if (scc->modem.clocksrc != CLK_EXTERNAL)
+ OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */
+
+ or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */
+ } else { /* DCD is now OFF */
+ cl(scc,R3,ENT_HM|RxENABLE); /* disable the receiver */
+ flush_FIFO(scc);
+ }
+ }
+
+
+ /* CTS: use external TxDelay (what's that good for?!) */
+
+ if (chg_and_stat & CTS) /* CTS is now ON */
+ {
+ if (!Running(t_txdel) && scc->kiss.txdelay == 0) /* zero TXDELAY = wait for CTS */
+ scc->t_txdel = 0; /* kick it! */
+
+ }
+
+ if ((scc->stat.tx_state == TXS_ACTIVE) && (status & TxEOM))
+ {
+ scc->stat.tx_under++; /* oops, an underrun! count 'em */
+ Outb(scc->ctrl, RES_Tx_P);
+ Outb(scc->ctrl, RES_EXT_INT); /* reset ext/status interrupts */
+ scc->t_maxk = 1;
+ scc->tbp = scc_free_chain(scc->tbp, BT_TRANSMIT);
+
+ if (--scc->stat.tx_queued < 0) scc->stat.tx_queued = 0;
+ or(scc,R10,ABUNDER);
+ }
+
+ if (status & ZCOUNT) /* Oops? */
+ {
+ scc->stat.tx_under = 9999; /* errr... yes. */
+ Outb(scc->ctrl, RES_Tx_P); /* just to be sure */
+ scc->t_maxk = 1;
+ scc->tbp = scc_free_chain(scc->tbp, BT_TRANSMIT);
+ if (--scc->stat.tx_queued < 0) scc->stat.tx_queued = 0;
+ scc->kiss.tx_inhibit = 1; /* don't try it again! */
+ }
+
+
+ scc->status = status;
+ Outb(scc->ctrl,RES_EXT_INT);
+}
+
+
+/* Receiver interrupt handler */
+static void
+scc_rxint(register struct scc_channel *scc)
+{
+ register struct mbuf *bp;
+
+ scc->stat.rxints++;
+
+ if( Running(t_maxk) && !(scc->kiss.fulldup))
+ {
+ Inb(scc->data); /* discard char */
+ or(scc,R3,ENT_HM); /* enter hunt mode for next flag */
+ return;
+ }
+
+ if ((bp = scc->rbp1) == NULLBUF || bp->cnt >= bp->size)
+ { /* no buffer available or buffer full */
+ if (scc->rbp == NULLBUF)
+ {
+ if ((bp = scc_get_buffer(BT_RECEIVE)) != NULLBUF)
+ scc->rbp = scc->rbp1 = bp;
+
+ }
+ else if ((bp = scc_get_buffer(BT_RECEIVE)))
+ {
+ scc_append_to_chain(&scc->rbp, bp);
+ scc->rbp1 = bp;
+ }
+
+ if (bp == NULLBUF) /* no buffer available? */
+ {
+ Inb(scc->data); /* discard character */
+ or(scc,R3,ENT_HM); /* enter hunt mode */
+ scc_toss_buffer(scc); /* throw away buffers */
+ scc->stat.nospace++; /* and count this error */
+ return;
+ }
+ }
+
+ /* now, we have a buffer. read character and store it */
+ bp->data[bp->cnt++] = Inb(scc->data);
+}
+
+
+/* Receive Special Condition interrupt handler */
+static void
+scc_spint(register struct scc_channel *scc)
+{
+ register unsigned char status;
+ register struct mbuf *bp;
+
+ scc->stat.spints++;
+
+ status = InReg(scc->ctrl,R1); /* read receiver status */
+
+ Inb(scc->data); /* throw away Rx byte */
+
+ if(status & Rx_OVR) /* receiver overrun */
+ {
+ scc->stat.rx_over++; /* count them */
+ or(scc,R3,ENT_HM); /* enter hunt mode for next flag */
+ scc_toss_buffer(scc); /* rewind the buffer and toss */
+ }
+
+ if(status & END_FR && scc->rbp != NULLBUF) /* end of frame */
+ {
+ /* CRC okay, frame ends on 8 bit boundary and received something ? */
+
+ if (!(status & CRC_ERR) && (status & 0xe) == RES8 && scc->rbp->cnt)
+ {
+ /* ignore last received byte (first of the CRC bytes) */
+
+ for (bp = scc->rbp; bp->next != NULLBUF; bp = bp->next) ;
+ bp->cnt--; /* last byte is first CRC byte */
+
+ scc_enqueue(&scc->rcvq,scc->rbp);
+ scc->rbp = scc->rbp1 = NULLBUF;
+ scc->stat.rxframes++;
+ scc->stat.rx_queued++;
+ } else { /* a bad frame */
+ scc_toss_buffer(scc); /* throw away frame */
+ scc->stat.rxerrs++;
+ }
+ }
+
+ Outb(scc->ctrl,ERR_RES);
+}
+
+
+/* ******************************************************************** */
+/* * Init Channel */
+/* ******************************************************************** */
+
+
+/* ----> set SCC channel speed <---- */
+
+static inline void set_brg(register struct scc_channel *scc, unsigned int tc)
+{
+ unsigned long flags;
+
+ save_flags(flags); cli(); /* 2-step register accesses... */
+
+ cl(scc,R14,BRENABL); /* disable baudrate generator */
+ wr(scc,R12,tc & 255); /* brg rate LOW */
+ wr(scc,R13,tc >> 8); /* brg rate HIGH */
+ or(scc,R14,BRENABL); /* enable baudrate generator */
+
+ restore_flags(flags);
+}
+
+static inline void set_speed(register struct scc_channel *scc)
+{
+ set_brg(scc, (unsigned) (Clock / (scc->modem.speed * 64)) - 2);
+}
+
+
+/* ----> initialize a SCC channel <---- */
+
+static inline void init_brg(register struct scc_channel *scc)
+{
+ wr(scc, R14, BRSRC); /* BRG source = PCLK */
+ OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]); /* DPLL source = BRG */
+ OutReg(scc->ctrl, R14, SNRZI|scc->wreg[R14]); /* DPLL NRZI mode */
+}
+
+static void
+init_channel(register struct scc_channel *scc)
+{
+ unsigned long flags;
+
+ save_flags(flags); cli();
+
+ wr(scc,R1,0); /* no W/REQ operation */
+ wr(scc,R3,Rx8|RxCRC_ENAB); /* RX 8 bits/char, CRC, disabled */
+ wr(scc,R4,X1CLK|SDLC); /* *1 clock, SDLC mode */
+ wr(scc,R5,Tx8|DTR|TxCRC_ENAB); /* TX 8 bits/char, disabled, DTR */
+ wr(scc,R6,0); /* SDLC address zero (not used) */
+ wr(scc,R7,FLAG); /* SDLC flag value */
+ wr(scc,R9,VIS); /* vector includes status */
+ wr(scc,R10,(scc->modem.nrz? NRZ : NRZI)|CRCPS|ABUNDER); /* abort on underrun, preset CRC generator, NRZ(I) */
+ wr(scc,R14, 0);
+
+
+/* set clock sources:
+
+ CLK_DPLL: normal halfduplex operation
+
+ RxClk: use DPLL
+ TxClk: use DPLL
+ TRxC mode DPLL output
+
+ CLK_EXTERNAL: external clocking (G3RUH or DF9IC modem)
+
+ BayCom: others:
+
+ TxClk = pin RTxC TxClk = pin TRxC
+ RxClk = pin TRxC RxClk = pin RTxC
+
+
+ CLK_DIVIDER:
+ RxClk = use DPLL
+ TxClk = pin RTxC
+
+ BayCom: others:
+ pin TRxC = DPLL pin TRxC = BRG
+ (RxClk * 1) (RxClk * 32)
+*/
+
+
+ switch(scc->modem.clocksrc)
+ {
+ case CLK_DPLL:
+ wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP);
+ init_brg(scc);
+ break;
+
+ case CLK_DIVIDER:
+ wr(scc, R11, ((Board & BAYCOM)? TRxCDP : TRxCBR) | RCDPLL|TCRTxCP|TRxCOI);
+ init_brg(scc);
+ break;
+
+ case CLK_EXTERNAL:
+ wr(scc, R11, (Board & BAYCOM)? RCTRxCP|TCRTxCP : RCRTxCP|TCTRxCP);
+ OutReg(scc->ctrl, R14, DISDPLL);
+ break;
+
+ }
+
+ /* enable CTS (not for Baycom), ABORT & DCD interrupts */
+ wr(scc,R15,((Board & BAYCOM) ? 0 : CTSIE)|BRKIE|DCDIE|TxUIE);
+
+ if(scc->enhanced)
+ {
+ or(scc,R15,SHDLCE|FIFOE); /* enable FIFO, SDLC/HDLC Enhancements (From now R7 is R7') */
+ wr(scc,R7,AUTOEOM);
+ }
+
+ if((InReg(scc->ctrl,R0)) & DCD) /* DCD is now ON */
+ {
+ if (scc->modem.clocksrc != CLK_EXTERNAL)
+ or(scc,R14, SEARCH);
+
+ or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */
+ }
+
+ Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */
+ Outb(scc->ctrl,RES_EXT_INT); /* must be done twice */
+
+ scc->status = InReg(scc->ctrl,R0); /* read initial status */
+
+ or(scc,R1,INT_ALL_Rx|TxINT_ENAB|EXT_INT_ENAB); /* enable interrupts */
+ or(scc,R9,MIE); /* master interrupt enable */
+
+ restore_flags(flags);
+
+ set_speed(scc);
+}
+
+
+
+
+/* ******************************************************************** */
+/* * SCC timer functions * */
+/* ******************************************************************** */
+
+
+/* ----> scc_key_trx sets the time constant for the baudrate
+ generator and keys the transmitter <---- */
+
+static void
+scc_key_trx(struct scc_channel *scc, char tx)
+{
+ unsigned int time_const;
+
+ if (scc->modem.speed < baud_table[1])
+ scc->modem.speed = 1200;
+
+ if (Board & PRIMUS)
+ Outb(scc->ctrl + 4, Option | (tx? 0x80 : 0));
+
+ time_const = (unsigned) (Clock / (scc->modem.speed * (tx? 2:64))) - 2;
+
+ if (scc->modem.clocksrc == CLK_DPLL)
+ { /* simplex operation */
+ if (tx)
+ {
+ cl(scc,R3,RxENABLE|ENT_HM); /* then switch off receiver */
+
+ set_brg(scc, time_const); /* reprogram baudrate generator */
+
+ /* DPLL -> Rx clk, BRG -> Tx CLK, TRxC mode output, TRxC = BRG */
+ wr(scc, R11, RCDPLL|TCBR|TRxCOI|TRxCBR);
+
+ or(scc,R5,RTS|TxENAB); /* set the RTS line and enable TX */
+ } else {
+ cl(scc,R5,RTS|TxENAB);
+
+ set_brg(scc, time_const); /* reprogram baudrate generator */
+
+ /* DPLL -> Rx clk, DPLL -> Tx CLK, TRxC mode output, TRxC = DPLL */
+ wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP);
+
+ or(scc,R3,RxENABLE|ENT_HM);
+ }
+ } else {
+ if (tx)
+ or(scc,R5,RTS|TxENAB); /* enable tx */
+ else
+ cl(scc,R5,RTS|TxENAB); /* disable tx */
+ }
+}
+
+
+/* ----> SCC timer interrupt handler and friends. Will be called every 1/TPS s <---- */
+
+static unsigned char Rand = 17;
+
+static inline int is_grouped(register struct scc_channel *scc)
+{
+ int k;
+ struct scc_channel *scc2;
+ unsigned char grp1, grp2;
+
+ grp1 = scc->kiss.group;
+
+ for (k = 0; k < (Nchips * 2); k++)
+ {
+ scc2 = &SCC_Info[k];
+ grp2 = scc2->kiss.group;
+
+ if (scc2 == scc || !(scc2->tty && grp2))
+ return 0;
+
+ if (grp1 & 0x3f == grp2 & 0x3f)
+ {
+ if ( (grp1 & TXGROUP) && (scc2->wreg[R5] & RTS) )
+ return 1;
+
+ if ( (grp1 & RXGROUP) && (scc2->status & DCD) )
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+static inline void dw_slot_timeout(register struct scc_channel *scc)
+{
+ scc->t_dwait = TIMER_STOPPED;
+ scc->t_slot = TIMER_STOPPED;
+
+ if (!scc->kiss.fulldup)
+ {
+ Rand = Rand * 17 + 31;
+
+ if ( (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD)) || (scc->kiss.persist) < Rand || (scc->kiss.group && is_grouped(scc)) )
+ {
+ if (scc->t_mbusy == TIMER_STOPPED)
+ scc->t_mbusy = TPS * scc->kiss.maxdefer;
+
+ scc->t_slot = scc->kiss.slottime;
+ return ;
+
+ }
+ }
+
+ if ( !(scc->wreg[R5] & RTS) )
+ {
+ scc->t_txdel = scc->kiss.txdelay;
+ scc_key_trx(scc, TX_ON);
+ } else {
+ scc->t_txdel = 0;
+ }
+}
+
+
+
+
+static inline void txdel_timeout(register struct scc_channel *scc)
+{
+ scc->t_txdel = TIMER_STOPPED;
+
+ scc->t_maxk = TPS * scc->kiss.maxkeyup;
+ prepare_next_txframe(scc);
+
+ if (scc->stat.tx_state != TXS_BUSY)
+ scc_txint(scc);
+}
+
+
+
+static inline void tail_timeout(register struct scc_channel *scc)
+{
+ scc->t_tail = TIMER_STOPPED;
+
+ /* when fulldup is 0 or 1, switch off the transmitter.
+ * when frames are still queued (because of transmit time limit),
+ * restart the procedure to get the channel after MINTIME.
+ * when fulldup is 2, the transmitter remains keyed and we
+ * continue sending after waiting for waittime. IDLETIME is an
+ * idle timeout in this case.
+ */
+
+ if (scc->kiss.fulldup < 2)
+ {
+ if (scc->sndq) /* we had a timeout? */
+ {
+ scc->stat.tx_state = TXS_BUSY;
+ scc->t_dwait = TPS * scc->kiss.mintime; /* try again */
+ }
+
+ scc->stat.tx_state = TXS_IDLE;
+ scc->t_maxk = TIMER_STOPPED;
+ scc_key_trx(scc, TX_OFF);
+ return;
+ }
+
+ if (scc->sndq) /* maxkeyup expired */ /* ?! */
+ {
+ scc->stat.tx_state = TXS_BUSY;
+ scc->t_txdel = TPS * scc->kiss.waittime;
+ }
+ else
+
+ scc->t_idle = TPS * scc->kiss.idletime;
+}
+
+
+static inline void busy_timeout(register struct scc_channel *scc)
+{
+#ifdef THROW_AWAY_AFTER_BUSY_TIMEOUT
+ register struct mbuf *bp; /* not tested */
+
+ bp = scc->sndq;
+
+ while (bp) bp = scc_free_chain(bp, BT_TRANSMIT);
+
+ scc->sndq = NULLBUF;
+ scc->stat.tx_state = TXS_IDLE;
+
+#else
+ scc->t_txdel = scc->kiss.txdelay; /* brute force ... */
+#endif
+ scc->t_mbusy = TIMER_STOPPED;
+
+}
+
+
+static inline void maxk_idle_timeout(register struct scc_channel *scc)
+{
+ scc->t_maxk = TIMER_STOPPED;
+ scc->t_idle = TIMER_STOPPED;
+
+ scc->stat.tx_state = TXS_BUSY;
+ scc->t_tail = scc->kiss.tailtime;
+}
+
+static inline void check_rcv_queue(register struct scc_channel *scc)
+{
+ register struct mbuf *bp;
+
+ if (scc->stat.rx_queued > QUEUE_THRES)
+ {
+ if (scc->rcvq == NULLBUF)
+ {
+ printk("z8530drv: Warning - scc->stat.rx_queued shows overflow"
+ " (%d) but queue is empty\n", scc->stat.rx_queued);
+
+ scc->stat.rx_queued = 0; /* correct it */
+ scc->stat.nospace = 12345; /* draw attention to it */
+ return;
+ }
+
+ bp = scc->rcvq->anext; /* don't use the one we currently use */
+
+ while (bp && (scc->stat.rx_queued > QUEUE_HYST))
+ {
+ bp = scc_free_chain(bp, BT_RECEIVE);
+ scc->stat.rx_queued--;
+ scc->stat.nospace++;
+ }
+
+ scc->rcvq->anext = bp;
+ }
+}
+
+static void
+scc_timer(void)
+{
+ register struct scc_channel *scc;
+ register int chan;
+ unsigned long flags;
+
+ save_flags(flags); cli();
+
+ for (chan = 0; chan < (Nchips * 2); chan++)
+ {
+ scc = &SCC_Info[chan];
+
+ if (scc->tty && scc->init)
+ {
+ kiss_encode(scc);
+ check_rcv_queue(scc);
+
+ /* KISS-TNC emulation */
+
+ if (Expired(t_dwait)) dw_slot_timeout(scc) ; else
+ if (Expired(t_slot)) dw_slot_timeout(scc) ; else
+ if (Expired(t_txdel)) txdel_timeout(scc) ; else
+ if (Expired(t_tail)) tail_timeout(scc) ;
+
+ /* watchdogs */
+
+ if (Expired(t_mbusy)) busy_timeout(scc);
+ if (Expired(t_maxk)) maxk_idle_timeout(scc);
+ if (Expired(t_idle)) maxk_idle_timeout(scc);
+ }
+ }
+
+ timer_table[SCC_TIMER].fn = scc_timer;
+ timer_table[SCC_TIMER].expires = jiffies + HZ/TPS;
+ timer_active |= 1 << SCC_TIMER;
+
+ restore_flags(flags);
+}
+
+
+static void
+scc_init_timer(struct scc_channel *scc)
+{
+ unsigned long flags;
+
+ save_flags(flags); cli();
+
+ Stop_Timer(t_dwait);
+ Stop_Timer(t_slot);
+ Stop_Timer(t_txdel);
+ Stop_Timer(t_tail);
+ Stop_Timer(t_mbusy);
+ Stop_Timer(t_maxk);
+ Stop_Timer(t_idle);
+ scc->stat.tx_state = TXS_IDLE;
+
+ restore_flags(flags);
+}
+
+
+
+/* ******************************************************************** */
+/* * KISS interpreter * */
+/* ******************************************************************** */
+
+
+/*
+ * this will set the "kiss" parameters through kiss itself
+ */
+
+static void
+kiss_set_param(struct scc_channel *scc,char cmd, unsigned int val)
+{
+
+#define VAL val=val*TPS/100
+#define SVAL val? val:TIMER_STOPPED
+
+ switch(cmd){
+ case PARAM_TXDELAY:
+ scc->kiss.txdelay = VAL; break;
+ case PARAM_PERSIST:
+ scc->kiss.persist = val; break;
+ case PARAM_SLOTTIME:
+ scc->kiss.slottime = VAL; break;
+ case PARAM_TXTAIL:
+ scc->kiss.tailtime = VAL; break;
+ case PARAM_FULLDUP:
+ scc->kiss.fulldup = val; break;
+ case PARAM_WAIT:
+ scc->kiss.waittime = VAL; break;
+ case PARAM_MAXKEY:
+ scc->kiss.maxkeyup = SVAL; break;
+ case PARAM_MIN:
+ scc->kiss.mintime = SVAL; break;
+ case PARAM_IDLE:
+ scc->kiss.idletime = val; break;
+ case PARAM_MAXDEFER:
+ scc->kiss.maxdefer = SVAL; break;
+ case PARAM_GROUP:
+ scc->kiss.group = val; break;
+ case PARAM_TX:
+ scc->kiss.tx_inhibit = val;
+ case PARAM_SOFTDCD:
+ scc->kiss.softdcd = val;
+ }
+ return;
+
+#undef VAL
+#undef SVAL
+}
+
+
+/* interpret frame: strip CRC and decode KISS */
+
+static void kiss_interpret_frame(struct scc_channel * scc)
+{
+ unsigned char kisscmd;
+ unsigned long flags;
+
+ if (scc->sndq1->cnt < 2)
+ {
+ if (scc->sndq1)
+ scc_free_chain(scc->sndq1, BT_TRANSMIT);
+ else
+ scc->sndq1 = NULLBUF;
+
+ scc->sndq2 = NULLBUF;
+ return;
+ }
+
+
+
+ if (scc->kiss.not_slip)
+ {
+ kisscmd = scc->sndq1->data[scc->sndq1->in_use++];
+ scc->sndq1->cnt--;
+ } else {
+ kisscmd = 0;
+ }
+
+ if (kisscmd & 0xa0)
+ {
+ if (scc->sndq1->cnt > 2)
+ scc->sndq1->cnt -= 2;
+ else
+ {
+ scc_free_chain(scc->sndq1, BT_TRANSMIT);
+ scc->sndq2 = NULLBUF;
+ return;
+ }
+ }
+
+
+ kisscmd &= 0x1f;
+
+
+ if (kisscmd)
+ {
+ kiss_set_param(scc, kisscmd, scc->sndq1->data[scc->sndq1->in_use]);
+ scc->sndq1->cnt=0;
+ scc->sndq1->in_use=0;
+
+ scc_free_chain(scc->sndq1, BT_TRANSMIT);
+ scc->sndq2 = NULLBUF;
+ return;
+ }
+
+ scc_enqueue(&scc->sndq,scc->sndq1); /* scc_enqueue packet */
+ scc->stat.txframes++;
+ scc->stat.tx_queued++;
+ scc->sndq2 = NULLBUF; /* acquire a new buffer next time */
+
+ save_flags(flags); cli();
+
+ if(scc->stat.tx_state == TXS_IDLE)
+ { /* when transmitter is idle */
+ scc_init_timer(scc);
+ scc->stat.tx_state = TXS_BUSY;
+ scc->t_dwait = (scc->kiss.fulldup? 0:scc->kiss.waittime);
+ }
+
+ restore_flags(flags);
+}
+
+static void kiss_store_byte(struct scc_channel *scc, unsigned char ch)
+{
+ if (scc->sndq2 == NULLBUF) return;
+
+ if(scc->sndq2->cnt == scc->sndq2->size) /* buffer full? */
+ {
+ if((scc->sndq2 = scc_get_buffer(BT_TRANSMIT)) == NULLBUF)
+ {
+ printk("\nsccdrv: running out of memory\n");
+ return;
+ }
+ scc_append_to_chain(&scc->sndq1,scc->sndq2); /* add buffer */
+ }
+
+ scc->sndq2->data[scc->sndq2->cnt++] = ch;
+}
+
+static inline int kiss_decode(struct scc_channel *scc, unsigned char ch)
+{
+ switch (scc->stat.tx_kiss_state)
+ {
+ case KISS_IDLE:
+ if (ch == FEND)
+ {
+ if (!(scc->sndq2 = scc->sndq1 = scc_get_buffer(BT_TRANSMIT)))
+ return 0;
+
+ scc->stat.tx_kiss_state = KISS_DATA;
+ } else scc->stat.txerrs++;
+ break;
+
+ case KISS_DATA:
+ if (ch == FESC)
+ scc->stat.tx_kiss_state = KISS_ESCAPE;
+ else if (ch == FEND)
+ {
+ kiss_interpret_frame(scc);
+ scc->stat.tx_kiss_state = KISS_IDLE;
+ }
+ else kiss_store_byte(scc, ch);
+ break;
+
+ case KISS_ESCAPE:
+ if (ch == TFEND)
+ {
+ kiss_store_byte(scc, FEND);
+ scc->stat.tx_kiss_state = KISS_DATA;
+ }
+ else if (ch == TFESC)
+ {
+ kiss_store_byte(scc, FESC);
+ scc->stat.tx_kiss_state = KISS_DATA;
+ }
+ else
+ {
+ scc_free_chain(scc->sndq1, BT_TRANSMIT);
+ scc->sndq2 = NULLBUF;
+ scc->stat.txerrs++;
+ scc->stat.tx_kiss_state = KISS_IDLE;
+ }
+ break;
+ } /* switch */
+
+ return 0;
+
+}
+
+/* ----> Encode received data and write it to the flip-buffer <---- */
+
+/* receive raw frame from SCC. used for AX.25 */
+static void
+kiss_encode(register struct scc_channel *scc)
+{
+ struct mbuf *bp,*bp2;
+ struct tty_struct * tty = scc->tty;
+ unsigned long flags;
+ unsigned char ch;
+
+ if(!scc->rcvq)
+ {
+ scc->stat.rx_kiss_state = KISS_IDLE;
+ return;
+ }
+
+ /* worst case: FEND 0 FESC TFEND -> 4 bytes */
+
+ while(tty->flip.count < TTY_FLIPBUF_SIZE-3)
+ {
+ if (scc->rcvq->cnt)
+ {
+ bp = scc->rcvq;
+
+ if (scc->stat.rx_kiss_state == KISS_IDLE)
+ {
+ tty_insert_flip_char(tty, FEND, 0);
+
+ if (scc->kiss.not_slip)
+ tty_insert_flip_char(tty, 0, 0);
+
+ scc->stat.rx_kiss_state = KISS_RXFRAME;
+ }
+
+ switch(ch = bp->data[bp->in_use++])
+ {
+ case FEND:
+ tty_insert_flip_char(tty, FESC, 0);
+ tty_insert_flip_char(tty, TFEND, 0);
+ break;
+ case FESC:
+ tty_insert_flip_char(tty, FESC, 0);
+ tty_insert_flip_char(tty, TFESC, 0);
+ break;
+ default:
+ tty_insert_flip_char(tty, ch, 0);
+ }
+
+ bp->cnt--;
+
+ } else {
+ save_flags(flags); cli();
+
+ while (!scc->rcvq->cnt)
+ { /* buffer empty? */
+ bp = scc->rcvq->next; /* next buffer */
+ bp2 = scc->rcvq->anext; /* next packet */
+
+
+ scc_return_buffer(scc->rcvq, BT_RECEIVE);
+
+ if (!bp) /* end of frame ? */
+ {
+ scc->rcvq = bp2;
+
+ if (--scc->stat.rx_queued < 0)
+ scc->stat.rx_queued = 0;
+
+ if (scc->stat.rx_kiss_state == KISS_RXFRAME) /* new packet? */
+ {
+ tty_insert_flip_char(tty, FEND, 0); /* send FEND for old frame */
+ scc->stat.rx_kiss_state = KISS_IDLE; /* generate FEND for new frame */
+ }
+
+ restore_flags(flags);
+ queue_task(&tty->flip.tqueue, &tq_timer);
+ return;
+
+ } else scc->rcvq = bp; /* next buffer */
+ }
+
+ restore_flags(flags);
+ }
+
+ }
+
+ queue_task(&tty->flip.tqueue, &tq_timer); /* kick it... */
+}
+
+
+/* ******************************************************************* */
+/* * Init channel structures, special HW, etc... * */
+/* ******************************************************************* */
+
+
+static void
+z8530_init(void)
+{
+ struct scc_channel *scc;
+ int chip,chan;
+ unsigned long flags;
+ int k;
+
+ /* reset and pre-init all chips in the system */
+ for (chip = 0; chip < Nchips; chip++)
+ {
+ /* Special SCC cards */
+
+ if(Board & EAGLE) /* this is an EAGLE card */
+ Outb(Special_Port,0x08); /* enable interrupt on the board */
+
+ if(Board & (PC100 | PRIMUS)) /* this is a PC100/EAGLE card */
+ Outb(Special_Port,Option); /* set the MODEM mode (22H normally) */
+
+ /* Init SCC */
+
+ scc=&SCC_Info[2*chip];
+ if (!scc->ctrl) continue;
+
+ save_flags(flags); cli();
+
+ Outb(scc->ctrl, 0);
+ OutReg(scc->ctrl,R9,FHWRES); /* force hardware reset */
+ for (k=1; k < 1000; k++) ;
+
+ for (chan = 0; chan < 2; chan++)
+ {
+ scc=&SCC_Info[2*chip+chan];
+
+ wr(scc,R1, 0);
+ wr(scc,R2, chip*16); /* No of chip is vector */
+ wr(scc,R9,VIS); /* vector includes status */
+ }
+
+ restore_flags(flags);
+ }
+
+ if (Ivec == 2) Ivec = 9; /* this f... IBM AT-design! */
+ request_irq(Ivec, scc_isr, SA_INTERRUPT, "AX.25 SCC");
+
+ Driver_Initialized = 1;
+}
+
+
+/* ******************************************************************** */
+/* * Filesystem Routines: open, close, ioctl, settermios, etc * */
+/* ******************************************************************** */
+
+
+
+/* scc_paranoia_check(): warn user if something went wrong */
+
+static inline int scc_paranoia_check(struct scc_channel *scc, dev_t device, const char *routine)
+{
+#ifdef SCC_PARANOIA_CHECK
+ static const char *badmagic =
+ "Warning: bad magic number for Z8530 SCC struct (%d, %d) in %s\n";
+ static const char *badinfo =
+ "Warning: Z8530 not found for (%d, %d) in %s\n";
+
+ if (!scc->init)
+ {
+ printk(badinfo, MAJOR(device), MINOR(device), routine);
+ return 1;
+ }
+ if (scc->magic != SCC_MAGIC) {
+ printk(badmagic, MAJOR(device), MINOR(device), routine);
+ return 1;
+ }
+#endif
+
+ return 0;
+}
+
+
+/* ----> this one is called whenever you open the device <---- */
+
+int scc_open(struct tty_struct *tty, struct file * filp)
+{
+ struct scc_channel *scc;
+ int chan;
+
+ chan = MINOR(tty->device) - tty->driver.minor_start;
+ if ((chan < 0) || (chan >= (Nchips * 2)))
+ return -ENODEV;
+
+ scc = &SCC_Info[chan];
+
+ tty->driver_data = scc;
+ tty->termios->c_cflag &= ~CBAUD;
+
+ if (!Driver_Initialized)
+ return 0;
+
+ if (scc->magic != SCC_MAGIC)
+ {
+ printk("ERROR: scc_open(): bad magic number for device (%d, %d)", MAJOR(tty->device), MINOR(tty->device));
+ return -ENODEV;
+ }
+
+ if(scc->tty != NULL)
+ {
+ scc->tty_opened++;
+ return 0;
+ }
+
+ if(!scc->init) return 0;
+
+ scc->tty = tty;
+ init_channel(scc);
+
+ scc->stat.tx_kiss_state = KISS_IDLE; /* don't change this... */
+ scc->stat.rx_kiss_state = KISS_IDLE; /* ...or this */
+
+ scc_init_timer(scc);
+
+ timer_table[SCC_TIMER].fn = scc_timer;
+ timer_table[SCC_TIMER].expires = 0; /* now! */
+ timer_active |= 1 << SCC_TIMER;
+
+ return 0;
+}
+
+
+/* ----> and this whenever you close the device <---- */
+
+static void
+scc_close(struct tty_struct *tty, struct file * filp)
+{
+ struct scc_channel *scc = tty->driver_data;
+ unsigned long flags;
+
+ if (!scc || (scc->magic != SCC_MAGIC))
+ return;
+
+ if(scc->tty_opened)
+ {
+ scc->tty_opened--;
+ return;
+ }
+
+ tty->driver_data = NULLBUF;
+
+ save_flags(flags); cli();
+
+ Outb(scc->ctrl,0); /* Make sure pointer is written */
+ wr(scc,R1,0); /* disable interrupts */
+ wr(scc,R3,0);
+
+ scc->tty = NULL;
+
+ restore_flags(flags);
+ tty->stopped = 0;
+}
+
+
+
+
+/*
+ * change scc_speed
+ */
+
+static void
+scc_change_speed(struct scc_channel * scc)
+{
+ if (scc->tty == NULL)
+ return;
+
+ scc->modem.speed = baud_table[scc->tty->termios->c_cflag & CBAUD];
+
+ if (scc->stat.tx_state == 0) /* only switch baudrate on rx... ;-) */
+ set_speed(scc);
+}
+
+
+/* ----> ioctl-routine of the driver <---- */
+
+/* perform ioctl on SCC (sdlc) channel
+ * this is used for AX.25 mode, and will set the "kiss" parameters
+ */
+
+/* TIOCMGET - get modem status arg: (unsigned long *) arg
+ * TIOCMBIS - set PTT arg: ---
+ * TIOCMBIC - reset PTT arg: ---
+ * TIOCMBIC - set PTT arg: ---
+ * TIOCSCCINI - initialize driver arg: ---
+ * TIOCCHANINI - initialize channel arg: (struct scc_modem *) arg
+ * TIOCGKISS - get level 1 parameter arg: (struct ioctl_command *) arg
+ * TIOCSKISS - set level 1 parameter arg: (struct ioctl_command *) arg
+ * TIOCSCCSTAT - get driver status arg: (struct scc_stat *) arg
+ */
+
+
+static int
+scc_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
+{
+ struct scc_channel * scc = tty->driver_data;
+ unsigned long flags, r;
+ unsigned int result;
+ unsigned int value;
+ struct ioctl_command kiss_cmd;
+ int error;
+
+ if (scc->magic != SCC_MAGIC)
+ {
+ printk("ERROR: scc_ioctl(): bad magic number for device %d,%d",
+ MAJOR(tty->device), MINOR(tty->device));
+
+ return -ENODEV;
+ }
+
+ r = NO_SUCH_PARAM;
+
+ if (!Driver_Initialized)
+ {
+ if (cmd == TIOCSCCINI)
+ {
+ if (!suser())
+ return -EPERM;
+
+ scc_alloc_buffer_pool();
+ z8530_init();
+ return 0;
+ }
+
+ return -EINVAL; /* confuse the user */
+ }
+
+ if (!scc->init)
+ {
+
+ if (cmd == TIOCCHANINI)
+ {
+ if (!arg)
+ return -EFAULT;
+
+ if (!suser())
+ return -EPERM;
+
+ memcpy_fromfs(&scc->modem, (void *) arg, sizeof(struct scc_modem));
+
+ /* default KISS Params */
+
+ if (scc->modem.speed < 4800)
+ {
+ scc->kiss.txdelay = 36*TPS/100; /* 360 ms */
+ scc->kiss.persist = 42; /* 25% persistence */ /* was 25 */
+ scc->kiss.slottime = 16*TPS/100; /* 160 ms */
+ scc->kiss.tailtime = 4; /* minimal reasonable value */
+ scc->kiss.fulldup = 0; /* CSMA */
+ scc->kiss.waittime = 50*TPS/100; /* 500 ms */
+ scc->kiss.maxkeyup = 10; /* 10 s */
+ scc->kiss.mintime = 3; /* 3 s */
+ scc->kiss.idletime = 30; /* 30 s */
+ scc->kiss.maxdefer = 120; /* 2 min */
+ scc->kiss.not_slip = 1; /* KISS mode */
+ scc->kiss.softdcd = 0; /* hardware dcd */
+ } else {
+ scc->kiss.txdelay = 10*TPS/100; /* 100 ms */
+ scc->kiss.persist = 64; /* 25% persistence */ /* was 25 */
+ scc->kiss.slottime = 8*TPS/100; /* 160 ms */
+ scc->kiss.tailtime = 1; /* minimal reasonable value */
+ scc->kiss.fulldup = 0; /* CSMA */
+ scc->kiss.waittime = 50*TPS/100; /* 500 ms */
+ scc->kiss.maxkeyup = 7; /* 7 s */
+ scc->kiss.mintime = 3; /* 3 s */
+ scc->kiss.idletime = 30; /* 30 s */
+ scc->kiss.maxdefer = 120; /* 2 min */
+ scc->kiss.not_slip = 1; /* KISS mode */
+ scc->kiss.softdcd = 0; /* hardware dcd */
+ }
+
+ scc->init = 1;
+
+ return 0;
+ }
+
+ return -EINVAL;
+ }
+
+ switch(cmd){
+ case TCSBRK:
+ return 0;
+ case TIOCMGET:
+ error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(unsigned int *));
+ if (error)
+ return error;
+
+ save_flags(flags); cli();
+
+ result = ((scc->wreg[R5] & RTS) ? TIOCM_RTS : 0)
+ | ((scc->wreg[R5] & DTR) ? TIOCM_DTR : 0)
+ | ((InReg(scc->ctrl,R0) & DCD) ? TIOCM_CAR : 0)
+ | ((InReg(scc->ctrl,R0) & CTS) ? TIOCM_CTS : 0);
+
+ restore_flags(flags);
+
+ put_fs_long(result,(unsigned long *) arg);
+ return 0;
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ switch (cmd) {
+ case TIOCMBIS:
+ scc->wreg[R5] |= DTR;
+ scc->wreg[R5] |= RTS;
+ break;
+ case TIOCMBIC:
+ scc->wreg[R5] &= ~DTR;
+ scc->wreg[R5] &= ~RTS;
+ break;
+ case TIOCMSET:
+ value = get_fs_long((unsigned long *) arg);
+
+ if(value & TIOCM_DTR)
+ scc->wreg[R5] |= DTR;
+ else
+ scc->wreg[R5] &= ~DTR;
+ if(value & TIOCM_RTS)
+ scc->wreg[R5] |= RTS;
+ else
+ scc->wreg[R5] &= ~RTS;
+ break;
+ }
+
+ save_flags(flags); cli();
+
+ if(scc->stat.tx_state == TXS_IDLE && !Running(t_idle))
+ maxk_idle_timeout(scc);
+
+ restore_flags(flags);
+
+ return 0;
+
+ case TCGETS:
+ error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct termios));
+ if (error)
+ return error;
+ if (!arg)
+ return -EFAULT;
+
+ memcpy_tofs((void *) arg, scc->tty->termios, sizeof(struct termios));
+ return 0;
+
+ case TCSETS:
+ case TCSETSF: /* should flush first, but... */
+ case TCSETSW: /* should wait 'till flush, but... */
+ if (!suser())
+ return -EPERM;
+ if (!arg)
+ return -EFAULT;
+
+ memcpy_fromfs(scc->tty->termios, (void *) arg, sizeof(struct termios));
+ scc_change_speed(scc);
+ return 0;
+
+
+ case TIOCSCCSTAT:
+ error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(struct scc_stat));
+ if (error)
+ return error;
+
+ if (!arg)
+ return -EFAULT;
+
+ scc->stat.used_buf = scc_count_used_buffers(&scc->stat.rx_alloc,
+ &scc->stat.tx_alloc);
+
+ memcpy_tofs((void *) arg, &scc->stat, sizeof(struct scc_stat));
+ return 0;
+
+#define TICKS (100/TPS)
+#define CAST(x) (unsigned long)(x)
+#define Val kiss_cmd.param
+#define VAL kiss_cmd.param*TPS/100
+#define SVAL kiss_cmd.param? kiss_cmd.param:TIMER_STOPPED
+
+ case TIOCGKISS:
+ error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(struct ioctl_command));
+ if (error)
+ return error;
+
+ if (!arg)
+ return -EFAULT;
+
+ memcpy_fromfs(&kiss_cmd, (void *) arg, sizeof(struct ioctl_command));
+
+ switch (kiss_cmd.command)
+ {
+ case PARAM_TXDELAY: r = CAST(scc->kiss.txdelay*TICKS); break;
+ case PARAM_PERSIST: r = CAST(scc->kiss.persist); break;
+ case PARAM_SLOTTIME: r = CAST(scc->kiss.slottime*TICKS); break;
+ case PARAM_TXTAIL: r = CAST(scc->kiss.tailtime*TICKS); break;
+ case PARAM_FULLDUP: r = CAST(scc->kiss.fulldup); break;
+ case PARAM_SOFTDCD: r = CAST(scc->kiss.softdcd); break;
+ case PARAM_DTR: r = CAST((scc->wreg[R5] & DTR)? 1:0); break;
+ case PARAM_RTS: r = CAST((scc->wreg[R5] & RTS)? 1:0); break;
+ case PARAM_SPEED: r = CAST(scc->modem.speed); break;
+ case PARAM_GROUP: r = CAST(scc->kiss.group); break;
+ case PARAM_IDLE: r = CAST(scc->kiss.idletime); break;
+ case PARAM_MIN: r = CAST(scc->kiss.mintime); break;
+ case PARAM_MAXKEY: r = CAST(scc->kiss.maxkeyup); break;
+ case PARAM_WAIT: r = CAST(scc->kiss.waittime); break;
+ case PARAM_MAXDEFER: r = CAST(scc->kiss.maxdefer); break;
+ case PARAM_TX: r = CAST(scc->kiss.tx_inhibit); break;
+ case PARAM_SLIP: r = CAST(!scc->kiss.not_slip); break;
+ default: r = NO_SUCH_PARAM;
+ }
+
+ kiss_cmd.param = r;
+
+ memcpy_tofs((void *) arg, &kiss_cmd, sizeof(struct ioctl_command));
+ return 0;
+ break;
+
+ case TIOCSKISS:
+ if (!arg)
+ return -EFAULT;
+
+ if (!suser())
+ return -EPERM;
+
+ memcpy_fromfs(&kiss_cmd, (void *) arg, sizeof(struct ioctl_command));
+
+ switch (kiss_cmd.command)
+ {
+ case PARAM_TXDELAY: scc->kiss.txdelay=VAL; break;
+ case PARAM_PERSIST: scc->kiss.persist=Val; break;
+ case PARAM_SLOTTIME: scc->kiss.slottime=VAL; break;
+ case PARAM_TXTAIL: scc->kiss.tailtime=VAL; break;
+ case PARAM_FULLDUP: scc->kiss.fulldup=Val; break;
+ case PARAM_SOFTDCD: scc->kiss.softdcd=Val; break;
+ case PARAM_DTR: break; /* does someone need this? */
+ case PARAM_RTS: break; /* or this? */
+ case PARAM_SPEED: scc->modem.speed=Val; break;
+ case PARAM_GROUP: scc->kiss.group=Val; break;
+ case PARAM_IDLE: scc->kiss.idletime=Val; break;
+ case PARAM_MIN: scc->kiss.mintime=SVAL; break;
+ case PARAM_MAXKEY: scc->kiss.maxkeyup=SVAL; break;
+ case PARAM_WAIT: scc->kiss.waittime=Val; break;
+ case PARAM_MAXDEFER: scc->kiss.maxdefer=SVAL; break;
+ case PARAM_TX: scc->kiss.tx_inhibit=Val; break;
+ case PARAM_SLIP: scc->kiss.not_slip=!Val; break;
+ default: return -ENOIOCTLCMD;
+ }
+
+ return 0;
+ break;
+#undef TICKS
+#undef CAST
+#undef VAL
+#undef SVAL
+#undef Val
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+
+/* ----- TERMIOS function ----- */
+
+static void
+scc_set_termios(struct tty_struct * tty, struct termios * old_termios)
+{
+ if (tty->termios->c_cflag == old_termios->c_cflag)
+ return;
+ scc_change_speed(tty->driver_data);
+}
+
+
+static inline void check_tx_queue(register struct scc_channel *scc)
+{
+ register struct mbuf *bp;
+
+ if (scc->stat.tx_queued > QUEUE_THRES)
+ {
+ if (scc->sndq1 == NULLBUF)
+ {
+ printk("z8530drv: Warning - scc->stat.tx_queued shows overflow"
+ " (%d) but queue is empty\n", scc->stat.tx_queued);
+
+ scc->stat.tx_queued = 0; /* correct it */
+ scc->stat.nospace = 54321; /* draw attention to it */
+ return;
+ }
+
+ bp = scc->sndq1->anext; /* don't use the one we currently use */
+
+ while (bp && (scc->stat.tx_queued > QUEUE_HYST))
+ {
+ bp = scc_free_chain(bp, BT_TRANSMIT);
+ scc->stat.tx_queued--;
+ scc->stat.nospace++;
+ }
+
+ scc->sndq1->anext = bp;
+ }
+}
+
+
+
+/* ----> tx routine: decode KISS data and scc_enqueue it <---- */
+
+/* send raw frame to SCC. used for AX.25 */
+int scc_write(struct tty_struct *tty, int from_user, unsigned char *buf, int count)
+{
+ struct scc_channel * scc = tty->driver_data;
+ unsigned char tbuf[BUFSIZE], *p;
+ int cnt, cnt2;
+
+ if (!tty) return count;
+
+ if (scc_paranoia_check(scc, tty->device, "scc_write"))
+ return 0;
+
+ if (scc->kiss.tx_inhibit) return count;
+
+ check_tx_queue(scc);
+
+ cnt2 = count;
+
+ while (cnt2)
+ {
+ cnt = cnt2 > BUFSIZE? BUFSIZE:cnt2;
+ cnt2 -= cnt;
+
+ if (from_user)
+ memcpy_fromfs(tbuf, buf, cnt);
+ else
+ memcpy(tbuf, buf, cnt);
+
+ buf += cnt;
+
+ p=tbuf;
+
+ while(cnt--)
+ if (kiss_decode(scc, *p++))
+ {
+ scc->stat.nospace++;
+ return 0;
+ }
+
+ } /* while cnt2 */
+
+ return count;
+}
+
+
+/* put a single char into the buffer */
+
+static void scc_put_char(struct tty_struct * tty, unsigned char ch)
+{
+ struct scc_channel *scc = tty->driver_data;
+ unsigned char ch2;
+
+ if (scc_paranoia_check(scc, tty->device, "scc_put_char"))
+ return;
+
+ ch2 = ch;
+ scc_write(tty, 0, &ch2, 1); /* that's all */
+}
+
+static void scc_flush_chars(struct tty_struct * tty)
+{
+ struct scc_channel *scc = tty->driver_data;
+
+ scc_paranoia_check(scc, tty->device, "scc_flush_chars"); /* just to annoy the user... */
+
+ return; /* no flush needed */
+}
+
+/* the kernel does NOT use this routine yet... */
+
+static int scc_write_room(struct tty_struct *tty)
+{
+ struct scc_channel *scc = tty->driver_data;
+
+ if (scc_paranoia_check(scc, tty->device, "scc_write_room"))
+ return 0;
+
+ if (scc->stat.tx_alloc >= QUEUE_THRES)
+ {
+ printk("scc_write_room(): buffer full (ignore)\n");
+ return 0;
+ }
+
+ return BUFSIZE;
+}
+
+static int scc_chars_in_buffer(struct tty_struct *tty)
+{
+ struct scc_channel *scc = tty->driver_data;
+
+ if (scc && scc->sndq2)
+ return scc->sndq2->cnt;
+ else
+ return 0;
+}
+
+static void scc_flush_buffer(struct tty_struct *tty)
+{
+ struct scc_channel *scc = tty->driver_data;
+
+ if (scc_paranoia_check(scc, tty->device, "scc_flush_buffer"))
+ return;
+
+ scc->stat.tx_kiss_state = KISS_IDLE;
+
+ wake_up_interruptible(&tty->write_wait);
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+}
+
+static void scc_throttle(struct tty_struct *tty)
+{
+ struct scc_channel *scc = tty->driver_data;
+
+ if (scc_paranoia_check(scc, tty->device, "scc_throttle"))
+ return;
+
+
+ /* dummy */
+}
+
+static void scc_unthrottle(struct tty_struct *tty)
+{
+ struct scc_channel *scc = tty->driver_data;
+
+ if (scc_paranoia_check(scc, tty->device, "scc_unthrottle"))
+ return;
+
+ /* dummy */
+}
+
+static void scc_start(struct tty_struct *tty)
+{
+ struct scc_channel *scc = tty->driver_data;
+
+ if (scc_paranoia_check(scc, tty->device, "scc_start"))
+ return;
+
+ /* dummy */
+}
+
+
+static void scc_stop(struct tty_struct *tty)
+{
+ struct scc_channel *scc = tty->driver_data;
+
+ if (scc_paranoia_check(scc, tty->device, "scc_stop"))
+ return;
+
+ /* dummy */
+}
+
+
+/* ******************************************************************** */
+/* * Init SCC driver * */
+/* ******************************************************************** */
+
+long scc_init (long kmem_start)
+{
+ int chip, chan;
+ register io_port ctrl;
+ long flags;
+
+
+ memset(&scc_driver, 0, sizeof(struct tty_driver));
+ scc_driver.magic = TTY_DRIVER_MAGIC;
+ scc_driver.name = "sc";
+ scc_driver.major = TTY_MAJOR;
+ scc_driver.minor_start = 96;
+ scc_driver.num = Nchips*2;
+ scc_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ scc_driver.subtype = 0; /* not needed */
+ scc_driver.init_termios = tty_std_termios;
+ scc_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ scc_driver.flags = TTY_DRIVER_REAL_RAW;
+ scc_driver.refcount = &scc_refcount; /* not needed yet */
+ scc_driver.table = scc_table;
+ scc_driver.termios = (struct termios **) scc_termios;
+ scc_driver.termios_locked = (struct termios **) scc_termios_locked;
+ scc_driver.open = scc_open;
+ scc_driver.close = scc_close;
+ scc_driver.write = scc_write;
+ scc_driver.start = scc_start;
+ scc_driver.stop = scc_stop;
+
+ scc_driver.put_char = scc_put_char;
+ scc_driver.flush_chars = scc_flush_chars;
+ scc_driver.write_room = scc_write_room;
+ scc_driver.chars_in_buffer = scc_chars_in_buffer;
+ scc_driver.flush_buffer = scc_flush_buffer;
+
+ scc_driver.throttle = scc_throttle;
+ scc_driver.unthrottle = scc_unthrottle;
+
+ scc_driver.ioctl = scc_ioctl;
+ scc_driver.set_termios = scc_set_termios;
+
+ if (tty_register_driver(&scc_driver))
+ panic("Couldn't register Z8530 SCC driver\n");
+
+ printk (BANNER);
+
+ if (Nchips > MAXSCC) Nchips = MAXSCC; /* to avoid the "DAU" (duemmster anzunehmender User) */
+
+ /* reset and pre-init all chips in the system */
+
+ for (chip = 0; chip < Nchips; chip++)
+ {
+ memset((char *) &SCC_Info[2*chip ], 0, sizeof(struct scc_channel));
+ memset((char *) &SCC_Info[2*chip+1], 0, sizeof(struct scc_channel));
+
+ ctrl = SCC_ctrl[chip * 2];
+ if (!ctrl) continue;
+
+ save_flags(flags); cli(); /* because of 2-step accesses */
+
+
+/* Hmm... this may fail on fast systems with cards who don't delay the INTACK */
+/* If you are sure you specified the right port addresses and the driver doesn't */
+/* recognize the chips, define DONT_CHECK in scc_config.h */
+
+#ifndef DONT_CHECK
+ check_region(ctrl, 1);
+
+ Outb(ctrl, 0);
+ OutReg(ctrl,R13,0x55); /* is this chip realy there? */
+
+ if (InReg(ctrl,R13) != 0x55 )
+ {
+ restore_flags(flags);
+ continue;
+ }
+#endif
+
+ SCC_Info[2*chip ].magic = SCC_MAGIC;
+ SCC_Info[2*chip ].ctrl = SCC_ctrl[2*chip];
+ SCC_Info[2*chip ].data = SCC_data[2*chip];
+ SCC_Info[2*chip ].enhanced = SCC_Enhanced[chip];
+
+ SCC_Info[2*chip+1].magic = SCC_MAGIC;
+ SCC_Info[2*chip+1].ctrl = SCC_ctrl[2*chip+1];
+ SCC_Info[2*chip+1].data = SCC_data[2*chip+1];
+ SCC_Info[2*chip+1].enhanced = SCC_Enhanced[chip];
+
+
+ restore_flags(flags);
+ }
+
+#ifdef VERBOSE_BOOTMSG
+ printk("Init Z8530 driver: %u channels, using irq %u\n",Nchips*2,Ivec);
+
+
+ for (chan = 0; chan < Nchips * 2 ; chan++)
+ {
+ printk("/dev/%s%i: data port = 0x%3.3x control port = 0x%3.3x -- %s\n",
+ scc_driver.name, chan, SCC_data[chan], SCC_ctrl[chan],
+ SCC_Info[chan].ctrl? "found" : "missing");
+
+ if (SCC_Info[chan].ctrl == 0)
+ {
+ SCC_ctrl[chan] = 0;
+ } else {
+ request_region(SCC_ctrl[chan], 1, "scc ctrl");
+ request_region(SCC_data[chan], 1, "scc data");
+ }
+ }
+#else
+ printk("Init Z8530 driver: %u channels\n",Nchips*2);
+#endif
+
+
+ return kmem_start;
+}
+#endif
diff --git a/drivers/char/scc_config.h b/drivers/char/scc_config.h
new file mode 100644
index 000000000..a52b7e2a2
--- /dev/null
+++ b/drivers/char/scc_config.h
@@ -0,0 +1,67 @@
+#include <linux/scc.h>
+
+/********* CONFIGURATION PARAMATERES; PLEASE CHANGE THIS TO YOUR OWN SITUATION **********/
+
+/* SCC hardware parameters */
+
+/* use the following board types:
+ *
+ * PA0HZP OptoSCC (PA0HZP)
+ * EAGLE EAGLE
+ * PC100 PC100
+ * PRIMUS PRIMUS-PC (DG9BL)
+ * DRSI DRSI PC*Packet
+ * BAYCOM BayCom (U)SCC
+ *
+ */
+
+int Nchips = 2 ; /* number of chips */
+io_port Vector_Latch = 0 ; /* addr. of INTACK-Latch (0 for poll mode) */
+int Ivec = 7 ; /* interrupt vector */
+long Clock = 4915200 ; /* frequency of the scc clock */
+char Board = BAYCOM ; /* what type of SCC card do you use? */
+int Option = 0 ; /* command for extra hardware */
+io_port Special_Port = 0 ; /* port address for special hardware */
+ /* (for EAGLE, PC100, PRIMUS, DRSI) */
+
+ /* ^ never remove the semicolon !! */
+
+
+
+/* Channel A B Chip */
+/* ============ ======== */
+/* Control ports: */
+
+io_port SCC_ctrl[MAXSCC * 2] = {0x304, 0x305, /* ...one... */
+ 0x306, 0x307, /* ...two... */
+ 0, 0, /* ...three... */
+ 0, 0}; /* ...four... */
+
+/* Data ports: */
+
+io_port SCC_data[MAXSCC * 2] = {0x300, 0x301, /* ...one... */
+ 0x302, 0x303, /* ...two... */
+ 0, 0, /* ...three... */
+ 0, 0}; /* ...four... */
+
+
+/* set to '1' if you have and want ESCC chip (8580/85180/85280) support */
+
+/* Chip */
+/* ======== */
+int SCC_Enhanced[MAXSCC] = {0, /* ...one... */
+ 0, /* ...two... */
+ 0, /* ...three... */
+ 0}; /* ...four... */
+
+/* some useful #defines. You might need them or not */
+
+#define VERBOSE_BOOTMSG 1
+#undef SCC_DELAY /* perhaps a 486DX2 is a *bit* too fast */
+#undef SCC_LDELAY /* slow it even a bit more down */
+#undef DONT_CHECK /* don't look if the SCCs you specified are available */
+
+
+/* The external clocking, nrz and fullduplex divider configuration is gone */
+/* you can set these parameters in /etc/z8530drv.rc and initialize the */
+/* driver with sccinit */
diff --git a/drivers/char/selection.c b/drivers/char/selection.c
new file mode 100644
index 000000000..552db4a61
--- /dev/null
+++ b/drivers/char/selection.c
@@ -0,0 +1,294 @@
+/*
+ * linux/drivers/char/selection.c
+ *
+ * This module exports the functions:
+ *
+ * 'int set_selection(const unsigned long arg)'
+ * 'void clear_selection(void)'
+ * 'int paste_selection(struct tty_struct *tty)'
+ * 'int sel_loadlut(const unsigned long arg)'
+ *
+ * Now that /dev/vcs exists, most of this can disappear again.
+ */
+
+#include <linux/tty.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+
+#include <asm/segment.h>
+
+#include "vt_kern.h"
+#include "consolemap.h"
+#include "selection.h"
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */
+#define isspace(c) ((c) == ' ')
+
+/* Variables for selection control. */
+/* Use a dynamic buffer, instead of static (Dec 1994) */
+ int sel_cons = 0; /* must not be disallocated */
+static volatile int sel_start = -1; /* cleared by clear_selection */
+static int sel_end;
+static int sel_buffer_lth = 0;
+static char *sel_buffer = NULL;
+
+#define sel_pos(n) inverse_translate(screen_word(sel_cons, n, 1) & 0xff)
+
+/* clear_selection, highlight and highlight_pointer can be called
+ from interrupt (via scrollback/front) */
+
+/* set reverse video on characters s-e of console with selection. */
+inline static void
+highlight(const int s, const int e) {
+ invert_screen(sel_cons, s, e-s+2, 1);
+}
+
+/* use complementary color to show the pointer */
+inline static void
+highlight_pointer(const int where) {
+ complement_pos(sel_cons, where);
+}
+
+/* remove the current selection highlight, if any,
+ from the console holding the selection. */
+void
+clear_selection(void) {
+ highlight_pointer(-1); /* hide the pointer */
+ if (sel_start != -1) {
+ highlight(sel_start, sel_end);
+ sel_start = -1;
+ }
+}
+
+/*
+ * User settable table: what characters are to be considered alphabetic?
+ * 256 bits
+ */
+static unsigned long inwordLut[8]={
+ 0x00000000, /* control chars */
+ 0x03FF0000, /* digits */
+ 0x87FFFFFE, /* uppercase and '_' */
+ 0x07FFFFFE, /* lowercase */
+ 0x00000000,
+ 0x00000000,
+ 0xFF7FFFFF, /* latin-1 accented letters, not multiplication sign */
+ 0xFF7FFFFF /* latin-1 accented letters, not division sign */
+};
+
+static inline int inword(const unsigned char c) {
+ return ( inwordLut[c>>5] >> (c & 0x1F) ) & 1;
+}
+
+/* set inwordLut contents. Invoked by ioctl(). */
+int sel_loadlut(const unsigned long arg)
+{
+ int i = verify_area(VERIFY_READ, (char *) arg, 36);
+ if (i)
+ return i;
+ memcpy_fromfs(inwordLut, (unsigned long *)(arg+4), 32);
+ return 0;
+}
+
+/* does screen address p correspond to character at LH/RH edge of screen? */
+static inline int atedge(const int p)
+{
+ return (!(p % video_size_row) || !((p + 2) % video_size_row));
+}
+
+/* constrain v such that v <= u */
+static inline unsigned short limit(const unsigned short v, const unsigned short u)
+{
+/* gcc miscompiles the ?: operator, so don't use it.. */
+ if (v > u)
+ return u;
+ return v;
+}
+
+/* set the current selection. Invoked by ioctl(). */
+int set_selection(const unsigned long arg, struct tty_struct *tty)
+{
+ int sel_mode, new_sel_start, new_sel_end, spc;
+ char *bp, *obp;
+ int i, ps, pe;
+
+ do_unblank_screen();
+
+ { unsigned short *args, xs, ys, xe, ye;
+
+ args = (unsigned short *)(arg + 1);
+ xs = get_fs_word(args++) - 1;
+ ys = get_fs_word(args++) - 1;
+ xe = get_fs_word(args++) - 1;
+ ye = get_fs_word(args++) - 1;
+ sel_mode = get_fs_word(args);
+
+ xs = limit(xs, video_num_columns - 1);
+ ys = limit(ys, video_num_lines - 1);
+ xe = limit(xe, video_num_columns - 1);
+ ye = limit(ye, video_num_lines - 1);
+ ps = ys * video_size_row + (xs << 1);
+ pe = ye * video_size_row + (xe << 1);
+
+ if (sel_mode == 4) {
+ /* useful for screendump without selection highlights */
+ clear_selection();
+ return 0;
+ }
+
+ if (mouse_reporting() && (sel_mode & 16)) {
+ mouse_report(tty, sel_mode & 15, xs, ys);
+ return 0;
+ }
+ }
+
+ if (ps > pe) /* make sel_start <= sel_end */
+ {
+ int tmp = ps;
+ ps = pe;
+ pe = tmp;
+ }
+
+ if (sel_cons != fg_console) {
+ clear_selection();
+ sel_cons = fg_console;
+ }
+
+ switch (sel_mode)
+ {
+ case 0: /* character-by-character selection */
+ new_sel_start = ps;
+ new_sel_end = pe;
+ break;
+ case 1: /* word-by-word selection */
+ spc = isspace(sel_pos(ps));
+ for (new_sel_start = ps; ; ps -= 2)
+ {
+ if ((spc && !isspace(sel_pos(ps))) ||
+ (!spc && !inword(sel_pos(ps))))
+ break;
+ new_sel_start = ps;
+ if (!(ps % video_size_row))
+ break;
+ }
+ spc = isspace(sel_pos(pe));
+ for (new_sel_end = pe; ; pe += 2)
+ {
+ if ((spc && !isspace(sel_pos(pe))) ||
+ (!spc && !inword(sel_pos(pe))))
+ break;
+ new_sel_end = pe;
+ if (!((pe + 2) % video_size_row))
+ break;
+ }
+ break;
+ case 2: /* line-by-line selection */
+ new_sel_start = ps - ps % video_size_row;
+ new_sel_end = pe + video_size_row
+ - pe % video_size_row - 2;
+ break;
+ case 3:
+ highlight_pointer(pe);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ /* remove the pointer */
+ highlight_pointer(-1);
+
+ /* select to end of line if on trailing space */
+ if (new_sel_end > new_sel_start &&
+ !atedge(new_sel_end) && isspace(sel_pos(new_sel_end))) {
+ for (pe = new_sel_end + 2; ; pe += 2)
+ if (!isspace(sel_pos(pe)) || atedge(pe))
+ break;
+ if (isspace(sel_pos(pe)))
+ new_sel_end = pe;
+ }
+ if (sel_start == -1) /* no current selection */
+ highlight(new_sel_start, new_sel_end);
+ else if (new_sel_start == sel_start)
+ {
+ if (new_sel_end == sel_end) /* no action required */
+ return 0;
+ else if (new_sel_end > sel_end) /* extend to right */
+ highlight(sel_end + 2, new_sel_end);
+ else /* contract from right */
+ highlight(new_sel_end + 2, sel_end);
+ }
+ else if (new_sel_end == sel_end)
+ {
+ if (new_sel_start < sel_start) /* extend to left */
+ highlight(new_sel_start, sel_start - 2);
+ else /* contract from left */
+ highlight(sel_start, new_sel_start - 2);
+ }
+ else /* some other case; start selection from scratch */
+ {
+ clear_selection();
+ highlight(new_sel_start, new_sel_end);
+ }
+ sel_start = new_sel_start;
+ sel_end = new_sel_end;
+
+ if (sel_buffer)
+ kfree(sel_buffer);
+ sel_buffer = kmalloc((sel_end-sel_start)/2+1, GFP_KERNEL);
+ if (!sel_buffer) {
+ printk("selection: kmalloc() failed\n");
+ clear_selection();
+ return -ENOMEM;
+ }
+
+ obp = bp = sel_buffer;
+ for (i = sel_start; i <= sel_end; i += 2) {
+ *bp = sel_pos(i);
+ if (!isspace(*bp++))
+ obp = bp;
+ if (! ((i + 2) % video_size_row)) {
+ /* strip trailing blanks from line and add newline,
+ unless non-space at end of line. */
+ if (obp != bp) {
+ bp = obp;
+ *bp++ = '\r';
+ }
+ obp = bp;
+ }
+ }
+ sel_buffer_lth = bp - sel_buffer;
+ return 0;
+}
+
+/* Insert the contents of the selection buffer into the queue of the
+ tty associated with the current console. Invoked by ioctl(). */
+int paste_selection(struct tty_struct *tty)
+{
+ struct wait_queue wait = { current, NULL };
+ char *bp = sel_buffer;
+ int c = sel_buffer_lth;
+ int l;
+ struct vt_struct *vt = (struct vt_struct *) tty->driver_data;
+
+ if (!bp || !c)
+ return 0;
+ do_unblank_screen();
+ current->state = TASK_INTERRUPTIBLE;
+ add_wait_queue(&vt->paste_wait, &wait);
+ while (c) {
+ if (test_bit(TTY_THROTTLED, &tty->flags)) {
+ schedule();
+ continue;
+ }
+ l = MIN(c, tty->ldisc.receive_room(tty));
+ tty->ldisc.receive_buf(tty, bp, 0, l);
+ c -= l;
+ bp += l;
+ }
+ current->state = TASK_RUNNING;
+ return 0;
+}
diff --git a/drivers/char/selection.h b/drivers/char/selection.h
new file mode 100644
index 000000000..9d51f7e87
--- /dev/null
+++ b/drivers/char/selection.h
@@ -0,0 +1,91 @@
+/*
+ * selection.h
+ *
+ * Interface between console.c, tty_io.c, vt.c, vc_screen.c and selection.c
+ */
+extern int sel_cons;
+
+extern void clear_selection(void);
+extern int set_selection(const unsigned long arg, struct tty_struct *tty);
+extern int paste_selection(struct tty_struct *tty);
+extern int sel_loadlut(const unsigned long arg);
+extern int mouse_reporting(void);
+extern void mouse_report(struct tty_struct * tty, int butt, int mrx, int mry);
+
+extern unsigned long video_num_columns;
+extern unsigned long video_num_lines;
+extern unsigned long video_size_row;
+
+extern void do_unblank_screen(void);
+extern unsigned short *screen_pos(int currcons, int w_offset, int viewed);
+extern unsigned short screen_word(int currcons, int offset, int viewed);
+extern void complement_pos(int currcons, int offset);
+extern void invert_screen(int currcons, int offset, int count, int shift);
+
+#define reverse_video_char(a) (((a) & 0x88) | ((((a) >> 4) | ((a) << 4)) & 0x77))
+#define reverse_video_short(a) (((a) & 0x88ff) | \
+ (((a) & 0x7000) >> 4) | (((a) & 0x0700) << 4))
+/* this latter line used to have masks 0xf000 and 0x0f00, but selection
+ requires a self-inverse operation; moreover, the old version looks wrong */
+
+extern void getconsxy(int currcons, char *p);
+extern void putconsxy(int currcons, char *p);
+
+/* how to access screen memory */
+#ifdef __alpha__
+
+#include <asm/io.h>
+
+static inline void scr_writeb(unsigned char val, unsigned char * addr)
+{
+ if ((long) addr < 0)
+ *addr = val;
+ else
+ writeb(val, (unsigned long) addr);
+}
+
+static inline unsigned char scr_readb(unsigned char * addr)
+{
+ if ((long) addr < 0)
+ return *addr;
+ return readb((unsigned long) addr);
+}
+
+static inline void scr_writew(unsigned short val, unsigned short * addr)
+{
+ if ((long) addr < 0)
+ *addr = val;
+ else
+ writew(val, (unsigned long) addr);
+}
+
+static inline unsigned short scr_readw(unsigned short * addr)
+{
+ if ((long) addr < 0)
+ return *addr;
+ return readw((unsigned long) addr);
+}
+
+#else
+
+static inline void scr_writeb(unsigned char val, unsigned char * addr)
+{
+ *addr = val;
+}
+
+static inline unsigned char scr_readb(unsigned char * addr)
+{
+ return *addr;
+}
+
+static inline void scr_writew(unsigned short val, unsigned short * addr)
+{
+ *addr = val;
+}
+
+static inline unsigned short scr_readw(unsigned short * addr)
+{
+ return *addr;
+}
+
+#endif
diff --git a/drivers/char/serial.c b/drivers/char/serial.c
index bd983057c..c662eb678 100644
--- a/drivers/char/serial.c
+++ b/drivers/char/serial.c
@@ -11,6 +11,8 @@
* set_serial_info fixed to set the flags, custom divisor, and uart
* type fields. Fix suggested by Michael K. Johnson 12/12/92.
*
+ * Mips JAZZ support by Andreas Busse, andy@waldorf-gmbh.de
+ *
* This module exports the following rs232 io functions:
*
* long rs_init(long);
@@ -32,11 +34,16 @@
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/major.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/bitops.h>
+#ifdef CONFIG_MIPS_JAZZ
+# include <asm/jazz.h>
+#endif
DECLARE_TASK_QUEUE(tq_serial);
@@ -65,11 +72,13 @@ static int serial_refcount;
#define SERIAL_PARANOIA_CHECK
#define CONFIG_SERIAL_NOPAUSE_IO
#define SERIAL_DO_RESTART
-#define CONFIG_SERIAL_NEW_ISR
#undef SERIAL_DEBUG_INTR
-#undef SERIAL_DEBUG_OPEN
-#undef SERIAL_DEBUG_FLOW
+#define SERIAL_DEBUG_OPEN
+#define SERIAL_DEBUG_FLOW
+
+#define RS_STROBE_TIME 10
+#define RS_ISR_PASS_LIMIT 256
#define _INLINE_ inline
@@ -79,6 +88,7 @@ static int serial_refcount;
*/
static struct async_struct *IRQ_ports[16];
+static struct rs_multiport_struct rs_multiport[16];
static int IRQ_timeout[16];
static volatile int rs_irq_triggered;
static volatile int rs_triggered;
@@ -95,6 +105,7 @@ static void change_speed(struct async_struct *info);
* megabits/second; but this requires the faster clock.
*/
#define BASE_BAUD ( 1843200 / 16 )
+#define JAZZ_BASE_BAUD ( 8000000 / 16 ) /* ( 3072000 / 16) */
/* Standard COM flags (except for COM4, because of the 8514 problem) */
#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST )
@@ -123,8 +134,13 @@ static void change_speed(struct async_struct *info);
struct async_struct rs_table[] = {
/* UART CLK PORT IRQ FLAGS */
+#ifdef CONFIG_MIPS_JAZZ
+ { 0, JAZZ_BASE_BAUD, JAZZ_SERIAL1_BASE, 3, STD_COM_FLAGS }, /* ttyS0 */
+ { 0, JAZZ_BASE_BAUD, JAZZ_SERIAL2_BASE, 4, STD_COM_FLAGS }, /* ttyS1 */
+#else
{ 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */
{ 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */
+#endif
{ 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS }, /* ttyS2 */
{ 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */
@@ -235,7 +251,12 @@ static inline unsigned int serial_in(struct async_struct *info, int offset)
return inb(info->port+1);
} else
#endif
- return inb(info->port + offset);
+#ifdef CONFIG_MIPS_JAZZ
+ if (info->port >= JAZZ_LOCAL_IO_SPACE)
+ return (*((volatile unsigned char *)info->port + offset));
+ else
+#endif
+ return inb(info->port + offset);
}
static inline unsigned int serial_inp(struct async_struct *info, int offset)
@@ -246,10 +267,15 @@ static inline unsigned int serial_inp(struct async_struct *info, int offset)
return inb_p(info->port+1);
} else
#endif
+#ifdef CONFIG_MIPS_JAZZ
+ if (info->port >= JAZZ_LOCAL_IO_SPACE)
+ return (*((volatile unsigned char *)info->port + offset));
+ else
+#endif
#ifdef CONFIG_SERIAL_NOPAUSE_IO
- return inb(info->port + offset);
+ return inb(info->port + offset);
#else
- return inb_p(info->port + offset);
+ return inb_p(info->port + offset);
#endif
}
@@ -259,9 +285,14 @@ static inline void serial_out(struct async_struct *info, int offset, int value)
if (info->hub6) {
outb(info->hub6 - 1 + offset, info->port);
outb(value, info->port+1);
- } else
+ } else
#endif
- outb(value, info->port+offset);
+#ifdef CONFIG_MIPS_JAZZ
+ if (info->port >= JAZZ_LOCAL_IO_SPACE)
+ *((volatile unsigned char *)info->port + offset) = value;
+ else
+#endif
+ outb(value, info->port+offset);
}
static inline void serial_outp(struct async_struct *info, int offset,
@@ -273,10 +304,15 @@ static inline void serial_outp(struct async_struct *info, int offset,
outb_p(value, info->port+1);
} else
#endif
+#ifdef CONFIG_MIPS_JAZZ
+ if (info->port >= JAZZ_LOCAL_IO_SPACE)
+ *((volatile unsigned char *)info->port + offset) = value;
+ else
+#endif
#ifdef CONFIG_SERIAL_NOPAUSE_IO
- outb(value, info->port+offset);
+ outb(value, info->port+offset);
#else
- outb_p(value, info->port+offset);
+ outb_p(value, info->port+offset);
#endif
}
@@ -345,7 +381,7 @@ static void rs_start(struct tty_struct *tty)
* This is the serial driver's interrupt routine while we are probing
* for submarines.
*/
-static void rs_probe(int irq)
+static void rs_probe(int irq, struct pt_regs * regs)
{
rs_irq_triggered = irq;
rs_triggered |= 1 << irq;
@@ -369,11 +405,15 @@ static _INLINE_ void receive_chars(struct async_struct *info,
{
struct tty_struct *tty = info->tty;
unsigned char ch;
+ int ignored = 0;
do {
ch = serial_inp(info, UART_RX);
- if (*status & info->ignore_status_mask)
+ if (*status & info->ignore_status_mask) {
+ if (++ignored > 100)
+ break;
goto ignore_char;
+ }
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
break;
tty->flip.count++;
@@ -414,9 +454,7 @@ static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
if ((info->xmit_cnt <= 0) || info->tty->stopped ||
info->tty->hw_stopped) {
info->IER &= ~UART_IER_THRI;
-#ifdef CONFIG_SERIAL_NEW_ISR
serial_out(info, UART_IER, info->IER);
-#endif
return;
}
@@ -439,9 +477,7 @@ static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
if (info->xmit_cnt <= 0) {
info->IER &= ~UART_IER_THRI;
-#ifdef CONFIG_SERIAL_NEW_ISR
serial_out(info, UART_IER, info->IER);
-#endif
}
}
@@ -463,7 +499,8 @@ static _INLINE_ void check_modem_status(struct async_struct *info)
#ifdef SERIAL_DEBUG_OPEN
printk("scheduling hangup...");
#endif
- rs_sched_event(info, RS_EVENT_HANGUP);
+ queue_task_irq_off(&info->tqueue_hangup,
+ &tq_scheduler);
}
}
if (info->flags & ASYNC_CTS_FLOW) {
@@ -474,9 +511,7 @@ static _INLINE_ void check_modem_status(struct async_struct *info)
#endif
info->tty->hw_stopped = 0;
info->IER |= UART_IER_THRI;
-#ifdef CONFIG_SERIAL_NEW_ISR
serial_out(info, UART_IER, info->IER);
-#endif
rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
return;
}
@@ -487,24 +522,23 @@ static _INLINE_ void check_modem_status(struct async_struct *info)
#endif
info->tty->hw_stopped = 1;
info->IER &= ~UART_IER_THRI;
-#ifdef CONFIG_SERIAL_NEW_ISR
serial_out(info, UART_IER, info->IER);
-#endif
}
}
}
}
-#ifdef CONFIG_SERIAL_NEW_ISR
/*
* This is the serial driver's generic interrupt routine
*/
-static void rs_interrupt(int irq)
+static void rs_interrupt(int irq, struct pt_regs * regs)
{
int status;
struct async_struct * info;
int pass_counter = 0;
struct async_struct *end_mark = 0;
+ int first_multi = 0;
+ struct rs_multiport_struct *multi;
#ifdef SERIAL_DEBUG_INTR
printk("rs_interrupt(%d)...", irq);
@@ -514,6 +548,10 @@ static void rs_interrupt(int irq)
if (!info)
return;
+ multi = &rs_multiport[irq];
+ if (multi->port_monitor)
+ first_multi = inb(multi->port_monitor);
+
do {
if (!info->tty ||
(serial_in(info, UART_IIR) & UART_IIR_NO_INT)) {
@@ -539,7 +577,7 @@ static void rs_interrupt(int irq)
info = info->next_port;
if (!info) {
info = IRQ_ports[irq];
- if (pass_counter++ > 64) {
+ if (pass_counter++ > RS_ISR_PASS_LIMIT) {
#if 0
printk("rs loop break\n");
#endif
@@ -548,6 +586,9 @@ static void rs_interrupt(int irq)
continue;
}
} while (end_mark != info);
+ if (multi->port_monitor)
+ printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n",
+ info->irq, first_multi, inb(multi->port_monitor));
#ifdef SERIAL_DEBUG_INTR
printk("end.\n");
#endif
@@ -556,11 +597,13 @@ static void rs_interrupt(int irq)
/*
* This is the serial driver's interrupt routine for a single port
*/
-static void rs_interrupt_single(int irq)
+static void rs_interrupt_single(int irq, struct pt_regs * regs)
{
int status;
int pass_counter = 0;
+ int first_multi = 0;
struct async_struct * info;
+ struct rs_multiport_struct *multi;
#ifdef SERIAL_DEBUG_INTR
printk("rs_interrupt_single(%d)...", irq);
@@ -570,6 +613,10 @@ static void rs_interrupt_single(int irq)
if (!info || !info->tty)
return;
+ multi = &rs_multiport[irq];
+ if (multi->port_monitor)
+ first_multi = inb(multi->port_monitor);
+
do {
status = serial_inp(info, UART_LSR) & info->read_status_mask;
#ifdef SERIAL_DEBUG_INTR
@@ -580,7 +627,7 @@ static void rs_interrupt_single(int irq)
check_modem_status(info);
if (status & UART_LSR_THRE)
transmit_chars(info, 0);
- if (pass_counter++ > 64) {
+ if (pass_counter++ > RS_ISR_PASS_LIMIT) {
#if 0
printk("rs_single loop break.\n");
#endif
@@ -588,102 +635,95 @@ static void rs_interrupt_single(int irq)
}
} while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT));
info->last_active = jiffies;
+ if (multi->port_monitor)
+ printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n",
+ info->irq, first_multi, inb(multi->port_monitor));
#ifdef SERIAL_DEBUG_INTR
printk("end.\n");
#endif
}
-#else /* CONFIG_SERIAL_NEW_ISR */
-
/*
- * This is the serial driver's generic interrupt routine
+ * This is the serial driver's for multiport boards
*/
-static void rs_interrupt(int irq)
+static void rs_interrupt_multi(int irq, struct pt_regs * regs)
{
int status;
struct async_struct * info;
- int done = 1, pass_counter = 0;
+ int pass_counter = 0;
+ int first_multi= 0;
+ struct rs_multiport_struct *multi;
-
#ifdef SERIAL_DEBUG_INTR
- printk("rs_interrupt(%d)...", irq);
+ printk("rs_interrupt_multi(%d)...", irq);
#endif
info = IRQ_ports[irq];
if (!info)
return;
+ multi = &rs_multiport[irq];
+ if (!multi->port1) {
+ /* Should never happen */
+ printk("rs_interrupt_multi: NULL port1!\n");
+ return;
+ }
+ if (multi->port_monitor)
+ first_multi = inb(multi->port_monitor);
while (1) {
- if (!info->tty)
+ if (!info->tty ||
+ (serial_in(info, UART_IIR) & UART_IIR_NO_INT))
goto next;
- serial_outp(info, UART_IER, 0);
+ info->last_active = jiffies;
+
status = serial_inp(info, UART_LSR) & info->read_status_mask;
- if (status & UART_LSR_DR) {
+#ifdef SERIAL_DEBUG_INTR
+ printk("status = %x...", status);
+#endif
+ if (status & UART_LSR_DR)
receive_chars(info, &status);
- done = 0;
- }
check_modem_status(info);
if (status & UART_LSR_THRE)
- transmit_chars(info, &done);
+ transmit_chars(info, 0);
next:
- info = info->next_port;
- if (!info) {
- info = IRQ_ports[irq];
- if (done)
- break;
- done = 1;
- if (pass_counter++ > 64) {
-#if 0
- printk("rs loop break\n");
+ info = info->next_port;
+ if (info)
+ continue;
+
+ info = IRQ_ports[irq];
+ if (pass_counter++ > RS_ISR_PASS_LIMIT) {
+#if 1
+ printk("rs_multi loop break\n");
#endif
- break; /* Prevent infinite loops */
- }
+ break; /* Prevent infinite loops */
}
- }
-
- /*
- * Reset the IER registers; info is already set up from the
- * above while loop.
- */
- do
- serial_outp(info, UART_IER, info->IER);
- while ((info = info->next_port) != NULL);
-}
-
-/*
- * This is the serial driver's interrupt routine for a single port
- */
-static void rs_interrupt_single(int irq)
-{
- int status;
- struct async_struct * info;
-
-
+ if (multi->port_monitor)
+ printk("rs port monitor irq %d: 0x%x, 0x%x\n",
+ info->irq, first_multi,
+ inb(multi->port_monitor));
+ if ((inb(multi->port1) & multi->mask1) != multi->match1)
+ continue;
+ if (!multi->port2)
+ break;
+ if ((inb(multi->port2) & multi->mask2) != multi->match2)
+ continue;
+ if (!multi->port3)
+ break;
+ if ((inb(multi->port3) & multi->mask3) != multi->match3)
+ continue;
+ if (!multi->port4)
+ break;
+ if ((inb(multi->port4) & multi->mask4) == multi->match4)
+ continue;
+ break;
+ }
#ifdef SERIAL_DEBUG_INTR
- printk("rs_interrupt_single(%d)...", irq);
+ printk("end.\n");
#endif
-
- info = IRQ_ports[irq];
- if (!info || !info->tty)
- return;
-
- serial_outp(info, UART_IER, 0);
- status = serial_inp(info, UART_LSR) & info->read_status_mask;
- if (status & UART_LSR_DR)
- receive_chars(info, &status);
- check_modem_status(info);
- if (status & UART_LSR_THRE)
- transmit_chars(info, 0);
-
- /*
- * Reset the IER register
- */
- serial_outp(info, UART_IER, info->IER);
}
-#endif /* CONFIG_SERIAL_NEW_ISR */
/*
* -------------------------------------------------------------------
@@ -714,12 +754,6 @@ static void do_softint(void *private_)
if (!tty)
return;
- if (clear_bit(RS_EVENT_HANGUP, &info->event)) {
- tty_hangup(tty);
- wake_up_interruptible(&info->open_wait);
- info->flags &= ~(ASYNC_NORMAL_ACTIVE|
- ASYNC_CALLOUT_ACTIVE);
- }
if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc.write_wakeup)
@@ -729,6 +763,28 @@ static void do_softint(void *private_)
}
/*
+ * This routine is called from the scheduler tqueue when the interrupt
+ * routine has signalled that a hangup has occurred. The path of
+ * hangup processing is:
+ *
+ * serial interrupt routine -> (scheduler tqueue) ->
+ * do_serial_hangup() -> tty->hangup() -> rs_hangup()
+ *
+ */
+static void do_serial_hangup(void *private_)
+{
+ struct async_struct *info = (struct async_struct *) private_;
+ struct tty_struct *tty;
+
+ tty = info->tty;
+ if (!tty)
+ return;
+
+ tty_hangup(tty);
+}
+
+
+/*
* This subroutine is called when the RS_TIMER goes off. It is used
* by the serial driver to handle ports that do not have an interrupt
* (irq=0). This doesn't work very well for 16450's, but gives barely
@@ -741,7 +797,7 @@ static void rs_timer(void)
struct async_struct *info;
unsigned int i;
- if ((jiffies - last_strobe) >= 60*HZ) {
+ if ((jiffies - last_strobe) >= RS_STROBE_TIME*HZ) {
for (i=1; i < 16; i++) {
info = IRQ_ports[i];
if (!info)
@@ -754,19 +810,22 @@ static void rs_timer(void)
serial_out(info, UART_IER, info->IER);
info = info->next_port;
} while (info);
- rs_interrupt(i);
+ if (rs_multiport[i].port1)
+ rs_interrupt_multi(i, NULL);
+ else
+ rs_interrupt(i, NULL);
} else
- rs_interrupt_single(i);
+ rs_interrupt_single(i, NULL);
sti();
}
}
last_strobe = jiffies;
- timer_table[RS_TIMER].expires = jiffies + 60 * HZ;
+ timer_table[RS_TIMER].expires = jiffies + RS_STROBE_TIME * HZ;
timer_active |= 1 << RS_TIMER;
if (IRQ_ports[0]) {
cli();
- rs_interrupt(0);
+ rs_interrupt(0, NULL);
sti();
timer_table[RS_TIMER].expires = jiffies + IRQ_timeout[0] - 2;
@@ -822,11 +881,11 @@ static void free_all_interrupts(int irq_lines)
static void figure_IRQ_timeout(int irq)
{
struct async_struct *info;
- int timeout = 6000; /* 60 seconds === a long time :-) */
+ int timeout = 60*HZ; /* 60 seconds === a long time :-) */
info = IRQ_ports[irq];
if (!info) {
- IRQ_timeout[irq] = 6000;
+ IRQ_timeout[irq] = 60*HZ;
return;
}
while (info) {
@@ -844,7 +903,7 @@ static int startup(struct async_struct * info)
unsigned short ICP;
unsigned long flags;
int retval;
- void (*handler)(int);
+ void (*handler)(int, struct pt_regs *);
if (info->flags & ASYNC_INITIALIZED)
return 0;
@@ -871,7 +930,11 @@ static int startup(struct async_struct * info)
* Clear the FIFO buffers and disable them
* (they will be reenabled in change_speed())
*/
- if (info->type == PORT_16550A) {
+ if (info->type == PORT_16650) {
+ serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
+ UART_FCR_CLEAR_XMIT));
+ info->xmit_fifo_size = 1; /* disabled for now */
+ } else if (info->type == PORT_16550A) {
serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
UART_FCR_CLEAR_XMIT));
info->xmit_fifo_size = 16;
@@ -900,7 +963,10 @@ static int startup(struct async_struct * info)
!IRQ_ports[info->irq]->next_port)) {
if (IRQ_ports[info->irq]) {
free_irq(info->irq);
- handler = rs_interrupt;
+ if (rs_multiport[info->irq].port1)
+ handler = rs_interrupt_multi;
+ else
+ handler = rs_interrupt;
} else
handler = rs_interrupt_single;
@@ -936,6 +1002,10 @@ static int startup(struct async_struct * info)
info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
info->MCR_noint = UART_MCR_DTR | UART_MCR_RTS;
}
+#if defined(__alpha__) && !defined(CONFIG_PCI)
+ info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2;
+ info->MCR_noint |= UART_MCR_OUT1 | UART_MCR_OUT2;
+#endif
if (info->irq == 0)
info->MCR = info->MCR_noint;
serial_outp(info, UART_MCR, info->MCR);
@@ -1127,8 +1197,16 @@ static void change_speed(struct async_struct *info)
return;
}
/* byte size and parity */
- cval = cflag & (CSIZE | CSTOPB);
- cval >>= 4;
+ switch (cflag & CSIZE) {
+ case CS5: cval = 0x00; break;
+ case CS6: cval = 0x01; break;
+ case CS7: cval = 0x02; break;
+ case CS8: cval = 0x03; break;
+ default: cval = 0x00; break; /* too keep GCC shut... */
+ }
+ if (cflag & CSTOPB) {
+ cval |= 0x04;
+ }
if (cflag & PARENB)
cval |= UART_LCR_PARITY;
if (!(cflag & PARODD))
@@ -1138,6 +1216,18 @@ static void change_speed(struct async_struct *info)
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
else
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
+ } else if (info->type == PORT_16650) {
+ /*
+ * On the 16650, we disable the FIFOs altogether
+ * because of a design bug in how the implement
+ * things. We could support it by completely changing
+ * how we handle the interrupt driver, but not today....
+ *
+ * N.B. Because there's no way to set a FIFO trigger
+ * at 1 char, we'd probably disable at speed below
+ * 2400 baud anyway...
+ */
+ fcr = 0;
} else
fcr = 0;
@@ -1182,7 +1272,8 @@ static void change_speed(struct async_struct *info)
info->read_status_mask |= UART_LSR_OE;
}
}
-
+
+printk("change_speed: quot = %08x\n",quot);
cli();
serial_outp(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
serial_outp(info, UART_DLL, quot & 0xff); /* LS of divisor */
@@ -1394,6 +1485,7 @@ static int get_serial_info(struct async_struct * info,
tmp.flags = info->flags;
tmp.baud_base = info->baud_base;
tmp.close_delay = info->close_delay;
+ tmp.closing_wait = info->closing_wait;
tmp.custom_divisor = info->custom_divisor;
tmp.hub6 = info->hub6;
memcpy_tofs(retinfo,&tmp,sizeof(*retinfo));
@@ -1461,7 +1553,9 @@ static int set_serial_info(struct async_struct * info,
info->custom_divisor = new_serial.custom_divisor;
info->type = new_serial.type;
info->close_delay = new_serial.close_delay;
+ info->closing_wait = new_serial.closing_wait;
+ release_region(info->port,8);
if (change_port || change_irq) {
/*
* We need to shutdown the serial port at the old
@@ -1472,6 +1566,9 @@ static int set_serial_info(struct async_struct * info,
info->port = new_serial.port;
info->hub6 = new_serial.hub6;
}
+ if(info->type != PORT_UNKNOWN)
+ request_region(info->port,8,"serial(set)");
+
check_and_exit:
if (!info->port || !info->type)
@@ -1533,8 +1630,13 @@ static int get_modem_info(struct async_struct * info, unsigned int *value)
static int set_modem_info(struct async_struct * info, unsigned int cmd,
unsigned int *value)
{
- unsigned int arg = get_fs_long((unsigned long *) value);
+ int error;
+ unsigned int arg;
+ error = verify_area(VERIFY_READ, value, sizeof(int));
+ if (error)
+ return error;
+ arg = get_fs_long((unsigned long *) value);
switch (cmd) {
case TIOCMBIS:
if (arg & TIOCM_RTS) {
@@ -1657,6 +1759,101 @@ static int check_wild_interrupts(int doprint)
return wild_interrupts;
}
+static int get_multiport_struct(struct async_struct * info,
+ struct serial_multiport_struct *retinfo)
+{
+ struct serial_multiport_struct ret;
+ struct rs_multiport_struct *multi;
+
+ multi = &rs_multiport[info->irq];
+
+ ret.port_monitor = multi->port_monitor;
+
+ ret.port1 = multi->port1;
+ ret.mask1 = multi->mask1;
+ ret.match1 = multi->match1;
+
+ ret.port2 = multi->port2;
+ ret.mask2 = multi->mask2;
+ ret.match2 = multi->match2;
+
+ ret.port3 = multi->port3;
+ ret.mask3 = multi->mask3;
+ ret.match3 = multi->match3;
+
+ ret.port4 = multi->port4;
+ ret.mask4 = multi->mask4;
+ ret.match4 = multi->match4;
+
+ ret.irq = info->irq;
+
+ memcpy_tofs(retinfo,&ret,sizeof(*retinfo));
+ return 0;
+
+}
+
+static int set_multiport_struct(struct async_struct * info,
+ struct serial_multiport_struct *in_multi)
+{
+ struct serial_multiport_struct new_multi;
+ struct rs_multiport_struct *multi;
+ int was_multi, now_multi;
+ int retval;
+ void (*handler)(int, struct pt_regs *);
+
+ if (!suser())
+ return -EPERM;
+ if (!in_multi)
+ return -EFAULT;
+ memcpy_fromfs(&new_multi, in_multi,
+ sizeof(struct serial_multiport_struct));
+
+ if (new_multi.irq != info->irq || info->irq == 0 ||
+ !IRQ_ports[info->irq])
+ return -EINVAL;
+
+ multi = &rs_multiport[info->irq];
+ was_multi = (multi->port1 != 0);
+
+ multi->port_monitor = new_multi.port_monitor;
+
+ multi->port1 = new_multi.port1;
+ multi->mask1 = new_multi.mask1;
+ multi->match1 = new_multi.match1;
+
+ multi->port2 = new_multi.port2;
+ multi->mask2 = new_multi.mask2;
+ multi->match2 = new_multi.match2;
+
+ multi->port3 = new_multi.port3;
+ multi->mask3 = new_multi.mask3;
+ multi->match3 = new_multi.match3;
+
+ multi->port4 = new_multi.port4;
+ multi->mask4 = new_multi.mask4;
+ multi->match4 = new_multi.match4;
+
+ now_multi = (multi->port1 != 0);
+
+ if (IRQ_ports[info->irq]->next_port &&
+ (was_multi != now_multi)) {
+ free_irq(info->irq);
+ if (now_multi)
+ handler = rs_interrupt_multi;
+ else
+ handler = rs_interrupt;
+
+ retval = request_irq(info->irq, handler, SA_INTERRUPT,
+ "serial");
+ if (retval) {
+ printk("Couldn't reallocate serial interrupt "
+ "driver!!\n");
+ }
+ }
+
+ return 0;
+}
+
static int rs_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
{
@@ -1666,13 +1863,20 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
return -ENODEV;
+
+ if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+ (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
+ (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ return -EIO;
+ }
switch (cmd) {
case TCSBRK: /* SVID version: non-zero arg --> no break */
retval = tty_check_change(tty);
if (retval)
return retval;
- wait_until_sent(tty, 0);
+ tty_wait_until_sent(tty, 0);
if (!arg)
send_break(info, HZ/4); /* 1/4 second */
return 0;
@@ -1680,7 +1884,7 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
retval = tty_check_change(tty);
if (retval)
return retval;
- wait_until_sent(tty, 0);
+ tty_wait_until_sent(tty, 0);
send_break(info, arg ? arg*(HZ/10) : HZ/4);
return 0;
case TIOCGSOFTCAR:
@@ -1752,6 +1956,16 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
info, sizeof(struct async_struct));
return 0;
+ case TIOCSERGETMULTI:
+ error = verify_area(VERIFY_WRITE, (void *) arg,
+ sizeof(struct serial_multiport_struct));
+ if (error)
+ return error;
+ return get_multiport_struct(info,
+ (struct serial_multiport_struct *) arg);
+ case TIOCSERSETMULTI:
+ return set_multiport_struct(info,
+ (struct serial_multiport_struct *) arg);
default:
return -ENOIOCTLCMD;
}
@@ -1773,9 +1987,17 @@ static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
rs_start(tty);
}
+#if 0
+ /*
+ * No need to wake up processes in open wait, since they
+ * sample the CLOCAL flag once, and don't recheck it.
+ * XXX It's not clear whether the current behavior is correct
+ * or not. Hence, this may change.....
+ */
if (!(old_termios->c_cflag & CLOCAL) &&
(tty->termios->c_cflag & CLOCAL))
wake_up_interruptible(&info->open_wait);
+#endif
}
/*
@@ -1838,16 +2060,22 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
if (info->flags & ASYNC_CALLOUT_ACTIVE)
info->callout_termios = *tty->termios;
/*
+ * Now we wait for the transmit buffer to clear; and we notify
+ * the line discipline to only process XON/XOFF characters.
+ */
+ tty->closing = 1;
+ if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, info->closing_wait);
+ /*
* At this point we stop accepting input. To do this, we
* disable the receive line status interrupts, and tell the
* interrupt driver to stop checking the data ready bit in the
* line status register.
*/
info->IER &= ~UART_IER_RLSI;
- serial_out(info, UART_IER, info->IER);
info->read_status_mask &= ~UART_LSR_DR;
if (info->flags & ASYNC_INITIALIZED) {
- wait_until_sent(tty, 3000); /* 30 seconds timeout */
+ serial_out(info, UART_IER, info->IER);
/*
* Before we drop DTR, make sure the UART transmitter
* has completely drained; this is especially
@@ -1867,6 +2095,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
tty->driver.flush_buffer(tty);
if (tty->ldisc.flush_buffer)
tty->ldisc.flush_buffer(tty);
+ tty->closing = 0;
info->event = 0;
info->tty = 0;
if (tty->ldisc.num != ldiscs[N_TTY].num) {
@@ -1901,6 +2130,7 @@ void rs_hangup(struct tty_struct *tty)
if (serial_paranoia_check(info, tty->device, "rs_hangup"))
return;
+ rs_flush_buffer(tty);
shutdown(info);
info->event = 0;
info->count = 0;
@@ -1957,19 +2187,25 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
}
/*
- * If non-blocking mode is set, then make the check up front
- * and then exit.
+ * If non-blocking mode is set, or the port is not enabled,
+ * then make the check up front and then exit.
*/
- if (filp->f_flags & O_NONBLOCK) {
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (tty->flags & (1 << TTY_IO_ERROR))) {
if (info->flags & ASYNC_CALLOUT_ACTIVE)
return -EBUSY;
info->flags |= ASYNC_NORMAL_ACTIVE;
return 0;
}
- if (info->normal_termios.c_cflag & CLOCAL)
- do_clocal = 1;
-
+ if (info->flags & ASYNC_CALLOUT_ACTIVE) {
+ if (info->normal_termios.c_cflag & CLOCAL)
+ do_clocal = 1;
+ } else {
+ if (tty->termios->c_cflag & CLOCAL)
+ do_clocal = 1;
+ }
+
/*
* Block waiting for the carrier detect and the line to become
* free (i.e., not in use by the callout). While we are in
@@ -2096,6 +2332,7 @@ int rs_open(struct tty_struct *tty, struct file * filp)
#ifdef SERIAL_DEBUG_OPEN
printk("rs_open ttys%d successful...", info->line);
+printk("rs_open ttys%d baud_base = %08x\n",info->line,info->baud_base);
#endif
return 0;
}
@@ -2115,7 +2352,7 @@ int rs_open(struct tty_struct *tty, struct file * filp)
*/
static void show_serial_version(void)
{
- printk("Serial driver version 4.00 with");
+ printk("Serial driver version 4.11 with");
#ifdef CONFIG_HUB6
printk(" HUB-6");
#define SERIAL_OPT
@@ -2289,6 +2526,10 @@ static void autoconfig(struct async_struct * info)
if (info->flags & ASYNC_AUTO_IRQ)
info->irq = do_auto_irq(info);
+ scratch2 = serial_in(info, UART_LCR);
+ serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB);
+ serial_outp(info, UART_EFR, 0); /* EFR is the same as FCR */
+ serial_outp(info, UART_LCR, scratch2);
serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
scratch = serial_in(info, UART_IIR) >> 6;
info->xmit_fifo_size = 1;
@@ -2303,8 +2544,15 @@ static void autoconfig(struct async_struct * info)
info->type = PORT_16550;
break;
case 3:
- info->type = PORT_16550A;
- info->xmit_fifo_size = 16;
+ serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB);
+ if (serial_in(info, UART_EFR) == 0) {
+ info->type = PORT_16650;
+ info->xmit_fifo_size = 32;
+ } else {
+ info->type = PORT_16550A;
+ info->xmit_fifo_size = 16;
+ }
+ serial_outp(info, UART_LCR, scratch2);
break;
}
if (info->type == PORT_16450) {
@@ -2318,11 +2566,20 @@ static void autoconfig(struct async_struct * info)
if ((status1 != 0xa5) || (status2 != 0x5a))
info->type = PORT_8250;
}
+ request_region(info->port,8,"serial(auto)");
/*
* Reset the UART.
*/
+#if defined(__alpha__) && !defined(CONFIG_PCI)
+ /*
+ * I wonder what DEC did to the OUT1 and OUT2 lines?
+ * clearing them results in endless interrupts.
+ */
+ serial_outp(info, UART_MCR, 0x0c);
+#else
serial_outp(info, UART_MCR, 0x00);
+#endif
serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
UART_FCR_CLEAR_XMIT));
(void)serial_in(info, UART_RX);
@@ -2339,15 +2596,18 @@ long rs_init(long kmem_start)
struct async_struct * info;
bh_base[SERIAL_BH].routine = do_serial_bh;
+ enable_bh(SERIAL_BH);
timer_table[RS_TIMER].fn = rs_timer;
timer_table[RS_TIMER].expires = 0;
#ifdef CONFIG_AUTO_IRQ
rs_wild_int_mask = check_wild_interrupts(1);
#endif
+/* printk("in rs_init()\n"); */
for (i = 0; i < 16; i++) {
IRQ_ports[i] = 0;
IRQ_timeout[i] = 0;
+ memset(&rs_multiport[i], 0, sizeof(struct rs_multiport_struct));
}
show_serial_version();
@@ -2396,11 +2656,13 @@ long rs_init(long kmem_start)
callout_driver.major = TTYAUX_MAJOR;
callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+/* printk("rs_init: register drivers\n"); */
if (tty_register_driver(&serial_driver))
panic("Couldn't register serial driver\n");
if (tty_register_driver(&callout_driver))
panic("Couldn't register callout driver\n");
-
+
+/* printk("rs_init: setting defaults:\n"); */
for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
info->magic = SERIAL_MAGIC;
info->line = i;
@@ -2408,12 +2670,15 @@ long rs_init(long kmem_start)
info->type = PORT_UNKNOWN;
info->custom_divisor = 0;
info->close_delay = 50;
+ info->closing_wait = 3000;
info->x_char = 0;
info->event = 0;
info->count = 0;
info->blocked_open = 0;
info->tqueue.routine = do_softint;
info->tqueue.data = info;
+ info->tqueue_hangup.routine = do_serial_hangup;
+ info->tqueue_hangup.data = info;
info->callout_termios =callout_driver.init_termios;
info->normal_termios = serial_driver.init_termios;
info->open_wait = 0;
@@ -2424,6 +2689,7 @@ long rs_init(long kmem_start)
info->irq = 9;
if (!(info->flags & ASYNC_BOOT_AUTOCONF))
continue;
+/* printk("rs_init: autoconfig(%08x)\n",info); */
autoconfig(info);
if (info->type == PORT_UNKNOWN)
continue;
@@ -2443,6 +2709,9 @@ long rs_init(long kmem_start)
case PORT_16550A:
printk(" is a 16550A\n");
break;
+ case PORT_16650:
+ printk(" is a 16650\n");
+ break;
default:
printk("\n");
break;
@@ -2523,3 +2792,121 @@ void unregister_serial(int line)
printk("tty%02d unloaded\n", info->line);
restore_flags(flags);
}
+
+#ifdef CONFIG_REMOTE_DEBUG
+#undef PRINT_DEBUG_PORT_INFO
+
+/*
+ * This is the interface to the remote debugger stub.
+ * I've put that here to be able to control the serial
+ * device more directly.
+ */
+
+static int initialized = 0;
+
+static int rs_debug_init(struct async_struct *info)
+{
+ int quot;
+
+ autoconfig(info); /* autoconfigure ttyS0, whatever that is */
+
+#ifdef PRINT_DEBUG_PORT_INFO
+ printk("kgdb debug interface:: tty%02d at 0x%04x", info->line, info->port);
+ switch (info->type) {
+ case PORT_8250:
+ printk(" is a 8250\n");
+ break;
+ case PORT_16450:
+ printk(" is a 16450\n");
+ break;
+ case PORT_16550:
+ printk(" is a 16550\n");
+ break;
+ case PORT_16550A:
+ printk(" is a 16550A\n");
+ break;
+ case PORT_16650:
+ printk(" is a 16650\n");
+ break;
+ default:
+ printk(" is of unknown type -- unusable\n");
+ break;
+ }
+#endif
+
+ if (info->port == PORT_UNKNOWN)
+ return -1;
+
+ /*
+ * Clear all interrupts
+ */
+
+ (void)serial_inp(info, UART_LSR);
+ (void)serial_inp(info, UART_RX);
+ (void)serial_inp(info, UART_IIR);
+ (void)serial_inp(info, UART_MSR);
+
+ /*
+ * Now, initialize the UART
+ */
+ serial_outp(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */
+ if (info->flags & ASYNC_FOURPORT) {
+ info->MCR = UART_MCR_DTR | UART_MCR_RTS;
+ info->MCR_noint = UART_MCR_DTR | UART_MCR_OUT1;
+ } else {
+ info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
+ info->MCR_noint = UART_MCR_DTR | UART_MCR_RTS;
+ }
+
+ info->MCR = info->MCR_noint; /* no interrupts, please */
+ serial_outp(info, UART_MCR, info->MCR);
+
+ /*
+ * and set the speed of the serial port
+ * (currently hardwired to 9600 8N1
+ */
+
+ quot = info->baud_base / 9600; /* baud rate is fixed to 9600 */
+ serial_outp(info, UART_LCR, UART_LCR_WLEN8 | UART_LCR_DLAB); /* set DLAB */
+ serial_outp(info, UART_DLL, quot & 0xff); /* LS of divisor */
+ serial_outp(info, UART_DLM, quot >> 8); /* MS of divisor */
+ serial_outp(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */
+
+ return 0;
+}
+
+int putDebugChar(char c)
+{
+ struct async_struct *info = rs_table;
+
+ if (!initialized) { /* need to init device first */
+ if (rs_debug_init(info) == 0)
+ initialized = 1;
+ else
+ return 0;
+ }
+
+ while ((serial_inp(info, UART_LSR) & UART_LSR_THRE) == 0)
+ ;
+ serial_out(info, UART_TX, c);
+
+ return 1;
+}
+
+char getDebugChar(void)
+{
+ struct async_struct *info = rs_table;
+
+ if (!initialized) { /* need to init device first */
+ if (rs_debug_init(info) == 0)
+ initialized = 1;
+ else
+ return 0;
+ }
+ while (!(serial_inp(info, UART_LSR) & 1))
+ ;
+
+ return(serial_inp(info, UART_RX));
+}
+
+#endif /* CONFIG_REMOTE_DEBUG */
diff --git a/drivers/char/tpqic02.c b/drivers/char/tpqic02.c
index ac717b6a6..38fd53500 100644
--- a/drivers/char/tpqic02.c
+++ b/drivers/char/tpqic02.c
@@ -212,6 +212,8 @@
#include <linux/fcntl.h>
#include <linux/delay.h>
#include <linux/tpqic02.h>
+#include <linux/config.h>
+#include <linux/mm.h>
#include <asm/dma.h>
#include <asm/system.h>
@@ -1794,7 +1796,7 @@ static void qic02_tape_times_out(void)
* When we are finished, set flags to indicate end, disable timer.
* NOTE: This *must* be fast!
*/
-static void qic02_tape_interrupt(int unused)
+static void qic02_tape_interrupt(int irq, struct pt_regs *regs)
{
int stat, r, i;
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 425b63e33..0e8d9b43d 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -52,27 +52,29 @@
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/malloc.h>
+#include <linux/config.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/bitops.h>
+#include <linux/scc.h>
+
#include "kbd_kern.h"
#include "vt_kern.h"
+#include "selection.h"
#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
#define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
#undef TTY_DEBUG_HANGUP
-#ifdef CONFIG_SELECTION
-extern int set_selection(const int arg, struct tty_struct *tty);
-extern int paste_selection(struct tty_struct *tty);
-extern int sel_loadlut(const int arg);
-extern int mouse_reporting(void);
-extern int shift_state;
-#endif /* CONFIG_SELECTION */
-extern int do_screendump(int arg, int mode);
+#define TTY_PARANOIA_CHECK
+#define CHECK_TTY_COUNT
+
+extern void do_blank_screen(int nopowersave);
+extern void do_unblank_screen(void);
+extern void set_vesa_blanking(const unsigned long arg);
struct termios tty_std_termios; /* for the benefit of tty drivers */
struct tty_driver *tty_drivers = NULL; /* linked list of tty drivers */
@@ -80,10 +82,12 @@ struct tty_ldisc ldiscs[NR_LDISCS]; /* line disc dispatch table */
/*
* fg_console is the current virtual console,
+ * last_console is the last used one
* redirect is the pseudo-tty that console output
* is redirected to if asked by TIOCCONS.
*/
int fg_console = 0;
+int last_console = 0;
struct tty_struct * redirect = NULL;
struct wait_queue * keypress_wait = NULL;
@@ -125,8 +129,6 @@ char *tty_name(struct tty_struct *tty)
return(_tty_name(tty, buf));
}
-#define TTY_PARANOIA_CHECK
-
inline int tty_paranoia_check(struct tty_struct *tty, dev_t device,
const char *routine)
{
@@ -148,6 +150,33 @@ inline int tty_paranoia_check(struct tty_struct *tty, dev_t device,
return 0;
}
+static int check_tty_count(struct tty_struct *tty, const char *routine)
+{
+#ifdef CHECK_TTY_COUNT
+ struct file *f;
+ int i, count = 0;
+
+ for (f = first_file, i=0; i<nr_files; i++, f = f->f_next) {
+ if (!f->f_count)
+ continue;
+ if (f->private_data == tty) {
+ count++;
+ }
+ }
+ if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
+ tty->driver.subtype == PTY_TYPE_SLAVE &&
+ tty->link && tty->link->count)
+ count++;
+ if (tty->count != count) {
+ printk("Warning: dev (%d, %d) tty->count(%d) != #fd's(%d) in %s\n",
+ MAJOR(tty->device), MINOR(tty->device), tty->count,
+ count, routine);
+ return count;
+ }
+#endif
+ return 0;
+}
+
int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc)
{
if (disc < N_TTY || disc >= NR_LDISCS)
@@ -177,6 +206,8 @@ static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
return 0; /* We are already in the desired discipline */
o_ldisc = tty->ldisc;
+ tty_wait_until_sent(tty, 0);
+
/* Shutdown the current discipline. */
if (tty->ldisc.close)
(tty->ldisc.close)(tty);
@@ -315,6 +346,7 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
if (!tty)
return;
+ check_tty_count(tty, "do_tty_hangup");
for (filp = first_file, i=0; i<nr_files; i++, filp = filp->f_next) {
if (!filp->f_count)
continue;
@@ -355,17 +387,21 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
}
}
- if (tty->session > 0) {
- kill_sl(tty->session,SIGHUP,1);
- kill_sl(tty->session,SIGCONT,1);
- }
- tty->flags = 0;
- tty->session = 0;
- tty->pgrp = -1;
for_each_task(p) {
+ if ((tty->session > 0) && (p->session == tty->session) &&
+ p->leader) {
+ send_sig(SIGHUP,p,1);
+ send_sig(SIGCONT,p,1);
+ if (tty->pgrp > 0)
+ p->tty_old_pgrp = tty->pgrp;
+ }
if (p->tty == tty)
p->tty = NULL;
}
+ tty->flags = 0;
+ tty->session = 0;
+ tty->pgrp = -1;
+ tty->ctrl_status = 0;
if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS)
*tty->termios = tty->driver.init_termios;
if (tty->driver.hangup)
@@ -408,13 +444,19 @@ void disassociate_ctty(int priv)
struct tty_struct *tty = current->tty;
struct task_struct *p;
- if (!tty)
+ if (!tty) {
+ if (current->tty_old_pgrp) {
+ kill_pg(current->tty_old_pgrp, SIGHUP, priv);
+ kill_pg(current->tty_old_pgrp, SIGCONT, priv);
+ }
return;
-
+ }
if (tty->pgrp > 0) {
kill_pg(tty->pgrp, SIGHUP, priv);
kill_pg(tty->pgrp, SIGCONT, priv);
}
+
+ current->tty_old_pgrp = 0;
tty->session = 0;
tty->pgrp = -1;
@@ -468,6 +510,7 @@ void complete_change_console(unsigned int new_console)
return;
if (!vc_cons_allocated(new_console))
return;
+ last_console = fg_console;
/*
* If we're switching, we could be going from KD_GRAPHICS to
@@ -513,11 +556,9 @@ void complete_change_console(unsigned int new_console)
if (old_vc_mode != vt_cons[new_console]->vc_mode)
{
if (vt_cons[new_console]->vc_mode == KD_TEXT)
- unblank_screen();
- else {
- timer_active &= ~(1<<BLANK_TIMER);
- blank_screen();
- }
+ do_unblank_screen();
+ else
+ do_blank_screen(1);
}
/*
@@ -828,8 +869,15 @@ repeat:
}
}
tty = NULL;
- } else
+ } else {
+ if ((*tty_loc)->flags & (1 << TTY_CLOSING)) {
+ printk("Attempt to open closing tty %s.\n",
+ tty_name(*tty_loc));
+ printk("Ack!!!! This should never happen!!\n");
+ return -EINVAL;
+ }
(*tty_loc)->count++;
+ }
if (driver->type == TTY_DRIVER_TYPE_PTY) {
if (!*o_tp_loc) {
*o_tp_loc = o_tp;
@@ -890,11 +938,12 @@ static void release_dev(struct file * filp)
struct task_struct **p;
int idx;
-
tty = (struct tty_struct *)filp->private_data;
if (tty_paranoia_check(tty, filp->f_inode->i_rdev, "release_dev"))
return;
+ check_tty_count(tty, "release_dev");
+
tty_fasync(filp->f_inode, filp, 0);
tp = tty->termios;
@@ -975,7 +1024,10 @@ static void release_dev(struct file * filp)
}
if (tty->count)
return;
-
+
+ /*
+ * We're committed; at this point, we must not block!
+ */
if (o_tty) {
if (o_tty->count)
return;
@@ -987,6 +1039,7 @@ static void release_dev(struct file * filp)
#ifdef TTY_DEBUG_HANGUP
printk("freeing tty structure...");
#endif
+ tty->flags |= (1 << TTY_CLOSING);
/*
* Make sure there aren't any processes that still think this
@@ -1013,7 +1066,6 @@ static void release_dev(struct file * filp)
if (o_tty->ldisc.close)
(o_tty->ldisc.close)(o_tty);
o_tty->ldisc = ldiscs[N_TTY];
- o_tty->termios->c_line = N_TTY;
}
tty->driver.table[idx] = NULL;
@@ -1045,6 +1097,7 @@ static void release_dev(struct file * filp)
tty->magic = 0;
(*tty->driver.refcount)--;
free_page((unsigned long) tty);
+ filp->private_data = 0;
if (o_tty) {
o_tty->magic = 0;
(*o_tty->driver.refcount)--;
@@ -1087,9 +1140,10 @@ retry_open:
minor = MINOR(device);
retval = init_dev(device, &tty);
- filp->private_data = tty;
if (retval)
return retval;
+ filp->private_data = tty;
+ check_tty_count(tty, "tty_open");
if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
tty->driver.subtype == PTY_TYPE_MASTER)
noctty = 1;
@@ -1126,6 +1180,7 @@ retry_open:
!current->tty &&
tty->session == 0) {
current->tty = tty;
+ current->tty_old_pgrp = 0;
tty->session = current->session;
tty->pgrp = current->pgrp;
}
@@ -1201,10 +1256,11 @@ static int tty_fasync(struct inode * inode, struct file * filp, int on)
return 0;
}
+#if 0
/*
* XXX does anyone use this anymore?!?
*/
-static int do_get_ps_info(int arg)
+static int do_get_ps_info(unsigned long arg)
{
struct tstruct {
int flag;
@@ -1232,6 +1288,7 @@ static int do_get_ps_info(int arg)
put_fs_long(0, (unsigned long *)(ts->present+n));
return(0);
}
+#endif
static int tty_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
@@ -1352,6 +1409,7 @@ static int tty_ioctl(struct inode * inode, struct file * file,
return -EPERM;
}
current->tty = tty;
+ current->tty_old_pgrp = 0;
tty->session = current->session;
tty->pgrp = current->pgrp;
return 0;
@@ -1397,26 +1455,32 @@ static int tty_ioctl(struct inode * inode, struct file * file,
arg = get_fs_long((unsigned long *) arg);
return tty_set_ldisc(tty, arg);
case TIOCLINUX:
+ if (tty->driver.type != TTY_DRIVER_TYPE_CONSOLE)
+ return -EINVAL;
+ if (current->tty != tty && !suser())
+ return -EPERM;
retval = verify_area(VERIFY_READ, (void *) arg, 1);
if (retval)
return retval;
switch (retval = get_fs_byte((char *)arg))
{
- case 0:
- return do_screendump(arg,0);
+ case 0:
+ case 8:
+ case 9:
+ printk("TIOCLINUX (0/8/9) ioctl is gone - use /dev/vcs\n");
+ return -EINVAL;
+#if 0
case 1:
printk("Deprecated TIOCLINUX (1) ioctl\n");
return do_get_ps_info(arg);
-#ifdef CONFIG_SELECTION
+#endif
case 2:
return set_selection(arg, tty);
case 3:
return paste_selection(tty);
-#endif /* CONFIG_SELECTION */
case 4:
- unblank_screen();
+ do_unblank_screen();
return 0;
-#ifdef CONFIG_SELECTION
case 5:
return sel_loadlut(arg);
case 6:
@@ -1431,13 +1495,21 @@ static int tty_ioctl(struct inode * inode, struct file * file,
case 7:
put_fs_byte(mouse_reporting(),arg);
return 0;
-#endif /* CONFIG_SELECTION */
- case 8: /* second arg is 1 or 2 */
- case 9: /* both are explained in console.c */
- return do_screendump(arg,retval-7);
+ case 10:
+ set_vesa_blanking(arg);
+ return 0;
default:
return -EINVAL;
}
+
+ case TIOCTTYGSTRUCT:
+ retval = verify_area(VERIFY_WRITE, (void *) arg,
+ sizeof(struct tty_struct));
+ if (retval)
+ return retval;
+ memcpy_tofs((struct tty_struct *) arg,
+ tty, sizeof(struct tty_struct));
+ return 0;
default:
if (tty->driver.ioctl) {
retval = (tty->driver.ioctl)(tty, file,
@@ -1588,7 +1660,7 @@ int tty_register_driver(struct tty_driver *driver)
driver->prev = 0;
driver->next = tty_drivers;
- if(tty_drivers) tty_drivers->prev = driver;
+ if (tty_drivers) tty_drivers->prev = driver;
tty_drivers = driver;
return error;
}
@@ -1601,7 +1673,7 @@ int tty_unregister_driver(struct tty_driver *driver)
int retval;
struct tty_driver *p;
int found = 0;
- int major_inuse = 0;
+ char *othername = NULL;
if (*driver->refcount)
return -EBUSY;
@@ -1610,14 +1682,15 @@ int tty_unregister_driver(struct tty_driver *driver)
if (p == driver)
found++;
else if (p->major == driver->major)
- major_inuse++;
+ othername = p->name;
}
- if (!major_inuse) {
+ if (othername == NULL) {
retval = unregister_chrdev(driver->major, driver->name);
if (retval)
return retval;
- }
+ } else
+ register_chrdev(driver->major, othername, &tty_fops);
if (driver->prev)
driver->prev->next = driver->next;
@@ -1625,7 +1698,7 @@ int tty_unregister_driver(struct tty_driver *driver)
tty_drivers = driver->next;
if (driver->next)
- driver->next = driver->next->prev;
+ driver->next->prev = driver->prev;
return 0;
}
@@ -1676,7 +1749,16 @@ long tty_init(long kmem_start)
panic("unable to get major %d for tty device", TTYAUX_MAJOR);
kmem_start = kbd_init(kmem_start);
+#if defined (__mips__) && defined (CONFIG_SERIAL)
kmem_start = rs_init(kmem_start);
+#endif
+#ifdef CONFIG_SCC
+ kmem_start = scc_init(kmem_start);
+#endif
+#ifdef CONFIG_CYCLADES
+ kmem_start = cy_init(kmem_start);
+#endif
kmem_start = pty_init(kmem_start);
+ kmem_start = vcs_init(kmem_start);
return kmem_start;
}
diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c
index ce215e158..7e006628b 100644
--- a/drivers/char/tty_ioctl.c
+++ b/drivers/char/tty_ioctl.c
@@ -12,12 +12,12 @@
#include <linux/termios.h>
#include <linux/errno.h>
#include <linux/sched.h>
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/tty.h>
#include <linux/fcntl.h>
#include <linux/string.h>
+#include <linux/mm.h>
#include <asm/io.h>
#include <asm/bitops.h>
@@ -40,7 +40,7 @@
#define TERMIOS_WAIT 2
#define TERMIOS_TERMIO 4
-void wait_until_sent(struct tty_struct * tty, int timeout)
+void tty_wait_until_sent(struct tty_struct * tty, int timeout)
{
struct wait_queue wait = { current, NULL };
@@ -132,7 +132,7 @@ static int set_termios(struct tty_struct * tty, unsigned long arg, int opt)
tty->ldisc.flush_buffer(tty);
if (opt & TERMIOS_WAIT)
- wait_until_sent(tty, 0);
+ tty_wait_until_sent(tty, 0);
cli();
*tty->termios = tmp_termios;
@@ -363,7 +363,7 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file,
retval = tty_check_change(tty);
if (retval)
return retval;
- wait_until_sent(tty, 0);
+ tty_wait_until_sent(tty, 0);
if (!tty->driver.ioctl)
return 0;
tty->driver.ioctl(tty, file, cmd, arg);
diff --git a/drivers/char/uni_to_437.c b/drivers/char/uni_to_437.c
deleted file mode 100644
index 46d0f0880..000000000
--- a/drivers/char/uni_to_437.c
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * non-Latin-1 (>0x00ff) Unicode -> IBM CP437 conversion hash table
- * Markus Kuhn
- */
-#define IBM_HASHSIZE 146
-#define IBM_HASHSTEP 19
-#define IBM_HASHLEVEL 4
-static struct {
- unsigned short ucs; /* a Unicode code */
- unsigned char ibm437; /* the corresponding IBM code */
-} ibm_hash[IBM_HASHSIZE] = {
- {0x221a,0xfb}, {0x2580,0xdf}, {0x2524,0xb4}, {0xffff,0x00}, {0x221e,0xec},
- {0x20a7,0x9e}, {0x2126,0xea}, {0x253c,0xc5}, {0xffff,0x00}, {0x2588,0xdb},
- {0x2666,0x04}, {0x2190,0x1b}, {0x2193,0x19}, {0x2665,0x03}, {0x2195,0x12},
- {0x2663,0x05}, {0x2660,0x06}, {0x2590,0xde}, {0x2022,0x07}, {0x2592,0xb1},
- {0x266c,0x0e}, {0x2502,0xb3}, {0x266a,0x0d}, {0x250c,0xda}, {0x258c,0xdd},
- {0xffff,0x00}, {0xffff,0x00}, {0xffff,0x00}, {0xffff,0x00}, {0x2191,0x18},
- {0x2261,0xf0}, {0x2514,0xc0}, {0x2192,0x1a}, {0x2264,0xf3}, {0x2194,0x1d},
- {0x2591,0xb0}, {0x0393,0xe2}, {0x2593,0xb2}, {0x2500,0xc4}, {0x251c,0xc3},
- {0x2205,0xed}, {0xffff,0x00}, {0x266b,0x0e}, {0x2518,0xd9}, {0x203c,0x13},
- {0xffff,0x00}, {0x2642,0x0b}, {0x2564,0xd1}, {0x2640,0x0c}, {0x2566,0xcb},
- {0x2561,0xb5}, {0x2560,0xcc}, {0x2563,0xb9}, {0x2562,0xb6}, {0x2510,0xbf},
- {0x256c,0xce}, {0x2008,0x00}, {0x03a6,0xe8}, {0x2569,0xca}, {0x2568,0xd0},
- {0x256b,0xd7}, {0x256a,0xd8}, {0x03a9,0xea}, {0xffff,0x00}, {0xffff,0x00},
- {0x25c0,0x11}, {0x0398,0xe9}, {0x2567,0xcf}, {0xffff,0x00}, {0x2248,0xf7},
- {0x03b1,0xe0}, {0x03a3,0xe4}, {0x2265,0xf2}, {0xffff,0x00}, {0x25cb,0x09},
- {0x03b4,0xeb}, {0x2320,0xf4}, {0xffff,0x00}, {0xffff,0x00}, {0xffff,0x00},
- {0x25c4,0x11}, {0xffff,0x00}, {0xffff,0x00}, {0x03bc,0xe6}, {0x2565,0xd2},
- {0xffff,0x00}, {0x22c5,0xf9}, {0x2302,0x7f}, {0x25d9,0x0a}, {0x25d8,0x08},
- {0xffff,0x00}, {0x03c4,0xe7}, {0xffff,0x00}, {0xffff,0x00}, {0x2555,0xb8},
- {0x2554,0xc9}, {0x2557,0xbb}, {0x2556,0xb7}, {0x2551,0xba}, {0x2550,0xcd},
- {0x2553,0xd6}, {0x2552,0xd5}, {0x255d,0xbc}, {0x255c,0xbd}, {0x255f,0xc7},
- {0x2310,0xa9}, {0x2559,0xd3}, {0x2558,0xd4}, {0x255b,0xbe}, {0x255a,0xc8},
- {0xffff,0x00}, {0x0192,0x9f}, {0x2319,0x1c}, {0x2321,0xf5}, {0xffff,0x00},
- {0x25a0,0xfe}, {0xffff,0x00}, {0x21a8,0x17}, {0x263c,0x0f}, {0x25ac,0x16},
- {0x263a,0x01}, {0x263b,0x02}, {0x03c3,0xe5}, {0xffff,0x00}, {0x255e,0xc6},
- {0x03c0,0xe3}, {0x03b2,0xe1}, {0x2300,0xed}, {0xffff,0x00}, {0x25b6,0x10},
- {0x207f,0xfc}, {0xffff,0x00}, {0x2208,0xee}, {0x25b2,0x1e}, {0x220e,0xfe},
- {0x25bc,0x1f}, {0x212b,0x8f}, {0x252c,0xc2}, {0xffff,0x00}, {0x2229,0xef},
- {0xffff,0x00}, {0x25ba,0x10}, {0xffff,0x00}, {0x2584,0xdc}, {0xffff,0x00},
- {0x2534,0xc1}
-};
-
-/*
- * Find the correct PC character set (CP437) code for a
- * UCS character outside Latin-1 by hash lookup in ibm_hash[].
- * Return -1 if character not available, return -2 for zero-width
- * space characters.
- */
-int
-conv_uni_to_pc(unsigned long ucs)
-{
- int c = -1;
- int i, h;
-
- if (ucs == 0xffff || ucs == 0xfffe)
- return -1;
- if (ucs == 0xfeff || (ucs >= 0x200a && ucs <= 0x200f))
- return -2;
-
- h = (ucs ^ (ucs >> 8)) % IBM_HASHSIZE;
- for (i = 0; i < IBM_HASHLEVEL && c == -1; i++)
- if (ibm_hash[h].ucs == ucs)
- c = ibm_hash[h].ibm437;
- else
- if ((h += IBM_HASHSTEP) >= IBM_HASHSIZE)
- h -= IBM_HASHSIZE;
-
- return c;
-}
-
-#if 0
-/*
- * Conversion from unicode to ibm pc character set (code page 437)
- *
- * aeb@cwi.nl, Dec 1993
- */
-#define NOTFOUND 254 /* small square */
-
-static struct unipc {
- long unicode;
- unsigned char pc;
-} uni_to_pc[255] = {
- {0x0020, 0x20}, {0x0021, 0x21}, {0x0022, 0x22}, {0x0023, 0x23},
- {0x0024, 0x24}, {0x0025, 0x25}, {0x0026, 0x26}, {0x0027, 0x27},
- {0x0028, 0x28}, {0x0029, 0x29}, {0x002A, 0x2A}, {0x002B, 0x2B},
- {0x002C, 0x2C}, {0x002D, 0x2D}, {0x002E, 0x2E}, {0x002F, 0x2F},
- {0x0030, 0x30}, {0x0031, 0x31}, {0x0032, 0x32}, {0x0033, 0x33},
- {0x0034, 0x34}, {0x0035, 0x35}, {0x0036, 0x36}, {0x0037, 0x37},
- {0x0038, 0x38}, {0x0039, 0x39}, {0x003A, 0x3A}, {0x003B, 0x3B},
- {0x003C, 0x3C}, {0x003D, 0x3D}, {0x003E, 0x3E}, {0x003F, 0x3F},
- {0x0040, 0x40}, {0x0041, 0x41}, {0x0042, 0x42}, {0x0043, 0x43},
- {0x0044, 0x44}, {0x0045, 0x45}, {0x0046, 0x46}, {0x0047, 0x47},
- {0x0048, 0x48}, {0x0049, 0x49}, {0x004A, 0x4A}, {0x004B, 0x4B},
- {0x004C, 0x4C}, {0x004D, 0x4D}, {0x004E, 0x4E}, {0x004F, 0x4F},
- {0x0050, 0x50}, {0x0051, 0x51}, {0x0052, 0x52}, {0x0053, 0x53},
- {0x0054, 0x54}, {0x0055, 0x55}, {0x0056, 0x56}, {0x0057, 0x57},
- {0x0058, 0x58}, {0x0059, 0x59}, {0x005A, 0x5A}, {0x005B, 0x5B},
- {0x005C, 0x5C}, {0x005D, 0x5D}, {0x005E, 0x5E}, {0x005F, 0x5F},
- {0x0060, 0x60}, {0x0061, 0x61}, {0x0062, 0x62}, {0x0063, 0x63},
- {0x0064, 0x64}, {0x0065, 0x65}, {0x0066, 0x66}, {0x0067, 0x67},
- {0x0068, 0x68}, {0x0069, 0x69}, {0x006A, 0x6A}, {0x006B, 0x6B},
- {0x006C, 0x6C}, {0x006D, 0x6D}, {0x006E, 0x6E}, {0x006F, 0x6F},
- {0x0070, 0x70}, {0x0071, 0x71}, {0x0072, 0x72}, {0x0073, 0x73},
- {0x0074, 0x74}, {0x0075, 0x75}, {0x0076, 0x76}, {0x0077, 0x77},
- {0x0078, 0x78}, {0x0079, 0x79}, {0x007A, 0x7A}, {0x007B, 0x7B},
- {0x007C, 0x7C}, {0x007D, 0x7D}, {0x007E, 0x7E}, {0x00A0, 0xFF},
- {0x00A1, 0xAD}, {0x00A2, 0x9B}, {0x00A3, 0x9C}, {0x00A5, 0x9D},
- {0x00A7, 0x15}, {0x00AA, 0xA6}, {0x00AB, 0xAE}, {0x00AC, 0xAA},
- {0x00B0, 0xF8}, {0x00B1, 0xF1}, {0x00B2, 0xFD}, {0x00B5, 0xE6},
- {0x00B6, 0x14}, {0x00B7, 0xFA}, {0x00BA, 0xA7}, {0x00BB, 0xAF},
- {0x00BC, 0xAC}, {0x00BD, 0xAB}, {0x00BF, 0xA8}, {0x00C4, 0x8E},
- {0x00C5, 0x8F}, {0x00C6, 0x92}, {0x00C7, 0x80}, {0x00C9, 0x90},
- {0x00D1, 0xA5}, {0x00D6, 0x99}, {0x00DC, 0x9A}, {0x00DF, 0xE1},
- {0x00E0, 0x85}, {0x00E1, 0xA0}, {0x00E2, 0x83}, {0x00E4, 0x84},
- {0x00E5, 0x86}, {0x00E6, 0x91}, {0x00E7, 0x87}, {0x00E8, 0x8A},
- {0x00E9, 0x82}, {0x00EA, 0x88}, {0x00EB, 0x89}, {0x00EC, 0x8D},
- {0x00ED, 0xA1}, {0x00EE, 0x8C}, {0x00EF, 0x8B}, {0x00F1, 0xA4},
- {0x00F2, 0x95}, {0x00F3, 0xA2}, {0x00F4, 0x93}, {0x00F6, 0x94},
- {0x00F7, 0xF6}, {0x00F9, 0x97}, {0x00FA, 0xA3}, {0x00FB, 0x96},
- {0x00FC, 0x81}, {0x00FF, 0x98}, {0x0192, 0x9F}, {0x0393, 0xE2},
- {0x0398, 0xE9}, {0x03A3, 0xE4}, {0x03A6, 0xE8}, {0x03A9, 0xEA},
- {0x03B1, 0xE0}, {0x03B4, 0xEB}, {0x03B5, 0xEE}, {0x03C0, 0xE3},
- {0x03C3, 0xE5}, {0x03C4, 0xE7}, {0x03C6, 0xED}, {0x2022, 0x07},
- {0x203C, 0x13}, {0x207F, 0xFC}, {0x20A7, 0x9E}, {0x2190, 0x1B},
- {0x2191, 0x18}, {0x2192, 0x1A}, {0x2193, 0x19}, {0x2194, 0x1D},
- {0x2195, 0x12}, {0x21A8, 0x17}, {0x2219, 0xF9}, {0x221A, 0xFB},
- {0x221E, 0xEC}, {0x221F, 0x1C}, {0x2229, 0xEF}, {0x2248, 0xF7},
- {0x2261, 0xF0}, {0x2264, 0xF3}, {0x2265, 0xF2}, {0x2302, 0x7F},
- {0x2310, 0xA9}, {0x2320, 0xF4}, {0x2321, 0xF5}, {0x2500, 0xC4},
- {0x2502, 0xB3}, {0x250C, 0xDA}, {0x2510, 0xBF}, {0x2514, 0xC0},
- {0x2518, 0xD9}, {0x251C, 0xC3}, {0x2524, 0xB4}, {0x252C, 0xC2},
- {0x2534, 0xC1}, {0x253C, 0xC5}, {0x2550, 0xCD}, {0x2551, 0xBA},
- {0x2552, 0xD5}, {0x2553, 0xD6}, {0x2554, 0xC9}, {0x2555, 0xB8},
- {0x2556, 0xB7}, {0x2557, 0xBB}, {0x2558, 0xD4}, {0x2559, 0xD3},
- {0x255A, 0xC8}, {0x255B, 0xBE}, {0x255C, 0xBD}, {0x255D, 0xBC},
- {0x255E, 0xC6}, {0x255F, 0xC7}, {0x2560, 0xCC}, {0x2561, 0xB5},
- {0x2562, 0xB6}, {0x2563, 0xB9}, {0x2564, 0xD1}, {0x2565, 0xD2},
- {0x2566, 0xCB}, {0x2567, 0xCF}, {0x2568, 0xD0}, {0x2569, 0xCA},
- {0x256A, 0xD8}, {0x256B, 0xD7}, {0x256C, 0xCE}, {0x2580, 0xDF},
- {0x2584, 0xDC}, {0x2588, 0xDB}, {0x258C, 0xDD}, {0x2590, 0xDE},
- {0x2591, 0xB0}, {0x2592, 0xB1}, {0x2593, 0xB2}, {0x25A0, 0xFE},
- {0x25AC, 0x16}, {0x25B2, 0x1E}, {0x25BA, 0x10}, {0x25BC, 0x1F},
- {0x25C4, 0x11}, {0x25CB, 0x09}, {0x25D8, 0x08}, {0x25D9, 0x0A},
- {0x263A, 0x01}, {0x263B, 0x02}, {0x263C, 0x0F}, {0x2640, 0x0C},
- {0x2642, 0x0B}, {0x2660, 0x06}, {0x2663, 0x05}, {0x2665, 0x03},
- {0x2666, 0x04}, {0x266A, 0x0D}, {0x266B, 0x0E}
-};
-
-unsigned char
-conv_uni_to_pc(unsigned long u) {
- /* Binary search - no doubt this can be sped up using hash codes */
- /* (or by table lookup if we are in the first half) */
- int step = 128;
- struct unipc *up = uni_to_pc + step - 1;
-
- while(1) {
- if(up->unicode == u)
- return up->pc;
- step >>= 1;
- if(!step)
- return NOTFOUND;
- if(up->unicode < u)
- up += step;
- else
- up -= step;
- }
-}
-#endif
diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c
new file mode 100644
index 000000000..f5f389cf8
--- /dev/null
+++ b/drivers/char/vc_screen.c
@@ -0,0 +1,212 @@
+/*
+ * linux/drivers/char/vc_screen.c
+ *
+ * Provide access to virtual console memory.
+ * /dev/vcs0: the screen as it is being viewed right now (possibly scrolled)
+ * /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63)
+ * [minor: N]
+ *
+ * /dev/vcsaN: idem, but including attributes, and prefixed with
+ * the 4 bytes lines,columns,x,y (as screendump used to give)
+ * [minor: N+128]
+ *
+ * This replaces screendump and part of selection, so that the system
+ * administrator can control access using file system permissions.
+ *
+ * aeb@cwi.nl - efter Friedas begravelse - 950211
+ */
+
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/fs.h>
+#include <asm/segment.h>
+#include "vt_kern.h"
+#include "selection.h"
+
+#define HEADER_SIZE 4
+
+static inline int
+vcs_size(struct inode *inode)
+{
+ int size = video_num_lines * video_num_columns;
+ if (MINOR(inode->i_rdev) & 128)
+ size = 2*size + HEADER_SIZE;
+ return size;
+}
+
+static int
+vcs_lseek(struct inode *inode, struct file *file, off_t offset, int orig)
+{
+ int size = vcs_size(inode);
+
+ switch (orig) {
+ case 0:
+ file->f_pos = offset;
+ break;
+ case 1:
+ file->f_pos += offset;
+ break;
+ case 2:
+ file->f_pos = size + offset;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (file->f_pos < 0 || file->f_pos > size)
+ return -EINVAL;
+ return file->f_pos;
+}
+
+static int
+vcs_read(struct inode *inode, struct file *file, char *buf, int count)
+{
+ unsigned long p = file->f_pos;
+ unsigned int cons = MINOR(inode->i_rdev);
+ int viewed, attr, size, read;
+ char *buf0;
+ unsigned short *org;
+
+ attr = (cons & 128);
+ cons = (cons & 127);
+ if (cons == 0) {
+ cons = fg_console;
+ viewed = 1;
+ } else {
+ cons--;
+ viewed = 0;
+ }
+ if (!vc_cons_allocated(cons))
+ return -ENXIO;
+
+ size = vcs_size(inode);
+ if (count < 0 || p > size)
+ return -EINVAL;
+ if (count > size - p)
+ count = size - p;
+
+ buf0 = buf;
+ if (!attr) {
+ org = screen_pos(cons, p, viewed);
+ while (count-- > 0)
+ put_fs_byte(scr_readw(org++) & 0xff, buf++);
+ } else {
+ if (p < HEADER_SIZE) {
+ char header[HEADER_SIZE];
+ header[0] = (char) video_num_lines;
+ header[1] = (char) video_num_columns;
+ getconsxy(cons, header+2);
+ while (p < HEADER_SIZE && count-- > 0)
+ put_fs_byte(header[p++], buf++);
+ }
+ p -= HEADER_SIZE;
+ org = screen_pos(cons, p/2, viewed);
+ if ((p & 1) && count-- > 0)
+ put_fs_byte(scr_readw(org++) >> 8, buf++);
+ while (count > 1) {
+ put_fs_word(scr_readw(org++), buf);
+ buf += 2;
+ count -= 2;
+ }
+ if (count > 0)
+ put_fs_byte(scr_readw(org) & 0xff, buf++);
+ }
+ read = buf - buf0;
+ file->f_pos += read;
+ return read;
+}
+
+static int
+vcs_write(struct inode *inode, struct file *file, char *buf, int count)
+{
+ unsigned long p = file->f_pos;
+ unsigned int cons = MINOR(inode->i_rdev);
+ int viewed, attr, size, written;
+ char *buf0;
+ unsigned short *org;
+
+ attr = (cons & 128);
+ cons = (cons & 127);
+ if (cons == 0) {
+ cons = fg_console;
+ viewed = 1;
+ } else {
+ cons--;
+ viewed = 0;
+ }
+ if (!vc_cons_allocated(cons))
+ return -ENXIO;
+
+ size = vcs_size(inode);
+ if (count < 0 || p > size)
+ return -EINVAL;
+ if (count > size - p)
+ count = size - p;
+
+ buf0 = buf;
+ if (!attr) {
+ org = screen_pos(cons, p, viewed);
+ while (count-- > 0) {
+ scr_writew((scr_readw(org) & 0xff00) |
+ get_fs_byte(buf++), org);
+ org++;
+ }
+ } else {
+ if (p < HEADER_SIZE) {
+ char header[HEADER_SIZE];
+ getconsxy(cons, header+2);
+ while (p < HEADER_SIZE && count-- > 0)
+ header[p++] = get_fs_byte(buf++);
+ if (!viewed)
+ putconsxy(cons, header+2);
+ }
+ p -= HEADER_SIZE;
+ org = screen_pos(cons, p/2, viewed);
+ if ((p & 1) && count-- > 0) {
+ scr_writew((get_fs_byte(buf++) << 8) |
+ (scr_readw(org) & 0xff), org);
+ org++;
+ }
+ while (count > 1) {
+ scr_writew(get_fs_word(buf), org++);
+ buf += 2;
+ count -= 2;
+ }
+ if (count > 0)
+ scr_writew((scr_readw(org) & 0xff00) |
+ get_fs_byte(buf++), org);
+ }
+ written = buf - buf0;
+ file->f_pos += written;
+ return written;
+}
+
+static int
+vcs_open(struct inode *inode, struct file *filp)
+{
+ unsigned int cons = (MINOR(inode->i_rdev) & 127);
+ if(cons && !vc_cons_allocated(cons-1))
+ return -ENXIO;
+ return 0;
+}
+
+static struct file_operations vcs_fops = {
+ vcs_lseek, /* lseek */
+ vcs_read, /* read */
+ vcs_write, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ vcs_open, /* open */
+ NULL, /* release */
+ NULL /* fsync */
+};
+
+long vcs_init(long kmem_start)
+{
+ if (register_chrdev(VCS_MAJOR, "vcs", &vcs_fops))
+ printk("unable to get major %d for vcs device", VCS_MAJOR);
+ return kmem_start;
+}
diff --git a/drivers/char/vesa_blank.c b/drivers/char/vesa_blank.c
new file mode 100644
index 000000000..8e5e17d31
--- /dev/null
+++ b/drivers/char/vesa_blank.c
@@ -0,0 +1,246 @@
+/*
+ * vesa_blank.c
+ *
+ * Exported functions:
+ * void vesa_blank(void);
+ * void vesa_unblank(void);
+ * void set_vesa_blanking(const unsigned long arg);
+ *
+ * Not all hardware reacts well to this code - activate at your own risk.
+ * Activation is done using a sufficiently recent version of setterm
+ * or using a tiny C program like the following.
+ *
+-----------------------------------------------------------------------
+|#include <stdio.h>
+|#include <linux/termios.h>
+|main(int argc, char *argv[]) {
+| int fd;
+| struct { char ten, onoff; } arg;
+|
+| if (argc != 2) {
+| fprintf(stderr, "usage: setvesablank ON|on|off\n");
+| exit(1);
+| }
+| if ((fd = open("/dev/console", 0)) < 0)
+| fd = 0;
+| arg.ten = 10;
+| arg.onoff = 0;
+| if (!strcmp(argv[1], "on"))
+| arg.onoff = 1;
+| else if (!strcmp(argv[1], "ON"))
+| arg.onoff = 2;
+| if (ioctl(fd, TIOCLINUX, &arg)) {
+| perror("setvesablank: TIOCLINUX");
+| exit(1);
+| }
+| exit(0);
+|}
+-----------------------------------------------------------------------
+*/
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+
+extern unsigned short video_port_reg, video_port_val;
+
+/*
+ * This file handles the VESA Power Saving Protocol that lets a
+ * monitor be powered down whenever not needed for a longer time.
+ * The VESA protocol defines:
+ *
+ * Mode/Status HSync VSync Video
+ * -------------------------------------------
+ * "On" on on active (mode 0)
+ * "Suspend" {either} on off blank (mode 1)
+ * { or } off on blank
+ * "Off" off off blank (mode 2)
+ *
+ * Original code taken from the Power Management Utility (PMU) of
+ * Huang shi chao, delivered together with many new monitor models
+ * capable of the VESA Power Saving Protocol.
+ *
+ * Adapted to Linux by Christoph Rimek (chrimek@toppoint.de) 15-may-94.
+ * A slightly adapted fragment of his README follows.
+ *
+Patch (based on Linux Kernel revision 1.0) for handling the Power Saving
+feature of the new monitor generation. The code works on all these monitors
+(mine is a Smile 1506) and should run on *all* video adapter cards (change
+some i/o-addresses), although tested only on two different VGA-cards: a
+cheap Cirrus Logic (5428) and a miro Crystal 8S (S3-805).
+
+You can choose from two options:
+
+(1) Setting vesa_blanking_mode to 1.
+ The code will save the current setting of your video adapters'
+ register settings and then program the controller to turn off
+ the vertical synchronisation pulse.
+
+(2) Setting vesa_blanking_mode to 2.
+ If your monitor locally has an Off_Mode timer then you should not
+ force your video card to send the OFF-signal - your monitor will
+ power down by itself.
+ If your monitor cannot handle this and needs the Off-signal directly,
+ or if you like your monitor to power down immediately when the
+ blank_timer times out, then you choose this option.
+
+On the other hand I'd recommend to not choose this second option unless
+it is absolutely necessary. Powering down a monitor to the Off_State with
+an approx. power consumption of 3-5 Watts is a rather strong action for
+the CRT and it should not be done every now and then. If the software only
+sends the signal to enter Standby mode, you have the chance to interfere
+before the monitor powers down. Do not set a too short period, if you love
+your hardware :-)) .
+
+If requested, in the future it may be possible to install another timer
+to provide a configurable delay between the two stages Standby and Off
+similar to the "setterm -blank"-feature.
+*/
+
+#define seq_port_reg (0x3c4) /* Sequencer register select port */
+#define seq_port_val (0x3c5) /* Sequencer register value port */
+#define video_misc_rd (0x3cc) /* Video misc. read port */
+#define video_misc_wr (0x3c2) /* Video misc. write port */
+
+/* structure holding original VGA register settings */
+static struct {
+ unsigned char SeqCtrlIndex; /* Sequencer Index reg. */
+ unsigned char CrtCtrlIndex; /* CRT-Contr. Index reg. */
+ unsigned char CrtMiscIO; /* Miscellaneous register */
+ unsigned char HorizontalTotal; /* CRT-Controller:00h */
+ unsigned char HorizDisplayEnd; /* CRT-Controller:01h */
+ unsigned char StartHorizRetrace; /* CRT-Controller:04h */
+ unsigned char EndHorizRetrace; /* CRT-Controller:05h */
+ unsigned char Overflow; /* CRT-Controller:07h */
+ unsigned char StartVertRetrace; /* CRT-Controller:10h */
+ unsigned char EndVertRetrace; /* CRT-Controller:11h */
+ unsigned char ModeControl; /* CRT-Controller:17h */
+ unsigned char ClockingMode; /* Seq-Controller:01h */
+} vga;
+
+static int vesa_blanking_mode = 0;
+static int vesa_blanked = 0;
+
+/* routine to blank a vesa screen */
+void vesa_blank(void)
+{
+ int mode;
+
+ if((mode = vesa_blanking_mode) == 0)
+ return;
+
+ /* save original values of VGA controller registers */
+ cli();
+ vga.SeqCtrlIndex = inb_p(seq_port_reg);
+ vga.CrtCtrlIndex = inb_p(video_port_reg);
+ vga.CrtMiscIO = inb_p(video_misc_rd);
+ sti();
+
+ if(mode == 2) {
+ outb_p(0x00,video_port_reg); /* HorizontalTotal */
+ vga.HorizontalTotal = inb_p(video_port_val);
+ outb_p(0x01,video_port_reg); /* HorizDisplayEnd */
+ vga.HorizDisplayEnd = inb_p(video_port_val);
+ outb_p(0x04,video_port_reg); /* StartHorizRetrace */
+ vga.StartHorizRetrace = inb_p(video_port_val);
+ outb_p(0x05,video_port_reg); /* EndHorizRetrace */
+ vga.EndHorizRetrace = inb_p(video_port_val);
+ }
+ outb_p(0x07,video_port_reg); /* Overflow */
+ vga.Overflow = inb_p(video_port_val);
+ outb_p(0x10,video_port_reg); /* StartVertRetrace */
+ vga.StartVertRetrace = inb_p(video_port_val);
+ outb_p(0x11,video_port_reg); /* EndVertRetrace */
+ vga.EndVertRetrace = inb_p(video_port_val);
+ outb_p(0x17,video_port_reg); /* ModeControl */
+ vga.ModeControl = inb_p(video_port_val);
+ outb_p(0x01,seq_port_reg); /* ClockingMode */
+ vga.ClockingMode = inb_p(seq_port_val);
+
+ /* assure that video is enabled */
+ /* "0x20" is VIDEO_ENABLE_bit in register 01 of sequencer */
+ cli();
+ outb_p(0x01,seq_port_reg);
+ outb_p(vga.ClockingMode | 0x20,seq_port_val);
+
+ /* test for vertical retrace in process.... */
+ if ((vga.CrtMiscIO & 0x80) == 0x80)
+ outb_p(vga.CrtMiscIO & 0xef,video_misc_wr);
+
+ /*
+ * Set <End of vertical retrace> to minimum (0) and
+ * <Start of vertical Retrace> to maximum (incl. overflow)
+ * Result: turn off vertical sync (VSync) pulse.
+ */
+ outb_p(0x10,video_port_reg); /* StartVertRetrace */
+ outb_p(0xff,video_port_val); /* maximum value */
+ outb_p(0x11,video_port_reg); /* EndVertRetrace */
+ outb_p(0x40,video_port_val); /* minimum (bits 0..3) */
+ outb_p(0x07,video_port_reg); /* Overflow */
+ outb_p(vga.Overflow | 0x84,video_port_val); /* bits 9,10 of */
+ /* vert. retrace */
+ if (mode == 2) {
+ /*
+ * Set <End of horizontal retrace> to minimum (0) and
+ * <Start of horizontal Retrace> to maximum
+ * Result: turn off horizontal sync (HSync) pulse.
+ */
+ outb_p(0x04,video_port_reg); /* StartHorizRetrace */
+ outb_p(0xff,video_port_val); /* maximum */
+ outb_p(0x05,video_port_reg); /* EndHorizRetrace */
+ outb_p(0x00,video_port_val); /* minimum (0) */
+ }
+
+ /* restore both index registers */
+ outb_p(vga.SeqCtrlIndex,seq_port_reg);
+ outb_p(vga.CrtCtrlIndex,video_port_reg);
+ sti();
+
+ vesa_blanked = mode;
+}
+
+/* routine to unblank a vesa screen */
+void vesa_unblank(void)
+{
+ if (!vesa_blanked)
+ return;
+
+ /* restore original values of VGA controller registers */
+ cli();
+ outb_p(vga.CrtMiscIO,video_misc_wr);
+
+ if (vesa_blanked == 2) {
+ outb_p(0x00,video_port_reg); /* HorizontalTotal */
+ outb_p(vga.HorizontalTotal,video_port_val);
+ outb_p(0x01,video_port_reg); /* HorizDisplayEnd */
+ outb_p(vga.HorizDisplayEnd,video_port_val);
+ outb_p(0x04,video_port_reg); /* StartHorizRetrace */
+ outb_p(vga.StartHorizRetrace,video_port_val);
+ outb_p(0x05,video_port_reg); /* EndHorizRetrace */
+ outb_p(vga.EndHorizRetrace,video_port_val);
+ }
+ outb_p(0x07,video_port_reg); /* Overflow */
+ outb_p(vga.Overflow,video_port_val);
+ outb_p(0x10,video_port_reg); /* StartVertRetrace */
+ outb_p(vga.StartVertRetrace,video_port_val);
+ outb_p(0x11,video_port_reg); /* EndVertRetrace */
+ outb_p(vga.EndVertRetrace,video_port_val);
+ outb_p(0x17,video_port_reg); /* ModeControl */
+ outb_p(vga.ModeControl,video_port_val);
+ outb_p(0x01,seq_port_reg); /* ClockingMode */
+ outb_p(vga.ClockingMode,seq_port_val);
+
+ /* restore index/control registers */
+ outb_p(vga.SeqCtrlIndex,seq_port_reg);
+ outb_p(vga.CrtCtrlIndex,video_port_reg);
+ sti();
+
+ vesa_blanked = 0;
+}
+
+void set_vesa_blanking(const unsigned long arg)
+{
+ char *argp = (char *)(arg + 1);
+ unsigned int mode = get_fs_byte(argp);
+ vesa_blanking_mode = ((mode < 3) ? mode : 0);
+}
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index 8abf51813..d2b79124d 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -24,10 +24,13 @@
#include "kbd_kern.h"
#include "vt_kern.h"
#include "diacr.h"
+#include "selection.h"
extern struct tty_driver console_driver;
+extern int sel_cons;
#define VT_IS_IN_USE(i) (console_driver.table[i] && console_driver.table[i]->count)
+#define VT_BUSY(i) (VT_IS_IN_USE(i) || i == fg_console || i == sel_cons)
/*
* Console (vt and kd) routines, as defined by USL SVR4 manual, and by
@@ -52,6 +55,8 @@ extern void compute_shiftstate(void);
extern void change_console(unsigned int new_console);
extern void complete_change_console(unsigned int new_console);
extern int vt_waitactive(void);
+extern void do_blank_screen(int nopowersave);
+extern void do_unblank_screen(void);
extern unsigned int keymap_count;
@@ -60,6 +65,9 @@ extern unsigned int keymap_count;
*/
extern int con_set_trans(char * table);
extern int con_get_trans(char * table);
+extern void con_clear_unimap(struct unimapinit *ui);
+extern int con_set_unimap(ushort ct, struct unipair *list);
+extern int con_get_unimap(ushort ct, ushort *uct, struct unipair *list);
extern int con_set_font(char * fontmap);
extern int con_get_font(char * fontmap);
@@ -127,7 +135,7 @@ kd_mksound(unsigned int count, unsigned int ticks)
int vt_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
{
- int i;
+ int i, perm;
unsigned int console;
unsigned char ucval;
struct kbd_struct * kbd;
@@ -138,13 +146,25 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
if (!vc_cons_allocated(console)) /* impossible? */
return -ENOIOCTLCMD;
+ /*
+ * To have permissions to do most of the vt ioctls, we either have
+ * to be the owner of the tty, or super-user.
+ */
+ perm = 0;
+ if (current->tty == tty || suser())
+ perm = 1;
+
kbd = kbd_table + console;
switch (cmd) {
case KIOCSOUND:
+ if (!perm)
+ return -EPERM;
kd_mksound((unsigned int)arg, 0);
return 0;
case KDMKTONE:
+ if (!perm)
+ return -EPERM;
{
unsigned int ticks = HZ * ((arg >> 16) & 0xffff) / 1000;
@@ -188,6 +208,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
* doesn't do a whole lot. i'm not sure if it should do any
* restoration of modes or what...
*/
+ if (!perm)
+ return -EPERM;
switch (arg) {
case KD_GRAPHICS:
break;
@@ -208,11 +230,9 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
* explicitly blank/unblank the screen if switching modes
*/
if (arg == KD_TEXT)
- unblank_screen();
- else {
- timer_active &= ~(1<<BLANK_TIMER);
- blank_screen();
- }
+ do_unblank_screen();
+ else
+ do_blank_screen(1);
return 0;
case KDGETMODE:
@@ -230,6 +250,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
return -EINVAL;
case KDSKBMODE:
+ if (!perm)
+ return -EPERM;
switch(arg) {
case K_RAW:
kbd->kbdmode = VC_RAW;
@@ -309,6 +331,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
struct kbkeycode * const a = (struct kbkeycode *)arg;
unsigned int sc, kc;
+ if (!perm)
+ return -EPERM;
i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbkeycode));
if (i)
return i;
@@ -346,8 +370,10 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
const struct kbentry * a = (struct kbentry *)arg;
ushort *key_map;
u_char s;
- u_short v;
+ u_short v, ov;
+ if (!perm)
+ return -EPERM;
i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbentry));
if (i)
return i;
@@ -359,7 +385,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
if (!i && v == K_NOSUCHMAP) {
/* disallocate map */
key_map = key_maps[s];
- if (key_map) {
+ if (s && key_map) {
key_maps[s] = 0;
if (key_map[0] == U(K_ALLOCATED)) {
kfree_s(key_map, sizeof(plain_map));
@@ -381,6 +407,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
return 0;
if (!(key_map = key_maps[s])) {
+ int j;
+
if (keymap_count >= MAX_NR_OF_USER_KEYMAPS && !suser())
return -EPERM;
@@ -390,18 +418,22 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
return -ENOMEM;
key_maps[s] = key_map;
key_map[0] = U(K_ALLOCATED);
- for (s = 1; s < NR_KEYS; s++)
- key_map[s] = U(K_HOLE);
+ for (j = 1; j < NR_KEYS; j++)
+ key_map[j] = U(K_HOLE);
keymap_count++;
}
+ ov = U(key_map[i]);
+ if (v == ov)
+ return 0; /* nothing to do */
/*
* Only the Superuser can set or unset the Secure
* Attention Key.
*/
- if (((key_map[i] == U(K_SAK)) || (v == K_SAK)) &&
- !suser())
+ if (((ov == K_SAK) || (v == K_SAK)) && !suser())
return -EPERM;
key_map[i] = U(v);
+ if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT))
+ compute_shiftstate();
return 0;
}
@@ -437,6 +469,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
u_char *p;
char *q;
+ if (!perm)
+ return -EPERM;
i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbsentry));
if (i)
return i;
@@ -520,6 +554,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
struct kbdiacrs *a = (struct kbdiacrs *)arg;
unsigned int ct;
+ if (!perm)
+ return -EPERM;
i = verify_area(VERIFY_READ, (void *) a, sizeof(struct kbdiacrs));
if (i)
return i;
@@ -542,6 +578,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
return 0;
case KDSKBLED:
+ if (!perm)
+ return -EPERM;
if (arg & ~0x77)
return -EINVAL;
kbd->ledflagstate = (arg & 7);
@@ -559,14 +597,40 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
return 0;
case KDSETLED:
+ if (!perm)
+ return -EPERM;
setledstate(kbd, arg);
return 0;
+ /*
+ * A process can indicate its willingness to accept signals
+ * generated by pressing an appropriate key combination.
+ * Thus, one can have a daemon that e.g. spawns a new console
+ * upon a keypress and then changes to it.
+ * Probably init should be changed to do this (and have a
+ * field ks (`keyboard signal') in inittab describing the
+ * desired action), so that the number of background daemons
+ * does not increase.
+ */
+ case KDSIGACCEPT:
+ {
+ extern int spawnpid, spawnsig;
+ if (!perm)
+ return -EPERM;
+ if (arg < 1 || arg > NSIG || arg == SIGKILL)
+ return -EINVAL;
+ spawnpid = current->pid;
+ spawnsig = arg;
+ return 0;
+ }
+
case VT_SETMODE:
{
struct vt_mode *vtmode = (struct vt_mode *)arg;
char mode;
+ if (!perm)
+ return -EPERM;
i = verify_area(VERIFY_WRITE, (void *)vtmode, sizeof(struct vt_mode));
if (i)
return i;
@@ -615,8 +679,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
put_fs_word(fg_console + 1, &vtstat->v_active);
state = 1; /* /dev/tty0 is always open */
for (i = 0, mask = 2; i < MAX_NR_CONSOLES && mask; ++i, mask <<= 1)
- if (console_driver.table[i] &&
- console_driver.table[i]->count > 0)
+ if (VT_IS_IN_USE(i))
state |= mask;
put_fs_word(state, &vtstat->v_state);
return 0;
@@ -642,6 +705,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
* to preserve sanity).
*/
case VT_ACTIVATE:
+ if (!perm)
+ return -EPERM;
if (arg == 0 || arg > MAX_NR_CONSOLES)
return -ENXIO;
arg--;
@@ -655,6 +720,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
* wait until the specified VT has been activated
*/
case VT_WAITACTIVE:
+ if (!perm)
+ return -EPERM;
if (arg == 0 || arg > MAX_NR_CONSOLES)
return -ENXIO;
arg--;
@@ -676,6 +743,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
* 2: completed switch-to OK
*/
case VT_RELDISP:
+ if (!perm)
+ return -EPERM;
if (vt_cons[console]->vt_mode.mode != VT_PROCESS)
return -EINVAL;
@@ -729,21 +798,15 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
if (arg == 0) {
/* disallocate all unused consoles, but leave 0 */
for (i=1; i<MAX_NR_CONSOLES; i++)
- if (! VT_IS_IN_USE(i)) {
- if (i == fg_console)
- change_console(0);
- vc_disallocate(i);
- }
+ if (! VT_BUSY(i))
+ vc_disallocate(i);
} else {
/* disallocate a single console, if possible */
arg--;
- if (VT_IS_IN_USE(arg))
+ if (VT_BUSY(arg))
return -EBUSY;
- if (arg) { /* leave 0 */
- if (arg == fg_console)
- change_console(0);
- vc_disallocate(arg);
- }
+ if (arg) /* leave 0 */
+ vc_disallocate(arg);
}
return 0;
@@ -751,6 +814,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
{
struct vt_sizes *vtsizes = (struct vt_sizes *) arg;
ushort ll,cc;
+ if (!perm)
+ return -EPERM;
i = verify_area(VERIFY_READ, (void *)vtsizes, sizeof(struct vt_sizes));
if (i)
return i;
@@ -760,21 +825,78 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
}
case PIO_FONT:
+ if (!perm)
+ return -EPERM;
+ if (vt_cons[fg_console]->vc_mode != KD_TEXT)
+ return -EINVAL;
return con_set_font((char *)arg);
/* con_set_font() defined in console.c */
case GIO_FONT:
+ if (vt_cons[fg_console]->vc_mode != KD_TEXT)
+ return -EINVAL;
return con_get_font((char *)arg);
/* con_get_font() defined in console.c */
case PIO_SCRNMAP:
+ if (!perm)
+ return -EPERM;
return con_set_trans((char *)arg);
- /* con_set_trans() defined in console.c */
case GIO_SCRNMAP:
return con_get_trans((char *)arg);
- /* con_get_trans() defined in console.c */
+ case PIO_UNIMAPCLR:
+ { struct unimapinit ui;
+ if (!perm)
+ return -EPERM;
+ i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct unimapinit));
+ if (i)
+ return i;
+ memcpy_fromfs(&ui, (void *)arg, sizeof(struct unimapinit));
+ con_clear_unimap(&ui);
+ return 0;
+ }
+
+ case PIO_UNIMAP:
+ { struct unimapdesc *ud;
+ u_short ct;
+ struct unipair *list;
+
+ if (!perm)
+ return -EPERM;
+ i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct unimapdesc));
+ if (i == 0) {
+ ud = (struct unimapdesc *) arg;
+ ct = get_fs_word(&ud->entry_ct);
+ list = (struct unipair *) get_fs_long(&ud->entries);
+ i = verify_area(VERIFY_READ, (void *) list,
+ ct*sizeof(struct unipair));
+ }
+ if (i)
+ return i;
+ return con_set_unimap(ct, list);
+ }
+
+ case GIO_UNIMAP:
+ { struct unimapdesc *ud;
+ u_short ct;
+ struct unipair *list;
+
+ i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct unimapdesc));
+ if (i == 0) {
+ ud = (struct unimapdesc *) arg;
+ ct = get_fs_word(&ud->entry_ct);
+ list = (struct unipair *) get_fs_long(&ud->entries);
+ if (ct)
+ i = verify_area(VERIFY_WRITE, (void *) list,
+ ct*sizeof(struct unipair));
+ }
+ if (i)
+ return i;
+ return con_get_unimap(ct, &(ud->entry_ct), list);
+ }
+
default:
return -ENOIOCTLCMD;
}
diff --git a/drivers/net/3c501.c b/drivers/net/3c501.c
index 5643c2d5e..3910587da 100644
--- a/drivers/net/3c501.c
+++ b/drivers/net/3c501.c
@@ -14,6 +14,65 @@
The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
Center of Excellence in Space Data and Information Sciences
Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ Fixed (again!) the missing interrupt locking on TX/RX shifting.
+ Alan Cox <Alan.Cox@linux.org>
+
+ Removed calls to init_etherdev since they are no longer needed, and
+ cleaned up modularization just a bit. The driver still allows only
+ the default address for cards when loaded as a module, but that's
+ really less braindead than anyone using a 3c501 board. :)
+ 19950208 (invid@msen.com)
+
+ Added traps for interrupts hitting the window as we clear and TX load
+ the board. Now getting 150K/second FTP with a 3c501 card. Still playing
+ with a TX-TX optimisation to see if we can touch 180-200K/second as seems
+ theoretically maximum.
+ 19950402 Alan Cox <Alan.Cox@linux.org>
+
+ Some notes on this thing if you have to hack it. [Alan]
+
+ 1] Some documentation is available from 3Com. Due to the boards age
+ standard responses when you ask for this will range from 'be serious'
+ to 'give it to a museum'. The documentation is incomplete and mostly
+ of historical interest anyway.
+
+ 2] The basic system is a single buffer which can be used to receive or
+ transmit a packet. A third command mode exists when you are setting
+ things up.
+
+ 3] If it's transmitting it's not receiving and vice versa. In fact the
+ time to get the board back into useful state after an operation is
+ quite large.
+
+ 4] The driver works by keeping the board in receive mode waiting for a
+ packet to arrive. When one arrives it is copied out of the buffer
+ and delivered to the kernel. The card is reloaded and off we go.
+
+ 5] When transmitting dev->tbusy is set and the card is reset (from
+ receive mode) [possibly losing a packet just received] to command
+ mode. A packet is loaded and transmit mode triggered. The interrupt
+ handler runs different code for transmit interrupts and can handle
+ returning to receive mode or retransmissions (yes you have to help
+ out with those too).
+
+ Problems:
+ There are a wide variety of undocumented error returns from the card
+ and you basically have to kick the board and pray if they turn up. Most
+ only occur under extreme load or if you do something the board doesn't
+ like (eg touching a register at the wrong time).
+
+ The driver is less efficient than it could be. It switches through
+ receive mode even if more transmits are queued. If this worries you buy
+ a real ethernet card.
+
+ The combination of slow receive restart and no real multicast
+ filter makes the board unusable with a kernel compiled for IP
+ multicasting in a real multicast environment. Thats down to the board,
+ but even with no multicast programs running a multicast IP kernel is
+ in group 224.0.0.1 and you will therefore be listening to all multicasts.
+ One nv conference running over that ethernet and you can give up.
+
*/
static char *version =
@@ -25,6 +84,14 @@ static char *version =
*/
#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/ptrace.h>
@@ -43,14 +110,6 @@ static char *version =
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
-#ifdef MODULE
-#include <linux/module.h>
-#include "../../tools/version.h"
-#endif
-
-extern struct device *init_etherdev(struct device *dev, int sizeof_private,
- unsigned long *mem_startp);
-
/* A zero-terminated list of I/O addresses to be probed.
The 3c501 can be at many locations, but here are the popular ones. */
static unsigned int netcard_portlist[] =
@@ -62,7 +121,7 @@ int el1_probe(struct device *dev);
static int el1_probe1(struct device *dev, int ioaddr);
static int el_open(struct device *dev);
static int el_start_xmit(struct sk_buff *skb, struct device *dev);
-static void el_interrupt(int reg_ptr);
+static void el_interrupt(int irq, struct pt_regs *regs);
static void el_receive(struct device *dev);
static void el_reset(struct device *dev);
static int el1_close(struct device *dev);
@@ -81,6 +140,7 @@ struct net_local {
struct enet_statistics stats;
int tx_pkt_start; /* The length of the current Tx packet. */
int collisions; /* Tx collisions this packet */
+ int loading; /* Spot buffer load collisions */
};
@@ -161,6 +221,8 @@ el1_probe(struct device *dev)
static int
el1_probe1(struct device *dev, int ioaddr)
{
+ #ifndef MODULE
+
char *mname; /* Vendor name */
unsigned char station_addr[6];
int autoirq = 0;
@@ -183,10 +245,7 @@ el1_probe1(struct device *dev, int ioaddr)
return ENODEV;
/* Grab the region so we can find the another board if autoIRQ fails. */
- snarf_region(ioaddr, EL1_IO_EXTENT);
-
- if (dev == NULL)
- dev = init_etherdev(0, sizeof(struct net_local), 0);
+ request_region(ioaddr, EL1_IO_EXTENT,"3c501");
/* We auto-IRQ by shutting off the interrupt line and letting it float
high. */
@@ -218,9 +277,13 @@ el1_probe1(struct device *dev, int ioaddr)
if (autoirq)
dev->irq = autoirq;
- printk("%s: %s EtherLink at %#x, using %sIRQ %d, melting ethernet.\n",
+ printk("%s: %s EtherLink at %#lx, using %sIRQ %d.\n",
dev->name, mname, dev->base_addr,
autoirq ? "auto":"assigned ", dev->irq);
+
+#ifdef CONFIG_IP_MULTICAST
+ printk("WARNING: Use of the 3c501 in a multicast kernel is NOT recommended.\n");
+#endif
if (el_debug)
printk("%s", version);
@@ -239,6 +302,7 @@ el1_probe1(struct device *dev, int ioaddr)
/* Setup the generic properties */
ether_setup(dev);
+#endif /* !MODULE */
return 0;
}
@@ -261,9 +325,7 @@ el_open(struct device *dev)
dev->start = 1;
outb(AX_RX, AX_CMD); /* Aux control, irq and receive enabled */
-#ifdef MODULE
MOD_INC_USE_COUNT;
-#endif
return 0;
}
@@ -272,6 +334,7 @@ el_start_xmit(struct sk_buff *skb, struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int ioaddr = dev->base_addr;
+ unsigned long flags;
if (dev->tbusy) {
if (jiffies - dev->trans_start < 20) {
@@ -296,24 +359,51 @@ el_start_xmit(struct sk_buff *skb, struct device *dev)
return 0;
}
+ save_flags(flags);
+ /* Avoid incoming interrupts between us flipping tbusy and flipping
+ mode as the driver assumes tbusy is a faithful indicator of card
+ state */
+ cli();
/* Avoid timer-based retransmission conflicts. */
if (set_bit(0, (void*)&dev->tbusy) != 0)
+ {
+ restore_flags(flags);
printk("%s: Transmitter access conflict.\n", dev->name);
+ }
else {
int gp_start = 0x800 - (ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN);
unsigned char *buf = skb->data;
+load_it_again_sam:
lp->tx_pkt_start = gp_start;
lp->collisions = 0;
+ /*
+ * Command mode with status cleared should [in theory]
+ * mean no more interrupts can be pending on the card.
+ */
outb(AX_SYS, AX_CMD);
inb(RX_STATUS);
inb(TX_STATUS);
- outw(0x00, RX_BUF_CLR); /* Set rx packet area to 0. */
- outw(gp_start, GP_LOW);
- outsb(DATAPORT,buf,skb->len);
- outw(gp_start, GP_LOW);
- outb(AX_XMIT, AX_CMD); /* Trigger xmit. */
+
+ lp->loading=1;
+
+ /*
+ * Turn interrupts back on while we spend a pleasant afternoon
+ * loading bytes into the board
+ */
+ restore_flags(flags);
+ outw(0x00, RX_BUF_CLR); /* Set rx packet area to 0. */
+ outw(gp_start, GP_LOW); /* aim - packet will be loaded into buffer start */
+ outsb(DATAPORT,buf,skb->len); /* load buffer (usual thing each byte increments the pointer) */
+ outw(gp_start, GP_LOW); /* the board reuses the same register */
+ if(lp->loading==2) /* A receive upset our load, despite our best efforts */
+ {
+ if(el_debug>2)
+ printk("%s: burped during tx load.\n", dev->name);
+ goto load_it_again_sam; /* Sigh... */
+ }
+ outb(AX_XMIT, AX_CMD); /* fire ... Trigger xmit. */
dev->trans_start = jiffies;
}
@@ -327,9 +417,8 @@ el_start_xmit(struct sk_buff *skb, struct device *dev)
/* The typical workload of the driver:
Handle the ether interface interrupts. */
static void
-el_interrupt(int reg_ptr)
+el_interrupt(int irq, struct pt_regs *regs)
{
- int irq = pt_regs2irq(reg_ptr);
struct device *dev = (struct device *)(irq2dev_map[irq]);
struct net_local *lp;
int ioaddr;
@@ -349,8 +438,15 @@ el_interrupt(int reg_ptr)
if (dev->interrupt)
printk("%s: Reentering the interrupt driver!\n", dev->name);
dev->interrupt = 1;
+
+ lp->loading=2; /* So we can spot loading interruptions */
if (dev->tbusy) {
+
+ /*
+ * Board in transmit mode.
+ */
+
int txsr = inb(TX_STATUS);
if (el_debug > 6)
@@ -358,12 +454,19 @@ el_interrupt(int reg_ptr)
inw(RX_LOW));
if ((axsr & 0x80) && (txsr & TX_READY) == 0) {
+ /*
+ * FIXME: is there a logic to whether to keep on trying or
+ * reset immediately ?
+ */
printk("%s: Unusual interrupt during Tx, txsr=%02x axsr=%02x"
" gp=%03x rp=%03x.\n", dev->name, txsr, axsr,
inw(ioaddr + EL1_DATAPTR), inw(ioaddr + EL1_RXPTR));
dev->tbusy = 0;
mark_bh(NET_BH);
} else if (txsr & TX_16COLLISIONS) {
+ /*
+ * Timed out
+ */
if (el_debug)
printk("%s: Transmit failed 16 times, ethernet jammed?\n",
dev->name);
@@ -372,6 +475,9 @@ el_interrupt(int reg_ptr)
} else if (txsr & TX_COLLISION) { /* Retrigger xmit. */
if (el_debug > 6)
printk(" retransmitting after a collision.\n");
+ /*
+ * Poor little chip can't reset its own start pointer
+ */
outb(AX_SYS, AX_CMD);
outw(lp->tx_pkt_start, GP_LOW);
outb(AX_XMIT, AX_CMD);
@@ -379,26 +485,42 @@ el_interrupt(int reg_ptr)
dev->interrupt = 0;
return;
} else {
+ /*
+ * It worked.. we will now fall through and receive
+ */
lp->stats.tx_packets++;
if (el_debug > 6)
printk(" Tx succeeded %s\n",
(txsr & TX_RDY) ? "." : "but tx is busy!");
+ /*
+ * This is safe the interrupt is atomic WRT itself.
+ */
dev->tbusy = 0;
- mark_bh(NET_BH);
+ mark_bh(NET_BH); /* In case more to transmit */
}
} else {
+
+ /*
+ * In receive mode.
+ */
+
int rxsr = inb(RX_STATUS);
if (el_debug > 5)
printk(" rxsr=%02x txsr=%02x rp=%04x", rxsr, inb(TX_STATUS),
inw(RX_LOW));
- /* Just reading rx_status fixes most errors. */
+ /*
+ * Just reading rx_status fixes most errors.
+ */
if (rxsr & RX_MISSED)
lp->stats.rx_missed_errors++;
if (rxsr & RX_RUNT) { /* Handled to avoid board lock-up. */
lp->stats.rx_length_errors++;
if (el_debug > 5) printk(" runt.\n");
} else if (rxsr & RX_GOOD) {
+ /*
+ * Receive worked.
+ */
el_receive(dev);
} else { /* Nothing? Something is broken! */
if (el_debug > 2)
@@ -410,6 +532,9 @@ el_interrupt(int reg_ptr)
printk(".\n");
}
+ /*
+ * Move into receive mode
+ */
outb(AX_RX, AX_CMD);
outw(0x00, RX_BUF_CLR);
inb(RX_STATUS); /* Be certain that interrupts are cleared. */
@@ -440,9 +565,17 @@ el_receive(struct device *dev)
lp->stats.rx_over_errors++;
return;
}
+
+ /*
+ * Command mode so we can empty the buffer
+ */
+
outb(AX_SYS, AX_CMD);
skb = alloc_skb(pkt_len, GFP_ATOMIC);
+ /*
+ * Start of frame
+ */
outw(0x00, GP_LOW);
if (skb == NULL) {
printk("%s: Memory squeeze, dropping packet.\n", dev->name);
@@ -452,8 +585,14 @@ el_receive(struct device *dev)
skb->len = pkt_len;
skb->dev = dev;
+ /*
+ * The read increments through the bytes. The interrupt
+ * handler will fix the pointer when it returns to
+ * receive mode.
+ */
+
insb(DATAPORT, skb->data, pkt_len);
-
+ skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
lp->stats.rx_packets++;
}
@@ -502,9 +641,7 @@ el1_close(struct device *dev)
outb(AX_RESET, AX_CMD); /* Reset the chip */
irq2dev_map[dev->irq] = 0;
-#ifdef MODULE
MOD_DEC_USE_COUNT;
-#endif
return 0;
}
@@ -544,10 +681,15 @@ static struct device dev_3c501 = {
0, 0, 0, 0,
0x280, 5,
0, 0, 0, NULL, el1_probe };
+
+int io=0x280;
+int irq=5;
int
init_module(void)
{
+ dev_3c501.irq=irq;
+ dev_3c501.base_addr=io;
if (register_netdev(&dev_3c501) != 0)
return -EIO;
return 0;
@@ -556,12 +698,15 @@ init_module(void)
void
cleanup_module(void)
{
- if (MOD_IN_USE)
- printk("3c501: device busy, remove delayed\n");
- else
- {
- unregister_netdev(&dev_3c501);
- }
+ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+ unregister_netdev(&dev_3c501);
+
+ /* Free up the private structure, or leak memory :-) */
+ kfree(dev_3c501.priv);
+ dev_3c501.priv = NULL; /* gets re-allocated by el1_probe1 */
+
+ /* If we don't do this, we can't re-insmod it later. */
+ release_region(dev_3c501.base_addr, EL1_IO_EXTENT);
}
#endif /* MODULE */
diff --git a/drivers/net/3c503.c b/drivers/net/3c503.c
index b034e15b4..2c3ab0351 100644
--- a/drivers/net/3c503.c
+++ b/drivers/net/3c503.c
@@ -25,11 +25,11 @@
static char *version =
"3c503.c:v1.10 9/23/93 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/string.h>
+#include <linux/delay.h>
#include <asm/io.h>
#include <asm/system.h>
@@ -145,8 +145,10 @@ el2_probe1(struct device *dev, int ioaddr)
static unsigned version_printed = 0;
/* Reset and/or avoid any lurking NE2000 */
- if (inb(ioaddr + 0x408) == 0xff)
+ if (inb(ioaddr + 0x408) == 0xff) {
+ udelay(1000);
return ENODEV;
+ }
/* We verify that it's a 3C503 board by checking the first three octets
of its ethernet address. */
@@ -170,7 +172,7 @@ el2_probe1(struct device *dev, int ioaddr)
return ENODEV;
}
- snarf_region(ioaddr, EL2_IO_EXTENT);
+ request_region(ioaddr, EL2_IO_EXTENT,"3c503");
if (dev == NULL)
dev = init_etherdev(0, sizeof(struct ei_device), 0);
@@ -315,6 +317,7 @@ el2_close(struct device *dev)
outb(EGACFR_IRQOFF, E33G_GACFR); /* disable interrupts. */
NS8390_init(dev, 0);
+ dev->start = 0;
return 0;
}
@@ -328,7 +331,7 @@ el2_reset_8390(struct device *dev)
{
if (ei_debug > 1) {
printk("%s: Resetting the 3c503 board...", dev->name);
- printk("%#x=%#02x %#x=%#02x %#x=%#02x...", E33G_IDCFR, inb(E33G_IDCFR),
+ printk("%#lx=%#02x %#lx=%#02x %#lx=%#02x...", E33G_IDCFR, inb(E33G_IDCFR),
E33G_CNTRL, inb(E33G_CNTRL), E33G_GACFR, inb(E33G_GACFR));
}
outb_p(ECNTRL_RESET|ECNTRL_THIN, E33G_CNTRL);
diff --git a/drivers/net/3c505.c b/drivers/net/3c505.c
index a980373c5..d00ef7ab5 100644
--- a/drivers/net/3c505.c
+++ b/drivers/net/3c505.c
@@ -1,22 +1,24 @@
/*
* Linux ethernet device driver for the 3Com Etherlink Plus (3C505)
- * By Craig Southeren
+ * By Craig Southeren and Juha Laiho
*
- * elplus.c This module implements an interface to the 3Com
+ * 3c505.c This module implements an interface to the 3Com
* Etherlink Plus (3c505) ethernet card. Linux device
* driver interface reverse engineered from the Linux 3C509
- * device drivers. Vital 3C505 information gleaned from
- * the Crynwr packet driver
+ * device drivers. Some 3C505 information gleaned from
+ * the Crynwr packet driver. Still this driver would not
+ * be here without 3C505 technical reference provided by
+ * 3Com.
*
- * Version: @(#)elplus.c 0.5 11-Jun-94
+ * Version: @(#)3c505.c 0.8 4-Jun-95
*
- * Authors: Linux 3c505 device driver by:
- * Craig Southeren, <geoffw@extro.ucc.su.oz.au>
- * Final debugging by:
+ * Authors: Linux 3c505 device driver by
+ * Craig Southeren, <craigs@ineluki.apana.org.au>
+ * Final debugging by
* Andrew Tridgell, <tridge@nimbus.anu.edu.au>
- * Auto irq, auto detect, cleanup and v1.1.4+ kernel mods by:
+ * Auto irq/address, tuning, cleanup and v1.1.4+ kernel mods by
* Juha Laiho, <jlaiho@ichaos.nullnet.fi>
- * Linux 3C509 driver by:
+ * Linux 3C509 driver by
* Donald Becker, <becker@super.org>
* Crynwr packet driver by
* Krishnan Gopalan and Gregg Stefancik,
@@ -26,37 +28,11 @@
* modified by Warren Van Houten and krus@diku.dk.
* 3C505 technical information provided by
* Terry Murphy, of 3Com Network Adapter Division
+ * Linux 1.3.0 changes by
+ * Alan Cox <Alan.Cox@linux.org>
*
*/
-
-/*********************************************************
- *
- * set ELP_KERNEL_TYPE to the following values depending upon
- * the kernel type:
- * 0 = 0.99pl14 or earlier
- * 1 = 0.99pl15 through 1.1.3
- * 2 = 1.1.4 through 1.1.11
- * 3 = 1.1.12 through 1.1.19
- * 4 = 1.1.20
- *
- *********************************************************/
-
-#define ELP_KERNEL_TYPE 4
-
-/*********************************************************
- *
- * set ELP_NEED_HARD_RESET to 1, if having problems with
- * "warm resets" from DOS. Bootup will then take some more
- * time, as the adapter will perform self-test upon hard
- * reset. This misbehaviour is reported to happen at least
- * after use of Windows real-mode NDIS drivers.
- *
- *********************************************************/
-
-#define ELP_NEED_HARD_RESET 0
-
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
@@ -69,25 +45,12 @@
#include <asm/bitops.h>
#include <asm/io.h>
-#if (ELP_KERNEL_TYPE < 2)
-#include "dev.h"
-#include "eth.h"
-#include "skbuff.h"
-#include "arp.h"
-#else
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
-#endif
-
-#ifndef port_read
-#include "iow.h"
-#endif
#include "3c505.h"
-#define ELPLUS_DEBUG 0
-
/*********************************************************
*
* define debug messages here as common strings to reduce space
@@ -96,15 +59,20 @@
static char * filename = __FILE__;
-static char * null_msg = "*** NULL at %s(%d) ***\n";
-#define CHECK_NULL(p) if (!p) printk(null_msg, filename, __LINE__)
+static char * null_msg = "*** NULL at %s:%s (line %d) ***\n";
+#define CHECK_NULL(p) \
+ if (!p) printk(null_msg, filename,__FUNCTION__,__LINE__)
-static char * timeout_msg = "*** timeout at %s(%d) ***\n";
-#define TIMEOUT_MSG() printk(timeout_msg, filename,__LINE__)
+static char * timeout_msg = "*** timeout at %s:%s (line %d) ***\n";
+#define TIMEOUT_MSG(lineno) \
+ printk(timeout_msg, filename,__FUNCTION__,(lineno))
-static char * invalid_pcb_msg = "*** invalid pcb length %d at %s(%d) ***\n";
+static char * invalid_pcb_msg =
+ "*** invalid pcb length %d at %s:%s (line %d) ***\n";
+#define INVALID_PCB_MSG(len) \
+ printk(invalid_pcb_msg, (len),filename,__FUNCTION__,__LINE__)
-static char * search_msg = "%s: Looking for 3c505 adapter at address 0x%x...";
+static char * search_msg = "%s: Looking for 3c505 adapter at address %#x...";
static char * stilllooking_msg = "still looking...";
@@ -120,32 +88,20 @@ static char * couldnot_msg = "%s: 3c505 not found\n";
*
*********************************************************/
-#ifdef ELPLUS_DEBUG
-static int elp_debug = ELPLUS_DEBUG;
+#ifdef ELP_DEBUG
+static int elp_debug = ELP_DEBUG;
#else
static int elp_debug = 0;
#endif
-#if (ELP_KERNEL_TYPE < 2)
-extern void skb_check(struct sk_buff *skb,int, char *);
-#ifndef IS_SKB
-#define IS_SKB(skb) skb_check((skb),__LINE__,filename)
-#endif
-#else
-#ifndef IS_SKB
-#define IS_SKB(skb) skb_check((skb),0,__LINE__,filename)
-#endif
-#endif
-
-
/*
- * 0 = no messages
+ * 0 = no messages (well, some)
* 1 = messages when high level commands performed
* 2 = messages when low level commands performed
* 3 = messages when interrupts received
*/
-#define ELPLUS_VERSION "0.4.0"
+#define ELP_VERSION "0.7.0"
/*****************************************************************
*
@@ -153,28 +109,6 @@ extern void skb_check(struct sk_buff *skb,int, char *);
*
*****************************************************************/
-/*
- * kernels before pl15 used an unobvious method for accessing
- * the skb data area
- */
-#if (ELP_KERNEL_TYPE < 1)
-#define SKB_DATA (skb+1)
-#else
-#define SKB_DATA (skb->data)
-#endif
-
-/*
- * not all kernels before 1.1.4 had an alloc_skb function (apparently!!)
- */
-#if (ELP_KERNEL_TYPE < 2)
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#endif
-#endif
-
-#define INB(port) inb((unsigned short)(port))
-#define OUTB(val,port) outb((unsigned char)(val),(unsigned short)(port));
-
#ifndef TRUE
#define TRUE 1
#endif
@@ -194,11 +128,52 @@ const int addr_list[]={0x300,0x280,0x310,0};
/*****************************************************************
*
- * PCB structure
+ * Functions for I/O (note the inline !)
*
*****************************************************************/
-#include "3c505dta.h"
+static inline unsigned char
+inb_status (unsigned int base_addr)
+{
+ return inb(base_addr+PORT_STATUS);
+}
+
+static inline unsigned char
+inb_control (unsigned int base_addr)
+{
+ return inb(base_addr+PORT_CONTROL);
+}
+
+static inline int
+inb_command (unsigned int base_addr)
+{
+ return inb(base_addr+PORT_COMMAND);
+}
+
+static inline void
+outb_control (unsigned char val, unsigned int base_addr)
+{
+ outb(val, base_addr+PORT_CONTROL);
+}
+
+static inline void
+outb_command (unsigned char val, unsigned int base_addr)
+{
+ outb(val, base_addr+PORT_COMMAND);
+}
+
+static inline unsigned int
+inw_data (unsigned int base_addr)
+{
+ return inw(base_addr+PORT_DATA);
+}
+
+static inline void
+outw_data (unsigned int val, unsigned int base_addr)
+{
+ outw(val, base_addr+PORT_DATA);
+}
+
/*****************************************************************
*
@@ -207,16 +182,16 @@ const int addr_list[]={0x300,0x280,0x310,0};
*****************************************************************/
typedef struct {
- int io_addr; /* base I/O address */
- char * name; /* used for debug output */
- short got[NUM_TRANSMIT_CMDS]; /* flags for command completion */
- pcb_struct tx_pcb; /* PCB for foreground sending */
- pcb_struct rx_pcb; /* PCB for foreground receiving */
- pcb_struct itx_pcb; /* PCB for background sending */
- pcb_struct irx_pcb; /* PCB for background receiving */
- struct enet_statistics stats;
+ short got[NUM_TRANSMIT_CMDS]; /* flags for command completion */
+ pcb_struct tx_pcb; /* PCB for foreground sending */
+ pcb_struct rx_pcb; /* PCB for foreground receiving */
+ pcb_struct itx_pcb; /* PCB for background sending */
+ pcb_struct irx_pcb; /* PCB for background receiving */
+ struct enet_statistics stats;
} elp_device;
+static int reset_count=0;
+
/*****************************************************************
*
* useful functions for accessing the adapter
@@ -229,46 +204,101 @@ typedef struct {
*/
/* get adapter PCB status */
-#define GET_ASF() (get_status(adapter)&ASF_PCB_MASK)
-#define GET_STATUS() (get_status(adapter))
-
-static int get_status (elp_device * adapter)
+#define GET_ASF(addr) \
+ (get_status(addr)&ASF_PCB_MASK)
+static inline int
+get_status (unsigned int base_addr)
{
- int timeout = jiffies + TIMEOUT;
- register int stat1;
- do {
- stat1 = INB(adapter->io_addr+PORT_STATUS);
- } while (stat1 != INB(adapter->io_addr+PORT_STATUS) && jiffies < timeout);
- if (jiffies >= timeout)
- TIMEOUT_MSG();
- return stat1;
+ int timeout = jiffies + 10;
+ register int stat1;
+ do {
+ stat1 = inb_status(base_addr);
+ } while (stat1 != inb_status(base_addr) && jiffies < timeout);
+ if (jiffies >= timeout)
+ TIMEOUT_MSG(__LINE__);
+ return stat1;
}
-#define SET_HSF(hsf) (set_hsf(adapter,hsf))
-
-static void set_hsf (elp_device * adapter, int hsf)
+static inline void
+set_hsf (unsigned int base_addr, int hsf)
+{
+ cli();
+ outb_control((inb_control(base_addr)&~HSF_PCB_MASK)|hsf, base_addr);
+ sti();
+}
+#define WAIT_HCRE(addr,toval) wait_hcre((addr),(toval),__LINE__)
+static inline int
+wait_hcre (unsigned int base_addr, int toval, int lineno)
{
- cli();
- OUTB((INB(adapter->io_addr+PORT_CONTROL)&(~HSF_PCB_MASK))|hsf, adapter->io_addr+PORT_CONTROL);
- sti();
+ int timeout = jiffies + toval;
+ while (((inb_status(base_addr)&HCRE)==0) && (jiffies <= timeout))
+ ;
+ if (jiffies >= timeout) {
+ TIMEOUT_MSG(lineno);
+ return FALSE;
+ }
+ return TRUE;
}
-#define WAIT_HCRE(toval) (wait_hcre(adapter,toval))
+static inline int
+wait_fast_hcre (unsigned int base_addr, int toval, int lineno)
+{
+ int timeout = 0;
+ while (((inb_status(base_addr)&HCRE)==0) && (timeout++ < toval))
+ ;
+ if (timeout >= toval) {
+ sti();
+ TIMEOUT_MSG(lineno);
+ return FALSE;
+ }
+ return TRUE;
+}
-static int wait_hcre(elp_device * adapter, int toval)
+static int start_receive (struct device *, pcb_struct *);
+static void adapter_hard_reset (struct device *);
+inline static void
+adapter_reset (struct device * dev)
{
- int timeout = jiffies + toval;
- while(((INB(adapter->io_addr+PORT_STATUS)&STATUS_HCRE)==0) &&
- (jiffies <= timeout))
- ;
- if (jiffies >= timeout) {
- TIMEOUT_MSG();
- return FALSE;
- }
- return TRUE;
+ int timeout;
+ unsigned char orig_hcr=inb_control(dev->base_addr);
+
+ elp_device * adapter=dev->priv;
+
+ outb_control(0,dev->base_addr);
+
+ if (inb_status(dev->base_addr)&ACRF) {
+ do {
+ inb_command(dev->base_addr);
+ timeout=jiffies+2;
+ while ((jiffies<=timeout) && !(inb_status(dev->base_addr)&ACRF))
+ ;
+ } while (inb_status(dev->base_addr)&ACRF);
+ set_hsf(dev->base_addr,HSF_PCB_NAK);
+ }
+
+ outb_control(inb_control(dev->base_addr)|ATTN|DIR,dev->base_addr);
+ timeout=jiffies+1;
+ while (jiffies<=timeout)
+ ;
+ outb_control(inb_control(dev->base_addr)&~ATTN,dev->base_addr);
+ timeout=jiffies+1;
+ while (jiffies<=timeout)
+ ;
+ outb_control(inb_control(dev->base_addr)|FLSH,dev->base_addr);
+ timeout=jiffies+1;
+ while (jiffies<=timeout)
+ ;
+ outb_control(inb_control(dev->base_addr)&~FLSH,dev->base_addr);
+ timeout=jiffies+1;
+ while (jiffies<=timeout)
+ ;
+
+ outb_control(orig_hcr, dev->base_addr);
+ if (!start_receive(dev, &adapter->tx_pcb))
+ printk("%s: start receive command failed \n", dev->name);
}
/*****************************************************************
@@ -286,81 +316,67 @@ static int wait_hcre(elp_device * adapter, int toval)
*
*****************************************************************/
-static int send_pcb(elp_device * adapter, pcb_struct * pcb)
-
+static int
+send_pcb (struct device * dev, pcb_struct * pcb)
{
- int i;
- int cont;
- int retry = 0;
- int timeout;
-
- CHECK_NULL(pcb);
- CHECK_NULL(adapter);
-
- if (pcb->length > MAX_PCB_DATA)
- printk(invalid_pcb_msg, pcb->length, filename, __LINE__);
-
- while (1) {
-
- cont = 1;
-
- /*
- * load each byte into the command register and
- * wait for the HCRE bit to indicate the adapter
- * had read the byte
- */
- SET_HSF(0);
- OUTB(pcb->command, (adapter->io_addr)+PORT_COMMAND);
- cont = WAIT_HCRE(5);
-
- if (cont) {
- OUTB(pcb->length, (adapter->io_addr)+PORT_COMMAND);
- cont = WAIT_HCRE(2);
- }
-
- for (i = 0; cont && (i < pcb->length); i++) {
- OUTB(pcb->data.raw[i], (adapter->io_addr)+PORT_COMMAND);
- cont = WAIT_HCRE(2);
- }
-
- /* set the host status bits to indicate end of PCB */
- /* send the total packet length as well */
- /* wait for the adapter to indicate that it has read the PCB */
- if (cont) {
- SET_HSF(HSF_PCB_END);
- OUTB(2+pcb->length, adapter->io_addr+PORT_COMMAND);
- timeout = jiffies + 6;
- while (jiffies < timeout) {
- i = GET_ASF();
- if ((i == ASF_PCB_ACK) ||
- (i == ASF_PCB_NAK))
- break;
- }
-
- if (jiffies >= timeout)
- TIMEOUT_MSG();
-
- if (i == ASF_PCB_ACK) {
- SET_HSF(0);
- return TRUE;
- } else if (i == ASF_PCB_NAK) {
- SET_HSF(0);
- printk("%s: PCB send was NAKed\n", adapter->name);
- {
- int to = jiffies + 5;
- while (jiffies < to)
- ;
- }
- }
- }
-
- if (elp_debug >= 2)
- printk("%s: NAK/timeout on send PCB\n", adapter->name);
- if ((retry++ & 7) == 0)
- printk("%s: retry #%i on send PCB\n", adapter->name, retry);
- }
-
- return FALSE;
+ int i;
+ int timeout;
+ int cont;
+
+ /*
+ * load each byte into the command register and
+ * wait for the HCRE bit to indicate the adapter
+ * had read the byte
+ */
+ set_hsf(dev->base_addr,0);
+ if ((cont = WAIT_HCRE(dev->base_addr,5))) {
+ cli();
+ if (pcb->command==CMD_TRANSMIT_PACKET)
+ outb_control(inb_control(dev->base_addr)&~DIR,dev->base_addr);
+ outb_command(pcb->command, dev->base_addr);
+ sti();
+ cont = WAIT_HCRE(dev->base_addr,5);
+ }
+
+ if (cont) {
+ outb_command(pcb->length, dev->base_addr);
+ cont = WAIT_HCRE(dev->base_addr,5);
+ }
+
+ cli();
+ for (i = 0; cont && (i < pcb->length); i++) {
+ outb_command(pcb->data.raw[i], dev->base_addr);
+ cont = wait_fast_hcre(dev->base_addr,20000,__LINE__);
+ } /* if wait_fast_hcre() failed, has already done sti() */
+
+ /* set the host status bits to indicate end of PCB */
+ /* send the total packet length as well */
+ /* wait for the adapter to indicate that it has read the PCB */
+ if (cont) {
+ set_hsf(dev->base_addr,HSF_PCB_END);
+ outb_command(2+pcb->length, dev->base_addr);
+ sti();
+ timeout = jiffies + 7;
+ while (jiffies < timeout) {
+ i = GET_ASF(dev->base_addr);
+ if ((i == ASF_PCB_ACK) || (i == ASF_PCB_NAK))
+ break;
+ }
+
+ if (i == ASF_PCB_ACK) {
+ reset_count=0;
+ return TRUE;
+ }
+ else if (i == ASF_PCB_NAK) {
+ printk("%s: PCB send was NAKed\n", dev->name);
+ } else {
+ printk("%s: timeout after sending PCB\n", dev->name);
+ }
+ } else
+ printk("%s: timeout in middle of sending PCB\n", dev->name);
+
+ adapter_reset(dev);
+ return FALSE;
}
/*****************************************************************
@@ -368,144 +384,125 @@ static int send_pcb(elp_device * adapter, pcb_struct * pcb)
* receive_pcb
* Read a PCB to the adapter
*
- * wait for ACRF to be non-zero ---<---+
- * input a byte |
- * if ASF1 and ASF2 were not both one |
- * before byte was read, loop --->---+
- * set HSF1 and HSF2 for ack
+ * wait for ACRF to be non-zero ---<---+
+ * input a byte |
+ * if ASF1 and ASF2 were not both one |
+ * before byte was read, loop --->---+
+ * set HSF1 and HSF2 for ack
*
*****************************************************************/
-static int receive_pcb(elp_device * adapter, pcb_struct * pcb)
-
+static int
+receive_pcb (struct device * dev, pcb_struct * pcb)
{
- int i;
- int total_length;
- int stat;
- int timeout;
-
- CHECK_NULL(pcb);
- CHECK_NULL(adapter);
-
- /* get the command code */
- timeout = jiffies + TIMEOUT;
- while (((stat = GET_STATUS())&STATUS_ACRF) == 0 && jiffies < timeout)
- ;
- if (jiffies >= timeout)
- TIMEOUT_MSG();
-
- SET_HSF(0);
- pcb->command = INB(adapter->io_addr+PORT_COMMAND);
- if ((stat & ASF_PCB_MASK) != ASF_PCB_END) {
-
- /* read the data length */
- timeout = jiffies + TIMEOUT;
- while (((stat = GET_STATUS())&STATUS_ACRF) == 0 && jiffies < timeout)
- ;
- if (jiffies >= timeout)
- TIMEOUT_MSG();
- pcb->length = INB(adapter->io_addr+PORT_COMMAND);
-
- if (pcb->length > MAX_PCB_DATA)
- printk(invalid_pcb_msg, pcb->length, filename,__LINE__);
-
- if ((stat & ASF_PCB_MASK) != ASF_PCB_END) {
-
- /* read the data */
- i = 0;
- timeout = jiffies + TIMEOUT;
- do {
- while (((stat = GET_STATUS())&STATUS_ACRF) == 0 && jiffies < timeout)
- ;
- pcb->data.raw[i++] = INB(adapter->io_addr+PORT_COMMAND);
- if (i > MAX_PCB_DATA)
- printk(invalid_pcb_msg, i, filename, __LINE__);
- } while ((stat & ASF_PCB_MASK) != ASF_PCB_END && jiffies < timeout);
-
- if (jiffies >= timeout)
- TIMEOUT_MSG();
-
- /* woops, the last "data" byte was really the length! */
- total_length = pcb->data.raw[--i];
-
- /* safety check total length vs data length */
- if (total_length != (pcb->length + 2)) {
- if (elp_debug >= 2)
- printk("%s: mangled PCB received\n", adapter->name);
- SET_HSF(HSF_PCB_NAK);
- return FALSE;
- }
-
- SET_HSF(HSF_PCB_ACK);
- return TRUE;
- }
- }
-
- SET_HSF(HSF_PCB_NAK);
- return FALSE;
-}
+ int i, j;
+ int total_length;
+ int stat;
+ int timeout;
+
+ CHECK_NULL(pcb);
+ CHECK_NULL(dev);
+
+ set_hsf(dev->base_addr,0);
+
+ /* get the command code */
+ timeout = jiffies + 2;
+ while (((stat = get_status(dev->base_addr))&ACRF) == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout) {
+ TIMEOUT_MSG(__LINE__);
+ return FALSE;
+ }
-#if ELP_NEED_HARD_RESET
+ pcb->command = inb_command(dev->base_addr);
-static void adapter_hard_reset(elp_device * adapter)
+ /* read the data length */
+ timeout = jiffies + 3;
+ while (((stat = get_status(dev->base_addr)) & ACRF) == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout) {
+ TIMEOUT_MSG(__LINE__);
+ return FALSE;
+ }
+ pcb->length = inb_command(dev->base_addr);
-{
- int timeout;
-
- CHECK_NULL(adapter);
-
- /*
- * take FLSH and ATTN high
- */
- OUTB(CONTROL_ATTN|CONTROL_FLSH, adapter->io_addr+PORT_CONTROL);
-
- /*
- * wait for a little bit
- */
- for (timeout = jiffies + 20; jiffies <= timeout; )
- ;
-
- /*
- * now take them low
- */
- OUTB(0, adapter->io_addr+PORT_CONTROL);
-
- /*
- * wait for a little bit
- */
- for (timeout = jiffies + 20; jiffies <= timeout; )
- ;
-
- /*
- * now hang around until the board gets it's act together
- */
- for (timeout = jiffies + (100 * 15); jiffies <= timeout; )
- if (GET_ASF() != ASF_PCB_END)
- break;
-}
+ if (pcb->length > MAX_PCB_DATA) {
+ INVALID_PCB_MSG(pcb->length);
+ adapter_reset(dev);
+ return FALSE;
+ }
+
+ /* read the data */
+ cli();
+ i = 0;
+ do {
+ j = 0;
+ while (((stat = get_status(dev->base_addr))&ACRF) == 0 && j++ < 20000)
+ ;
+ pcb->data.raw[i++] = inb_command(dev->base_addr);
+ if (i > MAX_PCB_DATA)
+ INVALID_PCB_MSG(i);
+ } while ((stat & ASF_PCB_MASK) != ASF_PCB_END && j < 20000);
+ sti();
+ if (j >= 20000) {
+ TIMEOUT_MSG(__LINE__);
+ return FALSE;
+ }
+
+ /* woops, the last "data" byte was really the length! */
+ total_length = pcb->data.raw[--i];
-#endif /* ELP_NEED_HARD_RESET */
+ /* safety check total length vs data length */
+ if (total_length != (pcb->length + 2)) {
+ if (elp_debug >= 2)
+ printk("%s: mangled PCB received\n", dev->name);
+ set_hsf(dev->base_addr,HSF_PCB_NAK);
+ return FALSE;
+ }
+
+ set_hsf(dev->base_addr,HSF_PCB_ACK);
+ reset_count=0;
+ return TRUE;
+}
-static void adapter_reset(elp_device * adapter)
+static void
+adapter_hard_reset (struct device * dev)
{
- int timeout;
+ int timeout;
- CHECK_NULL(adapter);
+ CHECK_NULL(dev);
- cli();
- OUTB(CONTROL_ATTN|INB(adapter->io_addr+PORT_CONTROL), adapter->io_addr+PORT_CONTROL);
- sti();
+ if (elp_debug > 0)
+ printk("%s: Resetting the adapter, please wait (approx 20 s)\n",
+ dev->name);
+ /*
+ * take FLSH and ATTN high
+ */
+ outb_control(ATTN|FLSH, dev->base_addr);
- /*
- * wait for a little bit
- */
- for (timeout = jiffies + 20; jiffies <= timeout; )
- ;
+ /*
+ * wait for a little bit
+ */
+ for (timeout = jiffies + 20; jiffies <= timeout; )
+ ;
- cli();
- OUTB(INB(adapter->io_addr+PORT_CONTROL)&~(CONTROL_ATTN), adapter->io_addr+PORT_CONTROL);
- sti();
+ /*
+ * now take them low
+ */
+ outb_control(0, dev->base_addr);
+ /*
+ * wait for a little bit
+ */
+ for (timeout = jiffies + 20; jiffies <= timeout; )
+ ;
+
+ /*
+ * now hang around until the board gets it's act together
+ */
+ for (timeout = jiffies + (100 * 15); jiffies <= timeout; )
+ if (GET_ASF(dev->base_addr) != ASF_PCB_END)
+ break;
}
/******************************************************
@@ -515,20 +512,21 @@ static void adapter_reset(elp_device * adapter)
*
******************************************************/
-static int start_receive(elp_device * adapter, pcb_struct * tx_pcb)
-
+static int
+start_receive (struct device * dev, pcb_struct * tx_pcb)
{
- CHECK_NULL(adapter);
- CHECK_NULL(tx_pcb);
-
- if (elp_debug >= 3)
- printk("%s: restarting receiver\n", adapter->name);
- tx_pcb->command = CMD_RECEIVE_PACKET;
- tx_pcb->length = sizeof(struct Rcv_pkt);
- tx_pcb->data.rcv_pkt.buf_seg = tx_pcb->data.rcv_pkt.buf_ofs = 0; /* Unused */
- tx_pcb->data.rcv_pkt.buf_len = 1600;
- tx_pcb->data.rcv_pkt.timeout = 0; /* set timeout to zero */
- return send_pcb(adapter, tx_pcb);
+ CHECK_NULL(dev);
+ CHECK_NULL(tx_pcb);
+
+ if (elp_debug >= 3)
+ printk("%s: restarting receiver\n", dev->name);
+ tx_pcb->command = CMD_RECEIVE_PACKET;
+ tx_pcb->length = sizeof(struct Rcv_pkt);
+ tx_pcb->data.rcv_pkt.buf_seg
+ = tx_pcb->data.rcv_pkt.buf_ofs = 0; /* Unused */
+ tx_pcb->data.rcv_pkt.buf_len = 1600;
+ tx_pcb->data.rcv_pkt.timeout = 0; /* set timeout to zero */
+ return send_pcb(dev, tx_pcb);
}
/******************************************************
@@ -540,425 +538,386 @@ static int start_receive(elp_device * adapter, pcb_struct * tx_pcb)
*
******************************************************/
-static void receive_packet(struct device * dev,
- elp_device * adapter,
- int len)
-
+static void
+receive_packet (struct device * dev, int len)
{
- register int i;
- unsigned short * ptr;
- short d;
- int timeout;
- int rlen;
- struct sk_buff *skb;
-
- /*
- * allocate a buffer to put the packet into.
- * (for kernels prior to 1.1.4 only)
- */
-#if (ELP_KERNEL_TYPE < 2)
- int sksize = sizeof(struct sk_buff) + len + 4;
-#endif
+ register int i;
+ unsigned short * ptr;
+ int timeout;
+ int rlen;
+ struct sk_buff *skb;
+ elp_device * adapter;
- CHECK_NULL(dev);
- CHECK_NULL(adapter);
+ CHECK_NULL(dev);
+ adapter=dev->priv;
- if (len <= 0 || ((len & ~1) != len))
- if (elp_debug >= 3)
- printk("*** bad packet len %d at %s(%d)\n",len,filename,__LINE__);
+ if (len <= 0 || ((len & ~1) != len))
+ if (elp_debug >= 3) {
+ sti();
+ printk("*** bad packet len %d at %s(%d)\n",len,filename,__LINE__);
+ cli();
+ }
- rlen = (len+1) & ~1;
+ rlen = (len+1) & ~1;
-#if (ELP_KERNEL_TYPE < 2)
- skb = alloc_skb(sksize, GFP_ATOMIC);
-#else
- skb = alloc_skb(rlen, GFP_ATOMIC);
-#endif
+ skb = alloc_skb(rlen, GFP_ATOMIC);
- /*
- * make sure the data register is going the right way
- */
- OUTB(INB(adapter->io_addr+PORT_CONTROL)|CONTROL_DIR, adapter->io_addr+PORT_CONTROL);
-
- /*
- * if buffer could not be allocated, swallow it
- */
- if (skb == NULL) {
- for (i = 0; i < (rlen/2); i++) {
- timeout = jiffies + TIMEOUT;
- while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_HRDY) == 0 &&
- jiffies < timeout)
- ;
- if (jiffies >= timeout)
- TIMEOUT_MSG();
-
- d = inw(adapter->io_addr+PORT_DATA);
- }
- adapter->stats.rx_dropped++;
-
- } else {
- skb->lock = 0;
- skb->len = rlen;
- skb->dev = dev;
+ /*
+ * make sure the data register is going the right way
+ */
-/*
- * for kernels before 1.1.4, the driver allocated the buffer
- */
-#if (ELP_KERNEL_TYPE < 2)
- skb->mem_len = sksize;
- skb->mem_addr = skb;
-#endif
+ outb_control(inb_control(dev->base_addr)|DIR, dev->base_addr);
- /*
- * now read the data from the adapter
- */
- ptr = (unsigned short *)SKB_DATA;
- for (i = 0; i < (rlen/2); i++) {
- timeout = jiffies + TIMEOUT;
- while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_HRDY) == 0 &&
- jiffies < timeout)
- ;
- if (jiffies >= timeout)
- {
- printk("*** timeout at %s(%d) reading word %d of %d ***\n",
- filename,__LINE__, i, rlen/2);
-#if (ELP_KERNEL_TYPE < 2)
- kfree_s(skb, sksize);
-#else
- kfree_s(skb, rlen);
-#endif
- return;
+ /*
+ * if buffer could not be allocated, swallow it
+ */
+ if (skb == NULL) {
+ for (i = 0; i < (rlen/2); i++) {
+ timeout = 0;
+ while ((inb_status(dev->base_addr)&HRDY) == 0 && timeout++ < 20000)
+ ;
+ if (timeout >= 20000) {
+ sti();
+ TIMEOUT_MSG(__LINE__);
+ break;
+ }
+
+ inw_data(dev->base_addr);
+ }
+ adapter->stats.rx_dropped++;
+
+ } else {
+ skb->len = rlen;
+ skb->dev = dev;
+
+ /*
+ * now read the data from the adapter
+ */
+ ptr = (unsigned short *)(skb->data);
+ for (i = 0; i < (rlen/2); i++) {
+ timeout = 0;
+ while ((inb_status(dev->base_addr)&HRDY) == 0 && timeout++ < 20000)
+ ;
+ if (timeout >= 20000) {
+ sti();
+ printk("*** timeout at %s(%d) reading word %d of %d ***\n",
+ filename,__LINE__, i, rlen/2);
+ kfree_skb(skb, FREE_WRITE);
+ return;
+ }
+
+ *ptr = inw_data(dev->base_addr);
+ ptr++;
+ }
+
+ sti();
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
}
- *ptr = inw(adapter->io_addr+PORT_DATA);
- ptr++;
- }
+ outb_control(inb_control(dev->base_addr)&~DIR, dev->base_addr);
+}
- /*
- * the magic routine "dev_rint" passes the packet up the
- * protocol chain. If it returns 0, we can assume the packet was
- * swallowed up. If not, then we are responsible for freeing memory
- */
- IS_SKB(skb);
+/******************************************************
+ *
+ * interrupt handler
+ *
+ ******************************************************/
-/*
- * for kernels before 1.1.4, the driver allocated the buffer, so it had
- * to free it
- */
-#if (ELP_KERNEL_TYPE < 2)
- if (dev_rint((unsigned char *)skb, rlen, IN_SKBUFF, dev) != 0) {
- printk("%s: receive buffers full.\n", dev->name);
- kfree_s(skb, sksize);
- }
-#else
- netif_rx(skb);
-#endif
- }
+static void
+elp_interrupt (int irq, struct pt_regs *reg_ptr)
+{
+ int len;
+ int dlen;
+ struct device *dev;
+ elp_device * adapter;
+ int timeout;
+
+ if (irq < 0 || irq > 15) {
+ printk ("elp_interrupt(): illegal IRQ number found in interrupt routine (%i)\n", irq);
+ return;
+ }
+
+ dev = irq2dev_map[irq];
+
+ if (dev == NULL) {
+ printk ("elp_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ adapter = (elp_device *) dev->priv;
+
+ CHECK_NULL(adapter);
+
+ if (dev->interrupt)
+ if (elp_debug >= 2)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+ dev->interrupt = 1;
+
+ /*
+ * allow interrupts (we need timers!)
+ */
+ sti();
+
+ /*
+ * receive a PCB from the adapter
+ */
+ timeout = jiffies + 3;
+ while ((inb_status(dev->base_addr)&ACRF) != 0 && jiffies < timeout) {
+
+ if (receive_pcb(dev, &adapter->irx_pcb)) {
+
+ switch (adapter->irx_pcb.command) {
+
+ /*
+ * received a packet - this must be handled fast
+ */
+ case CMD_RECEIVE_PACKET_COMPLETE:
+ /* if the device isn't open, don't pass packets up the stack */
+ if (dev->start == 0)
+ break;
+ cli();
+ /* Set direction of adapter FIFO */
+ outb_control(inb_control(dev->base_addr)|DIR,
+ dev->base_addr);
+ len = adapter->irx_pcb.data.rcv_resp.pkt_len;
+ dlen = adapter->irx_pcb.data.rcv_resp.buf_len;
+ if (adapter->irx_pcb.data.rcv_resp.timeout != 0) {
+ printk("%s: interrupt - packet not received correctly\n", dev->name);
+ sti();
+ } else {
+ if (elp_debug >= 3) {
+ sti();
+ printk("%s: interrupt - packet received of length %i (%i)\n", dev->name, len, dlen);
+ cli();
+ }
+ receive_packet(dev, dlen);
+ sti();
+ if (elp_debug >= 3)
+ printk("%s: packet received\n", dev->name);
+ }
+ if (dev->start && !start_receive(dev, &adapter->itx_pcb))
+ if (elp_debug >= 2)
+ printk("%s: interrupt - failed to send receive start PCB\n", dev->name);
+ if (elp_debug >= 3)
+ printk("%s: receive procedure complete\n", dev->name);
+
+ break;
+
+ /*
+ * 82586 configured correctly
+ */
+ case CMD_CONFIGURE_82586_RESPONSE:
+ adapter->got[CMD_CONFIGURE_82586] = 1;
+ if (elp_debug >= 3)
+ printk("%s: interrupt - configure response received\n", dev->name);
+ break;
+
+ /*
+ * Adapter memory configuration
+ */
+ case CMD_CONFIGURE_ADAPTER_RESPONSE:
+ adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 1;
+ if (elp_debug >= 3)
+ printk("%s: Adapter memory configuration %s.\n",dev->name,
+ adapter->irx_pcb.data.failed?"failed":"succeeded");
+ break;
+
+ /*
+ * Multicast list loading
+ */
+ case CMD_LOAD_MULTICAST_RESPONSE:
+ adapter->got[CMD_LOAD_MULTICAST_LIST] = 1;
+ if (elp_debug >= 3)
+ printk("%s: Multicast address list loading %s.\n",dev->name,
+ adapter->irx_pcb.data.failed?"failed":"succeeded");
+ break;
+
+ /*
+ * Station address setting
+ */
+ case CMD_SET_ADDRESS_RESPONSE:
+ adapter->got[CMD_SET_STATION_ADDRESS] = 1;
+ if (elp_debug >= 3)
+ printk("%s: Ethernet address setting %s.\n",dev->name,
+ adapter->irx_pcb.data.failed?"failed":"succeeded");
+ break;
+
+
+ /*
+ * received board statistics
+ */
+ case CMD_NETWORK_STATISTICS_RESPONSE:
+ adapter->stats.rx_packets += adapter->irx_pcb.data.netstat.tot_recv;
+ adapter->stats.tx_packets += adapter->irx_pcb.data.netstat.tot_xmit;
+ adapter->stats.rx_crc_errors += adapter->irx_pcb.data.netstat.err_CRC;
+ adapter->stats.rx_frame_errors += adapter->irx_pcb.data.netstat.err_align;
+ adapter->stats.rx_fifo_errors += adapter->irx_pcb.data.netstat.err_ovrrun;
+ adapter->got[CMD_NETWORK_STATISTICS] = 1;
+ if (elp_debug >= 3)
+ printk("%s: interrupt - statistics response received\n", dev->name);
+ break;
+
+ /*
+ * sent a packet
+ */
+ case CMD_TRANSMIT_PACKET_COMPLETE:
+ if (elp_debug >= 3)
+ printk("%s: interrupt - packet sent\n", dev->name);
+ if (dev->start == 0)
+ break;
+ if (adapter->irx_pcb.data.xmit_resp.c_stat != 0)
+ if (elp_debug >= 2)
+ printk("%s: interrupt - error sending packet %4.4x\n",
+ dev->name, adapter->irx_pcb.data.xmit_resp.c_stat);
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ break;
+
+ /*
+ * some unknown PCB
+ */
+ default:
+ printk("%s: unknown PCB received - %2.2x\n", dev->name, adapter->irx_pcb.command);
+ break;
+ }
+ } else {
+ printk("%s: failed to read PCB on interrupt\n", dev->name);
+ adapter_reset(dev);
+ }
+ }
- OUTB(INB(adapter->io_addr+PORT_CONTROL)&(~CONTROL_DIR), adapter->io_addr+PORT_CONTROL);
+ /*
+ * indicate no longer in interrupt routine
+ */
+ dev->interrupt = 0;
}
/******************************************************
*
- * interrupt handler
+ * open the board
*
******************************************************/
-static void elp_interrupt(int reg_ptr)
-
+static int
+elp_open (struct device *dev)
{
- int len;
- int dlen;
- int irq = pt_regs2irq(reg_ptr);
- struct device *dev;
- elp_device * adapter;
- int timeout;
-
- if (irq < 0 || irq > 15) {
- printk ("elp_interrupt(): illegal IRQ number found in interrupt routine (%i)\n", irq);
- return;
- }
-
-/* FIXME: How do I do this kind of check without a fixed IRQ? */
-#if 0
- if (irq != ELP_IRQ) {
- printk ("elp_interrupt(): - interrupt routine has incorrect IRQ of %i\n", irq);
- return;
- }
-#endif
+ elp_device * adapter;
- dev = irq2dev_map[irq];
+ CHECK_NULL(dev);
- if (dev == NULL) {
- printk ("elp_interrupt(): irq %d for unknown device.\n", irq);
- return;
- }
+ adapter = dev->priv;
- adapter = (elp_device *) dev->priv;
+ if (elp_debug >= 3)
+ printk("%s: request to open device\n", dev->name);
- CHECK_NULL(adapter);
+ /*
+ * make sure we actually found the device
+ */
+ if (adapter == NULL) {
+ printk("%s: Opening a non-existent physical device\n", dev->name);
+ return -EAGAIN;
+ }
- if (dev->interrupt)
- if (elp_debug >= 2)
- printk("%s: Re-entering the interrupt handler.\n", dev->name);
- dev->interrupt = 1;
+ /*
+ * disable interrupts on the board
+ */
+ outb_control(0x00, dev->base_addr);
+
+ /*
+ * clear any pending interrupts
+ */
+ inb_command(dev->base_addr);
+ adapter_reset(dev);
- /*
- * allow interrupts (we need timers!)
- */
- sti();
+ /*
+ * interrupt routine not entered
+ */
+ dev->interrupt = 0;
- /*
- * receive a PCB from the adapter
- */
- timeout = jiffies + TIMEOUT;
- while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_ACRF) != 0 &&
- jiffies < timeout) {
-
- if (receive_pcb(adapter, &adapter->irx_pcb)) {
+ /*
+ * transmitter not busy
+ */
+ dev->tbusy = 0;
- switch (adapter->irx_pcb.command) {
+ /*
+ * make sure we can find the device header given the interrupt number
+ */
+ irq2dev_map[dev->irq] = dev;
- /*
- * 82586 configured correctly
- */
- case CMD_CONFIGURE_82586_RESPONSE:
- adapter->got[CMD_CONFIGURE_82586] = 1;
- if (elp_debug >= 3)
- printk("%s: interrupt - configure response received\n", dev->name);
- break;
+ /*
+ * install our interrupt service routine
+ */
+ if (request_irq(dev->irq, &elp_interrupt, 0, "3c505")) {
+ irq2dev_map[dev->irq] = NULL;
+ return -EAGAIN;
+ }
/*
- * Adapter memory configuration
+ * enable interrupts on the board
*/
- case CMD_CONFIGURE_ADAPTER_RESPONSE:
- adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 1;
- if (elp_debug >= 3)
- printk("%s: Adapter memory configuration %s.",dev->name,
- adapter->irx_pcb.data.failed?"failed":"succeeded");
- break;
-
+ outb_control(CMDE, dev->base_addr);
+
/*
- * Multicast list loading
+ * device is now officially open!
*/
- case CMD_LOAD_MULTICAST_RESPONSE:
- adapter->got[CMD_LOAD_MULTICAST_LIST] = 1;
- if (elp_debug >= 3)
- printk("%s: Multicast address list loading %s.",dev->name,
- adapter->irx_pcb.data.failed?"failed":"succeeded");
- break;
+ dev->start = 1;
/*
- * Station address setting
+ * configure adapter memory: we need 10 multicast addresses, default==0
*/
- case CMD_SET_ADDRESS_RESPONSE:
- adapter->got[CMD_SET_STATION_ADDRESS] = 1;
- if (elp_debug >= 3)
- printk("%s: Ethernet address setting %s.",dev->name,
- adapter->irx_pcb.data.failed?"failed":"succeeded");
- break;
-
-
- /*
- * received board statistics
- */
- case CMD_NETWORK_STATISTICS_RESPONSE:
- adapter->stats.rx_packets += adapter->irx_pcb.data.netstat.tot_recv;
- adapter->stats.tx_packets += adapter->irx_pcb.data.netstat.tot_xmit;
- adapter->stats.rx_crc_errors += adapter->irx_pcb.data.netstat.err_CRC;
- adapter->stats.rx_frame_errors += adapter->irx_pcb.data.netstat.err_align;
- adapter->stats.rx_fifo_errors += adapter->irx_pcb.data.netstat.err_ovrrun;
- adapter->got[CMD_NETWORK_STATISTICS] = 1;
- if (elp_debug >= 3)
- printk("%s: interrupt - statistics response received\n", dev->name);
- break;
-
- /*
- * received a packet
- */
- case CMD_RECEIVE_PACKET_COMPLETE:
- /* if the device isn't open, don't pass packets up the stack */
- if (dev->start == 0)
- break;
- len = adapter->irx_pcb.data.rcv_resp.pkt_len;
- dlen = adapter->irx_pcb.data.rcv_resp.buf_len;
- if (adapter->irx_pcb.data.rcv_resp.timeout != 0) {
- printk("%s: interrupt - packet not received correctly\n", dev->name);
- } else {
- if (elp_debug >= 3)
- printk("%s: interrupt - packet received of length %i (%i)\n", dev->name, len, dlen);
- receive_packet(dev, adapter, dlen);
- if (elp_debug >= 3)
- printk("%s: packet received\n", dev->name);
- }
- if (dev->start && !start_receive(adapter, &adapter->itx_pcb))
- if (elp_debug >= 2)
- printk("%s: interrupt - failed to send receive start PCB\n", dev->name);
- if (elp_debug >= 3)
- printk("%s: receive procedure complete\n", dev->name);
-
- break;
-
- /*
- * sent a packet
- */
- case CMD_TRANSMIT_PACKET_COMPLETE:
- if (elp_debug >= 3)
- printk("%s: interrupt - packet sent\n", dev->name);
- if (dev->start == 0)
- break;
- if (adapter->irx_pcb.data.xmit_resp.c_stat != 0)
- if (elp_debug >= 2)
- printk("%s: interrupt - error sending packet %4.4x\n",
- dev->name, adapter->irx_pcb.data.xmit_resp.c_stat);
- dev->tbusy = 0;
-#if (ELP_KERNEL_TYPE < 3)
- mark_bh(INET_BH);
-#else
- mark_bh(NET_BH);
-#endif
- break;
-
- /*
- * some unknown PCB
- */
- default:
- printk("%s: unknown PCB received - %2.2x\n", dev->name, adapter->irx_pcb.command);
- break;
- }
- } else
- printk("%s: failed to read PCB on interrupt\n", dev->name);
- }
- if (jiffies >= timeout)
- TIMEOUT_MSG();
-
- /*
- * indicate no longer in interrupt routine
- */
- dev->interrupt = 0;
-}
+ if (elp_debug >= 3)
+ printk("%s: sending 3c505 memory configuration command\n", dev->name);
+ adapter->tx_pcb.command = CMD_CONFIGURE_ADAPTER_MEMORY;
+ adapter->tx_pcb.data.memconf.cmd_q = 10;
+ adapter->tx_pcb.data.memconf.rcv_q = 20;
+ adapter->tx_pcb.data.memconf.mcast = 10;
+ adapter->tx_pcb.data.memconf.frame = 20;
+ adapter->tx_pcb.data.memconf.rcv_b = 20;
+ adapter->tx_pcb.data.memconf.progs = 0;
+ adapter->tx_pcb.length = sizeof(struct Memconf);
+ adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb))
+ printk("%s: couldn't send memory configuration command\n", dev->name);
+ else {
+ int timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout)
+ TIMEOUT_MSG(__LINE__);
+ }
-/******************************************************
- *
- * open the board
- *
- ******************************************************/
+ /*
+ * configure adapter to receive broadcast messages and wait for response
+ */
+ if (elp_debug >= 3)
+ printk("%s: sending 82586 configure command\n", dev->name);
+ adapter->tx_pcb.command = CMD_CONFIGURE_82586;
+ adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD;
+ adapter->tx_pcb.length = 2;
+ adapter->got[CMD_CONFIGURE_82586] = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb))
+ printk("%s: couldn't send 82586 configure command\n", dev->name);
+ else {
+ int timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_CONFIGURE_82586] == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout)
+ TIMEOUT_MSG(__LINE__);
+ }
-static int elp_open (struct device *dev)
+ /*
+ * queue receive commands to provide buffering
+ */
+ if (!start_receive(dev, &adapter->tx_pcb))
+ printk("%s: start receive command failed \n", dev->name);
+ if (elp_debug >= 3)
+ printk("%s: start receive command sent\n", dev->name);
-{
- elp_device * adapter = (elp_device *) dev->priv;
-
- CHECK_NULL(dev);
-
- if (elp_debug >= 3)
- printk("%s: request to open device\n", dev->name);
-
- /*
- * make sure we actually found the device
- */
- if (adapter == NULL) {
- printk("%s: Opening a non-existent physical device\n", dev->name);
- return -EAGAIN;
- }
-
- /*
- * disable interrupts on the board
- */
- OUTB(0x00, adapter->io_addr+PORT_CONTROL);
-
- /*
- * clear any pending interrupts
- */
- INB(adapter->io_addr+PORT_COMMAND);
-
- /*
- * interrupt routine not entered
- */
- dev->interrupt = 0;
-
- /*
- * transmitter not busy
- */
- dev->tbusy = 0;
-
- /*
- * install our interrupt service routine
- */
- if (request_irq(dev->irq, &elp_interrupt, 0, "3c505"))
- return -EAGAIN;
-
- /*
- * make sure we can find the device header given the interrupt number
- */
- irq2dev_map[dev->irq] = dev;
-
- /*
- * enable interrupts on the board
- */
- OUTB(CONTROL_CMDE, adapter->io_addr+PORT_CONTROL);
-
- /*
- * device is now officially open!
- */
- dev->start = 1;
-
- /*
- * configure adapter memory: we need 10 multicast addresses, default==0
- */
- if (elp_debug >= 3)
- printk("%s: sending 3c505 memory configuration command\n", dev->name);
- adapter->tx_pcb.command = CMD_CONFIGURE_ADAPTER_MEMORY;
- adapter->tx_pcb.data.memconf.cmd_q = 10;
- adapter->tx_pcb.data.memconf.rcv_q = 20;
- adapter->tx_pcb.data.memconf.mcast = 10;
- adapter->tx_pcb.data.memconf.frame = 20;
- adapter->tx_pcb.data.memconf.rcv_b = 20;
- adapter->tx_pcb.data.memconf.progs = 0;
- adapter->tx_pcb.length = sizeof(struct Memconf);
- adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 0;
- if (!send_pcb(adapter, &adapter->tx_pcb))
- printk("%s: couldn't send memory configuration command\n", dev->name);
- else {
- int timeout = jiffies + TIMEOUT;
- while (adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] == 0 && jiffies < timeout)
- ;
- if (jiffies >= timeout)
- TIMEOUT_MSG();
- }
-
-
- /*
- * configure adapter to receive broadcast messages and wait for response
- */
- if (elp_debug >= 3)
- printk("%s: sending 82586 configure command\n", dev->name);
- adapter->tx_pcb.command = CMD_CONFIGURE_82586;
- adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD;
- adapter->tx_pcb.length = 2;
- adapter->got[CMD_CONFIGURE_82586] = 0;
- if (!send_pcb(adapter, &adapter->tx_pcb))
- printk("%s: couldn't send 82586 configure command\n", dev->name);
- else {
- int timeout = jiffies + TIMEOUT;
- while (adapter->got[CMD_CONFIGURE_82586] == 0 && jiffies < timeout)
- ;
- if (jiffies >= timeout)
- TIMEOUT_MSG();
- }
-
- /*
- * queue receive commands to provide buffering
- */
- if (!start_receive(adapter, &adapter->tx_pcb))
- printk("%s: start receive command failed \n", dev->name);
- if (elp_debug >= 3)
- printk("%s: start receive command sent\n", dev->name);
-
- return 0; /* Always succeed */
+ return 0; /* Always succeed */
}
@@ -968,58 +927,60 @@ static int elp_open (struct device *dev)
*
******************************************************/
-static int send_packet (elp_device * adapter, unsigned char * ptr, int len)
-
+static int
+send_packet (struct device * dev, unsigned char * ptr, int len)
{
- int i;
-
- /*
- * make sure the length is even and no shorter than 60 bytes
- */
- unsigned int nlen = (((len < 60) ? 60 : len) + 1) & (~1);
-
- CHECK_NULL(adapter);
- CHECK_NULL(ptr);
-
- if (nlen < len)
- printk("Warning, bad length nlen=%d len=%d %s(%d)\n",nlen,len,filename,__LINE__);
-
- /*
- * send the adapter a transmit packet command. Ignore segment and offset
- * and make sure the length is even
- */
- adapter->tx_pcb.command = CMD_TRANSMIT_PACKET;
- adapter->tx_pcb.length = sizeof(struct Xmit_pkt);
- adapter->tx_pcb.data.xmit_pkt.buf_ofs = adapter->tx_pcb.data.xmit_pkt.buf_seg = 0; /* Unused */
- adapter->tx_pcb.data.xmit_pkt.pkt_len = nlen;
- if (!send_pcb(adapter, &adapter->tx_pcb))
- return FALSE;
-
- /*
- * make sure the data register is going the right way
- */
- cli();
- OUTB(INB(adapter->io_addr+PORT_CONTROL)&(~CONTROL_DIR), adapter->io_addr+PORT_CONTROL);
- sti();
-
- /*
- * write data to the adapter
- */
- for (i = 0; i < (nlen/2);i++) {
- int timeout = jiffies + TIMEOUT;
- while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_HRDY) == 0 && jiffies < timeout)
- ;
- if (jiffies >= timeout) {
- printk("*** timeout at %s(%d) writing word %d of %d ***\n",
- filename,__LINE__, i, nlen/2);
- return FALSE;
- }
-
- outw(*(short *)ptr, adapter->io_addr+PORT_DATA);
- ptr +=2;
- }
-
- return TRUE;
+ int i;
+ int timeout = 0;
+ elp_device * adapter;
+
+ /*
+ * make sure the length is even and no shorter than 60 bytes
+ */
+ unsigned int nlen = (((len < 60) ? 60 : len) + 1) & (~1);
+
+ CHECK_NULL(dev);
+ CHECK_NULL(ptr);
+
+ adapter = dev->priv;
+
+ if (nlen < len)
+ printk("Warning, bad length nlen=%d len=%d %s(%d)\n",nlen,len,filename,__LINE__);
+
+ /*
+ * send the adapter a transmit packet command. Ignore segment and offset
+ * and make sure the length is even
+ */
+ adapter->tx_pcb.command = CMD_TRANSMIT_PACKET;
+ adapter->tx_pcb.length = sizeof(struct Xmit_pkt);
+ adapter->tx_pcb.data.xmit_pkt.buf_ofs
+ = adapter->tx_pcb.data.xmit_pkt.buf_seg = 0; /* Unused */
+ adapter->tx_pcb.data.xmit_pkt.pkt_len = nlen;
+ if (!send_pcb(dev, &adapter->tx_pcb)) {
+ return FALSE;
+ }
+
+ /*
+ * write data to the adapter
+ */
+ cli();
+ for (i = 0; i < (nlen/2);i++) {
+ while (((inb_status(dev->base_addr)&HRDY) == 0)
+ && (timeout++ < 20000))
+ ;
+ if (timeout >= 20000) {
+ sti();
+ printk("%s: timeout at %s(%d) writing word %d of %d ***\n",
+ dev->name,filename,__LINE__, i, nlen/2);
+ return FALSE;
+ }
+
+ outw_data(*(short *)ptr, dev->base_addr);
+ ptr +=2;
+ }
+ sti();
+
+ return TRUE;
}
/******************************************************
@@ -1029,95 +990,72 @@ static int send_packet (elp_device * adapter, unsigned char * ptr, int len)
*
******************************************************/
-static int elp_start_xmit(struct sk_buff *skb, struct device *dev)
-
+static int
+elp_start_xmit (struct sk_buff *skb, struct device *dev)
{
- elp_device * adapter = (elp_device *) dev->priv;
-
- CHECK_NULL(dev);
-
- /*
- * not sure what this does, but the 3c509 driver does it, so...
- */
- if (skb == NULL) {
- dev_tint(dev);
- return 0;
- }
-
- /*
- * Fill in the ethernet header
- * (for kernels prior to 1.1.4 only)
- */
-#if (ELP_KERNEL_TYPE < 2)
- IS_SKB(skb);
- if (!skb->arp && dev->rebuild_header(SKB_DATA, dev)) {
- skb->dev = dev;
- IS_SKB(skb);
- arp_queue (skb);
- return 0;
- }
-#endif
+ CHECK_NULL(dev);
- /*
- * if we ended up with a munged length, don't send it
- */
- if (skb->len <= 0)
- return 0;
-
- if (elp_debug >= 3)
- printk("%s: request to send packet of length %d\n", dev->name, (int)skb->len);
-
- /*
- * if the transmitter is still busy, we have a transmit timeout...
- */
- if (dev->tbusy) {
- int tickssofar = jiffies - dev->trans_start;
- if (tickssofar < 200) /* was 500, AJT */
- return 1;
- printk("%s: transmit timed out, resetting adapter\n", dev->name);
- if ((INB(adapter->io_addr+PORT_STATUS)&STATUS_ACRF) != 0)
- printk("%s: hmmm...seemed to have missed an interrupt!\n", dev->name);
- adapter_reset(adapter);
- dev->trans_start = jiffies;
- dev->tbusy = 0;
- }
-
- /*
- * send the packet at skb->data for skb->len
- */
- if (!send_packet(adapter, (unsigned char *)SKB_DATA, skb->len)) {
- printk("%s: send packet PCB failed\n", dev->name);
- return 1;
- }
-
- if (elp_debug >= 3)
- printk("%s: packet of length %d sent\n", dev->name, (int)skb->len);
-
-
- /*
- * start the transmit timeout
- */
- dev->trans_start = jiffies;
-
- /*
- * the transmitter is now busy
- */
- dev->tbusy = 1;
-
- /*
- * if we have been asked to free the buffer, do so
- */
-#if (ELP_KERNEL_TYPE < 4)
- if (skb->free)
- {
- IS_SKB(skb);
- kfree_skb(skb, FREE_WRITE);
- }
-#else
- dev_kfree_skb(skb, FREE_WRITE);
-#endif
+ /*
+ * not sure what this does, but the 3c509 driver does it, so...
+ */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /*
+ * if we ended up with a munged length, don't send it
+ */
+ if (skb->len <= 0)
+ return 0;
+
+ if (elp_debug >= 3)
+ printk("%s: request to send packet of length %d\n", dev->name, (int)skb->len);
+
+ /*
+ * if the transmitter is still busy, we have a transmit timeout...
+ */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ int stat;
+ if (tickssofar < 50) /* was 500, AJT */
+ return 1;
+ printk("%s: transmit timed out, not resetting adapter\n", dev->name);
+ if (((stat=inb_status(dev->base_addr))&ACRF) != 0)
+ printk("%s: hmmm...seemed to have missed an interrupt!\n", dev->name);
+ printk("%s: status %#02x\n", dev->name, stat);
+ dev->trans_start = jiffies;
+ dev->tbusy = 0;
+ }
- return 0;
+ /*
+ * send the packet at skb->data for skb->len
+ */
+ if (!send_packet(dev, skb->data, skb->len)) {
+ printk("%s: send packet PCB failed\n", dev->name);
+ return 1;
+ }
+
+ if (elp_debug >= 3)
+ printk("%s: packet of length %d sent\n", dev->name, (int)skb->len);
+
+
+ /*
+ * start the transmit timeout
+ */
+ dev->trans_start = jiffies;
+
+ /*
+ * the transmitter is now busy
+ */
+ dev->tbusy = 1;
+
+ /*
+ * free the buffer
+ */
+ dev_kfree_skb(skb, FREE_WRITE);
+
+ return 0;
}
/******************************************************
@@ -1126,38 +1064,37 @@ static int elp_start_xmit(struct sk_buff *skb, struct device *dev)
*
******************************************************/
-static struct enet_statistics * elp_get_stats(struct device *dev)
-
+static struct enet_statistics *
+elp_get_stats (struct device *dev)
{
- elp_device *adapter = (elp_device *) dev->priv;
-
- if (elp_debug >= 3)
- printk("%s: request for stats\n", dev->name);
-
- /* If the device is closed, just return the latest stats we have,
- - we cannot ask from the adapter without interrupts */
- if (!dev->start)
- return &adapter->stats;
-
- /* send a get statistics command to the board */
- adapter->tx_pcb.command = CMD_NETWORK_STATISTICS;
- adapter->tx_pcb.length = 0;
- adapter->got[CMD_NETWORK_STATISTICS] = 0;
- if (!send_pcb(adapter, &adapter->tx_pcb))
- printk("%s: couldn't send get statistics command\n", dev->name);
- else
- {
- int timeout = jiffies + TIMEOUT;
- while (adapter->got[CMD_NETWORK_STATISTICS] == 0 && jiffies < timeout)
- ;
- if (jiffies >= timeout) {
- TIMEOUT_MSG();
- return &adapter->stats;
- }
- }
-
- /* statistics are now up to date */
- return &adapter->stats;
+ elp_device *adapter = (elp_device *) dev->priv;
+
+ if (elp_debug >= 3)
+ printk("%s: request for stats\n", dev->name);
+
+ /* If the device is closed, just return the latest stats we have,
+ - we cannot ask from the adapter without interrupts */
+ if (!dev->start)
+ return &adapter->stats;
+
+ /* send a get statistics command to the board */
+ adapter->tx_pcb.command = CMD_NETWORK_STATISTICS;
+ adapter->tx_pcb.length = 0;
+ adapter->got[CMD_NETWORK_STATISTICS] = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb))
+ printk("%s: couldn't send get statistics command\n", dev->name);
+ else {
+ int timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_NETWORK_STATISTICS] == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout) {
+ TIMEOUT_MSG(__LINE__);
+ return &adapter->stats;
+ }
+ }
+
+ /* statistics are now up to date */
+ return &adapter->stats;
}
/******************************************************
@@ -1166,49 +1103,50 @@ static struct enet_statistics * elp_get_stats(struct device *dev)
*
******************************************************/
-static int elp_close (struct device *dev)
-
+static int
+elp_close (struct device *dev)
{
- elp_device * adapter = (elp_device *) dev->priv;
-
- CHECK_NULL(dev);
- CHECK_NULL(adapter);
-
- if (elp_debug >= 3)
- printk("%s: request to close device\n", dev->name);
-
- /* Someone may request the device statistic information even when
- * the interface is closed. The following will update the statistics
- * structure in the driver, so we'll be able to give current statistics.
- */
- (void) elp_get_stats(dev);
-
- /*
- * disable interrupts on the board
- */
- OUTB(0x00, adapter->io_addr+PORT_CONTROL);
-
- /*
- * flag transmitter as busy (i.e. not available)
- */
- dev->tbusy = 1;
-
- /*
- * indicate device is closed
- */
- dev->start = 0;
-
- /*
- * release the IRQ
- */
- free_irq(dev->irq);
-
- /*
- * and we no longer have to map irq to dev either
- */
- irq2dev_map[dev->irq] = 0;
-
- return 0;
+ elp_device * adapter;
+
+ CHECK_NULL(dev);
+ adapter = dev->priv;
+ CHECK_NULL(adapter);
+
+ if (elp_debug >= 3)
+ printk("%s: request to close device\n", dev->name);
+
+ /* Someone may request the device statistic information even when
+ * the interface is closed. The following will update the statistics
+ * structure in the driver, so we'll be able to give current statistics.
+ */
+ (void) elp_get_stats(dev);
+
+ /*
+ * disable interrupts on the board
+ */
+ outb_control(0x00, dev->base_addr);
+
+ /*
+ * flag transmitter as busy (i.e. not available)
+ */
+ dev->tbusy = 1;
+
+ /*
+ * indicate device is closed
+ */
+ dev->start = 0;
+
+ /*
+ * release the IRQ
+ */
+ free_irq(dev->irq);
+
+ /*
+ * and we no longer have to map irq to dev either
+ */
+ irq2dev_map[dev->irq] = 0;
+
+ return 0;
}
@@ -1221,56 +1159,57 @@ static int elp_close (struct device *dev)
*
************************************************************/
-static void elp_set_mc_list(struct device *dev, int num_addrs, void *addrs)
+static void
+elp_set_mc_list (struct device *dev, int num_addrs, void *addrs)
{
- elp_device *adapter = (elp_device *) dev->priv;
- int i;
-
- if (elp_debug >= 3)
- printk("%s: request to set multicast list\n", dev->name);
-
- if (num_addrs != -1) {
- /* send a "load multicast list" command to the board, max 10 addrs/cmd */
- /* if num_addrs==0 the list will be cleared */
- adapter->tx_pcb.command = CMD_LOAD_MULTICAST_LIST;
- adapter->tx_pcb.length = 6*num_addrs;
- for (i=0;i<num_addrs;i++)
- memcpy(adapter->tx_pcb.data.multicast[i], addrs+6*i,6);
- adapter->got[CMD_LOAD_MULTICAST_LIST] = 0;
- if (!send_pcb(adapter, &adapter->tx_pcb))
- printk("%s: couldn't send set_multicast command\n", dev->name);
- else {
- int timeout = jiffies + TIMEOUT;
- while (adapter->got[CMD_LOAD_MULTICAST_LIST] == 0 && jiffies < timeout)
- ;
- if (jiffies >= timeout) {
- TIMEOUT_MSG();
- }
- }
- if (num_addrs)
- adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD | RECV_MULTI;
- else /* num_addrs == 0 */
- adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD;
- } else /* num_addrs == -1 */
- adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_ALL;
- /*
- * configure adapter to receive messages (as specified above)
- * and wait for response
- */
- if (elp_debug >= 3)
- printk("%s: sending 82586 configure command\n", dev->name);
- adapter->tx_pcb.command = CMD_CONFIGURE_82586;
- adapter->tx_pcb.length = 2;
- adapter->got[CMD_CONFIGURE_82586] = 0;
- if (!send_pcb(adapter, &adapter->tx_pcb))
- printk("%s: couldn't send 82586 configure command\n", dev->name);
- else {
- int timeout = jiffies + TIMEOUT;
- while (adapter->got[CMD_CONFIGURE_82586] == 0 && jiffies < timeout)
- ;
- if (jiffies >= timeout)
- TIMEOUT_MSG();
- }
+ elp_device *adapter = (elp_device *) dev->priv;
+ int i;
+
+ if (elp_debug >= 3)
+ printk("%s: request to set multicast list\n", dev->name);
+
+ if (num_addrs != -1) {
+ /* send a "load multicast list" command to the board, max 10 addrs/cmd */
+ /* if num_addrs==0 the list will be cleared */
+ adapter->tx_pcb.command = CMD_LOAD_MULTICAST_LIST;
+ adapter->tx_pcb.length = 6*num_addrs;
+ for (i=0;i<num_addrs;i++)
+ memcpy(adapter->tx_pcb.data.multicast[i], addrs+6*i,6);
+ adapter->got[CMD_LOAD_MULTICAST_LIST] = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb))
+ printk("%s: couldn't send set_multicast command\n", dev->name);
+ else {
+ int timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_LOAD_MULTICAST_LIST] == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout) {
+ TIMEOUT_MSG(__LINE__);
+ }
+ }
+ if (num_addrs)
+ adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD | RECV_MULTI;
+ else /* num_addrs == 0 */
+ adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD;
+ } else /* num_addrs == -1 */
+ adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_PROMISC;
+ /*
+ * configure adapter to receive messages (as specified above)
+ * and wait for response
+ */
+ if (elp_debug >= 3)
+ printk("%s: sending 82586 configure command\n", dev->name);
+ adapter->tx_pcb.command = CMD_CONFIGURE_82586;
+ adapter->tx_pcb.length = 2;
+ adapter->got[CMD_CONFIGURE_82586] = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb))
+ printk("%s: couldn't send 82586 configure command\n", dev->name);
+ else {
+ int timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_CONFIGURE_82586] == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout)
+ TIMEOUT_MSG(__LINE__);
+ }
}
/******************************************************
@@ -1279,91 +1218,36 @@ static void elp_set_mc_list(struct device *dev, int num_addrs, void *addrs)
*
******************************************************/
-static void elp_init(struct device *dev)
-
+static void
+elp_init (struct device *dev)
{
- elp_device * adapter;
-
- CHECK_NULL(dev);
-
- /*
- * NULL out buffer pointers
- * (kernels prior to 1.1.4 only)
- */
-#if (ELP_KERNEL_TYPE < 2)
- {
- int i;
- for (i = 0; i < DEV_NUMBUFFS; i++)
- dev->buffs[i] = NULL;
- }
-#endif
+ elp_device * adapter;
- /*
- * set ptrs to various functions
- */
- dev->open = elp_open; /* local */
- dev->stop = elp_close; /* local */
- dev->get_stats = elp_get_stats; /* local */
- dev->hard_start_xmit = elp_start_xmit; /* local */
- dev->set_multicast_list = elp_set_mc_list; /* local */
-
-#if (ELP_KERNEL_TYPE < 2)
- dev->hard_header = eth_header; /* eth.c */
- dev->add_arp = eth_add_arp; /* eth.c */
- dev->rebuild_header = eth_rebuild_header; /* eth.c */
- dev->type_trans = eth_type_trans; /* eth.c */
- dev->queue_xmit = dev_queue_xmit; /* dev.c */
-#else
- /* Setup the generic properties */
- ether_setup(dev);
-#endif
+ CHECK_NULL(dev);
- /*
- * setup ptr to adapter specific information
- */
- adapter = (elp_device *)(dev->priv = kmalloc(sizeof(elp_device), GFP_KERNEL));
- CHECK_NULL(adapter);
- adapter->io_addr = dev->base_addr;
- adapter->name = dev->name;
- memset(&(adapter->stats), 0, sizeof(struct enet_statistics));
-
-
- /*
- * Ethernet information
- * (for kernels prior to 1.1.4 only)
- */
-#if (ELP_KERNEL_TYPE < 2)
- dev->type = ARPHRD_ETHER;
- dev->hard_header_len = ETH_HLEN;
- dev->mtu = 1500; /* eth_mtu */
- dev->addr_len = ETH_ALEN;
- {
- int i;
- for (i = 0; i < dev->addr_len; i++)
- dev->broadcast[i] = 0xff;
- }
-
- /*
- * New-style flags.
- */
- dev->flags = IFF_BROADCAST;
- dev->family = AF_INET;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = sizeof(unsigned long);
-#endif
+ /*
+ * set ptrs to various functions
+ */
+ dev->open = elp_open; /* local */
+ dev->stop = elp_close; /* local */
+ dev->get_stats = elp_get_stats; /* local */
+ dev->hard_start_xmit = elp_start_xmit; /* local */
+ dev->set_multicast_list = elp_set_mc_list; /* local */
- /*
- * memory information
- */
- dev->mem_start = dev->mem_end = dev->rmem_end = dev->mem_start = 0;
+ /* Setup the generic properties */
+ ether_setup(dev);
-#if ELP_NEED_HARD_RESET
- adapter_hard_reset(adapter);
-#else
- adapter_reset(adapter);
-#endif
+ /*
+ * setup ptr to adapter specific information
+ */
+ adapter = (elp_device *)(dev->priv = kmalloc(sizeof(elp_device), GFP_KERNEL));
+ CHECK_NULL(adapter);
+ memset(&(adapter->stats), 0, sizeof(struct enet_statistics));
+
+ /*
+ * memory information
+ */
+ dev->mem_start = dev->mem_end = dev->rmem_end = dev->rmem_start = 0;
}
/************************************************************
@@ -1372,45 +1256,68 @@ static void elp_init(struct device *dev)
* Called only by elp_autodetect
************************************************************/
-static int elp_sense(int addr)
+static int
+elp_sense (struct device * dev)
{
- int timeout;
- byte orig_HCR=INB(addr+PORT_CONTROL),
- orig_HSR=INB(addr+PORT_STATUS);
-
- if (((orig_HCR==0xff) && (orig_HSR==0xff)) ||
- ( (orig_HCR & CONTROL_DIR) != (orig_HSR & STATUS_DIR) ) )
- return 1; /* It can't be 3c505 if HCR.DIR != HSR.DIR */
-
- /* Wait for a while; the adapter may still be booting up */
- if (elp_debug > 0)
- printk(stilllooking_msg);
- for (timeout = jiffies + (100 * 15); jiffies <= timeout; )
- if ((INB(addr+PORT_STATUS) & ASF_PCB_MASK) != ASF_PCB_END)
- break;
-
- if (orig_HCR & CONTROL_DIR) {
- /* If HCR.DIR is up, we pull it down. HSR.DIR should follow. */
- OUTB(orig_HCR & ~CONTROL_DIR,addr+PORT_CONTROL);
- timeout = jiffies+30;
- while (jiffies < timeout)
- ;
- if (INB(addr+PORT_STATUS) & STATUS_DIR) {
- OUTB(orig_HCR,addr+PORT_CONTROL);
- return 2;
- }
- } else {
- /* If HCR.DIR is down, we pull it up. HSR.DIR should follow. */
- OUTB(orig_HCR | CONTROL_DIR,addr+PORT_CONTROL);
- timeout = jiffies+300;
- while (jiffies < timeout)
- ;
- if (!(INB(addr+PORT_STATUS) & STATUS_DIR)) {
- OUTB(orig_HCR,addr+PORT_CONTROL);
- return 3;
- }
- }
- return 0; /* It certainly looks like a 3c505. */
+ int timeout;
+ int addr=dev->base_addr;
+ char *name=dev->name;
+
+ byte orig_HCR=inb_control(addr),
+ orig_HSR=inb_status(addr);
+
+ if (elp_debug > 0)
+ printk(search_msg, name, addr);
+
+ if (((orig_HCR==0xff) && (orig_HSR==0xff)) ||
+ ((orig_HCR & DIR) != (orig_HSR & DIR))) {
+ if (elp_debug > 0)
+ printk(notfound_msg, 1);
+ return -1; /* It can't be 3c505 if HCR.DIR != HSR.DIR */
+ }
+
+ /* Wait for a while; the adapter may still be booting up */
+ if (elp_debug > 0)
+ printk(stilllooking_msg);
+ for (timeout = jiffies + (100 * 15); jiffies <= timeout; )
+ if (GET_ASF(addr) != ASF_PCB_END)
+ break;
+
+ if (orig_HCR & DIR) {
+ /* If HCR.DIR is up, we pull it down. HSR.DIR should follow. */
+ outb_control(orig_HCR & ~DIR,addr);
+ timeout = jiffies+30;
+ while (jiffies < timeout)
+ ;
+ if (inb_status(addr) & DIR) {
+ outb_control(orig_HCR,addr);
+ if (elp_debug > 0)
+ printk(notfound_msg, 2);
+ return -1;
+ }
+ } else {
+ /* If HCR.DIR is down, we pull it up. HSR.DIR should follow. */
+ outb_control(orig_HCR | DIR,addr);
+ timeout = jiffies+300;
+ while (jiffies < timeout)
+ ;
+ if (!(inb_status(addr) & DIR)) {
+ outb_control(orig_HCR,addr);
+ if (elp_debug > 0)
+ printk(notfound_msg, 3);
+ return -1;
+ }
+ }
+ /*
+ * It certainly looks like a 3c505. If it has DMA enabled, it needs
+ * a hard reset. Also, do a hard reset if selected at the compile time.
+ */
+ if (elp_debug > 0)
+ printk(found_msg);
+
+ if (((orig_HCR==0x35) && (orig_HSR==0x5b)) || ELP_NEED_HARD_RESET)
+ adapter_hard_reset(dev);
+ return 0;
}
/*************************************************************
@@ -1419,37 +1326,26 @@ static int elp_sense(int addr)
* Called only by eplus_probe
*************************************************************/
-static int elp_autodetect(struct device * dev)
+static int
+elp_autodetect (struct device * dev)
{
- int idx=0, addr;
-
- /* if base address set, then only check that address
- otherwise, run through the table */
- if ( (addr=dev->base_addr) ) { /* dev->base_addr == 0 ==> plain autodetect */
- if (elp_debug > 0)
- printk(search_msg, dev->name, addr);
- if (elp_sense(addr) == 0)
- {
- if (elp_debug > 0)
- printk(found_msg);
- return addr;
- } else if (elp_debug > 0)
- printk(notfound_msg);
- } else while ( (addr=addr_list[idx++]) ) {
- if (elp_debug > 0)
- printk(search_msg, dev->name, addr);
- if (elp_sense(addr) == 0) {
- if (elp_debug > 0)
- printk(found_msg);
- return addr;
- } else if (elp_debug > 0)
- printk(notfound_msg);
- }
-
- /* could not find an adapter */
- if (elp_debug == 0)
- printk(couldnot_msg, dev->name);
- return 0; /* Because of this, the layer above will return -ENODEV */
+ int idx=0;
+
+ /* if base address set, then only check that address
+ otherwise, run through the table */
+ if (dev->base_addr != 0) { /* dev->base_addr == 0 ==> plain autodetect */
+ if (elp_sense(dev) == 0)
+ return dev->base_addr;
+ } else while ( (dev->base_addr=addr_list[idx++]) ) {
+ if (elp_sense(dev) == 0)
+ return dev->base_addr;
+ }
+
+ /* could not find an adapter */
+ if (elp_debug > 0)
+ printk(couldnot_msg, dev->name);
+
+ return 0; /* Because of this, the layer above will return -ENODEV */
}
/******************************************************
@@ -1458,93 +1354,93 @@ static int elp_autodetect(struct device * dev)
*
******************************************************/
-int elplus_probe(struct device *dev)
-
+int
+elplus_probe (struct device *dev)
{
- elp_device adapter;
- int i;
-
- CHECK_NULL(dev);
-
- /*
- * setup adapter structure
- */
-
- adapter.io_addr = dev->base_addr = elp_autodetect(dev);
- if ( !adapter.io_addr )
- return -ENODEV;
-
- /*
- * As we enter here from bootup, the adapter should have IRQs enabled,
- * but we can as well enable them anyway.
- */
- OUTB(INB(dev->base_addr+PORT_CONTROL) | CONTROL_CMDE,
- dev->base_addr+PORT_CONTROL);
- autoirq_setup(0);
-
- /*
- * use ethernet address command to probe for board in polled mode
- * (this also makes us the IRQ that we need for automatic detection)
- */
- adapter.tx_pcb.command = CMD_STATION_ADDRESS;
- adapter.tx_pcb.length = 0;
- if (!send_pcb (&adapter, &adapter.tx_pcb) ||
- !receive_pcb(&adapter, &adapter.rx_pcb) ||
- (adapter.rx_pcb.command != CMD_ADDRESS_RESPONSE) ||
- (adapter.rx_pcb.length != 6)) {
- printk("%s: not responding to first PCB\n", dev->name);
- return -ENODEV;
- }
- if (dev->irq) { /* Is there a preset IRQ? */
- if (dev->irq != autoirq_report(0)) {
- printk("%s: Detected IRQ doesn't match user-defined one.\n",dev->name);
- return -ENODEV;
- }
- /* if dev->irq == autoirq_report(0), all is well */
- } else /* No preset IRQ; just use what we can detect */
- dev->irq=autoirq_report(0);
- switch (dev->irq) { /* Legal, sane? */
- case 0: printk("%s: No IRQ reported by autoirq_report().\n",dev->name);
- printk("%s: Check the jumpers of your 3c505 board.\n",dev->name);
- return -ENODEV;
- case 1:
- case 6:
- case 8:
- case 13:
- printk("%s: Impossible IRQ %d reported by autoirq_report().\n",
- dev->name,
- dev->irq);
- return -ENODEV;
- }
- /*
- * Now we have the IRQ number so we can disable the interrupts from
- * the board until the board is opened.
- */
- OUTB(INB(dev->base_addr+PORT_CONTROL) & ~CONTROL_CMDE,
- dev->base_addr+PORT_CONTROL);
-
- /*
- * copy ethernet address into structure
- */
- for (i = 0; i < 6; i++)
- dev->dev_addr[i] = adapter.rx_pcb.data.eth_addr[i];
-
- /*
- * print remainder of startup message
- */
-#if (ELP_KERNEL_TYPE < 2)
- printk("%s: 3c505 card found at I/O 0x%x using IRQ%d has address %s\n",
- dev->name, dev->base_addr, dev->irq, eth_print(dev->dev_addr));
-#else
- printk("%s: 3c505 card found at I/O 0x%x using IRQ%d has address %02x:%02x:%02x:%02x:%02x:%02x\n",
- dev->name, dev->base_addr, dev->irq,
- dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
- dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
-#endif
-
- /*
- * initialise the device
- */
- elp_init(dev);
- return 0;
+ elp_device adapter;
+ int i;
+
+ CHECK_NULL(dev);
+
+ /*
+ * setup adapter structure
+ */
+
+ dev->base_addr = elp_autodetect(dev);
+ if ( !(dev->base_addr) )
+ return -ENODEV;
+
+ /*
+ * As we enter here from bootup, the adapter should have IRQs enabled,
+ * but we can as well enable them anyway.
+ */
+ outb_control(inb_control(dev->base_addr) | CMDE, dev->base_addr);
+ autoirq_setup(0);
+
+ /*
+ * use ethernet address command to probe for board in polled mode
+ * (this also makes us the IRQ that we need for automatic detection)
+ */
+ adapter.tx_pcb.command = CMD_STATION_ADDRESS;
+ adapter.tx_pcb.length = 0;
+ if (!send_pcb (dev, &adapter.tx_pcb) ||
+ !receive_pcb(dev, &adapter.rx_pcb) ||
+ (adapter.rx_pcb.command != CMD_ADDRESS_RESPONSE) ||
+ (adapter.rx_pcb.length != 6)) {
+ printk("%s: not responding to first PCB\n", dev->name);
+ return -ENODEV;
+ }
+
+ if (dev->irq) { /* Is there a preset IRQ? */
+ if (dev->irq != autoirq_report(0)) {
+ printk("%s: Detected IRQ doesn't match user-defined one.\n",dev->name);
+ return -ENODEV;
+ }
+ /* if dev->irq == autoirq_report(0), all is well */
+ } else /* No preset IRQ; just use what we can detect */
+ dev->irq=autoirq_report(0);
+ switch (dev->irq) { /* Legal, sane? */
+ case 0:
+ printk("%s: No IRQ reported by autoirq_report().\n",dev->name);
+ printk("%s: Check the jumpers of your 3c505 board.\n",dev->name);
+ return -ENODEV;
+ case 1:
+ case 6:
+ case 8:
+ case 13:
+ printk("%s: Impossible IRQ %d reported by autoirq_report().\n",
+ dev->name, dev->irq);
+ return -ENODEV;
+ }
+ /*
+ * Now we have the IRQ number so we can disable the interrupts from
+ * the board until the board is opened.
+ */
+ outb_control(inb_control(dev->base_addr) & ~CMDE, dev->base_addr);
+
+ /*
+ * copy ethernet address into structure
+ */
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = adapter.rx_pcb.data.eth_addr[i];
+
+ /*
+ * print remainder of startup message
+ */
+ printk("%s: 3c505 card found at I/O %#lx using IRQ%d"
+ " has address %02x:%02x:%02x:%02x:%02x:%02x\n",
+ dev->name, dev->base_addr, dev->irq,
+ dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+ dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+
+ /*
+ * and reserve the address region
+ */
+ request_region(dev->base_addr,16,"3c505");
+
+ /*
+ * initialise the device
+ */
+ elp_init(dev);
+ return 0;
}
diff --git a/drivers/net/3c505.h b/drivers/net/3c505.h
index ecb5f902a..17b0b2fd6 100644
--- a/drivers/net/3c505.h
+++ b/drivers/net/3c505.h
@@ -7,56 +7,56 @@
/*
* I/O register offsets
*/
-#define PORT_COMMAND 0x00 /* read/write */
-#define PORT_STATUS 0x02 /* read only */
-#define PORT_AUXDMA 0x02 /* write only */
-#define PORT_DATA 0x04 /* read/write */
-#define PORT_CONTROL 0x06 /* read/write */
+#define PORT_COMMAND 0x00 /* read/write, 8-bit */
+#define PORT_STATUS 0x02 /* read only, 8-bit */
+#define PORT_AUXDMA 0x02 /* write only, 8-bit */
+#define PORT_DATA 0x04 /* read/write, 16-bit */
+#define PORT_CONTROL 0x06 /* read/write, 8-bit */
/*
* host control registers bits
*/
-#define CONTROL_ATTN 0x80 /* attention */
-#define CONTROL_FLSH 0x40 /* flush data register */
-#define CONTROL_DMAE 0x20 /* DMA enable */
-#define CONTROL_DIR 0x10 /* direction */
-#define CONTROL_TCEN 0x08 /* terminal count interrupt enable */
-#define CONTROL_CMDE 0x04 /* command register interrupt enable */
-#define CONTROL_HSF2 0x02 /* host status flag 2 */
-#define CONTROL_HSF1 0x01 /* host status flag 1 */
+#define ATTN 0x80 /* attention */
+#define FLSH 0x40 /* flush data register */
+#define DMAE 0x20 /* DMA enable */
+#define DIR 0x10 /* direction */
+#define TCEN 0x08 /* terminal count interrupt enable */
+#define CMDE 0x04 /* command register interrupt enable */
+#define HSF2 0x02 /* host status flag 2 */
+#define HSF1 0x01 /* host status flag 1 */
/*
* combinations of HSF flags used for PCB transmission
*/
-#define HSF_PCB_ACK (CONTROL_HSF1)
-#define HSF_PCB_NAK (CONTROL_HSF2)
-#define HSF_PCB_END (CONTROL_HSF2|CONTROL_HSF1)
-#define HSF_PCB_MASK (CONTROL_HSF2|CONTROL_HSF1)
+#define HSF_PCB_ACK HSF1
+#define HSF_PCB_NAK HSF2
+#define HSF_PCB_END (HSF2|HSF1)
+#define HSF_PCB_MASK (HSF2|HSF1)
/*
* host status register bits
*/
-#define STATUS_HRDY 0x80 /* data register ready */
-#define STATUS_HCRE 0x40 /* command register empty */
-#define STATUS_ACRF 0x20 /* adapter command register full */
-#define STATUS_DIR 0x10 /* direction */
-#define STATUS_DONE 0x08 /* DMA done */
-#define STATUS_ASF3 0x04 /* adapter status flag 3 */
-#define STATUS_ASF2 0x02 /* adapter status flag 2 */
-#define STATUS_ASF1 0x01 /* adapter status flag 1 */
+#define HRDY 0x80 /* data register ready */
+#define HCRE 0x40 /* command register empty */
+#define ACRF 0x20 /* adapter command register full */
+/* #define DIR 0x10 direction - same as in control register */
+#define DONE 0x08 /* DMA done */
+#define ASF3 0x04 /* adapter status flag 3 */
+#define ASF2 0x02 /* adapter status flag 2 */
+#define ASF1 0x01 /* adapter status flag 1 */
/*
* combinations of ASF flags used for PCB reception
*/
-#define ASF_PCB_ACK (STATUS_ASF1)
-#define ASF_PCB_NAK (STATUS_ASF2)
-#define ASF_PCB_END (STATUS_ASF2|STATUS_ASF1)
-#define ASF_PCB_MASK (STATUS_ASF2|STATUS_ASF1)
+#define ASF_PCB_ACK ASF1
+#define ASF_PCB_NAK ASF2
+#define ASF_PCB_END (ASF2|ASF1)
+#define ASF_PCB_MASK (ASF2|ASF1)
/*
* host aux DMA register bits
*/
-#define AUXDMA_BRST 0x01 /* DMA burst */
+#define DMA_BRST 0x01 /* DMA burst */
/*
* maximum amount of data data allowed in a PCB
@@ -123,3 +123,121 @@ enum {
CMD_ADAPTER_INFO_RESPONSE = 0x41
};
+/* Definitions for the PCB data structure */
+
+/* Data units */
+typedef unsigned char byte;
+typedef unsigned short int word;
+typedef unsigned long int dword;
+
+/* Data structures */
+struct Memconf {
+ word cmd_q,
+ rcv_q,
+ mcast,
+ frame,
+ rcv_b,
+ progs;
+};
+
+struct Rcv_pkt {
+ word buf_ofs,
+ buf_seg,
+ buf_len,
+ timeout;
+};
+
+struct Xmit_pkt {
+ word buf_ofs,
+ buf_seg,
+ pkt_len;
+};
+
+struct Rcv_resp {
+ word buf_ofs,
+ buf_seg,
+ buf_len,
+ pkt_len,
+ timeout,
+ status;
+ dword timetag;
+};
+
+struct Xmit_resp {
+ word buf_ofs,
+ buf_seg,
+ c_stat,
+ status;
+};
+
+
+struct Netstat {
+ dword tot_recv,
+ tot_xmit;
+ word err_CRC,
+ err_align,
+ err_res,
+ err_ovrrun;
+};
+
+
+struct Selftest {
+ word error;
+ union {
+ word ROM_cksum;
+ struct {
+ word ofs, seg;
+ } RAM;
+ word i82586;
+ } failure;
+};
+
+struct Info {
+ byte minor_vers,
+ major_vers;
+ word ROM_cksum,
+ RAM_sz,
+ free_ofs,
+ free_seg;
+};
+
+struct Memdump {
+ word size,
+ off,
+ seg;
+};
+
+/*
+Primary Command Block. The most important data structure. All communication
+between the host and the adapter is done with these. (Except for the actual
+ethernet data, which has different packaging.)
+*/
+typedef struct {
+ byte command;
+ byte length;
+ union {
+ struct Memconf memconf;
+ word configure;
+ struct Rcv_pkt rcv_pkt;
+ struct Xmit_pkt xmit_pkt;
+ byte multicast[10][6];
+ byte eth_addr[6];
+ byte failed;
+ struct Rcv_resp rcv_resp;
+ struct Xmit_resp xmit_resp;
+ struct Netstat netstat;
+ struct Selftest selftest;
+ struct Info info;
+ struct Memdump memdump;
+ byte raw[62];
+ } data;
+} pcb_struct;
+
+/* These defines for 'configure' */
+#define RECV_STATION 0x00
+#define RECV_BROAD 0x01
+#define RECV_MULTI 0x02
+#define RECV_PROMISC 0x04
+#define NO_LOOPBACK 0x00
+#define INT_LOOPBACK 0x08
+#define EXT_LOOPBACK 0x10
diff --git a/drivers/net/3c505dta.h b/drivers/net/3c505dta.h
deleted file mode 100644
index 5c7004fa5..000000000
--- a/drivers/net/3c505dta.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/* This header file defines some data structures used by the 3c505 driver */
-
-/* Data units */
-typedef unsigned char byte;
-typedef unsigned short int word;
-typedef unsigned long int dword;
-
-
-/* Data structures */
-struct Memconf {
- word cmd_q,
- rcv_q,
- mcast,
- frame,
- rcv_b,
- progs;
-};
-
-struct Rcv_pkt {
- word buf_ofs,
- buf_seg,
- buf_len,
- timeout;
-};
-
-struct Xmit_pkt {
- word buf_ofs,
- buf_seg,
- pkt_len;
-};
-
-struct Rcv_resp {
- word buf_ofs,
- buf_seg,
- buf_len,
- pkt_len,
- timeout,
- status;
- dword timetag;
-};
-
-struct Xmit_resp {
- word buf_ofs,
- buf_seg,
- c_stat,
- status;
-};
-
-
-struct Netstat {
- dword tot_recv,
- tot_xmit;
- word err_CRC,
- err_align,
- err_res,
- err_ovrrun;
-};
-
-
-struct Selftest {
- word error;
- union {
- word ROM_cksum;
- struct {
- word ofs, seg;
- } RAM;
- word i82586;
- } failure;
-};
-
-struct Info {
- byte minor_vers,
- major_vers;
- word ROM_cksum,
- RAM_sz,
- free_ofs,
- free_seg;
-};
-
-struct Memdump {
- word size,
- off,
- seg;
-};
-
-/*
-Primary Command Block. The most important data structure. All communication
-between the host and the adapter is done with these. (Except for the ethernet
-data, which has different packaging.)
-*/
-typedef struct {
- byte command;
- byte length;
- union {
- struct Memconf memconf;
- word configure;
- struct Rcv_pkt rcv_pkt;
- struct Xmit_pkt xmit_pkt;
- byte multicast[10][6];
- byte eth_addr[6];
- byte failed;
- struct Rcv_resp rcv_resp;
- struct Xmit_resp xmit_resp;
- struct Netstat netstat;
- struct Selftest selftest;
- struct Info info;
- struct Memdump memdump;
- byte raw[62];
- } data;
-} pcb_struct;
-
-/* These defines for 'configure' */
-#define RECV_STATION 0x00
-#define RECV_BROAD 0x01
-#define RECV_MULTI 0x02
-#define RECV_ALL 0x04
-#define NO_LOOPBACK 0x00
-#define INT_LOOPBACK 0x08
-#define EXT_LOOPBACK 0x10
diff --git a/drivers/net/3c507.c b/drivers/net/3c507.c
index 5cc261c51..944a3401b 100644
--- a/drivers/net/3c507.c
+++ b/drivers/net/3c507.c
@@ -282,7 +282,7 @@ extern int el16_probe(struct device *dev); /* Called from Space.c */
static int el16_probe1(struct device *dev, int ioaddr);
static int el16_open(struct device *dev);
static int el16_send_packet(struct sk_buff *skb, struct device *dev);
-static void el16_interrupt(int reg_ptr);
+static void el16_interrupt(int irq, struct pt_regs *regs);
static void el16_rx(struct device *dev);
static int el16_close(struct device *dev);
static struct enet_statistics *el16_get_stats(struct device *dev);
@@ -370,7 +370,7 @@ int el16_probe1(struct device *dev, int ioaddr)
}
/* We've committed to using the board, and can start filling in *dev. */
- snarf_region(ioaddr, EL16_IO_EXTENT);
+ request_region(ioaddr, EL16_IO_EXTENT,"3c507");
dev->base_addr = ioaddr;
outb(0x01, ioaddr + MISC_CTRL);
@@ -509,9 +509,8 @@ el16_send_packet(struct sk_buff *skb, struct device *dev)
/* The typical workload of the driver:
Handle the network interface interrupts. */
static void
-el16_interrupt(int reg_ptr)
+el16_interrupt(int irq, struct pt_regs *regs)
{
- int irq = pt_regs2irq(reg_ptr);
struct device *dev = (struct device *)(irq2dev_map[irq]);
struct net_local *lp;
int ioaddr, status, boguscount = 0;
@@ -848,6 +847,7 @@ el16_rx(struct device *dev)
/* 'skb->data' points to the start of sk_buff data area. */
memcpy(skb->data, data_frame + 5, pkt_len);
+ skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
lp->stats.rx_packets++;
}
diff --git a/drivers/net/3c509.c b/drivers/net/3c509.c
index 395a096c6..ffe65f3e9 100644
--- a/drivers/net/3c509.c
+++ b/drivers/net/3c509.c
@@ -27,6 +27,11 @@
static char *version = "3c509.c:1.03 10/8/94 becker@cesdis.gsfc.nasa.gov\n";
#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
@@ -42,11 +47,6 @@ static char *version = "3c509.c:1.03 10/8/94 becker@cesdis.gsfc.nasa.gov\n";
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
-#ifdef MODULE
-#include <linux/module.h>
-#include "../../tools/version.h"
-#endif
-
#ifdef EL3_DEBUG
@@ -102,7 +102,7 @@ static ushort id_read_eeprom(int index);
static ushort read_eeprom(short ioaddr, int index);
static int el3_open(struct device *dev);
static int el3_start_xmit(struct sk_buff *skb, struct device *dev);
-static void el3_interrupt(int reg_ptr);
+static void el3_interrupt(int irq, struct pt_regs *regs);
static void update_stats(int addr, struct device *dev);
static struct enet_statistics *el3_get_stats(struct device *dev);
static int el3_rx(struct device *dev);
@@ -224,11 +224,11 @@ int el3_probe(struct device *dev)
dev->base_addr = ioaddr;
dev->irq = irq;
dev->if_port = if_port;
- snarf_region(dev->base_addr, 16);
+ request_region(dev->base_addr, 16,"3c509");
{
char *if_names[] = {"10baseT", "AUI", "undefined", "BNC"};
- printk("%s: 3c509 at %#3.3x tag %d, %s port, address ",
+ printk("%s: 3c509 at %#3.3lx tag %d, %s port, address ",
dev->name, dev->base_addr, current_tag, if_names[dev->if_port]);
}
@@ -460,10 +460,8 @@ el3_start_xmit(struct sk_buff *skb, struct device *dev)
/* The EL3 interrupt handler. */
static void
-el3_interrupt(int reg_ptr)
+el3_interrupt(int irq, struct pt_regs *regs)
{
- int irq = pt_regs2irq(reg_ptr);
-
struct device *dev = (struct device *)(irq2dev_map[irq]);
int ioaddr, status;
int i = 0;
@@ -605,6 +603,7 @@ el3_rx(struct device *dev)
insl(ioaddr+RX_FIFO, skb->data,
(pkt_len + 3) >> 2);
+ skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
lp->stats.rx_packets++;
@@ -634,8 +633,13 @@ static void
set_multicast_list(struct device *dev, int num_addrs, void *addrs)
{
short ioaddr = dev->base_addr;
- if (el3_debug > 1)
- printk("%s: Setting Rx mode to %d addresses.\n", dev->name, num_addrs);
+ if (el3_debug > 1) {
+ static int old = 0;
+ if (old != num_addrs) {
+ old = num_addrs;
+ printk("%s: Setting Rx mode to %d addresses.\n", dev->name, num_addrs);
+ }
+ }
if (num_addrs > 0) {
outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
} else if (num_addrs < 0) {
diff --git a/drivers/net/8390.c b/drivers/net/8390.c
index ebed123a7..161e20a20 100644
--- a/drivers/net/8390.c
+++ b/drivers/net/8390.c
@@ -15,11 +15,15 @@
This is the chip-specific code for many 8390-based ethernet adaptors.
This is not a complete driver, it must be combined with board-specific
code such as ne.c, wd.c, 3c503.c, etc.
+
+ 13/04/95 -- Don't blindly swallow ENISR_RDC interrupts for non-shared
+ memory cards. We need to follow these closely for neX000 cards.
+ Plus other minor cleanups. -- Paul Gortmaker
+
*/
static char *version =
"8390.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
-#include <linux/config.h>
/*
Braindamage remaining:
@@ -30,7 +34,11 @@ static char *version =
The National Semiconductor LAN Databook, and the 3Com 3c503 databook.
*/
-#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
@@ -52,11 +60,6 @@ static char *version =
#include "8390.h"
-#ifdef MODULE
-#include <linux/module.h>
-#include "../../tools/version.h"
-#endif
-
/* These are the operational function interfaces to board-specific
routines.
void reset_8390(struct device *dev)
@@ -85,7 +88,7 @@ int ei_debug = 1;
#endif
/* Max number of packets received at one Intr.
- Current this may only be examined by a kernel debugger. */
+ Currently this may only be examined by a kernel debugger. */
static int high_water_mark = 0;
/* Index to functions. */
@@ -126,19 +129,25 @@ static int ei_start_xmit(struct sk_buff *skb, struct device *dev)
int e8390_base = dev->base_addr;
struct ei_device *ei_local = (struct ei_device *) dev->priv;
int length, send_length;
+ unsigned long flags;
- /* We normally shouldn't be called if dev->tbusy is set, but the
- existing code does anyway.
- If it has been too long (> 100 or 150ms.) since the last Tx we assume
- the board has died and kick it. */
-
+/*
+ * We normally shouldn't be called if dev->tbusy is set, but the
+ * existing code does anyway. If it has been too long since the
+ * last Tx, we assume the board has died and kick it.
+ */
+
if (dev->tbusy) { /* Do timeouts, just like the 8003 driver. */
int txsr = inb(e8390_base+EN0_TSR), isr;
int tickssofar = jiffies - dev->trans_start;
- if (tickssofar < 10 || (tickssofar < 15 && ! (txsr & ENTSR_PTX))) {
+ if (tickssofar < TX_TIMEOUT || (tickssofar < (TX_TIMEOUT+5) && ! (txsr & ENTSR_PTX))) {
return 1;
}
isr = inb(e8390_base+EN0_ISR);
+ if (dev->start == 0) {
+ printk("%s: xmit on stopped card\n", dev->name);
+ return 1;
+ }
printk(KERN_DEBUG "%s: transmit timed out, TX status %#2x, ISR %#2x.\n",
dev->name, txsr, isr);
/* Does the 8390 thinks it has posted an interrupt? */
@@ -168,15 +177,22 @@ static int ei_start_xmit(struct sk_buff *skb, struct device *dev)
if (skb->len <= 0)
return 0;
- /* Block a timer-based transmit from overlapping. */
- if (set_bit(0, (void*)&dev->tbusy) != 0) {
- printk("%s: Transmitter access conflict.\n", dev->name);
- return 1;
- }
+ save_flags(flags);
+ cli();
+
+ /* Block a timer-based transmit from overlapping. */
+ if ((set_bit(0, (void*)&dev->tbusy) != 0) || ei_local->irqlock) {
+ printk("%s: Tx access conflict. irq=%d lock=%d tx1=%d tx2=%d last=%d\n",
+ dev->name, dev->interrupt, ei_local->irqlock, ei_local->tx1,
+ ei_local->tx2, ei_local->lasttx);
+ restore_flags(flags);
+ return 1;
+ }
/* Mask interrupts from the ethercard. */
- outb(0x00, e8390_base + EN0_IMR);
+ outb(0x00, e8390_base + EN0_IMR);
ei_local->irqlock = 1;
+ restore_flags(flags);
send_length = ETH_ZLEN < length ? length : ETH_ZLEN;
@@ -198,28 +214,30 @@ static int ei_start_xmit(struct sk_buff *skb, struct device *dev)
ei_local->txing);
} else { /* We should never get here. */
if (ei_debug)
- printk("%s: No packet buffer space for ping-pong use.\n",
- dev->name);
+ printk("%s: No Tx buffers free. irq=%d tx1=%d tx2=%d last=%d\n",
+ dev->name, dev->interrupt, ei_local->tx1,
+ ei_local->tx2, ei_local->lasttx);
ei_local->irqlock = 0;
dev->tbusy = 1;
- outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ outb_p(ENISR_ALL, e8390_base + EN0_IMR);
return 1;
}
ei_block_output(dev, length, skb->data, output_page);
if (! ei_local->txing) {
+ ei_local->txing = 1;
NS8390_trigger_send(dev, send_length, output_page);
dev->trans_start = jiffies;
if (output_page == ei_local->tx_start_page)
ei_local->tx1 = -1, ei_local->lasttx = -1;
else
ei_local->tx2 = -1, ei_local->lasttx = -2;
- ei_local->txing = 1;
} else
ei_local->txqueue++;
dev->tbusy = (ei_local->tx1 && ei_local->tx2);
} else { /* No pingpong, just a single Tx buffer. */
ei_block_output(dev, length, skb->data, ei_local->tx_start_page);
+ ei_local->txing = 1;
NS8390_trigger_send(dev, send_length, ei_local->tx_start_page);
dev->trans_start = jiffies;
dev->tbusy = 1;
@@ -236,12 +254,11 @@ static int ei_start_xmit(struct sk_buff *skb, struct device *dev)
/* The typical workload of the driver:
Handle the ether interface interrupts. */
-void ei_interrupt(int reg_ptr)
+void ei_interrupt(int irq, struct pt_regs * regs)
{
- int irq = pt_regs2irq(reg_ptr);
struct device *dev = (struct device *)(irq2dev_map[irq]);
int e8390_base;
- int interrupts, boguscount = 0;
+ int interrupts, nr_serviced = 0;
struct ei_device *ei_local;
if (dev == NULL) {
@@ -252,7 +269,6 @@ void ei_interrupt(int reg_ptr)
ei_local = (struct ei_device *) dev->priv;
if (dev->interrupt || ei_local->irqlock) {
/* The "irqlock" check is only for testing. */
- sti();
printk(ei_local->irqlock
? "%s: Interrupted while interrupts are masked! isr=%#2x imr=%#2x.\n"
: "%s: Reentering the interrupt handler! isr=%#2x imr=%#2x.\n",
@@ -262,7 +278,6 @@ void ei_interrupt(int reg_ptr)
}
dev->interrupt = 1;
- sti(); /* Allow other interrupts. */
/* Change to page 0 and read the intr status reg. */
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
@@ -272,10 +287,11 @@ void ei_interrupt(int reg_ptr)
/* !!Assumption!! -- we stay in page 0. Don't break this. */
while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0
- && ++boguscount < 9) {
- if (interrupts & ENISR_RDC) {
- /* Ack meaningless DMA complete. */
- outb_p(ENISR_RDC, e8390_base + EN0_ISR);
+ && ++nr_serviced < MAX_SERVICE) {
+ if (dev->start == 0) {
+ printk("%s: interrupt from stopped card\n", dev->name);
+ interrupts = 0;
+ break;
}
if (interrupts & ENISR_OVER) {
ei_rx_overrun(dev);
@@ -297,17 +313,25 @@ void ei_interrupt(int reg_ptr)
if (interrupts & ENISR_TX_ERR) {
outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */
}
+
+ if (interrupts & ENISR_RDC) {
+ if (dev->mem_start)
+ outb_p(ENISR_RDC, e8390_base + EN0_ISR);
+ }
+
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
}
- if (interrupts && ei_debug) {
- if (boguscount == 9)
+ if ((interrupts & ~ENISR_RDC) && ei_debug) {
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
+ if (nr_serviced == MAX_SERVICE) {
printk("%s: Too much work at interrupt, status %#2.2x\n",
dev->name, interrupts);
- else
+ outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */
+ } else {
printk("%s: unknown interrupt %#2x\n", dev->name, interrupts);
- outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
- outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
+ outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
+ }
}
dev->interrupt = 0;
return;
@@ -332,9 +356,9 @@ static void ei_tx_intr(struct device *dev)
ei_local->tx1 = 0;
dev->tbusy = 0;
if (ei_local->tx2 > 0) {
+ ei_local->txing = 1;
NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6);
dev->trans_start = jiffies;
- ei_local->txing = 1;
ei_local->tx2 = -1,
ei_local->lasttx = 2;
} else
@@ -346,9 +370,9 @@ static void ei_tx_intr(struct device *dev)
ei_local->tx2 = 0;
dev->tbusy = 0;
if (ei_local->tx1 > 0) {
+ ei_local->txing = 1;
NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page);
dev->trans_start = jiffies;
- ei_local->txing = 1;
ei_local->tx1 = -1;
ei_local->lasttx = 1;
} else
@@ -396,7 +420,7 @@ static void ei_receive(struct device *dev)
rxing_page = inb_p(e8390_base + EN1_CURPAG);
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
- /* Remove one frame from the ring. Boundary is alway a page behind. */
+ /* Remove one frame from the ring. Boundary is always a page behind. */
this_frame = inb_p(e8390_base + EN0_BOUNDARY) + 1;
if (this_frame >= ei_local->stop_page)
this_frame = ei_local->rx_start_page;
@@ -453,6 +477,7 @@ static void ei_receive(struct device *dev)
ei_block_input(dev, pkt_len, (char *) skb->data,
current_offset + sizeof(rx_frame));
+ skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
ei_local->stat.rx_packets++;
}
@@ -469,12 +494,12 @@ static void ei_receive(struct device *dev)
/* This _should_ never happen: it's here for avoiding bad clones. */
if (next_frame >= ei_local->stop_page) {
- printk("%s: next frame inconsistency, %#2x..", dev->name,
+ printk("%s: next frame inconsistency, %#2x\n", dev->name,
next_frame);
next_frame = ei_local->rx_start_page;
}
ei_local->current_page = next_frame;
- outb(next_frame-1, e8390_base+EN0_BOUNDARY);
+ outb_p(next_frame-1, e8390_base+EN0_BOUNDARY);
}
/* If any worth-while packets have been received, dev_rint()
has done a mark_bh(NET_BH) for us and will work on them
@@ -533,6 +558,9 @@ static struct enet_statistics *get_stats(struct device *dev)
short ioaddr = dev->base_addr;
struct ei_device *ei_local = (struct ei_device *) dev->priv;
+ /* If the card is stopped, just return the present stats. */
+ if (dev->start == 0) return &ei_local->stat;
+
/* Read the counter registers, assuming we are in page 0. */
ei_local->stat.rx_frame_errors += inb_p(ioaddr + EN0_COUNTER0);
ei_local->stat.rx_crc_errors += inb_p(ioaddr + EN0_COUNTER1);
@@ -604,6 +632,7 @@ void NS8390_init(struct device *dev, int startp)
struct ei_device *ei_local = (struct ei_device *) dev->priv;
int i;
int endcfg = ei_local->word16 ? (0x48 | ENDCFG_WTS) : 0x48;
+ unsigned long flags;
/* Follow National Semi's recommendations for initing the DP83902. */
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base); /* 0x21 */
@@ -627,6 +656,7 @@ void NS8390_init(struct device *dev, int startp)
/* Copy the station address into the DS8390 registers,
and set the multicast hash bitmap to receive all multicasts. */
+ save_flags(flags);
cli();
outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base); /* 0x61 */
for(i = 0; i < 6; i++) {
@@ -639,7 +669,7 @@ void NS8390_init(struct device *dev, int startp)
outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base);
- sti();
+ restore_flags(flags);
dev->tbusy = 0;
dev->interrupt = 0;
ei_local->tx1 = ei_local->tx2 = 0;
@@ -661,7 +691,6 @@ static void NS8390_trigger_send(struct device *dev, unsigned int length,
{
int e8390_base = dev->base_addr;
- ei_status.txing = 1;
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base);
if (inb_p(e8390_base) & E8390_TRANS) {
diff --git a/drivers/net/8390.h b/drivers/net/8390.h
index 80f42ec63..41369b48d 100644
--- a/drivers/net/8390.h
+++ b/drivers/net/8390.h
@@ -24,7 +24,7 @@ extern int ethif_init(struct device *dev);
extern int ethdev_init(struct device *dev);
extern void NS8390_init(struct device *dev, int startp);
extern int ei_open(struct device *dev);
-extern void ei_interrupt(int reg_ptr);
+extern void ei_interrupt(int irq, struct pt_regs *regs);
#ifndef HAVE_AUTOIRQ
/* From auto_irq.c */
@@ -44,14 +44,13 @@ struct ei_device {
unsigned open:1;
unsigned word16:1; /* We have the 16-bit (vs 8-bit) version of the card. */
unsigned txing:1; /* Transmit Active */
- unsigned dmaing:2; /* Remote DMA Active */
unsigned irqlock:1; /* 8390's intrs disabled when '1'. */
unsigned pingpong:1; /* Using the ping-pong driver */
unsigned char tx_start_page, rx_start_page, stop_page;
unsigned char current_page; /* Read pointer in buffer */
unsigned char interface_num; /* Net port (AUI, 10bT.) to use. */
unsigned char txqueue; /* Tx Packet buffer queue length. */
- unsigned char in_interrupt;
+ unsigned char dmaing; /* Remote DMA (Tx/Rx/Active) */
short tx1, tx2; /* Packet lengths for ping-pong tx. */
short lasttx; /* Alpha version consistency check. */
unsigned char reg0; /* Register '0' in a WD8013 */
@@ -61,6 +60,12 @@ struct ei_device {
struct enet_statistics stat;
};
+/* The maximum number of 8390 interrupt service routines called per IRQ. */
+#define MAX_SERVICE 12
+
+/* The maximum number of jiffies waited before assuming a Tx failed. */
+#define TX_TIMEOUT 20
+
#define ei_status (*(struct ei_device *)(dev->priv))
/* Some generic ethernet register configurations. */
diff --git a/drivers/net/CONFIG b/drivers/net/CONFIG
index c8381b572..6f5b72751 100644
--- a/drivers/net/CONFIG
+++ b/drivers/net/CONFIG
@@ -39,6 +39,29 @@
# EWRK3 The DIGITAL series of AT Ethernet Cards (DE203/4/5)
# EWRK3_DEBUG Set the desired debug level
#
+# DE4x5 The DIGITAL series of PCI/EISA Ethernet Cards,
+# DE425, DE434, DE435, DE500
+# DE4X5_DEBUG Set the desired debug level
+# IS_NOT_DEC May allow driver to work with Zynx & SMC cards -
+# see linux/drivers/net/README.de4x5
+# DE4X5_AUTOSENSE (Default) auto media/mode selection
+# If you want at least one board to not autosense then
+# no board can autosense. For a board mix of several
+# types, OR the manual values [eg for a DE500 (100M) with
+# a DE450 (AUI) use '-DDE4X5_AUTOSENSE=(0x80|0x08)']
+# For full auto media/mode selection = 0x4000
+# For manual TP media selection = 0x01
+# For manual TP/Nway media selection (DC21041) = 0x02
+# For manual BNC media selection = 0x04
+# For manual AUI media selection = 0x08
+# For manual BNC/AUI media selection (DC21040) = 0x10
+# For manual 10Mb/s mode selection (DC21140) = 0x40
+# For manual 100Mb/s mode selection (DC21140) = 0x80
+# The DC21040 will default to TP if TP_NW is specified
+# The DC21041 will default to BNC if BNC_AUI is specified
+# The DC21140 needs it's speed to be manually set to
+# 10Mb/s or 100Mb/s (AUTO defaults to 10Mb/s)
+#
# The following options exist, but cannot be set in this file.
# lance.c
@@ -60,3 +83,5 @@ HP_OPTS =
PLIP_OPTS =
DEPCA_OPTS = -DDEPCA_DEBUG=1
EWRK3_OPTS = -DEWRK3_DEBUG=1
+DE4X5_OPTS = -DDE4X5_DEBUG=1 -DDE4X5_AUTOSENSE=0x4000
+ELP_OPTS = -DELP_DEBUG=1 -DELP_NEED_HARD_RESET=0
diff --git a/drivers/net/MODULES b/drivers/net/MODULES
deleted file mode 100644
index 6ef0c7d3b..000000000
--- a/drivers/net/MODULES
+++ /dev/null
@@ -1,8 +0,0 @@
-MODULES = \
- 3c509.o \
- de600.o \
- de620.o \
- 3c501.o \
- eexpress.o \
- plip.o \
- 8390.o
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 3df086130..53c2cfa0d 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -6,55 +6,63 @@
# This will go away in some future future: hidden configuration files
# are difficult for users to deal with.
include CONFIG
-include MODULES
+
+# Build MODULES by appending to this string for every driver below
+MODULES :=
NETDRV_OBJS := Space.o auto_irq.o net_init.o loopback.o
-CFLAGS := $(CFLAGS) -I../../net/inet
-CPP := $(CPP) -I../../net/inet
.c.o:
$(CC) $(CFLAGS) -c $<
# The point of the makefile...
-all: net.a modules
+all: net.a
Space.o: Space.c ../../include/linux/autoconf.h
$(CC) $(CFLAGS) $(OPTS) -c $<
net_init.o: ../../include/linux/autoconf.h
+ifdef CONFIG_IBMTR
+NETDRV_OBJS := $(NETDRV_OBJS) ibmtr.o
+endif
+
ifdef CONFIG_SK_G16
NETDRV_OBJS := $(NETDRV_OBJS) sk_g16.o
endif
+ifdef CONFIG_NET_IPIP
+NETDRV_OBJS := $(NETDRV_OBJS) tunnel.o
+endif
+
ifdef CONFIG_WD80x3
NETDRV_OBJS := $(NETDRV_OBJS) wd.o
CONFIG_8390 = CONFIG_8390
+endif
wd.o: wd.c CONFIG
$(CC) $(CPPFLAGS) $(CFLAGS) $(WD_OPTS) -c $<
-endif
ifdef CONFIG_EL2
NETDRV_OBJS := $(NETDRV_OBJS) 3c503.o
CONFIG_8390 = CONFIG_8390
+endif
3c503.o: 3c503.c CONFIG
$(CC) $(CPPFLAGS) $(CFLAGS) $(EL2_OPTS) -c $<
-endif
ifdef CONFIG_NE2000
NETDRV_OBJS := $(NETDRV_OBJS) ne.o
CONFIG_8390 = CONFIG_8390
+endif
ne.o: ne.c CONFIG
$(CC) $(CPPFLAGS) $(CFLAGS) $(NE_OPTS) -c $<
-endif
ifdef CONFIG_HPLAN
NETDRV_OBJS := $(NETDRV_OBJS) hp.o
CONFIG_8390 = CONFIG_8390
+endif
hp.o: hp.c CONFIG
$(CC) $(CPPFLAGS) $(CFLAGS) $(HP_OPTS) -c $<
-endif
ifdef CONFIG_HPLAN_PLUS
NETDRV_OBJS := $(NETDRV_OBJS) hp-plus.o
@@ -73,42 +81,57 @@ endif
ifdef CONFIG_PLIP
NETDRV_OBJS := $(NETDRV_OBJS) plip.o
+else
+MODULES := $(MODULES) plip.o
+endif
plip.o: plip.c CONFIG
$(CC) $(CPPFLAGS) $(CFLAGS) $(PLIP_OPTS) -c $<
-endif
ifdef CONFIG_PPP
-NETDRV_OBJS := $(NETDRV_OBJS) ppp.o slhc.o
+NETDRV_OBJS := $(NETDRV_OBJS) ppp.o
+CONFIG_SLHC = CONFIG_SLHC
+else
+MODULES := $(MODULES) ppp.o
endif
ifdef CONFIG_SLIP
-NETDRV_OBJS := $(NETDRV_OBJS) slip.o slhc.o
+NETDRV_OBJS := $(NETDRV_OBJS) slip.o
+CONFIG_SLHC = CONFIG_SLHC
+else
+MODULES := $(MODULES) slip.o
+endif
slip.o: slip.c CONFIG
$(CC) $(CPPFLAGS) $(CFLAGS) -c $<
-endif
ifdef CONFIG_DE650
NETDRV_OBJS := $(NETDRV_OBJS) de650.o
CONFIG_8390 = CONFIG_8390
endif
+
ifdef CONFIG_3C589
NETDRV_OBJS := $(NETDRV_OBJS) 3c589.o
endif
ifdef CONFIG_DUMMY
NETDRV_OBJS := $(NETDRV_OBJS) dummy.o
+else
+MODULES := $(MODULES) dummy.o
+endif
dummy.o: dummy.c CONFIG
$(CC) $(CPPFLAGS) $(CFLAGS) -c $<
-endif
ifdef CONFIG_DE600
NETDRV_OBJS := $(NETDRV_OBJS) de600.o
+else
+MODULES := $(MODULES) de600.o
endif
de600.o: de600.c CONFIG
$(CC) $(CPPFLAGS) $(CFLAGS) $(DE600_OPTS) -c $<
ifdef CONFIG_DE620
NETDRV_OBJS := $(NETDRV_OBJS) de620.o
+else
+MODULES := $(MODULES) de620.o
endif
de620.o: de620.c CONFIG
$(CC) $(CPPFLAGS) $(CFLAGS) $(DE620_OPTS) -c $<
@@ -116,93 +139,160 @@ de620.o: de620.c CONFIG
ifdef CONFIG_AT1500
NETDRV_OBJS := $(NETDRV_OBJS) lance.o
endif
+
ifdef CONFIG_LANCE
NETDRV_OBJS := $(NETDRV_OBJS) lance.o
endif
+
ifdef CONFIG_AT1700
NETDRV_OBJS := $(NETDRV_OBJS) at1700.o
endif
+
ifdef CONFIG_EL1
NETDRV_OBJS := $(NETDRV_OBJS) 3c501.o
+else
+MODULES := $(MODULES) 3c501.o
endif
+
ifdef CONFIG_EL16
NETDRV_OBJS := $(NETDRV_OBJS) 3c507.o
endif
+
ifdef CONFIG_EL3
NETDRV_OBJS := $(NETDRV_OBJS) 3c509.o
+else
+MODULES := $(MODULES) 3c509.o
endif
+
ifdef CONFIG_EEXPRESS
NETDRV_OBJS := $(NETDRV_OBJS) eexpress.o
+else
+MODULES := $(MODULES) eexpress.o
endif
+
+ifdef CONFIG_WAVELAN
+NETDRV_OBJS := $(NETDRV_OBJS) wavelan.o
+else
+MODULES := $(MODULES) wavelan.o
+endif
+
ifdef CONFIG_ZNET
NETDRV_OBJS := $(NETDRV_OBJS) znet.o
endif
+
ifdef CONFIG_DEPCA
NETDRV_OBJS := $(NETDRV_OBJS) depca.o
+else
+MODULES := $(MODULES) depca.o
+endif
depca.o: depca.c CONFIG
$(CC) $(CPPFLAGS) $(CFLAGS) $(DEPCA_OPTS) -c $<
-endif
+
ifdef CONFIG_EWRK3
NETDRV_OBJS := $(NETDRV_OBJS) ewrk3.o
+else
+MODULES := $(MODULES) ewrk3.o
+endif
ewrk3.o: ewrk3.c CONFIG
$(CC) $(CPPFLAGS) $(CFLAGS) $(EWRK3_OPTS) -c $<
-endif
+
ifdef CONFIG_ATP
NETDRV_OBJS := $(NETDRV_OBJS) atp.o
endif
+
+ifdef CONFIG_DE4X5
+NETDRV_OBJS := $(NETDRV_OBJS) de4x5.o
+else
+MODULES := $(MODULES) de4x5.o
+endif
+de4x5.o: de4x5.c CONFIG
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(DE4x5_OPTS) -c $<
+
ifdef CONFIG_NI52
NETDRV_OBJS := $(NETDRV_OBJS) ni52.o
endif
+
ifdef CONFIG_NI65
NETDRV_OBJS := $(NETDRV_OBJS) ni65.o
endif
+
ifdef CONFIG_ELPLUS
NETDRV_OBJS := $(NETDRV_OBJS) 3c505.o
endif
+3c505.o: 3c505.c CONFIG
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(ELP_OPTS) -c $<
+
ifdef CONFIG_AC3200
NETDRV_OBJS := $(NETDRV_OBJS) ac3200.o
CONFIG_8390 = CONFIG_8390
endif
+
ifdef CONFIG_APRICOT
NETDRV_OBJS := $(NETDRV_OBJS) apricot.o
+else
+MODULES := $(MODULES) apricot.o
endif
-ifdef CONFIG_8390
-NETDRV_OBJS := $(NETDRV_OBJS) 8390.o
+ifdef CONFIG_DEC_ELCP
+NETDRV_OBJS := $(NETDRV_OBJS) tulip.o
+endif
+
+ifdef CONFIG_ARCNET
+NETDRV_OBJS := $(NETDRV_OBJS) arcnet.o
+else
+MODULES := $(MODULES) arcnet.o
endif
ifdef CONFIG_PI
NETDRV_OBJS := $(NETDRV_OBJS) pi2.o
CONFIG_PI = CONFIG_PI
+endif
pi2.o: pi2.c CONFIG
$(CC) $(CPPFLAGS) $(CFLAGS) $(PI_OPTS) -c $<
+
+ifdef CONFIG_SLHC
+NETDRV_OBJS := $(NETDRV_OBJS) slhc.o
+else
+MODULES := slhc.o $(MODULES)
+endif
+
+ifdef CONFIG_8390
+NETDRV_OBJS := $(NETDRV_OBJS) 8390.o
+else
+MODULES := 8390.o $(MODULES)
+endif
+
+ifdef CONFIG_EQUALIZER
+NETDRV_OBJS := $(NETDRV_OBJS) eql.o
+endif
+
+ifdef CONFIG_MIPS_JAZZ_SONIC
+NETDRV_OBJS := $(NETDRV_OBJS) sonic.o
+else
+MODULES := sonic.o $(MODULES)
endif
net.a: $(NETDRV_OBJS)
rm -f net.a
- ar rc net.a $(NETDRV_OBJS)
- ranlib net.a
+ $(AR) rcs net.a $(NETDRV_OBJS)
clean:
rm -f core *.o *.a *.s
dep:
- $(CPP) -M *.c > .depend
+ $(CPP) -M $(NETDRV_OBJS:.o=.c) > .depend
+ifdef MODULES
+ $(CPP) -M -DMODULE $(MODULES:.o=.c) >> .depend
+endif
tar:
ifdef MODULES
-modules:
+modules: $(MODULES)
echo $(MODULES) > ../../modules/NET_MODULES
- @(cd ../../modules; \
- for i in $(MODULES:.o=.c); \
- do ln -sf ../drivers/net/$$i .; \
- done ; \
- ln -sf ../drivers/net/CONFIG . ; \
- $(MAKE) -f../drivers/net/Makefile -I../drivers/net \
- CFLAGS="$(CFLAGS) -I../net/inet -I../drivers/net -DMODULE" $(MODULES); \
- rm $(MODULES:.o=.c) CONFIG)
+ cd ../../modules; \
+ for i in $(MODULES); do ln -sf ../drivers/net/$$i .; done
else
diff --git a/drivers/net/README.3c505 b/drivers/net/README.3c505
index f97bb32c8..01adc6093 100644
--- a/drivers/net/README.3c505
+++ b/drivers/net/README.3c505
@@ -1,35 +1,36 @@
The address and IRQ used by the 3c505 driver can be configured at boot
time by typing 'ether=eth0,15,0x300' (replace IRQ and base address with
-ones that tell how your adapter is jumpered).
+ones that tell how your adapter is jumpered). The driver does not yet
+use DMA.
If no base address is given at the boot time, the driver will look for
a 3c505 adapter at addresses 0x300, 0x280 and 0x310 in this order,
possibly messing up any other hardware residing in these addresses.
If a base address is given, it will be verified.
-There's two #defines one may need to change in the 3c505 driver:
-ELP_KERNEL_TYPE
- this exists just to adapt the driver with pretty wide range of kernels.
- See 3c505.c for exact information.
-
+The driver has two compile-time settings in the CONFIG file:
ELP_NEED_HARD_RESET
- some DOS drivers seem to get the adapter to some irrecoverable state
+ Some DOS drivers seem to get the adapter to some irrecoverable state
if the machine is "warm booted" from DOS to Linux. If you experience
problems when warm booting, but "cold boot" works, #defining this
- to 1 may help.
-
+ to 1 may help. As of 3c505.c v0.8 the driver should be able to find
+ out whether of not this is needed, but I'm not completely sure.
+ELP_DEBUG
+ The driver debug level. 1 is ok for most everything, 0 will provide
+ less verbose bootup messages, and 2 and 3 are usually too verbose
+ for anything.
Known problems:
- when 'ifconfig up' is run for the first time after bootup, the driver
- complains:
-elp_interrupt(): irq 15 for unknown device.
- ^^
- There should be the IRQ the ELPlus adapter is using. IF the IRQ doesn't
- match, something is seriously wrong.
+ During startup the driver shows the following two messages:
+ *** timeout at 3c505.c:elp_set_mc_list (line 1158) ***
+ *** timeout at 3c505.c:elp_set_mc_list (line 1183) ***
+ These are because upper parts of the networking code attempt
+ to load multicast address lists to the adapter before the
+ adapter is properly up and running.
Authors:
- The driver is mainly written by Craig Southeren, email c/o
- <geoffw@extro.ucc.su.OZ.AU>.
+ The driver is mainly written by Craig Southeren, email
+ <craigs@ineluki.apana.org.au>.
Parts of the driver (adapting the driver to 1.1.4+ kernels,
- IRQ/address detection, minor changes) and this (lousy) 'readme'
- by Juha Laiho <jlaiho@ichaos.nullnet.fi>.
+ IRQ/address detection, some changes) and this README by
+ Juha Laiho <jlaiho@ichaos.nullnet.fi>.
diff --git a/drivers/net/README.arcnet b/drivers/net/README.arcnet
new file mode 100644
index 000000000..faf3562a6
--- /dev/null
+++ b/drivers/net/README.arcnet
@@ -0,0 +1,204 @@
+
+---------------------------------------------------------------------------
+NOTE: See also README.arcnet-jumpers in this directory for jumper-setting
+information if you're like most of us and didn't happen to get a manual with
+your ARCnet card.
+---------------------------------------------------------------------------
+
+Since no one seems to listen to me otherwise, perhaps a poem will get your
+attention:
+ This is scary software
+ If it works I DO CARE.
+
+Hmm, I think I'm allowed to call that a poem, even though it's only two
+lines. Hey, I'm in Computer Science, not English. Give me a break.
+
+The point is: I REALLY REALLY REALLY REALLY REALLY want to hear from you if
+you test this and get it working. Or if you don't. Or anything.
+
+ARCnet 0.32 ALPHA first made it into the Linux kernel 1.1.80 - this was
+nice, but after that even FEWER people started writing to me because they
+didn't even have to install the patch. <sigh>
+
+Come on, be a sport! Send me a success report!
+
+(hey, that was even better than my original poem... this is getting bad!)
+
+Anyway, enough complaining. Let's get started:
+
+---------------------------------------------------------------------------
+
+These are the ARCnet drivers for Linux.
+
+This is the first non-ALPHA release, so please be careful, and send all
+possible success/failure reports to me. If I don't know when/if/how it
+works, I won't be able to answer people's questions. Do we want that? Of
+course not.
+
+Once again: DO send me success reports! I want to know if this is working!
+(You know, it might be argued that I'm pushing this point a little too much.
+If you think so, why not flame me in a quick little email? Please also
+include the type of card(s) you're using, software, size of network, and
+whether it's working or not.)
+
+My e-mail address is:
+ apenwarr@tourism.807-city.on.ca
+
+
+Where do I discuss these drivers?
+---------------------------------
+
+As of the 0.22 release, we have a mailing list specifically for discussion
+of the ARCnet drivers for Linux, and anything you might want to interface
+them with (ie. DOS). I'll also post new versions of the Linux-ARCnet
+distribution to the list in tar-gzip-uuencode format.
+
+To subscribe to the list, send a message to listserv@tourism.807-city.on.ca
+with the following line in the BODY (not the SUBJECT) of your message:
+ subscribe linux-arcnet YOUR REAL NAME
+Remember to remove your signature, or you'll get an error back.
+
+Send all bug (or success) reports to me or to the list.
+
+You're free to use the comp.os.linux.development newsgroup too, but I can't
+guarantee I'll see it there. (Hopefully, if my news server stays sane, I
+will.)
+
+Also, SMC (one of the companies that makes ARCnet cards) has a WorldWideWeb
+site you might be interested in, which includes several drivers for various
+cards including ARCnet. Try:
+ http://www.smc.com/
+
+Novell makes a networking stack for DOS which includes ARCnet drivers. Try
+ftp'ing to ftp.novell.com.
+
+You can get the Crynwr packet driver collection (including arcether.com, the
+one you'll want for arcnet cards) from oak.oakland.edu:/pub/msdos/pktdrvr.
+It won't work perfectly on a 386+ without patches, though, and also doesn't
+like several cards. Mail me if you want a fixed version.
+
+
+Last warning: This driver may be extremely dangerous, crash your computer,
+or kill your dog! (although I'm pretty sure that I've worked that one
+out...)
+
+
+Loadable Module Support
+-----------------------
+
+This is a new feature as of 0.42 ALPHA.
+
+Configure and rebuild Linux. When asked, say NO to "arcnet support" if you
+want loadable module support.
+
+ make config
+ make dep
+ make clean
+ make zImage
+ make modules
+
+
+Booting into your "ARCnet" Kernel
+---------------------------------
+
+If you're using a loadable module, you need to use insmod to load the
+module, and you need to specify various characteristics of your card on the
+command line. For example:
+ cd /usr/src/linux/modules
+ insmod arcnet.o io=0x300 irqnum=2 shmem=0xd0000
+You can also add a num=1, num=2 etc for additional arcnet cards that will
+use arc1, arc2 etc for their device names (instead of the default, arc0).
+
+Otherwise the driver will load and probe for your card automatically.
+
+Now go read the NET-2-HOWTO and ETHERNET-HOWTO for Linux; they should be
+available where you picked up this driver. Think of your ARCnet as a
+souped-up (or down, as the case may be) ethernet card.
+
+By the way, be sure to change all references from "eth0" to "arc0" in the
+HOWTOs. Remember that ARCnet isn't a "true" ethernet, and the device name
+is DIFFERENT.
+
+
+How do I get it to work with...?
+--------------------------------
+
+NFS: Should be fine linux->linux, just pretend you're using ethernet cards.
+ oak.oakland.edu:/pub/msdos/nfs has some nice DOS clients. I can't
+ get SOSS (dos-based server) to work, although someone has and can't
+ figure out why it won't work for me.
+
+DOS: If you're using the freeware arcether.com, you might want to install
+ the source code patch. It helps with PC/TCP, and also can get
+ arcether to load if it timed out too quickly. Mail me if you need a
+ precompiled version of arcether.com. (ie. you if don't have a DOS
+ assembler)
+
+Windows: See DOS :)
+
+OS2: May work okay. Please e-mail me if you find a freeware TCP/IP stack
+ for OS/2.
+
+LAN Manager and Windows for Workgroups: These programs use protocols that
+ are incompatible with ARCnet for Linux. Rather than using the
+ internet-standard ARCnet protocol, they try to pretend the cards are
+ ethernet, and confuse everyone else on the network.
+
+ An upcoming release of ARCnet for Linux may have workarounds for
+ this stupid behaviour.
+
+
+It works: what now?
+-------------------
+
+Send mail describing your setup, preferably including driver version, kernel
+version, ARCnet card model, CPU type, number of systems on your network, and
+list of software in use to me at the following address:
+ apenwarr@tourism.807-city.on.ca
+
+I do send (sometimes automated) replies to all messages I receive. My mail
+host is quite weird, so if you don't get a reply within a reasonable time,
+please resend.
+
+
+It doesn't work: what now?
+--------------------------
+
+Do the same as above, but also include the output of the ifconfig and route
+commands, as well as any pertinent log entries (ie: anything that starts
+with "arcnet:" and has shown up since the last reboot) in your mail.
+
+If you want to try fixing it yourself (I highly recommend that you mail me
+about the problem first, since it might already have been solved) you may
+want to try some of the debug levels available. For heavy testing on
+DEBUG_DURING or more, it would be a REALLY good idea to kill your klogd
+daemon first! DEBUG_DURING displays 4-5 lines for each packet sent or
+received. DEBUG_TX and RX actually DISPLAY each packet as it is sent or
+received, which is obviously quite big.
+
+You can run the arcdump shell script (available from me) as root to list the
+contents of the arcnet buffers at any time. To make any sense at all out of
+this, you should grab the pertinent RFC's. (some are listed near the top of
+arcnet.c). arcdump assumes your card is at 0xD0000. If it isn't, edit the
+script.
+
+Buffers #0 and 1 are used for receiving, and Buffers #2 and 3 are for
+sending. Ping-pong buffers are implemented both ways, just to confuse you.
+
+If your debug level is DEBUG_DURING or more, the buffers are cleared to a
+constant value of 0x42 every time the card is reset (which should only
+happen when you do an ifconfig up, or when Linux decides that the driver is
+broken). This is to make it easier to figure out which bytes are being used
+by a packet.
+
+You can change the debug level without recompiling the kernel by typing:
+ ifconfig arc0 down metric 1x
+ /etc/rc.d/rc.inet1
+where "x" is the debug level you want. For example, "metric 14" would put
+you at debug level 4. Debug level 3 is the default (D_EXTRA).
+
+
+I want to send money: what now?
+-------------------------------
+
+Go take a nap or something. You'll feel better in the morning.
diff --git a/drivers/net/README.arcnet-jumpers b/drivers/net/README.arcnet-jumpers
new file mode 100644
index 000000000..5f38ac755
--- /dev/null
+++ b/drivers/net/README.arcnet-jumpers
@@ -0,0 +1,1561 @@
+
+-----------------------------------------------------------------------------
+This file is a supplement to README.arcnet. Please read that for general
+driver configuration help.
+-----------------------------------------------------------------------------
+
+Because so many people (myself included) seem to have obtained ARCnet cards
+without manuals, this will be a quick listing of all jumper settings I can
+find. Please e-mail apenwarr@tourism.807-city.on.ca with any settings for
+your particular card.
+
+Even if your ARCnet model isn't listed, but has the same jumpers, please
+e-mail me to say so.
+
+If your model isn't listed, and has different settings, PLEASE PLEASE tell
+me. I had to figure mine out without the manual, and it WASN'T FUN!
+
+Cards Listed in this file:
+
+ Manufacturer Model # Bits
+ ------------ ------- ----
+ SMC PC100 8
+ SMC PC110 8
+ SMC PC120 8
+ SMC PC130 8
+ SMC PC270E 8
+ SMC PC500 16
+ SMC PC500Longboard 16
+ SMC PC550Longboard 16
+ SMC PC600 16
+ Puredata PDI507 16
+ CNet Tech CN120-Series 8
+ CNet Tech CN160-Series 16
+ No Name -- 8/16
+
+** SMC = Standard Microsystems Corp.
+** CNet Tech = CNet Technology, Inc.
+
+The model # is listed right above specifics for that card. Don't forget to
+read "quick briefing" first, since it applies to all ARCnets.
+
+
+Unclassified Stuff
+------------------
+ - Please send any other information you can find.
+
+ - And some unknowns (other info is welcome!):
+ From: root@ultraworld.xs4all.nl (Timo Hilbrink)
+ To: apenwarr@tourism.807-city.on.ca (Avery Pennarun)
+ Date: Wed, 26 Oct 1994 02:10:32 +0000 (GMT)
+ Reply-To: timoh@xs4all.nl
+
+ [...parts deleted...]
+
+ About the jumpers: On my PC130 there is one more jumper, located near the
+ cable-connector and it's for changing to star or bus topology;
+ closed: star - open: bus
+ On the PC500 are some more jumper-pins, one block labeled with RX,PDN,TXI
+ and another with ALE,LA17,LA18,LA19 these are undocumented..
+
+ [...more parts deleted...]
+
+ --- CUT ---
+
+
+Quick Briefing:
+---------------
+
+All ARCnet cards should have a total of four different settings:
+ - the I/O address: this is the "port" your ARCnet card is on. Probed
+ values, as of v0.14, are only from 0x200 through 0x3F0. (If your card
+ has additional ones, which is possible, please tell me.) This should not
+ be the same as any other device on your system. Supposedly MS Windows
+ prefers values of 0x300 or more, eating net connections on my system
+ otherwise.
+ - Avery's favourite: 0x300.
+
+ - the IRQ: on 8-bit cards, it might be 2 (9), 3, 4, 5, or 7.
+ on 16-bit cards, it might be 2 (9), 3, 4, 5, 7, or 9-15. Make
+ sure this is different from any other card on your system. Note that
+ IRQ2 is the same as IRQ9, as far as Linux is concerned.
+ - Avery's favourite: IRQ2.
+
+ - the memory address: Unlike most cards, ARCnets use "shared memory" for
+ copying buffers around. Make SURE it doesn't conflict with any other
+ used memory in your system!
+ A0000 - VGA graphics memory (ok if you don't have VGA)
+ B0000 - Monochrome text mode
+ C0000 \ One of these is your VGA BIOS - usually C0000.
+ E0000 /
+ F0000 - System BIOS
+
+ Anything less than 0xA0000 is, well, a BAD idea since it isn't above
+ 640k.
+ - Avery's favourite: 0xD0000
+
+ - the station address: Every ARCnet card has its own "unique" network
+ address from 0 to 255. Unlike ethernet, you can set this address
+ yourself. Since it's only 8 bits, you can only have 254 ARCnet cards on
+ a network. DON'T use 0 or 255, since these are reserved. (although neat
+ stuff will probably happen if you DO use them). By the way, if you
+ haven't already guessed, don't set this the same as any other ARCnet on
+ your network!
+ - Avery's favourite: 3 and 4. Not that it matters.
+
+
+** Standard Microsystems Corp (SMC) **
+PC100, PC110, PC120, PC130 (8-bit cards)
+PC500, PC600 (16-bit cards)
+---------------------------------
+ - mainly from Avery Pennarun <apenwarr@tourism.807-city.on.ca>
+ - values depicted are from Avery's setup.
+ - special thanks to Timo Hilbrink <timoh@xs4all.nl> for noting that PC120,
+ 130, 500, and 600 all have the same switches as Avery's PC100.
+ PC500/600 have several extra, undocumented pins though. (?)
+ - PC110 settings were verified by Stephen A. Wood <saw@cebaf.gov>
+
+
+ JP5 [|] : : : :
+(IRQ Setting) IRQ2 IRQ3 IRQ4 IRQ5 IRQ7
+ Put exactly one jumper on exactly one set of pins.
+
+ 1 2 3 4 5 6 7 8 9 10
+ S1 /----------------------------------\
+(I/O and Memory | 1 1 * 0 0 0 0 * 1 1 0 1 |
+ addresses) \----------------------------------/
+ |--| |--------| |--------|
+ (a) (b) (m)
+
+ a: The first digit of the I/O address.
+ Setting Value
+ ------- -----
+ 00 0
+ 01 1
+ 10 2
+ 11 3
+
+ b: The second digit of the I/O address.
+ Setting Value
+ ------- -----
+ 0000 0
+ 0001 1
+ 0010 2
+ ... ...
+ 1110 E
+ 1111 F
+
+ The I/O address is in the form ab0. For example, if
+ a is 0x2 and b is 0xE, the address will be 0x2E0.
+
+ DO NOT SET THIS LESS THAN 0x200!!!!!
+
+
+ m: The first digit of the memory address.
+ Setting Value
+ ------- -----
+ 0000 0
+ 0001 1
+ 0010 2
+ ... ...
+ 1110 E
+ 1111 F
+
+ The memory address is in the form m0000. For example, if
+ m is D, the address will be 0xD0000.
+
+ DO NOT SET THIS TO C0000, F0000, OR LESS THAN A0000!
+
+ 1 2 3 4 5 6 7 8
+ S2 /--------------------------\
+(Station Address) | 1 1 0 0 0 0 0 0 |
+ \--------------------------/
+
+ Setting Value
+ ------- -----
+ 00000000 00
+ 10000000 01
+ 01000000 02
+ ...
+ 01111111 FE
+ 11111111 FF
+
+ Note that this is binary with the digits reversed!
+
+ DO NOT SET THIS TO 0 OR 255 (0xFF)!
+
+*****************************************************************************
+
+** Standard Microsystems Corp (SMC) **
+PC130E/PC270E (8-bit cards)
+---------------------------
+ - from Juergen Seifert <seifert@htwm.de>
+
+
+STANDARD MICROSYSTEMS CORPORATION (SMC) ARCNET(R)-PC130E/PC270E
+===============================================================
+
+This description has been written by Juergen Seifert <seifert@htwm.de>
+using information from the following Original SMC Manual
+
+ "Configuration Guide for
+ ARCNET(R)-PC130E/PC270
+ Network Controller Boards
+ Pub. # 900.044A
+ June, 1989"
+
+ARCNET is a registered trademark of the Datapoint Corporation
+SMC is a registered trademark of the Standard Microsystems Corporation
+
+The PC130E is an enhanced version of the PC130 board, is equipped with a
+standard BNC female connector for connection to RG-62/U coax cable.
+Since this board is designed both for point-to-point connection in star
+networks and for connection to bus networks, it is downwardly compatible
+with all the other standard boards designed for coax networks (that is,
+the PC120, PC110 and PC100 star topology boards and the PC220, PC210 and
+PC200 bus topology boards).
+
+The PC270E is an enhanced version of the PC260 board, is equipped with two
+modular RJ11-type jacks for connection to twisted pair wiring.
+It can be used in a star or a daisy-chained network.
+
+
+ 8 7 6 5 4 3 2 1
+ ________________________________________________________________
+ | | S1 | |
+ | |_________________| |
+ | Offs|Base |I/O Addr |
+ | RAM Addr | ___|
+ | ___ ___ CR3 |___|
+ | | \/ | CR4 |___|
+ | | PROM | ___|
+ | | | N | | 8
+ | | SOCKET | o | | 7
+ | |________| d | | 6
+ | ___________________ e | | 5
+ | | | A | S | 4
+ | |oo| EXT2 | | d | 2 | 3
+ | |oo| EXT1 | SMC | d | | 2
+ | |oo| ROM | 90C63 | r |___| 1
+ | |oo| IRQ7 | | |o| _____|
+ | |oo| IRQ5 | | |o| | J1 |
+ | |oo| IRQ4 | | STAR |_____|
+ | |oo| IRQ3 | | | J2 |
+ | |oo| IRQ2 |___________________| |_____|
+ |___ ______________|
+ | |
+ |_____________________________________________|
+
+Legend:
+
+SMC 90C63 ARCNET Controller / Transceiver /Logic
+S1 1-3: I/O Base Address Select
+ 4-6: Memory Base Address Select
+ 7-8: RAM Offset Select
+S2 1-8: Node ID Select
+EXT Extended Timeout Select
+ROM ROM Enable Select
+STAR Selected - Star Topology (PC130E only)
+ Deselected - Bus Topology (PC130E only)
+CR3/CR4 Diagnostic LEDs
+J1 BNC RG62/U Connector (PC130E only)
+J1 6-position Telephone Jack (PC270E only)
+J2 6-position Telephone Jack (PC270E only)
+
+Setting one of the switches to Off/Open means "1", On/Closed means "0".
+
+
+Setting the Node ID
+-------------------
+
+The eight switches in group S2 are used to set the node ID.
+Each node attached to the network must have an unique node ID which
+must be different from 0.
+Switch 1 serves as the least significant bit (LSB).
+
+The node ID is the sum of the values of all switches set to "1"
+These values are:
+ Switch | Value
+ -------|-------
+ 1 | 1
+ 2 | 2
+ 3 | 4
+ 4 | 8
+ 5 | 16
+ 6 | 32
+ 7 | 64
+ 8 | 128
+
+Some Examples:
+
+ Switch | Hex | Decimal
+ 8 7 6 5 4 3 2 1 | Node ID | Node ID
+ ----------------|---------|---------
+ 0 0 0 0 0 0 0 0 | not allowed
+ 0 0 0 0 0 0 0 1 | 1 | 1
+ 0 0 0 0 0 0 1 0 | 2 | 2
+ 0 0 0 0 0 0 1 1 | 3 | 3
+ . . . | |
+ 0 1 0 1 0 1 0 1 | 55 | 85
+ . . . | |
+ 1 0 1 0 1 0 1 0 | AA | 170
+ . . . | |
+ 1 1 1 1 1 1 0 1 | FD | 253
+ 1 1 1 1 1 1 1 0 | FE | 254
+ 1 1 1 1 1 1 1 1 | FF | 255
+
+
+Setting the I/O Base Address
+----------------------------
+
+The first three switches in switch group S1 are used to select one
+of eight possible I/O Base addresses using the following table
+
+
+ Switch | Hex I/O
+ 1 2 3 | Address
+ -------|--------
+ 0 0 0 | 260
+ 0 0 1 | 290
+ 0 1 0 | 2E0 (Manufacturer's default)
+ 0 1 1 | 2F0
+ 1 0 0 | 300
+ 1 0 1 | 350
+ 1 1 0 | 380
+ 1 1 1 | 3E0
+
+
+Setting the Base Memory (RAM) buffer Address
+--------------------------------------------
+
+The memory buffer requires 2K of a 16K block of RAM. The base of this
+16K block can be located in any of eight positions.
+Switches 4-6 of switch group S1 select the Base of the 16K block.
+Within that 16K address space, the buffer may be assigned any one of four
+positions, determined by the offset, switches 7 and 8 of group S1.
+
+ Switch | Hex RAM | Hex ROM
+ 4 5 6 7 8 | Address | Address *)
+ -----------|---------|-----------
+ 0 0 0 0 0 | C0000 | C2000
+ 0 0 0 0 1 | C0800 | C2000
+ 0 0 0 1 0 | C1000 | C2000
+ 0 0 0 1 1 | C1800 | C2000
+ | |
+ 0 0 1 0 0 | C4000 | C6000
+ 0 0 1 0 1 | C4800 | C6000
+ 0 0 1 1 0 | C5000 | C6000
+ 0 0 1 1 1 | C5800 | C6000
+ | |
+ 0 1 0 0 0 | CC000 | CE000
+ 0 1 0 0 1 | CC800 | CE000
+ 0 1 0 1 0 | CD000 | CE000
+ 0 1 0 1 1 | CD800 | CE000
+ | |
+ 0 1 1 0 0 | D0000 | D2000 (Manufacturer's default)
+ 0 1 1 0 1 | D0800 | D2000
+ 0 1 1 1 0 | D1000 | D2000
+ 0 1 1 1 1 | D1800 | D2000
+ | |
+ 1 0 0 0 0 | D4000 | D6000
+ 1 0 0 0 1 | D4800 | D6000
+ 1 0 0 1 0 | D5000 | D6000
+ 1 0 0 1 1 | D5800 | D6000
+ | |
+ 1 0 1 0 0 | D8000 | DA000
+ 1 0 1 0 1 | D8800 | DA000
+ 1 0 1 1 0 | D9000 | DA000
+ 1 0 1 1 1 | D9800 | DA000
+ | |
+ 1 1 0 0 0 | DC000 | DE000
+ 1 1 0 0 1 | DC800 | DE000
+ 1 1 0 1 0 | DD000 | DE000
+ 1 1 0 1 1 | DD800 | DE000
+ | |
+ 1 1 1 0 0 | E0000 | E2000
+ 1 1 1 0 1 | E0800 | E2000
+ 1 1 1 1 0 | E1000 | E2000
+ 1 1 1 1 1 | E1800 | E2000
+
+*) To enable the 8K Boot PROM install the jumper ROM.
+ The default is jumper ROM not installed.
+
+
+Setting the Timeouts and Interrupt
+----------------------------------
+
+The jumpers labeled EXT1 and EXT2 are used to determine the timeout
+parameters. These two jumpers are normally left open.
+Refer to the COM9026 Data Sheet for alternate configurations.
+
+To select a hardware interrupt level set one (only one!) of the jumpers
+IRQ2, IRQ3, IRQ4, IRQ5, IRQ7. The manufacturer's default is IRQ2.
+
+
+Configuring the PC130E for Star or Bus Topology
+-----------------------------------------------
+
+The single jumper labeled STAR is used to configure the PC130E board for
+star or bus topology.
+When the jumper is installed, the board may be used in a star network, when
+it is removed, the board can be used in a bus topology.
+
+
+Diagnostic LEDs
+---------------
+
+Two diagnostic LEDs are visible on the rear bracket of the board.
+The green LED monitors the network activity: the red one shows the
+board activity:
+
+ Green | Status Red | Status
+ -------|------------------- ---------|-------------------
+ on | normal activity flash/on | data transfer
+ blink | reconfiguration off | no data transfer;
+ off | defective board or | incorrect memory or
+ | node ID is zero | I/O address
+
+
+*****************************************************************************
+
+** Standard Microsystems Corp (SMC) **
+PC500/PC550 Long Board (16-bit cards)
+-------------------------------------
+ - from Juergen Seifert <seifert@htwm.de>
+
+
+STANDARD MICROSYSTEMS CORPORATION (SMC) ARCNET-PC500/PC550 Long Board
+=====================================================================
+
+Note: There is another Version of the PC500 called Short Version, which
+ is different in hard- and software! The most important differences
+ are:
+ - The long board has no Shared memory
+ - On the long board the selection of the interrupt is done by binary
+ coded switch, on the short board directly by jumper.
+
+
+This description has been written by Juergen Seifert <seifert@htwm.de>
+using information from the following Original SMC Manual
+
+ "Configuration Guide for
+ SMC ARCNET-PC500/PC550
+ Series Network Controller Boards
+ Pub. # 900.033 Rev. A
+ November, 1989"
+
+ARCNET is a registered trademark of the Datapoint Corporation
+SMC is a registered trademark of the Standard Microsystems Corporation
+
+The PC500 is equipped with a standard BNC female connector for connection
+to RG-62/U coax cable.
+The board is designed both for point-to-point connection in star networks
+and for connection to bus networks.
+
+The PC550 is equipped with two modular RJ11-type jacks for connection
+to twisted pair wiring.
+It can be used in a star or a daisy-chained network.
+
+ 1
+ 0 9 8 7 6 5 4 3 2 1 6 5 4 3 2 1
+ ____________________________________________________________________
+ < | SW1 | | SW2 | |
+ > |_____________________| |_____________| |
+ < IRQ |I/O Addr |
+ > ___|
+ < CR4 |___|
+ > CR3 |___|
+ < ___|
+ > N | | 8
+ < o | | 7
+ > d | S | 6
+ < e | W | 5
+ > A | 3 | 4
+ < d | | 3
+ > d | | 2
+ < r |___| 1
+ > |o| _____|
+ < |o| | J1 |
+ > 3 1 JP6 |_____|
+ < |o|o| JP2 | J2 |
+ > |o|o| |_____|
+ < 4 2__ ______________|
+ > | | |
+ <____| |_____________________________________________|
+
+Legend:
+
+SW1 1-6: I/O Base Address Select
+ 7-10: Interrupt Select
+SW2 1-6: Reserved for Future Use
+SW3 1-8: Node ID Select
+JP2 1-4: Extended Timeout Select
+JP6 Selected - Star Topology (PC500 only)
+ Deselected - Bus Topology (PC500 only)
+CR3 Green Monitors Network Activity
+CR4 Red Monitors Board Activity
+J1 BNC RG62/U Connector (PC500 only)
+J1 6-position Telephone Jack (PC550 only)
+J2 6-position Telephone Jack (PC550 only)
+
+Setting one of the switches to Off/Open means "1", On/Closed means "0".
+
+
+Setting the Node ID
+-------------------
+
+The eight switches in group SW3 are used to set the node ID. Each node
+attached to the network must have an unique node ID which must be
+different from 0.
+Switch 1 serves as the least significant bit (LSB).
+
+The node ID is the sum of the values of all switches set to "1"
+These values are:
+
+ Switch | Value
+ -------|-------
+ 1 | 1
+ 2 | 2
+ 3 | 4
+ 4 | 8
+ 5 | 16
+ 6 | 32
+ 7 | 64
+ 8 | 128
+
+Some Examples:
+
+ Switch | Hex | Decimal
+ 8 7 6 5 4 3 2 1 | Node ID | Node ID
+ ----------------|---------|---------
+ 0 0 0 0 0 0 0 0 | not allowed
+ 0 0 0 0 0 0 0 1 | 1 | 1
+ 0 0 0 0 0 0 1 0 | 2 | 2
+ 0 0 0 0 0 0 1 1 | 3 | 3
+ . . . | |
+ 0 1 0 1 0 1 0 1 | 55 | 85
+ . . . | |
+ 1 0 1 0 1 0 1 0 | AA | 170
+ . . . | |
+ 1 1 1 1 1 1 0 1 | FD | 253
+ 1 1 1 1 1 1 1 0 | FE | 254
+ 1 1 1 1 1 1 1 1 | FF | 255
+
+
+Setting the I/O Base Address
+----------------------------
+
+The first six switches in switch group SW1 are used to select one
+of 32 possible I/O Base addresses using the following table
+
+ Switch | Hex I/O
+ 6 5 4 3 2 1 | Address
+ -------------|--------
+ 0 1 0 0 0 0 | 200
+ 0 1 0 0 0 1 | 210
+ 0 1 0 0 1 0 | 220
+ 0 1 0 0 1 1 | 230
+ 0 1 0 1 0 0 | 240
+ 0 1 0 1 0 1 | 250
+ 0 1 0 1 1 0 | 260
+ 0 1 0 1 1 1 | 270
+ 0 1 1 0 0 0 | 280
+ 0 1 1 0 0 1 | 290
+ 0 1 1 0 1 0 | 2A0
+ 0 1 1 0 1 1 | 2B0
+ 0 1 1 1 0 0 | 2C0
+ 0 1 1 1 0 1 | 2D0
+ 0 1 1 1 1 0 | 2E0 (Manufacturer's default)
+ 0 1 1 1 1 1 | 2F0
+ 1 1 0 0 0 0 | 300
+ 1 1 0 0 0 1 | 310
+ 1 1 0 0 1 0 | 320
+ 1 1 0 0 1 1 | 330
+ 1 1 0 1 0 0 | 340
+ 1 1 0 1 0 1 | 350
+ 1 1 0 1 1 0 | 360
+ 1 1 0 1 1 1 | 370
+ 1 1 1 0 0 0 | 380
+ 1 1 1 0 0 1 | 390
+ 1 1 1 0 1 0 | 3A0
+ 1 1 1 0 1 1 | 3B0
+ 1 1 1 1 0 0 | 3C0
+ 1 1 1 1 0 1 | 3D0
+ 1 1 1 1 1 0 | 3E0
+ 1 1 1 1 1 1 | 3F0
+
+
+Setting the Interrupt
+---------------------
+
+Switches seven through ten of switch group SW1 are used to select the
+interrupt level. The interrupt level is binary coded, so selections
+from 0 to 15 would be possible, but only the following eight values will
+be supported: 3, 4, 5, 7, 9, 10, 11, 12.
+
+ Switch | IRQ
+ 10 9 8 7 |
+ ---------|--------
+ 0 0 1 1 | 3
+ 0 1 0 0 | 4
+ 0 1 0 1 | 5
+ 0 1 1 1 | 7
+ 1 0 0 1 | 9 (=2) (default)
+ 1 0 1 0 | 10
+ 1 0 1 1 | 11
+ 1 1 0 0 | 12
+
+
+Setting the Timeouts
+--------------------
+
+The two jumpers JP2 (1-4) are used to determine the timeout parameters.
+These two jumpers are normally left open.
+Refer to the COM9026 Data Sheet for alternate configurations.
+
+
+Configuring the PC500 for Star or Bus Topology
+----------------------------------------------
+
+The single jumper labeled JP6 is used to configure the PC500 board for
+star or bus topology.
+When the jumper is installed, the board may be used in a star network, when
+it is removed, the board can be used in a bus topology.
+
+
+Diagnostic LEDs
+---------------
+
+Two diagnostic LEDs are visible on the rear bracket of the board.
+The green LED monitors the network activity: the red one shows the
+board activity:
+
+ Green | Status Red | Status
+ -------|------------------- ---------|-------------------
+ on | normal activity flash/on | data transfer
+ blink | reconfiguration off | no data transfer;
+ off | defective board or | incorrect memory or
+ | node ID is zero | I/O address
+
+
+*****************************************************************************
+
+** PureData Corp **
+PDI507 (16-bit card)
+--------------------
+ - from Mark Rejhon <mdrejhon@magi.com> (slight modifications by
+ Avery)
+ - Send questions/suggestions/etc about this text to Mark.
+
+Jumpers:
+
+ There is a jumper array at the bottom of the card, near the edge
+ connector. This array is labelled J1. They control the IRQs and
+ something else. Put only one jumper on the IRQ pins.
+
+ IRQ2 - Use IRQ 2 (same as IRQ 9 as far as software is concerned)
+ IRQ3 - Use IRQ 3 (used by COM2 or COM4 serial port if either exists)
+ IRQ4 - Use IRQ 4 (used by COM1 or COM3 serial port if either exists)
+ IRQ5 - Use IRQ 5 (used by LPT2 parallel port if one exists)
+ IRQ6 - Use IRQ 6 (used by Floppy Disk Controller if one exists)
+ IRQ7 - Use IRQ 7 (used by LPT1 parallel port if one exists)
+
+[Avery's note: This "unknown" set of two jumpers appears to be on all
+ARCnet cards by SMC as well. Putting jumpers on them seems to affect the
+status register, but only for the two "reserved" bits, ETS1 and ETS2. Any
+further information is welcome.]
+
+ ET1 - What is this? (Not tested, no jumper put on it)
+ ET2 - What is this? (Not tested, no jumper put on it)
+
+ There is a J2 jumper on two pins. A jumper should be put on them,
+ since it was already there when I got the card. I don't know what
+ this jumper is for though.
+
+ There is a two-jumper array for J3. I don't know what it is for,
+ but there were already two jumpers on it when I got the card. It's
+ a six pin grid in a two-by-three fashion. The jumpers were
+ configured as follows:
+
+ .-------.
+ o | o o |
+ :-------: ------> Accessible end of card with connectors
+ o | o o | in this direction ------->
+ `-------'
+
+ There is also a J4 jumper on two pins. A jumper should be put on
+ them, since it was already there when I got the card. I don't know
+ what this jumper is for though.
+
+
+DIP Switches:
+
+ The dip switches accessible on the accessible end of the card while
+ it is installed, is used to set the arcnet address. There are 8
+ switches. Use an address from 1 to 254.
+
+ Switch No.
+ 12345678 Arcnet address
+ -----------------------------------------
+ 00000000 FF (Don't use this!)
+ 00000001 FE
+ 00000010 FD
+ ....
+ 11111101 2
+ 11111110 1
+ 11111111 0 (Don't use this!)
+
+ There is another dipswitch array of 8 switches at the top of the
+ card. There are five labelled MS0-MS4 which seem to control the
+ memory address, and another three labelled IO0-IO2 which seem to
+ control the base I/O address of the card.
+
+ This was difficult to test by trial and error, and the I/O addresses
+ are in a weird order. This was tested by setting the DIP switches,
+ rebooting the computer, and attempting to load ARCETHER at various
+ addresses (mostly between 0x200 and 0x400). The address that caused
+ the red transmit LED to blink, is the one that I thought works.
+
+ Also, the address 0x3D0 seem to have a special meaning, since the
+ ARCETHER packet driver loaded fine, but without the red LED
+ blinking. I don't know what 0x3D0 is for though. I recommend using
+ an address of 0x300 since Windows may not like addresses below
+ 0x300.
+
+ IO Switch No.
+ 210 I/O address
+ -------------------------------
+ 111 0x260
+ 110 0x290
+ 101 0x2E0
+ 100 0x2F0
+ 011 0x300
+ 010 0x350
+ 001 0x380
+ 000 0x3E0
+
+ The memory switches set a reserved address space of 0x1000 bytes
+ (0x100 segment units, or 4k). For example if I set an address of
+ 0xD000, it will use up addresses 0xD000 to 0xD100.
+
+ The memory switches were tested by booting using QEMM386 stealth,
+ and using LOADHI to see what address automatically became excluded
+ from the upper memory regions, and then attempting to load ARCETHER
+ using these addresses.
+
+ I recommend using an arcnet memory address of 0xD000, and putting
+ the EMS page frame at 0xC000 while using QEMM stealth mode. That
+ way, you get contiguous high memory from 0xD100 almost all the way
+ the end of the megabyte.
+
+ Memory Switch 0 (MS0) didn't seem to work properly when set to OFF
+ on my card. It could be malfunctioning on my card. Experiment with
+ it ON first, and if it doesn't work, set it to OFF. (It may be a
+ modifier for the 0x200 bit?)
+
+ MS Switch No.
+ 43210 Memory address
+ --------------------------------
+ 00001 0xE100 (guessed - was not detected by QEMM)
+ 00011 0xE000 (guessed - was not detected by QEMM)
+ 00101 0xDD00
+ 00111 0xDC00
+ 01001 0xD900
+ 01011 0xD800
+ 01101 0xD500
+ 01111 0xD400
+ 10001 0xD100
+ 10011 0xD000
+ 10101 0xCD00
+ 10111 0xCC00
+ 11001 0xC900 (guessed - crashes tested system)
+ 11011 0xC800 (guessed - crashes tested system)
+ 11101 0xC500 (guessed - crashes tested system)
+ 11111 0xC400 (guessed - crashes tested system)
+
+
+*****************************************************************************
+
+** CNet Technology Inc. **
+120 Series (8-bit cards)
+------------------------
+ - from Juergen Seifert <seifert@htwm.de>
+
+CNET TECHNOLOGY INC. (CNet) ARCNET 120A SERIES
+==============================================
+
+This description has been written by Juergen Seifert <seifert@htwm.de>
+using information from the following Original CNet Manual
+
+ "ARCNET
+ USER'S MANUAL
+ for
+ CN120A
+ CN120AB
+ CN120TP
+ CN120ST
+ CN120SBT
+ P/N:12-01-0007
+ Revision 3.00"
+
+ARCNET is a registered trademark of the Datapoint Corporation
+
+P/N 120A ARCNET 8 bit XT/AT Star
+P/N 120AB ARCNET 8 bit XT/AT Bus
+P/N 120TP ARCNET 8 bit XT/AT Twisted Pair
+P/N 120ST ARCNET 8 bit XT/AT Star, Twisted Pair
+P/N 120SBT ARCNET 8 bit XT/AT Star, Bus, Twisted Pair
+
+ __________________________________________________________________
+ | |
+ | ___|
+ | LED |___|
+ | ___|
+ | N | | ID7
+ | o | | ID6
+ | d | S | ID5
+ | e | W | ID4
+ | ___________________ A | 2 | ID3
+ | | | d | | ID2
+ | | | 1 2 3 4 5 6 7 8 d | | ID1
+ | | | _________________ r |___| ID0
+ | | 90C65 || SW1 | ____|
+ | JP 8 7 | ||_________________| | |
+ | |o|o| JP1 | | | J2 |
+ | |o|o| |oo| | | JP 1 1 1 | |
+ | ______________ | | 0 1 2 |____|
+ | | PROM | |___________________| |o|o|o| _____|
+ | > SOCKET | JP 6 5 4 3 2 |o|o|o| | J1 |
+ | |______________| |o|o|o|o|o| |o|o|o| |_____|
+ |_____ |o|o|o|o|o| ______________|
+ | |
+ |_____________________________________________|
+
+Legend:
+
+90C65 ARCNET Probe
+S1 1-5: Base Memory Address Select
+ 6-8: Base I/O Address Select
+S2 1-8: Node ID Select (ID0-ID7)
+JP1 ROM Enable Select
+JP2 IRQ2
+JP3 IRQ3
+JP4 IRQ4
+JP5 IRQ5
+JP6 IRQ7
+JP7/JP8 ET1, ET2 Timeout Parameters
+JP10/JP11 Coax / Twisted Pair Select (CN120ST/SBT only)
+JP12 Terminator Select (CN120AB/ST/SBT only)
+J1 BNC RG62/U Connector (all except CN120TP)
+J2 Two 6-position Telephone Jack (CN120TP/ST/SBT only)
+
+Setting one of the switches to Off means "1", On means "0".
+
+
+Setting the Node ID
+-------------------
+
+The eight switches in SW2 are used to set the node ID. Each node attached
+to the network must have an unique node ID which must be different from 0.
+Switch 1 (ID0) serves as the least significant bit (LSB).
+
+The node ID is the sum of the values of all switches set to "1"
+These values are:
+
+ Switch | Label | Value
+ -------|-------|-------
+ 1 | ID0 | 1
+ 2 | ID1 | 2
+ 3 | ID2 | 4
+ 4 | ID3 | 8
+ 5 | ID4 | 16
+ 6 | ID5 | 32
+ 7 | ID6 | 64
+ 8 | ID7 | 128
+
+Some Examples:
+
+ Switch | Hex | Decimal
+ 8 7 6 5 4 3 2 1 | Node ID | Node ID
+ ----------------|---------|---------
+ 0 0 0 0 0 0 0 0 | not allowed
+ 0 0 0 0 0 0 0 1 | 1 | 1
+ 0 0 0 0 0 0 1 0 | 2 | 2
+ 0 0 0 0 0 0 1 1 | 3 | 3
+ . . . | |
+ 0 1 0 1 0 1 0 1 | 55 | 85
+ . . . | |
+ 1 0 1 0 1 0 1 0 | AA | 170
+ . . . | |
+ 1 1 1 1 1 1 0 1 | FD | 253
+ 1 1 1 1 1 1 1 0 | FE | 254
+ 1 1 1 1 1 1 1 1 | FF | 255
+
+
+Setting the I/O Base Address
+----------------------------
+
+The last three switches in switch block SW1 are used to select one
+of eight possible I/O Base addresses using the following table
+
+
+ Switch | Hex I/O
+ 6 7 8 | Address
+ ------------|--------
+ ON ON ON | 260
+ OFF ON ON | 290
+ ON OFF ON | 2E0 (Manufacturer's default)
+ OFF OFF ON | 2F0
+ ON ON OFF | 300
+ OFF ON OFF | 350
+ ON OFF OFF | 380
+ OFF OFF OFF | 3E0
+
+
+Setting the Base Memory (RAM) buffer Address
+--------------------------------------------
+
+The memory buffer (RAM) requires 2K. The base of this buffer can be
+located in any of eight positions. The address of the Boot Prom is
+memory base + 8K or memory base + 0x2000.
+Switches 1-5 of switch block SW1 select the Memory Base address.
+
+ Switch | Hex RAM | Hex ROM
+ 1 2 3 4 5 | Address | Address *)
+ --------------------|---------|-----------
+ ON ON ON ON ON | C0000 | C2000
+ ON ON OFF ON ON | C4000 | C6000
+ ON ON ON OFF ON | CC000 | CE000
+ ON ON OFF OFF ON | D0000 | D2000 (Manufacturer's default)
+ ON ON ON ON OFF | D4000 | D6000
+ ON ON OFF ON OFF | D8000 | DA000
+ ON ON ON OFF OFF | DC000 | DE000
+ ON ON OFF OFF OFF | E0000 | E2000
+
+*) To enable the Boot ROM install the jumper JP1
+
+Note: Since the switches 1 and 2 are always set to ON it may be possible
+ that they can be used to add an offset of 2K, 4K or 6K to the base
+ address, but this feature is not documented in the manual and I
+ haven't tested it yet.
+
+
+Setting the Interrupt Line
+--------------------------
+
+To select a hardware interrupt level install one (only one!) of the jumpers
+JP2, JP3, JP4, JP5, JP6. JP2 is the default.
+
+ Jumper | IRQ
+ -------|-----
+ 2 | 2
+ 3 | 3
+ 4 | 4
+ 5 | 5
+ 6 | 7
+
+
+Setting the Internal Terminator on CN120AB/TP/SBT
+--------------------------------------------------
+
+The jumper JP12 is used to enable the internal terminator.
+
+ -----
+ 0 | 0 |
+ ----- ON | | ON
+ | 0 | | 0 |
+ | | OFF ----- OFF
+ | 0 | 0
+ -----
+ Terminator Terminator
+ disabled enabled
+
+
+Selecting the Connector Type on CN120ST/SBT
+-------------------------------------------
+
+ JP10 JP11 JP10 JP11
+ ----- -----
+ 0 0 | 0 | | 0 |
+ ----- ----- | | | |
+ | 0 | | 0 | | 0 | | 0 |
+ | | | | ----- -----
+ | 0 | | 0 | 0 0
+ ----- -----
+ Coaxial Cable Twisted Pair Cable
+ (Default)
+
+
+Setting the Timeout Parameters
+------------------------------
+
+The jumpers labeled EXT1 and EXT2 are used to determine the timeout
+parameters. These two jumpers are normally left open.
+
+
+
+*****************************************************************************
+
+** CNet Technology Inc. **
+160 Series (16-bit cards)
+-------------------------
+ - from Juergen Seifert <seifert@htwm.de>
+
+CNET TECHNOLOGY INC. (CNet) ARCNET 160A SERIES
+==============================================
+
+This description has been written by Juergen Seifert <seifert@htwm.de>
+using information from the following Original CNet Manual
+
+ "ARCNET
+ USER'S MANUAL
+ for
+ CN160A
+ CN160AB
+ CN160TP
+ P/N:12-01-0006
+ Revision 3.00"
+
+ARCNET is a registered trademark of the Datapoint Corporation
+
+P/N 160A ARCNET 16 bit XT/AT Star
+P/N 160AB ARCNET 16 bit XT/AT Bus
+P/N 160TP ARCNET 16 bit XT/AT Twisted Pair
+
+ ___________________________________________________________________
+ < _________________________ ___|
+ > |oo| JP2 | | LED |___|
+ < |oo| JP1 | 9026 | LED |___|
+ > |_________________________| ___|
+ < N | | ID7
+ > 1 o | | ID6
+ < 1 2 3 4 5 6 7 8 9 0 d | S | ID5
+ > _______________ _____________________ e | W | ID4
+ < | PROM | | SW1 | A | 2 | ID3
+ > > SOCKET | |_____________________| d | | ID2
+ < |_______________| | IO-Base | MEM | d | | ID1
+ > r |___| ID0
+ < ____|
+ > | |
+ < | J1 |
+ > | |
+ < |____|
+ > 1 1 1 1 |
+ < 3 4 5 6 7 JP 8 9 0 1 2 3 |
+ > |o|o|o|o|o| |o|o|o|o|o|o| |
+ < |o|o|o|o|o| __ |o|o|o|o|o|o| ___________|
+ > | | |
+ <____________| |_______________________________________|
+
+Legend:
+
+9026 ARCNET Probe
+SW1 1-6: Base I/O Address Select
+ 7-10: Base Memory Address Select
+SW2 1-8: Node ID Select (ID0-ID7)
+JP1/JP2 ET1, ET2 Timeout Parameters
+JP3-JP13 Interrupt Select
+J1 BNC RG62/U Connector (CN160A/AB only)
+J1 Two 6-position Telephone Jack (CN160TP only)
+LED
+
+Setting one of the switches to Off means "1", On means "0".
+
+
+Setting the Node ID
+-------------------
+
+The eight switches in SW2 are used to set the node ID. Each node attached
+to the network must have an unique node ID which must be different from 0.
+Switch 1 (ID0) serves as the least significant bit (LSB).
+
+The node ID is the sum of the values of all switches set to "1"
+These values are:
+
+ Switch | Label | Value
+ -------|-------|-------
+ 1 | ID0 | 1
+ 2 | ID1 | 2
+ 3 | ID2 | 4
+ 4 | ID3 | 8
+ 5 | ID4 | 16
+ 6 | ID5 | 32
+ 7 | ID6 | 64
+ 8 | ID7 | 128
+
+Some Examples:
+
+ Switch | Hex | Decimal
+ 8 7 6 5 4 3 2 1 | Node ID | Node ID
+ ----------------|---------|---------
+ 0 0 0 0 0 0 0 0 | not allowed
+ 0 0 0 0 0 0 0 1 | 1 | 1
+ 0 0 0 0 0 0 1 0 | 2 | 2
+ 0 0 0 0 0 0 1 1 | 3 | 3
+ . . . | |
+ 0 1 0 1 0 1 0 1 | 55 | 85
+ . . . | |
+ 1 0 1 0 1 0 1 0 | AA | 170
+ . . . | |
+ 1 1 1 1 1 1 0 1 | FD | 253
+ 1 1 1 1 1 1 1 0 | FE | 254
+ 1 1 1 1 1 1 1 1 | FF | 255
+
+
+Setting the I/O Base Address
+----------------------------
+
+The first six switches in switch block SW1 are used to select the I/O Base
+address using the following table:
+
+ Switch | Hex I/O
+ 1 2 3 4 5 6 | Address
+ ------------------------|--------
+ OFF ON ON OFF OFF ON | 260
+ OFF ON OFF ON ON OFF | 290
+ OFF ON OFF OFF OFF ON | 2E0 (Manufacturer's default)
+ OFF ON OFF OFF OFF OFF | 2F0
+ OFF OFF ON ON ON ON | 300
+ OFF OFF ON OFF ON OFF | 350
+ OFF OFF OFF ON ON ON | 380
+ OFF OFF OFF OFF OFF ON | 3E0
+
+Note: Other IO-Base addresses seem to be selectable, but only the above
+ combinations are documented.
+
+
+Setting the Base Memory (RAM) buffer Address
+--------------------------------------------
+
+The switches 7-10 of switch block SW1 are used to select the Memory
+Base address of the RAM (2K) and the PROM.
+
+ Switch | Hex RAM | Hex ROM
+ 7 8 9 10 | Address | Address
+ ----------------|---------|-----------
+ OFF OFF ON ON | C0000 | C8000
+ OFF OFF ON OFF | D0000 | D8000 (Default)
+ OFF OFF OFF ON | E0000 | E8000
+
+Note: Other MEM-Base addresses seem to be selectable, but only the above
+ combinations are documented.
+
+
+Setting the Interrupt Line
+--------------------------
+
+To select a hardware interrupt level install one (only one!) of the jumpers
+JP3 through JP13 using the following table:
+
+ Jumper | IRQ
+ -------|-----------------
+ 3 | 14
+ 4 | 15
+ 5 | 12
+ 6 | 11
+ 7 | 10
+ 8 | 3
+ 9 | 4
+ 10 | 5
+ 11 | 6
+ 12 | 7
+ 13 | 2 (=9) Default!
+
+Note: - Do not use JP11=IRQ6, it may conflict with your Floppy Disk
+ Controller
+ - Use JP3=IRQ14 only, if you don't have an IDE-, MFM-, or RLL-
+ Hard Disk, it may conflict with their controllers
+
+
+Setting the Timeout Parameters
+------------------------------
+
+The jumpers labeled JP1 and JP2 are used to determine the timeout
+parameters. These two jumpers are normally left open.
+
+
+*****************************************************************************
+
+** No Name **
+8-bit cards, 16-bit cards
+-------------------------
+ - from Juergen Seifert <seifert@htwm.de>
+
+NONAME 8-BIT ARCNET
+===================
+
+I have named this ARCnet card "NONAME", since there is no name of any
+manufacturer on the Installation manual nor on the shipping box. The only
+hint to the existence of a manufacturer at all is written into copper,
+it is "Made in Taiwan"
+
+This description has been written by Juergen Seifert <seifert@htwm.de>
+using information from the Original
+ "ARCnet Installation Manual"
+
+
+ ________________________________________________________________
+ | |STAR| BUS| T/P| |
+ | |____|____|____| |
+ | _____________________ |
+ | | | |
+ | | | |
+ | | | |
+ | | SMC | |
+ | | | |
+ | | COM90C65 | |
+ | | | |
+ | | | |
+ | |__________-__________| |
+ | _____|
+ | _______________ | CN |
+ | | PROM | |_____|
+ | > SOCKET | |
+ | |_______________| 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 |
+ | _______________ _______________ |
+ | |o|o|o|o|o|o|o|o| | SW1 || SW2 ||
+ | |o|o|o|o|o|o|o|o| |_______________||_______________||
+ |___ 2 3 4 5 7 E E R Node ID IOB__|__MEM____|
+ | \ IRQ / T T O |
+ |__________________1_2_M______________________|
+
+Legend:
+
+COM90C65: Arcnet Probe
+S1 1-8: Node ID Select
+S2 1-3: I/O Base Address Select
+ 4-6: Memory Base Address Select
+ 7-8: RAM Offset Select
+ET1, ET2 Extended Timeout Select
+ROM ROM Enable Select
+CN RG62 Coax Connector
+STAR| BUS | T/P Three fields for placing a sign (colored circle)
+ indicating the topology of the card
+
+Setting one of the switches to Off means "1", On means "0".
+
+
+Setting the Node ID
+-------------------
+
+The eight switches in group SW1 are used to set the node ID.
+Each node attached to the network must have an unique node ID which
+must be different from 0.
+Switch 8 serves as the least significant bit (LSB).
+
+The node ID is the sum of the values of all switches set to "1"
+These values are:
+
+ Switch | Value
+ -------|-------
+ 8 | 1
+ 7 | 2
+ 6 | 4
+ 5 | 8
+ 4 | 16
+ 3 | 32
+ 2 | 64
+ 1 | 128
+
+Some Examples:
+
+ Switch | Hex | Decimal
+ 1 2 3 4 5 6 7 8 | Node ID | Node ID
+ ----------------|---------|---------
+ 0 0 0 0 0 0 0 0 | not allowed
+ 0 0 0 0 0 0 0 1 | 1 | 1
+ 0 0 0 0 0 0 1 0 | 2 | 2
+ 0 0 0 0 0 0 1 1 | 3 | 3
+ . . . | |
+ 0 1 0 1 0 1 0 1 | 55 | 85
+ . . . | |
+ 1 0 1 0 1 0 1 0 | AA | 170
+ . . . | |
+ 1 1 1 1 1 1 0 1 | FD | 253
+ 1 1 1 1 1 1 1 0 | FE | 254
+ 1 1 1 1 1 1 1 1 | FF | 255
+
+
+Setting the I/O Base Address
+----------------------------
+
+The first three switches in switch group SW2 are used to select one
+of eight possible I/O Base addresses using the following table
+
+ Switch | Hex I/O
+ 1 2 3 | Address
+ ------------|--------
+ ON ON ON | 260
+ ON ON OFF | 290
+ ON OFF ON | 2E0 (Manufacturer's default)
+ ON OFF OFF | 2F0
+ OFF ON ON | 300
+ OFF ON OFF | 350
+ OFF OFF ON | 380
+ OFF OFF OFF | 3E0
+
+
+Setting the Base Memory (RAM) buffer Address
+--------------------------------------------
+
+The memory buffer requires 2K of a 16K block of RAM. The base of this
+16K block can be located in any of eight positions.
+Switches 4-6 of switch group SW2 select the Base of the 16K block.
+Within that 16K address space, the buffer may be assigned any one of four
+positions, determined by the offset, switches 7 and 8 of group SW2.
+
+ Switch | Hex RAM | Hex ROM
+ 4 5 6 7 8 | Address | Address *)
+ -----------|---------|-----------
+ 0 0 0 0 0 | C0000 | C2000
+ 0 0 0 0 1 | C0800 | C2000
+ 0 0 0 1 0 | C1000 | C2000
+ 0 0 0 1 1 | C1800 | C2000
+ | |
+ 0 0 1 0 0 | C4000 | C6000
+ 0 0 1 0 1 | C4800 | C6000
+ 0 0 1 1 0 | C5000 | C6000
+ 0 0 1 1 1 | C5800 | C6000
+ | |
+ 0 1 0 0 0 | CC000 | CE000
+ 0 1 0 0 1 | CC800 | CE000
+ 0 1 0 1 0 | CD000 | CE000
+ 0 1 0 1 1 | CD800 | CE000
+ | |
+ 0 1 1 0 0 | D0000 | D2000 (Manufacturer's default)
+ 0 1 1 0 1 | D0800 | D2000
+ 0 1 1 1 0 | D1000 | D2000
+ 0 1 1 1 1 | D1800 | D2000
+ | |
+ 1 0 0 0 0 | D4000 | D6000
+ 1 0 0 0 1 | D4800 | D6000
+ 1 0 0 1 0 | D5000 | D6000
+ 1 0 0 1 1 | D5800 | D6000
+ | |
+ 1 0 1 0 0 | D8000 | DA000
+ 1 0 1 0 1 | D8800 | DA000
+ 1 0 1 1 0 | D9000 | DA000
+ 1 0 1 1 1 | D9800 | DA000
+ | |
+ 1 1 0 0 0 | DC000 | DE000
+ 1 1 0 0 1 | DC800 | DE000
+ 1 1 0 1 0 | DD000 | DE000
+ 1 1 0 1 1 | DD800 | DE000
+ | |
+ 1 1 1 0 0 | E0000 | E2000
+ 1 1 1 0 1 | E0800 | E2000
+ 1 1 1 1 0 | E1000 | E2000
+ 1 1 1 1 1 | E1800 | E2000
+
+*) To enable the 8K Boot PROM install the jumper ROM.
+ The default is jumper ROM not installed.
+
+
+Setting Interrupt Request Lines (IRQ)
+-------------------------------------
+
+To select a hardware interrupt level set one (only one!) of the jumpers
+IRQ2, IRQ3, IRQ4, IRQ5 or IRQ7. The manufacturer's default is IRQ2.
+
+
+Setting the Timeouts
+--------------------
+
+The two jumpers labeled ET1 and ET2 are used to determine the timeout
+parameters (response and reconfiguration time). Every node in a network
+must be set to the same timeout values.
+
+ ET1 ET2 | Response Time (us) | Reconfiguration Time (ms)
+ --------|--------------------|--------------------------
+ Off Off | 78 | 840 (Default)
+ Off On | 285 | 1680
+ On Off | 563 | 1680
+ On On | 1130 | 1680
+
+On means jumper installed, Off means jumper not installed
+
+
+NONAME 16-BIT ARCNET
+====================
+
+The manual of my 8-Bit NONAME ARCnet Card contains another description
+of a 16-Bit Coax / Twisted Pair Card. This description is incomplete,
+because there are missing two pages in the manual booklet. (The table
+of contents reports pages ... 2-9, 2-11, 2-12, 3-1, ... but inside
+the booklet there is a different way of counting ... 2-9, 2-10, A-1,
+(empty page), 3-1, ..., 3-18, A-1 (again), A-2)
+Also the picture of the board layout is not as good as the picture of
+8-Bit card, because there isn't any letter like "SW1" written to the
+picture.
+Should somebody have such a board, please feel free to complete this
+description or to send a mail to me!
+
+This description has been written by Juergen Seifert <seifert@htwm.de>
+using information from the Original
+ "ARCnet Installation Manual"
+
+
+ ___________________________________________________________________
+ < _________________ _________________ |
+ > | SW? || SW? | |
+ < |_________________||_________________| |
+ > ____________________ |
+ < | | |
+ > | | |
+ < | | |
+ > | | |
+ < | | |
+ > | | |
+ < | | |
+ > |____________________| |
+ < ____|
+ > ____________________ | |
+ < | | | J1 |
+ > | < | |
+ < |____________________| ? ? ? ? ? ? |____|
+ > |o|o|o|o|o|o| |
+ < |o|o|o|o|o|o| |
+ > |
+ < __ ___________|
+ > | | |
+ <____________| |_______________________________________|
+
+
+Setting one of the switches to Off means "1", On means "0".
+
+
+Setting the Node ID
+-------------------
+
+The eight switches in group SW2 are used to set the node ID.
+Each node attached to the network must have an unique node ID which
+must be different from 0.
+Switch 8 serves as the least significant bit (LSB).
+
+The node ID is the sum of the values of all switches set to "1"
+These values are:
+
+ Switch | Value
+ -------|-------
+ 8 | 1
+ 7 | 2
+ 6 | 4
+ 5 | 8
+ 4 | 16
+ 3 | 32
+ 2 | 64
+ 1 | 128
+
+Some Examples:
+
+ Switch | Hex | Decimal
+ 1 2 3 4 5 6 7 8 | Node ID | Node ID
+ ----------------|---------|---------
+ 0 0 0 0 0 0 0 0 | not allowed
+ 0 0 0 0 0 0 0 1 | 1 | 1
+ 0 0 0 0 0 0 1 0 | 2 | 2
+ 0 0 0 0 0 0 1 1 | 3 | 3
+ . . . | |
+ 0 1 0 1 0 1 0 1 | 55 | 85
+ . . . | |
+ 1 0 1 0 1 0 1 0 | AA | 170
+ . . . | |
+ 1 1 1 1 1 1 0 1 | FD | 253
+ 1 1 1 1 1 1 1 0 | FE | 254
+ 1 1 1 1 1 1 1 1 | FF | 255
+
+
+Setting the I/O Base Address
+----------------------------
+
+The first three switches in switch group SW1 are used to select one
+of eight possible I/O Base addresses using the following table
+
+ Switch | Hex I/O
+ 3 2 1 | Address
+ ------------|--------
+ ON ON ON | 260
+ ON ON OFF | 290
+ ON OFF ON | 2E0 (Manufacturer's default)
+ ON OFF OFF | 2F0
+ OFF ON ON | 300
+ OFF ON OFF | 350
+ OFF OFF ON | 380
+ OFF OFF OFF | 3E0
+
+
+Setting the Base Memory (RAM) buffer Address
+--------------------------------------------
+
+The memory buffer requires 2K of a 16K block of RAM. The base of this
+16K block can be located in any of eight positions.
+Switches 6-8 of switch group SW1 select the Base of the 16K block.
+Within that 16K address space, the buffer may be assigned any one of four
+positions, determined by the offset, switches 4 and 5 of group SW1.
+
+ Switch | Hex RAM | Hex ROM
+ 8 7 6 5 4 | Address | Address
+ -----------|---------|-----------
+ 0 0 0 0 0 | C0000 | C2000
+ 0 0 0 0 1 | C0800 | C2000
+ 0 0 0 1 0 | C1000 | C2000
+ 0 0 0 1 1 | C1800 | C2000
+ | |
+ 0 0 1 0 0 | C4000 | C6000
+ 0 0 1 0 1 | C4800 | C6000
+ 0 0 1 1 0 | C5000 | C6000
+ 0 0 1 1 1 | C5800 | C6000
+ | |
+ 0 1 0 0 0 | CC000 | CE000
+ 0 1 0 0 1 | CC800 | CE000
+ 0 1 0 1 0 | CD000 | CE000
+ 0 1 0 1 1 | CD800 | CE000
+ | |
+ 0 1 1 0 0 | D0000 | D2000 (Manufacturer's default)
+ 0 1 1 0 1 | D0800 | D2000
+ 0 1 1 1 0 | D1000 | D2000
+ 0 1 1 1 1 | D1800 | D2000
+ | |
+ 1 0 0 0 0 | D4000 | D6000
+ 1 0 0 0 1 | D4800 | D6000
+ 1 0 0 1 0 | D5000 | D6000
+ 1 0 0 1 1 | D5800 | D6000
+ | |
+ 1 0 1 0 0 | D8000 | DA000
+ 1 0 1 0 1 | D8800 | DA000
+ 1 0 1 1 0 | D9000 | DA000
+ 1 0 1 1 1 | D9800 | DA000
+ | |
+ 1 1 0 0 0 | DC000 | DE000
+ 1 1 0 0 1 | DC800 | DE000
+ 1 1 0 1 0 | DD000 | DE000
+ 1 1 0 1 1 | DD800 | DE000
+ | |
+ 1 1 1 0 0 | E0000 | E2000
+ 1 1 1 0 1 | E0800 | E2000
+ 1 1 1 1 0 | E1000 | E2000
+ 1 1 1 1 1 | E1800 | E2000
+
+
+Setting Interrupt Request Lines (IRQ)
+-------------------------------------
+
+??????????????????????????????????????
+
+
+Setting the Timeouts
+--------------------
+
+??????????????????????????????????????
+
+
+*****************************************************************************
+
+Other Cards
+-----------
+
+I have no information on other models of ARCnet cards at the moment. Please
+send any and all info to:
+ apenwarr@tourism.807-city.on.ca
+
+Thanks.
diff --git a/drivers/net/README.de4x5 b/drivers/net/README.de4x5
new file mode 100644
index 000000000..1952fa76c
--- /dev/null
+++ b/drivers/net/README.de4x5
@@ -0,0 +1,48 @@
+The de425/de434/de435/de500 driver in this distribution is designed to work
+with the Digital Equipment Corporation series of PCI/EISA ethernet cards
+(DE425, DE434, DE435, DE500) and with all kernels that support PCI.
+
+Auto media detection is provided so that the media choice isn't compiled in
+and is flexible enough to be able to reconfigure on-the-fly. This feature
+hasn't been included for the DE500 unfortunately, due to a potential patent
+dispute. When I get around to implementing the autosense algorithm by myself
+(which could legally be difficult to prove since I'm part of the group that
+has implemented the patented algorithm) you'll have an auto speed selection
+for the de500. If you want the auto speed feature yell at Digital. If enough
+of you do things might change.
+
+The ability to load this driver as a loadable module has been included,
+although I don't recommend its use with PCI, since PCI dynamically allocates
+where the card will go at boot time (i.e. the card would have to be present
+in the system at boot time for its address/IRQ to be assigned).
+
+The performance we've achieved so far has been measured through the 'ttcp'
+tool at 1.06MB/s for TCP and 1.17MB/s for UDP. This measures the total
+stack performance which includes the card, so don't expect to get much
+nearer the 1.25MB/s theoretical ethernet rate.
+
+ TCP UDP
+ TX RX TX RX
+ DE425 1030k 997k 1170k 1128k (EISA on a Dell 433DE)
+ DE434 1063k 995k 1170k 1125k (PCI: DECpc XL 466d2)
+ DE435 1063k 995k 1170k 1125k (PCI: DECpc XL 466d2)
+ DE500 1063k 998k 1170k 1125k in 10Mb/s mode (PCI: DECpc XL 466d2)
+
+All values are typical (in kBytes/sec) from a sample of 4 for each
+measurement. Their error is approx +/-20k on a quiet (private) network and
+also depend on what load the CPU has, CPU speed etc.
+
+ZYNX and SMC cards, which use the PCI DECchip DC21040, are not specifically
+supported in this driver because
+
+a) I have no information on them.
+b) I cannot test them with the driver.
+c) Donald Becker's 'tulip.c' driver works with them....well one person says
+ they do and another says they do not, so take your pick!
+
+This driver can be made to work with the ZYNX (and may be the SMC) card by
+setting a compile time flag (IS_NOT_DEC) in linux/drivers/net/CONFIG
+
+Enjoy!
+
+Dave
diff --git a/drivers/net/README.eql b/drivers/net/README.eql
new file mode 100644
index 000000000..db5ceb6b9
--- /dev/null
+++ b/drivers/net/README.eql
@@ -0,0 +1,528 @@
+ EQL Driver: Serial IP Load Balancing HOWTO
+ Simon "Guru Aleph-Null" Janes, simon@ncm.com
+ v1.1, Feburary 27, 1995
+
+ This is the manual for the EQL device driver. EQL is a software device
+ that lets you load-balance IP serial links (SLIP or uncompressed PPP)
+ to increase your bandwidth. It will not reduce your latency (i.e. ping
+ times) except in the case where you already have lots of traffic on
+ your link, in which it will help them out. This driver has been tested
+ with the 1.1.75 kernel, and is known to have patched cleanly with
+ 1.1.86. Some testing with 1.1.92 has been done with the v1.1 patch
+ which was only created to patch cleanly in the very latest kernel
+ source trees. (Yes, it worked fine.)
+
+ 1. Introduction
+
+ Which is worse? A huge fee for a 56K leased line or two phone lines?
+ Its probably the former. If you find yourself craving more bandwidth,
+ and have a ISP that is flexible, it is now possible to bind modems
+ together to work as one point-to-point link to increase your
+ bandwidth. All without having to have a special black box on either
+ side.
+
+
+ The eql driver has only been tested with the Livingston PortMaster-2e
+ terminal server. I do not know if other terminal servers support load-
+ balancing, but I do know that the PortMaster does it, and does it
+ almost as well as the eql driver seems to do it (-- Unfortunately, in
+ my testing so far, the Livingston PortMaster 2e's load-balancing is a
+ good 1 to 2 KB/s slower than the test machine working with a 28.8 Kbps
+ and 14.4 Kbps connection. However, I am not sure that it really is
+ the PortMaster, or if its Linux's TCP drivers. I'm told that Linux's
+ TCP implementation is pretty fast though.--)
+
+
+ I suggest to ISP's out there that it would probably be fair to charge
+ a load-balancing client 75% of the cost of the second line and 50% of
+ the cost of the third line etc...
+
+
+ Hey, we can all dream you know...
+
+
+ 2. Kernel Configuration
+
+ Here I describe the general steps of getting a kernel up and working
+ with the eql driver. From patching, building, to installing.
+
+
+ 2.1. Patching The Kernel
+
+ If you do not have or cannot get a copy of the kernel with the eql
+ driver folded into it, get your copy of the driver from
+ ftp://slaughter.ncm.com/pub/Linux/LOAD_BALANCING/eql-1.1.tar.gz.
+ Unpack this archive someplace obvious like /usr/local/src/. It will
+ create the following files:
+
+
+
+ ______________________________________________________________________
+ -rw-r--r-- guru/ncm 198 Jan 19 18:53 1995 eql-1.1/NO-WARRANTY
+ -rw-r--r-- guru/ncm 30620 Feb 27 21:40 1995 eql-1.1/eql-1.1.patch
+ -rwxr-xr-x guru/ncm 16111 Jan 12 22:29 1995 eql-1.1/eql_enslave
+ -rw-r--r-- guru/ncm 2195 Jan 10 21:48 1995 eql-1.1/eql_enslave.c
+ ______________________________________________________________________
+
+ Unpack a recent kernel (something after 1.1.92) Someplace convenient
+ like say /usr/src/linux-1.1.92.eql. Use symbolic links to point
+ /usr/src/linux to this development directory.
+
+
+ Apply the patch by running the commands:
+
+
+ ______________________________________________________________________
+ cd /usr/src
+ patch </usr/local/src/eql-1.1/eql-1.1.patch
+ ______________________________________________________________________
+
+
+
+
+
+ 2.2. Building The Kernel
+
+ After patching the kernel, run make config and configure the kernel
+ for your hardware.
+
+
+ After configuration, make and install according to your habit.
+
+
+ 3. Network Configuration
+
+ So far, I have only used the eql device with the DSLIP SLIP connection
+ manager by Matt Dillon (-- "The man who sold his soul to code so much
+ so quickly."--) . How you configure it for other "connection"
+ managers is up to you. Most other connection managers that I've seen
+ don't do a very good job when it comes to handling more than one
+ connection.
+
+
+ 3.1. /etc/rc.d/rc.inet1
+
+ In rc.inet1, ifconfig the eql device to the IP address you usually use
+ for your machine, and the MTU you prefer for your SLIP lines. One
+ could argue that MTU should be roughly half the usuall size for two
+ modems, one-third for three, one-fourth for four, etc... But going
+ too far below 296 is probably overkill. Here is an example ifconfig
+ command that sets up the eql device:
+
+
+
+ ______________________________________________________________________
+ ifconfig eql 198.67.33.239 mtu 1006
+ ______________________________________________________________________
+
+
+
+
+
+ Once the eql device is up and running, add a static default route to
+ it in the routing table using the cool new route syntax that makes
+ life so much easier:
+
+
+
+ ______________________________________________________________________
+ route add default eql
+ ______________________________________________________________________
+
+
+ 3.2. Enslaving Devices By Hand
+
+ Enslaving devices by hand requires two utility programs: eql_enslave
+ and eql_emancipate (-- eql_emancipate hasn't been written because when
+ an enslaved device "dies", it is automatically taken out of the queue.
+ I haven't found a good reason to write it yet... other than for
+ completeness, but that isn't a good motivator is it?--)
+
+
+ The syntax for enslaving a device is "eql_enslave <master-name>
+ <slave-name> <estimated-bps>". Here are some example enslavings:
+
+
+
+ ______________________________________________________________________
+ eql_enslave eql sl0 28800
+ eql_enslave eql ppp0 14400
+ eql_enslave eql sl1 57600
+ ______________________________________________________________________
+
+
+
+
+
+ When you want to free a device from its life of slavery, you can
+ either down the device with ifconfig (eql will automatically bury the
+ dead slave and remove it from its queue) or use eql_emancipate to free
+ it. (-- Or just ifconfig it down, and the eql driver will take it out
+ for you.--)
+
+
+
+ ______________________________________________________________________
+ eql_emancipate eql sl0
+ eql_emancipate eql ppp0
+ eql_emancipate eql sl1
+ ______________________________________________________________________
+
+
+
+
+
+ 3.3. DSLIP Configuration for the eql Device
+
+ The general idea is to bring up and keep up as many SLIP connections
+ as you need, automatically.
+
+
+ 3.3.1. /etc/slip/runslip.conf
+
+ Here is an example runslip.conf:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ______________________________________________________________________
+ name sl-line-1
+ enabled
+ baud 38400
+ mtu 576
+ ducmd -e /etc/slip/dialout/cua2-288.xp -t 9
+ command eql_enslave eql $interface 28800
+ address 198.67.33.239
+ line /dev/cua2
+
+ name sl-line-2
+ enabled
+ baud 38400
+ mtu 576
+ ducmd -e /etc/slip/dialout/cua3-288.xp -t 9
+ command eql_enslave eql $interface 28800
+ address 198.67.33.239
+ line /dev/cua3
+ ______________________________________________________________________
+
+
+
+
+
+ 3.4. Using PPP and the eql Device
+
+ I have not yet done any load-balancing testing for PPP devices, mainly
+ because I don't have a PPP-connection manager like SLIP has with
+ DSLIP. I did find a good tip from LinuxNET:Billy for PPP performance:
+ make sure you have asyncmap set to something so that control
+ characters are not escaped.
+
+
+ I tried to fix up a PPP script/system for redialing lost PPP
+ connections for use with the eql driver the weekend of Feb 25-26 '95
+ (Hereafter known as the 8-hour PPP Hate Festival). Perhaps later this
+ year.
+
+
+ 4. About the Slave Scheduler Algorithm
+
+ The slave scheduler probably could be replaced with a dozen other
+ things and push traffic much faster. The formula in the current set
+ up of the driver was tuned to handle slaves with wildly different
+ bits-per-second "priorities".
+
+
+ All testing I have done was with two 28.8 V.FC modems, one connecting
+ at 28800 bps or slower, and the other connecting at 14400 bps all the
+ time.
+
+
+ One version of the scheduler was able to push 5.3 K/s through the
+ 28800 and 14400 connections, but when the priorities on the links were
+ very wide apart (57600 vs. 14400) The "faster" modem received all
+ traffic and the "slower" modem starved.
+
+
+ 5. Tester's Reports
+
+ Some people have experimented with the eql device with newer kernels
+ kernels (than 1.1.75). I have since updated the driver to patch
+ cleanly in newer kernels because of the removal of the old "slave-
+ balancing" driver config option.
+
+
+ o icee from LinuxNET patched 1.1.86 without any rejects and was able
+ to boot the kernel and enslave a couple of ISDN PPP links.
+
+ 5.1. Randoph Bentson's Test Report
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ From bentson@grieg.seaslug.org Wed Feb 8 19:08:09 1995
+ Date: Tue, 7 Feb 95 22:57 PST
+ From: Randolph Bentson <bentson@grieg.seaslug.org>
+ To: guru@ncm.com
+ Subject: EQL driver tests
+
+
+ I have been checking out your eql driver. (Nice work, that!)
+ Although you may already done this performance testing, here
+ are some data I've discovered.
+
+ Randolph Bentson
+ bentson@grieg.seaslug.org
+
+ ---------------------------------------------------------
+
+
+ A pseudo-device driver, EQL, written by Simon Janes, can be used
+ to bundle multiple SLIP connections into what appears to be a
+ single connection. This allows one to improve dial-up network
+ connectivity gradually, without having to buy expensive DSU/CSU
+ hardware and services.
+
+ I have done some testing of this software, with two goals in
+ mind: first, to ensure it actually works as described and
+ second, as a method of exercising my device driver.
+
+ The following performance measurements were derived from a set
+ of SLIP connections run between two Linux systems (1.1.84) using
+ a 486DX2/66 with a Cyclom-8Ys and a 486SLC/40 with a Cyclom-16Y.
+ (Ports 0,1,2,3 were used. A later configuration will distribute
+ port selection across the different Cirrus chips on the boards.)
+ Once a link was established, I timed a binary ftp transfer of
+ 289284 bytes of data. If there were no overhead (packet headers,
+ inter-character and inter-packet delays, etc.) the transfers
+ would take the following times:
+
+ bits/sec seconds
+ 345600 8.3
+ 234600 12.3
+ 172800 16.7
+ 153600 18.8
+ 76800 37.6
+ 57600 50.2
+ 38400 75.3
+ 28800 100.4
+ 19200 150.6
+ 9600 301.3
+
+ A single line running at the lower speeds and with large packets
+ comes to within 2% of this. Performance is limited for the higher
+ speeds (as predicted by the Cirrus databook) to an aggregate of
+ about 160 kbits/sec. The next round of testing will distribute
+ the load across two or more Cirrus chips.
+
+ The good news is that one gets nearly the full advantage of the
+ second, third, and fourth line's bandwidth. (The bad news is
+ that the connection establishment seemed fragile for the higher
+ speeds. Once established, the connection seemed robust enough.)
+
+ #lines speed mtu seconds theory actual %of
+ kbit/sec duration speed speed max
+ 3 115200 900 _ 345600
+ 3 115200 400 18.1 345600 159825 46
+ 2 115200 900 _ 230400
+ 2 115200 600 18.1 230400 159825 69
+ 2 115200 400 19.3 230400 149888 65
+ 4 57600 900 _ 234600
+ 4 57600 600 _ 234600
+ 4 57600 400 _ 234600
+ 3 57600 600 20.9 172800 138413 80
+ 3 57600 900 21.2 172800 136455 78
+ 3 115200 600 21.7 345600 133311 38
+ 3 57600 400 22.5 172800 128571 74
+ 4 38400 900 25.2 153600 114795 74
+ 4 38400 600 26.4 153600 109577 71
+ 4 38400 400 27.3 153600 105965 68
+ 2 57600 900 29.1 115200 99410.3 86
+ 1 115200 900 30.7 115200 94229.3 81
+ 2 57600 600 30.2 115200 95789.4 83
+ 3 38400 900 30.3 115200 95473.3 82
+ 3 38400 600 31.2 115200 92719.2 80
+ 1 115200 600 31.3 115200 92423 80
+ 2 57600 400 32.3 115200 89561.6 77
+ 1 115200 400 32.8 115200 88196.3 76
+ 3 38400 400 33.5 115200 86353.4 74
+ 2 38400 900 43.7 76800 66197.7 86
+ 2 38400 600 44 76800 65746.4 85
+ 2 38400 400 47.2 76800 61289 79
+ 4 19200 900 50.8 76800 56945.7 74
+ 4 19200 400 53.2 76800 54376.7 70
+ 4 19200 600 53.7 76800 53870.4 70
+ 1 57600 900 54.6 57600 52982.4 91
+ 1 57600 600 56.2 57600 51474 89
+ 3 19200 900 60.5 57600 47815.5 83
+ 1 57600 400 60.2 57600 48053.8 83
+ 3 19200 600 62 57600 46658.7 81
+ 3 19200 400 64.7 57600 44711.6 77
+ 1 38400 900 79.4 38400 36433.8 94
+ 1 38400 600 82.4 38400 35107.3 91
+ 2 19200 900 84.4 38400 34275.4 89
+ 1 38400 400 86.8 38400 33327.6 86
+ 2 19200 600 87.6 38400 33023.3 85
+ 2 19200 400 91.2 38400 31719.7 82
+ 4 9600 900 94.7 38400 30547.4 79
+ 4 9600 400 106 38400 27290.9 71
+ 4 9600 600 110 38400 26298.5 68
+ 3 9600 900 118 28800 24515.6 85
+ 3 9600 600 120 28800 24107 83
+ 3 9600 400 131 28800 22082.7 76
+ 1 19200 900 155 19200 18663.5 97
+ 1 19200 600 161 19200 17968 93
+ 1 19200 400 170 19200 17016.7 88
+ 2 9600 600 176 19200 16436.6 85
+ 2 9600 900 180 19200 16071.3 83
+ 2 9600 400 181 19200 15982.5 83
+ 1 9600 900 305 9600 9484.72 98
+ 1 9600 600 314 9600 9212.87 95
+ 1 9600 400 332 9600 8713.37 90
+
+
+
+
+
+ 5.2. Anthony Healy's Report
+
+
+
+
+
+
+
+ Date: Mon, 13 Feb 1995 16:17:29 +1100 (EST)
+ From: Antony Healey <ahealey@st.nepean.uws.edu.au>
+ To: Simon Janes <guru@ncm.com>
+ Subject: Re: Load Balancing
+
+ Hi Simon,
+ I've installed your patch and it works great. I have trialed
+ it over twin SL/IP lines, just over null modems, but I was
+ able to data at over 48Kb/s [ISDN link -Simon]. I managed a
+ transfer of upto 7.5 Kbyte/s on one go, but averaged around
+ 6.4 Kbyte/s, which I think is pretty cool. :)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/drivers/net/README.tunnel b/drivers/net/README.tunnel
new file mode 100644
index 000000000..9736b484f
--- /dev/null
+++ b/drivers/net/README.tunnel
@@ -0,0 +1,123 @@
+
+This is the alpha version of my IP tunneling driver.
+
+Protocol Tunneling:
+
+ A network tunneling driver encapsulates packets of one
+protocol type within packets of another protocol type. It sends
+them out over the network to a relay (or destination) where the
+packet is unwrapped and is forwarded to it's ultimate destination.
+Packet tunneling is useful in situations where you want to route
+packets of a non-standard protocol type over the common network.
+A good example of this is 'IPX encapsulation', in which IPX packets
+from a DOS network are routed across an IP network by encapsulating
+them in IP packets.
+
+ There are two parts to every protocol tunnel. There is
+the encapsulator, and the decapsulator. The encapsulator wraps
+the packets in the host protocol and sends them on their way,
+while the decapsulator takes wrapped packets at the other end
+and unwraps them and forwards them (or whatever else should be
+done with them.)
+
+ IP tunneling is a specific case of protocol tunneling,
+in which the encapsulating protocol is IP, and the encapsulated
+protocol may be any other protocol, including Apple-Talk, IPX,
+or even IP within IP.
+
+ For more information on the semantics and specifications
+of IP encapsulation, see RFC-1241, also included in this package.
+
+
+My Implementation:
+
+ My implementation of IP tunneling for Linux consists
+of two loadable module drivers, one an encapsulator (tunnel.o)
+and the other a decapsulator (ipip.o). Both are used for
+setting up a working IP-in-IP tunnel. Currently, the drivers
+only support IP encapsulated in IP.
+
+ The tunnel driver is implemented as a network device,
+based on the Linux loopback driver written (in part) by Ross Biro,
+Fred N. van Kempen, and Donald Becker. After the driver is
+loaded, it can be set up as any other network interface, using
+ifconfig. The tunnel device is given it's own IP address, which
+can match that of the machine, and also is given a pointopoint
+address. This pointopoint address is the address of the machine
+providing the decapsulating endpoint for the IP tunnel. After
+the device is configured for use, the 'route' command can be used
+to route traffic through the IP tunnel. There must be a route to
+the decapsulating endpoint that does not go through the tunnel
+device, otherwise a looping tunnel is created, preventing the
+network traffic from leaving the local endpoint.
+
+ The decapsulating endpoint must have loaded the ipip.o
+decapsulator module for it to understand IP-in-IP encapsulation.
+This module takes any IP-in-IP packet that is destined for the local
+machine, unwraps it, and sends it on it's way, using standard
+routing rules. The current implementation of IP decapsulation does
+no checking on the packet, other than making sure wrapper is bound
+for the local machine.
+
+ Note that the above setup only provides a one-way pipe.
+To provide a full two-way IP tunnel, the decapsulation host must
+set up an IP encapsulation driver, and the encapsulating host must
+load the IP decapsulation module, providing full duplex communication
+through the IP tunnel.
+
+An example setup might be as follows.
+
+ Machine A has an ethernet interface with an IP address
+of 111.112.101.37, while machine B is on a different network, with
+an ethernet interface at IP address 111.112.100.86. For some
+reason, machine A needs to appear on machine B's network. It could
+do that by setting up an IP tunnel with machine B.
+
+First, the commands that would be run on machine A:
+(Assuming both machines are Linux hosts, running Linux 1.1.x)
+
+# insmod ipip.o ; insmod tunnel.o // Here the drivers are loaded.
+# ifconfig tunl 111.112.100.87 pointopoint 111.112.100.86
+# ifconfig tunl netmask 255.255.255.0 // Set a proper netmask.
+# route add 111.112.100.86 dev eth0 // Set a static route to B.
+# route add -net 111.112.100.0 dev tunl // Set up other routes.
+
+At this point, machine A is ready to route all traffic to the
+network that machine B resides on. But now, machine B needs to
+set up its half of the IP tunnel:
+
+# insmod ipip.o ; insmod tunnel.o // Here the drivers are loaded.
+# ifconfig tunl 111.112.100.86 pointopoint 111.112.101.37
+# ifconfig tunl netmask 255.255.255.0 // Set a proper netmask.
+# route add 111.112.100.87 dev eth0 // Set a static route to B.
+# arp -s 111.112.100.87 EE.EE.EE.EE.EE pub // Act as a proxy arp server.
+
+The extra step of "arp -s" is needed so that when machines on
+network B query to see if 111.112.100.87 (the "ghost" host)
+exists, machine B will respond, acting as an arp proxy for machine
+A. In the command line, EE.EE.EE.EE.EE should be replaced with
+the ethernet hardware address of machine B's ethernet card.
+
+Notice that machine B's setup is almost the inverse of machine A's
+setup. This is because IP tunneling is a peer-to-peer concept.
+There is no client and no server, there is no state to keep track
+of. The concept is simple. Every IP packet outbound through the
+tunnel interface is wrapped and sent to the pointopoint address
+and every incoming IP-in-IP packet bound for the local machine is
+unwrapped and re-routed normally.
+The only difference in the two machines setup shown above is that
+machine A set it's tunnel address to one existing on machine B's
+network, while B set a route to machine A's tunnel device address
+through the tunnel. This is because machine A wants to have a new
+address on network B, and machine B is simply acting as a proxy
+for machine A. Machine A needs it's tunnel address to be on network
+B so that when packets from machine B are unwrapped, the Linux
+routing system knows that the address is a local one. Due to a
+feature of Linux, any packets recieved locally, bound for another
+local address, are simply routed through the loopback interface.
+This means that the tunnel device should never recieve packets. Even
+on machine B, it is the ethernet interface that is receiving wrapped
+packets, and once they are unwrapped they go back out the ethernet
+interface. This could cause Linux to generate ICMP redirect messages
+if this special routing case isn't caught (see /linux/net/inet/ip.c)
+
diff --git a/drivers/net/README.wavelan b/drivers/net/README.wavelan
new file mode 100644
index 000000000..435dba8eb
--- /dev/null
+++ b/drivers/net/README.wavelan
@@ -0,0 +1,31 @@
+#if defined(CONFIG_WAVELAN)
+
+Thu Feb 23 00:10:31 EST 1995
+
+1. At present the driver autoprobes for a WaveLAN card only at I/O address 0x390.
+ The version of the card that I use (NCR) supports four I/O addresses (selectable
+ via a pair of DIP switches). If you want the driver to autoprobe a different
+ subset of the four valid addresses then you will need to edit
+ .../drivers/net/wavelan.c (near line 714) and change the initialisation of the
+ `iobase[]' array. Normally, I use a LILO configuration file directive to
+ obviate the need for autoprobing entirely, a course of action I heartily
+ recommend.
+
+2. By default, the driver uses the Network ID (NWID) stored in the card's Parameter
+ Storage Area (PSA). However, the PSA NWID can be overridden by a value passed
+ explicitly as the third numeric argument to LILO's "ether=" directive, either
+ at the LILO prompt at boot time or within LILO's configuration file.
+ For example, the following line from such a LILO configuration file would
+ auto-configure the IRQ value, set the I/O base to 0x390 and set the NWID to
+ 0x4321, all on a WaveLAN card labelled "eth0":
+
+ ..
+ append ="ether=0,0x390,0x4321,eth0"
+ ..
+
+3. If you encounter any problems send me some email.
+
+Good luck,
+Bruce Janson (bruce@cs.usyd.edu.au)
+
+#endif /* defined(CONFIG_WAVELAN) */
diff --git a/drivers/net/Space.c b/drivers/net/Space.c
index 4a82265e5..21c35fd89 100644
--- a/drivers/net/Space.c
+++ b/drivers/net/Space.c
@@ -19,6 +19,9 @@
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
* Donald J. Becker, <becker@super.org>
*
+ * FIXME:
+ * Sort the device chain fastest first.
+ *
* 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
@@ -28,8 +31,6 @@
#include <linux/netdevice.h>
#include <linux/errno.h>
-#define LOOPBACK /* always present, right? */
-
#define NEXT_DEV NULL
@@ -49,14 +50,20 @@ extern int el3_probe(struct device *);
extern int at1500_probe(struct device *);
extern int at1700_probe(struct device *);
extern int depca_probe(struct device *);
+extern int apricot_probe(struct device *);
extern int ewrk3_probe(struct device *);
+extern int de4x5_probe(struct device *);
extern int el1_probe(struct device *);
+#if defined(CONFIG_WAVELAN)
+extern int wavelan_probe(struct device *);
+#endif /* defined(CONFIG_WAVELAN) */
extern int el16_probe(struct device *);
extern int elplus_probe(struct device *);
extern int ac3200_probe(struct device *);
extern int e2100_probe(struct device *);
extern int ni52_probe(struct device *);
extern int ni65_probe(struct device *);
+extern int sonic_probe(struct device *);
extern int SK_init(struct device *);
/* Detachable devices ("pocket adaptors") */
@@ -112,9 +119,18 @@ ethif_probe(struct device *dev)
#ifdef CONFIG_EWRK3 /* DEC EtherWORKS 3 */
&& ewrk3_probe(dev)
#endif
+#ifdef CONFIG_DE4X5 /* DEC DE425, DE434, DE435 adapters */
+ && de4x5_probe(dev)
+#endif
+#ifdef CONFIG_APRICOT /* Apricot I82596 */
+ && apricot_probe(dev)
+#endif
#ifdef CONFIG_EL1 /* 3c501 */
&& el1_probe(dev)
#endif
+#if defined(CONFIG_WAVELAN) /* WaveLAN */
+ && wavelan_probe(dev)
+#endif /* defined(CONFIG_WAVELAN) */
#ifdef CONFIG_EL16 /* 3c507 */
&& el16_probe(dev)
#endif
@@ -142,6 +158,9 @@ ethif_probe(struct device *dev)
#ifdef CONFIG_NI65
&& ni65_probe(dev)
#endif
+#ifdef CONFIG_MIPS_JAZZ_SONIC
+ && sonic_probe(dev)
+#endif
&& 1 ) {
return 1; /* -ENODEV or -EAGAIN would be more accurate. */
}
@@ -149,6 +168,17 @@ ethif_probe(struct device *dev)
}
+#ifdef CONFIG_NETROM
+ extern int nr_init(struct device *);
+
+ static struct device nr3_dev = { "nr3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, nr_init, };
+ static struct device nr2_dev = { "nr2", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr3_dev, nr_init, };
+ static struct device nr1_dev = { "nr1", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr2_dev, nr_init, };
+ static struct device nr0_dev = { "nr0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr1_dev, nr_init, };
+
+# undef NEXT_DEV
+# define NEXT_DEV (&nr0_dev)
+#endif
/* Run-time ATtachable (Pocket) devices have a different (not "eth#") name. */
#ifdef CONFIG_ATP /* AT-LAN-TEC (RealTek) pocket adaptor. */
@@ -158,6 +188,14 @@ static struct device atp_dev = {
# define NEXT_DEV (&atp_dev)
#endif
+#ifdef CONFIG_ARCNET
+ extern int arcnet_probe(struct device *dev);
+ static struct device arcnet_dev = {
+ "arc0", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, arcnet_probe, };
+# undef NEXT_DEV
+# define NEXT_DEV (&arcnet_dev)
+#endif
+
/* The first device defaults to I/O base '0', which means autoprobe. */
#ifndef ETH0_ADDR
# define ETH0_ADDR 0
@@ -269,6 +307,24 @@ static struct device eth0_dev = {
#if defined(CONFIG_PPP)
extern int ppp_init(struct device *);
+#ifdef CONFIG_PPP_LOTS
+
+ static struct device ppp15_dev={"ppp15",0,0,0,0,15,0,0,0,0,NEXT_DEV,ppp_init};
+ static struct device ppp14_dev={"ppp14",0,0,0,0,14,0,0,0,0,&ppp15_dev,ppp_init};
+ static struct device ppp13_dev={"ppp13",0,0,0,0,13,0,0,0,0,&ppp14_dev,ppp_init};
+ static struct device ppp12_dev={"ppp12",0,0,0,0,12,0,0,0,0,&ppp13_dev,ppp_init};
+ static struct device ppp11_dev={"ppp11",0,0,0,0,11,0,0,0,0,&ppp12_dev,ppp_init};
+ static struct device ppp10_dev={"ppp10",0,0,0,0,10,0,0,0,0,&ppp11_dev,ppp_init};
+ static struct device ppp9_dev={"ppp9",0,0,0,0,9,0,0,0,0,&ppp10_dev,ppp_init};
+ static struct device ppp8_dev={"ppp8",0,0,0,0,8,0,0,0,0,&ppp9_dev,ppp_init};
+ static struct device ppp7_dev={"ppp7",0,0,0,0,7,0,0,0,0,&ppp8_dev,ppp_init};
+ static struct device ppp6_dev={"ppp6",0,0,0,0,6,0,0,0,0,&ppp7_dev,ppp_init};
+ static struct device ppp5_dev={"ppp5",0,0,0,0,5,0,0,0,0,&ppp6_dev,ppp_init};
+ static struct device ppp4_dev={"ppp4",0,0,0,0,4,0,0,0,0,&ppp5_dev,ppp_init};
+# undef NEXT_DEV
+# define NEXT_DEV (&ppp4_dev)
+#endif /* CONFIG_PPP_LOTS */
+
static struct device ppp3_dev = {
"ppp3", 0x0, 0x0, 0x0, 0x0, 3, 0, 0, 0, 0, NEXT_DEV, ppp_init, };
static struct device ppp2_dev = {
@@ -289,23 +345,107 @@ static struct device ppp0_dev = {
# define NEXT_DEV (&dummy_dev)
#endif
-#ifdef LOOPBACK
- extern int loopback_init(struct device *dev);
- static struct device loopback_dev = {
- "lo", /* Software Loopback interface */
+#ifdef CONFIG_EQUALIZER
+extern int eql_init(struct device *dev);
+struct device eql_dev = {
+ "eql", /* Master device for IP traffic load
+ balancing */
+ 0x0, 0x0, 0x0, 0x0, /* recv end/start; mem end/start */
+ 0, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ NEXT_DEV, /* next device */
+ eql_init /* set up the rest */
+};
+# undef NEXT_DEV
+# define NEXT_DEV (&eql_dev)
+#endif
+
+#ifdef CONFIG_IBMTR
+
+ extern int tok_probe(struct device *dev);
+ static struct device ibmtr_dev1 = {
+ "tr1", /* IBM Token Ring (Non-DMA) Interface */
0x0, /* recv memory end */
0x0, /* recv memory start */
0x0, /* memory end */
0x0, /* memory start */
- 0, /* base I/O address */
+ 0xa24, /* base I/O address */
0, /* IRQ */
0, 0, 0, /* flags */
NEXT_DEV, /* next device */
- loopback_init /* loopback_init should set up the rest */
+ tok_probe /* ??? Token_init should set up the rest */
};
# undef NEXT_DEV
-# define NEXT_DEV (&loopback_dev)
-#endif
+# define NEXT_DEV (&ibmtr_dev1)
+
+
+ extern int tok_probe(struct device *dev);
+ static struct device ibmtr_dev0 = {
+ "tr0", /* IBM Token Ring (Non-DMA) Interface */
+ 0x0, /* recv memory end */
+ 0x0, /* recv memory start */
+ 0x0, /* memory end */
+ 0x0, /* memory start */
+ 0xa20, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ NEXT_DEV, /* next device */
+ tok_probe /* ??? Token_init should set up the rest */
+ };
+# undef NEXT_DEV
+# define NEXT_DEV (&ibmtr_dev0)
+
+#endif
+#ifdef CONFIG_NET_IPIP
+#ifdef CONFIG_IP_FORWARD
+ extern int tunnel_init(struct device *);
+
+ static struct device tunnel_dev1 =
+ {
+ "tunl1", /* IPIP tunnel */
+ 0x0, /* recv memory end */
+ 0x0, /* recv memory start */
+ 0x0, /* memory end */
+ 0x0, /* memory start */
+ 0x0, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ NEXT_DEV, /* next device */
+ tunnel_init /* Fill in the details */
+ };
+
+ static struct device tunnel_dev0 =
+ {
+ "tunl0", /* IPIP tunnel */
+ 0x0, /* recv memory end */
+ 0x0, /* recv memory start */
+ 0x0, /* memory end */
+ 0x0, /* memory start */
+ 0x0, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ &tunnel_dev1, /* next device */
+ tunnel_init /* Fill in the details */
+ };
+# undef NEXT_DEV
+# define NEXT_DEV (&tunnel_dev0)
+#endif
+#endif
+
+extern int loopback_init(struct device *dev);
+struct device loopback_dev = {
+ "lo", /* Software Loopback interface */
+ 0x0, /* recv memory end */
+ 0x0, /* recv memory start */
+ 0x0, /* memory end */
+ 0x0, /* memory start */
+ 0, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ NEXT_DEV, /* next device */
+ loopback_init /* loopback_init should set up the rest */
+};
-struct device *dev_base = NEXT_DEV;
+struct device *dev_base = &loopback_dev;
diff --git a/drivers/net/ac3200.c b/drivers/net/ac3200.c
index 27f9a8022..8e7424d96 100644
--- a/drivers/net/ac3200.c
+++ b/drivers/net/ac3200.c
@@ -17,7 +17,6 @@
static char *version =
"ac3200.c:v1.01 7/1/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
diff --git a/drivers/net/apricot.c b/drivers/net/apricot.c
index f3da8c8e9..79ee2fb14 100644
--- a/drivers/net/apricot.c
+++ b/drivers/net/apricot.c
@@ -3,6 +3,8 @@
Apricot
Written 1994 by Mark Evans.
This driver is for the Apricot 82596 bus-master interface
+
+ Modularised 12/94 Mark Evans
Driver skeleton
Written 1993 by Donald Becker.
@@ -17,9 +19,13 @@
*/
-static char *version = "apricot.c:v0.02 19/05/94\n";
+static char *version = "apricot.c:v0.2 05/12/94\n";
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
@@ -38,7 +44,7 @@ static char *version = "apricot.c:v0.02 19/05/94\n";
#ifndef HAVE_PORTRESERVE
#define check_region(addr, size) 0
-#define snarf_region(addr, size) do ; while(0)
+#define request_region(addr, size,name) do ; while(0)
#endif
#ifndef HAVE_ALLOC_SKB
@@ -46,9 +52,6 @@ static char *version = "apricot.c:v0.02 19/05/94\n";
#define kfree_skbmem(buff, size) kfree_s(buff,size)
#endif
-struct device *init_etherdev(struct device *dev, int sizeof_private,
- unsigned long *mem_start);
-
#define APRICOT_DEBUG 1
#ifdef APRICOT_DEBUG
@@ -59,6 +62,8 @@ int i596_debug = 1;
#define APRICOT_TOTAL_SIZE 17
+#define I596_NULL -1
+
#define CMD_EOL 0x8000 /* The last command of the list, stop. */
#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */
#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */
@@ -116,7 +121,7 @@ struct i596_rfd {
char data[1532];
};
-#define RX_RING_SIZE 16
+#define RX_RING_SIZE 8
struct i596_scb {
unsigned short status;
@@ -154,7 +159,6 @@ struct i596_private {
char i596_config[16];
struct i596_cmd tdr;
unsigned long stat;
- struct i596_rfd rx[RX_RING_SIZE];
int last_restart;
struct i596_rfd *rx_tail;
struct i596_cmd *cmd_tail;
@@ -180,11 +184,9 @@ char init_setup[] = {
0x00,
0x7f /* *multi IA */ };
-char adds[] = {0x00, 0x00, 0x49, 0x20, 0x54, 0xDA, 0x80, 0x00, 0x4e, 0x02, 0xb7, 0xb8};
-
static int i596_open(struct device *dev);
static int i596_start_xmit(struct sk_buff *skb, struct device *dev);
-static void i596_interrupt(int reg_ptr);
+static void i596_interrupt(int irq, struct pt_regs *regs);
static int i596_close(struct device *dev);
static struct enet_statistics *i596_get_stats(struct device *dev);
static void i596_add_cmd(struct device *dev, struct i596_cmd *cmd);
@@ -194,52 +196,59 @@ static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
#endif
-static inline void
-init_rx_bufs(struct device *dev)
+static inline int
+init_rx_bufs(struct device *dev, int num)
{
struct i596_private *lp = (struct i596_private *)dev->priv;
int i;
- int boguscnt = 100;
- short ioaddr = dev->base_addr;
+ struct i596_rfd *rfd;
+
+ lp->scb.rfd = (struct i596_rfd *)I596_NULL;
- if (i596_debug > 1) printk ("%s: init_rx_bufs.\n", dev->name);
+ if (i596_debug > 1) printk ("%s: init_rx_bufs %d.\n", dev->name, num);
- for (i = 0; i < RX_RING_SIZE; i++)
+ for (i = 0; i < num; i++)
{
- if (i == 0)
- {
- lp->scb.rfd = &lp->rx[0];
- }
- if (i == (RX_RING_SIZE - 1))
- {
- lp->rx_tail = &(lp->rx[i]);
- lp->rx[i].next = &lp->rx[0];
- lp->rx[i].cmd = CMD_EOL;
- }
- else
- {
- lp->rx[i].next = &lp->rx[i+1];
- lp->rx[i].cmd = 0x0000;
- }
- lp->rx[i].stat = 0x0000;
- lp->rx[i].rbd = 0xffffffff;
- lp->rx[i].count = 0;
- lp->rx[i].size = 1532;
+ if (!(rfd = (struct i596_rfd *)kmalloc(sizeof(struct i596_rfd), GFP_KERNEL)))
+ break;
+
+ rfd->stat = 0x0000;
+ rfd->rbd = I596_NULL;
+ rfd->count = 0;
+ rfd->size = 1532;
+ if (i == 0)
+ {
+ rfd->cmd = CMD_EOL;
+ lp->rx_tail = rfd;
+ }
+ else
+ rfd->cmd = 0x0000;
+
+ rfd->next = lp->scb.rfd;
+ lp->scb.rfd = rfd;
}
- while (lp->scb.status, lp->scb.command)
- if (--boguscnt == 0)
- {
- printk("%s: init_rx_bufs timed out with status %4.4x, cmd %4.4x.\n",
- dev->name, lp->scb.status, lp->scb.command);
- break;
- }
+ if (i != 0)
+ lp->rx_tail->next = lp->scb.rfd;
- lp->scb.command = RX_START;
- outw(0, ioaddr+4);
+ return (i);
+}
- return;
+static inline void
+remove_rx_bufs(struct device *dev)
+{
+ struct i596_private *lp = (struct i596_private *)dev->priv;
+ struct i596_rfd *rfd = lp->scb.rfd;
+ lp->rx_tail->next = (struct i596_rfd *)I596_NULL;
+
+ do
+ {
+ lp->scb.rfd = rfd->next;
+ kfree_s(rfd, sizeof(struct i596_rfd));
+ rfd = lp->scb.rfd;
+ }
+ while (rfd != lp->rx_tail);
}
static inline void
@@ -256,7 +265,7 @@ init_i596_mem(struct device *dev)
outw(((((int)&lp->scp) & 0xffff) | 2), ioaddr);
outw((((int)&lp->scp)>>16) & 0xffff, ioaddr);
- lp->last_cmd=jiffies;
+ lp->last_cmd = jiffies;
lp->scp.sysbus = 0x00440000;
lp->scp.iscp = &(lp->iscp);
@@ -264,7 +273,7 @@ init_i596_mem(struct device *dev)
lp->iscp.stat = 0x0001;
lp->cmd_backlog = 0;
- lp->cmd_head = lp->scb.cmd = (struct i596_cmd *) -1;
+ lp->cmd_head = lp->scb.cmd = (struct i596_cmd *) I596_NULL;
if (i596_debug > 2) printk("%s: starting i82596.\n", dev->name);
@@ -280,6 +289,8 @@ init_i596_mem(struct device *dev)
break;
}
+ lp->scb.command = 0;
+
memcpy (lp->i596_config, init_setup, 14);
lp->set_conf.command = CmdConfigure;
i596_add_cmd(dev, &lp->set_conf);
@@ -291,9 +302,19 @@ init_i596_mem(struct device *dev)
lp->tdr.command = CmdTDR;
i596_add_cmd(dev, &lp->tdr);
- init_rx_bufs(dev);
+ boguscnt = 200;
+ while (lp->scb.status, lp->scb.command)
+ if (--boguscnt == 0)
+ {
+ printk("%s: receive unit start timed out with status %4.4x, cmd %4.4x.\n",
+ dev->name, lp->scb.status, lp->scb.command);
+ break;
+ }
+
+ lp->scb.command = RX_START;
+ outw(0, ioaddr+4);
- boguscnt=200;
+ boguscnt = 200;
while (lp->scb.status, lp->scb.command)
if (--boguscnt == 0)
{
@@ -309,7 +330,7 @@ static inline int
i596_rx(struct device *dev)
{
struct i596_private *lp = (struct i596_private *)dev->priv;
- int frames=0;
+ int frames = 0;
if (i596_debug > 3) printk ("i596_rx()\n");
@@ -333,9 +354,10 @@ i596_rx(struct device *dev)
}
skb->len = pkt_len;
- skb->dev=dev;
+ skb->dev = dev;
memcpy(skb->data, lp->scb.rfd->data, pkt_len);
+ skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
lp->stats.rx_packets++;
@@ -353,12 +375,12 @@ i596_rx(struct device *dev)
if ((lp->scb.rfd->stat) & 0x1000) lp->stats.rx_length_errors++;
}
- lp->scb.rfd->stat=0;
- lp->rx_tail->cmd=0;
- lp->rx_tail=lp->scb.rfd;
- lp->scb.rfd=lp->scb.rfd->next;
- lp->rx_tail->count=0;
- lp->rx_tail->cmd=CMD_EOL;
+ lp->scb.rfd->stat = 0;
+ lp->rx_tail->cmd = 0;
+ lp->rx_tail = lp->scb.rfd;
+ lp->scb.rfd = lp->scb.rfd->next;
+ lp->rx_tail->count = 0;
+ lp->rx_tail->cmd = CMD_EOL;
}
@@ -375,7 +397,7 @@ i596_cleanup_cmd(struct i596_private *lp)
if (i596_debug > 4) printk ("i596_cleanup_cmd\n");
- while (lp->cmd_head != (struct i596_cmd *) -1)
+ while (lp->cmd_head != (struct i596_cmd *) I596_NULL)
{
ptr = lp->cmd_head;
@@ -394,7 +416,7 @@ i596_cleanup_cmd(struct i596_private *lp)
lp->stats.tx_errors++;
lp->stats.tx_aborted_errors++;
- ptr->next = (struct i596_cmd * ) -1;
+ ptr->next = (struct i596_cmd * ) I596_NULL;
kfree_s((unsigned char *)tx_cmd, (sizeof (struct tx_cmd) + sizeof (struct i596_tbd)));
break;
}
@@ -402,12 +424,12 @@ i596_cleanup_cmd(struct i596_private *lp)
{
unsigned short count = *((unsigned short *) (ptr + 1));
- ptr->next = (struct i596_cmd * ) -1;
+ ptr->next = (struct i596_cmd * ) I596_NULL;
kfree_s((unsigned char *)ptr, (sizeof (struct i596_cmd) + count + 2));
break;
}
default:
- ptr->next = (struct i596_cmd * ) -1;
+ ptr->next = (struct i596_cmd * ) I596_NULL;
}
}
@@ -437,10 +459,10 @@ i596_reset(struct device *dev, struct i596_private *lp, int ioaddr)
break;
}
- dev->start=0;
- dev->tbusy=1;
+ dev->start = 0;
+ dev->tbusy = 1;
- lp->scb.command=CUC_ABORT|RX_ABORT;
+ lp->scb.command = CUC_ABORT|RX_ABORT;
outw(0, ioaddr+4);
/* wait for shutdown */
@@ -457,9 +479,9 @@ i596_reset(struct device *dev, struct i596_private *lp, int ioaddr)
i596_cleanup_cmd(lp);
i596_rx(dev);
- dev->start=1;
- dev->tbusy=0;
- dev->interrupt=0;
+ dev->start = 1;
+ dev->tbusy = 0;
+ dev->interrupt = 0;
init_i596_mem(dev);
}
@@ -474,15 +496,15 @@ static void i596_add_cmd(struct device *dev, struct i596_cmd *cmd)
cmd->status = 0;
cmd->command |= (CMD_EOL|CMD_INTR);
- cmd->next = (struct i596_cmd *) -1;
+ cmd->next = (struct i596_cmd *) I596_NULL;
save_flags(flags);
cli();
- if (lp->cmd_head != (struct i596_cmd *) -1)
+ if (lp->cmd_head != (struct i596_cmd *) I596_NULL)
lp->cmd_tail->next = cmd;
else
{
- lp->cmd_head=cmd;
+ lp->cmd_head = cmd;
while (lp->scb.status, lp->scb.command)
if (--boguscnt == 0)
{
@@ -495,10 +517,10 @@ static void i596_add_cmd(struct device *dev, struct i596_cmd *cmd)
lp->scb.command = CUC_START;
outw (0, ioaddr+4);
}
- lp->cmd_tail=cmd;
+ lp->cmd_tail = cmd;
lp->cmd_backlog++;
- lp->cmd_head=lp->scb.cmd;
+ lp->cmd_head = lp->scb.cmd;
restore_flags(flags);
if (lp->cmd_backlog > 16)
@@ -516,19 +538,34 @@ static void i596_add_cmd(struct device *dev, struct i596_cmd *cmd)
static int
i596_open(struct device *dev)
{
- if (request_irq(dev->irq, &i596_interrupt, 0, "apricot")) {
+ int i;
+
+ if (i596_debug > 1)
+ printk("%s: i596_open() irq %d.\n", dev->name, dev->irq);
+
+ if (request_irq(dev->irq, &i596_interrupt, 0, "apricot"))
return -EAGAIN;
- }
irq2dev_map[dev->irq] = dev;
- if (i596_debug > 1)
- printk("%s: i596_open() irq %d.\n",
- dev->name, dev->irq);
+ i = init_rx_bufs(dev, RX_RING_SIZE);
+
+ if ((i = init_rx_bufs(dev, RX_RING_SIZE)) < RX_RING_SIZE)
+ printk("%s: only able to allocate %d receive buffers\n", dev->name, i);
+
+ if (i < 4)
+ {
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+ return -EAGAIN;
+ }
dev->tbusy = 0;
dev->interrupt = 0;
dev->start = 1;
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
/* Initialize the 82596 memory */
init_i596_mem(dev);
@@ -563,7 +600,7 @@ i596_start_xmit(struct sk_buff *skb, struct device *dev)
/* Issue a channel attention signal */
if (i596_debug > 1) printk ("Kicking board.\n");
- lp->scb.command=CUC_START|RX_START;
+ lp->scb.command = CUC_START|RX_START;
outw(0, ioaddr+4);
lp->last_restart = lp->stats.tx_packets;
@@ -592,7 +629,7 @@ i596_start_xmit(struct sk_buff *skb, struct device *dev)
else
{
short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
- dev->trans_start=jiffies;
+ dev->trans_start = jiffies;
tx_cmd = (struct tx_cmd *) kmalloc ((sizeof (struct tx_cmd) + sizeof (struct i596_tbd)), GFP_ATOMIC);
if (tx_cmd == NULL)
@@ -605,7 +642,7 @@ i596_start_xmit(struct sk_buff *skb, struct device *dev)
else
{
tx_cmd->tbd = (struct i596_tbd *) (tx_cmd + 1);
- tx_cmd->tbd->next = (struct i596_tbd *) -1;
+ tx_cmd->tbd->next = (struct i596_tbd *) I596_NULL;
tx_cmd->cmd.command = CMD_FLEX|CmdTx;
@@ -646,10 +683,10 @@ static void print_eth(char *add)
printk ("type %2.2X%2.2X\n", (unsigned char)add[12], (unsigned char)add[13]);
}
-unsigned long apricot_init(unsigned long mem_start, unsigned long mem_end)
+int apricot_probe(struct device *dev)
{
- struct device *dev;
int i;
+ struct i596_private *lp;
int checksum = 0;
int ioaddr = 0x300;
char eth_addr[6];
@@ -658,28 +695,30 @@ unsigned long apricot_init(unsigned long mem_start, unsigned long mem_end)
/* first check nothing is already registered here */
if (check_region(ioaddr, APRICOT_TOTAL_SIZE))
- return mem_start;
+ return ENODEV;
for (i = 0; i < 8; i++)
- checksum += inb(ioaddr + 8 + i);
+ {
+ eth_addr[i] = inb(ioaddr+8+i);
+ checksum += eth_addr[i];
+ }
/* checksum is a multiple of 0x100, got this wrong first time
some machines have 0x100, some 0x200. The DOS driver doesn't
even bother with the checksum */
- if (checksum % 0x100) return mem_start;
+ if (checksum % 0x100) return ENODEV;
-
- for(i = 0; i < 6 ; i++)
- eth_addr[i] = inb(ioaddr +8 +i);
-
/* Some other boards trip the checksum.. but then appear as ether
address 0. Trap these - AC */
- if(memcmp(eth_addr,"\x00\x00\x00\x00\x00\x00",6)==0)
- return mem_start;
+ if(memcmp(eth_addr,"\x00\x00\x49",3)!= 0)
+ return ENODEV;
- dev = init_etherdev(0, (sizeof (struct i596_private) + 0xf), &mem_start);
+ request_region(ioaddr, APRICOT_TOTAL_SIZE,"apricot");
+
+ dev->base_addr = ioaddr;
+ ether_setup(dev);
printk("%s: Apricot 82596 at %#3x,", dev->name, ioaddr);
for (i = 0; i < 6; i++)
@@ -689,10 +728,7 @@ unsigned long apricot_init(unsigned long mem_start, unsigned long mem_end)
dev->irq = 10;
printk(" IRQ %d.\n", dev->irq);
- snarf_region(ioaddr, APRICOT_TOTAL_SIZE);
-
- if (i596_debug > 0)
- printk(version);
+ if (i596_debug > 0) printk(version);
/* The APRICOT-specific entries in the device structure. */
dev->open = &i596_open;
@@ -703,21 +739,27 @@ unsigned long apricot_init(unsigned long mem_start, unsigned long mem_end)
dev->set_multicast_list = &set_multicast_list;
#endif
+ dev->mem_start = (int)kmalloc(sizeof(struct i596_private)+ 0x0f, GFP_KERNEL);
/* align for scp */
- dev->priv = (void *)(((int) dev->priv + 0xf) & 0xfffffff0);
+ dev->priv = (void *)((dev->mem_start + 0xf) & 0xfffffff0);
- return mem_start;
+ lp = (struct i596_private *)dev->priv;
+ memset((void *)lp, 0, sizeof(struct i596_private));
+ lp->scb.command = 0;
+ lp->scb.cmd = (struct i596_cmd *) I596_NULL;
+ lp->scb.rfd = (struct i596_rfd *)I596_NULL;
+
+ return 0;
}
static void
-i596_interrupt(int reg_ptr)
+i596_interrupt(int irq, struct pt_regs *regs)
{
- int irq = pt_regs2irq(reg_ptr);
struct device *dev = (struct device *)(irq2dev_map[irq]);
struct i596_private *lp;
short ioaddr;
int boguscnt = 200;
- unsigned short status, ack_cmd=0;
+ unsigned short status, ack_cmd = 0;
if (dev == NULL) {
printk ("i596_interrupt(): irq %d for unknown device.\n", irq);
@@ -757,7 +799,7 @@ i596_interrupt(int reg_ptr)
if ((i596_debug > 4) && (status & 0x2000))
printk("%s: i596 interrupt command unit inactive %x.\n", dev->name, status & 0x0700);
- while ((lp->cmd_head != (struct i596_cmd *) -1) && (lp->cmd_head->status & STAT_C))
+ while ((lp->cmd_head != (struct i596_cmd *) I596_NULL) && (lp->cmd_head->status & STAT_C))
{
ptr = lp->cmd_head;
@@ -788,7 +830,7 @@ i596_interrupt(int reg_ptr)
}
- ptr->next = (struct i596_cmd * ) -1;
+ ptr->next = (struct i596_cmd * ) I596_NULL;
kfree_s((unsigned char *)tx_cmd, (sizeof (struct tx_cmd) + sizeof (struct i596_tbd)));
break;
}
@@ -796,7 +838,7 @@ i596_interrupt(int reg_ptr)
{
unsigned short count = *((unsigned short *) (ptr + 1));
- ptr->next = (struct i596_cmd * ) -1;
+ ptr->next = (struct i596_cmd * ) I596_NULL;
kfree_s((unsigned char *)ptr, (sizeof (struct i596_cmd) + count + 2));
break;
}
@@ -822,20 +864,20 @@ i596_interrupt(int reg_ptr)
}
}
default:
- ptr->next = (struct i596_cmd * ) -1;
+ ptr->next = (struct i596_cmd * ) I596_NULL;
- lp->last_cmd=jiffies;
+ lp->last_cmd = jiffies;
}
}
ptr = lp->cmd_head;
- while ((ptr != (struct i596_cmd *) -1) && (ptr != lp->cmd_tail))
+ while ((ptr != (struct i596_cmd *) I596_NULL) && (ptr != lp->cmd_tail))
{
ptr->command &= 0x1fff;
ptr = ptr->next;
}
- if ((lp->cmd_head != (struct i596_cmd *) -1) && (dev->start)) ack_cmd |= CUC_START;
+ if ((lp->cmd_head != (struct i596_cmd *) I596_NULL) && (dev->start)) ack_cmd |= CUC_START;
lp->scb.cmd = lp->cmd_head;
}
@@ -854,7 +896,7 @@ i596_interrupt(int reg_ptr)
/* acknowledge the interrupt */
/*
- if ((lp->scb.cmd != (struct i596_cmd *) -1) && (dev->start)) ack_cmd |= CUC_START;
+ if ((lp->scb.cmd != (struct i596_cmd *) I596_NULL) && (dev->start)) ack_cmd | = CUC_START;
*/
boguscnt = 100;
while (lp->scb.status, lp->scb.command)
@@ -881,6 +923,7 @@ i596_close(struct device *dev)
{
int ioaddr = dev->base_addr;
struct i596_private *lp = (struct i596_private *)dev->priv;
+ int boguscnt = 200;
dev->start = 0;
dev->tbusy = 1;
@@ -894,8 +937,20 @@ i596_close(struct device *dev)
i596_cleanup_cmd(lp);
+ while (lp->scb.status, lp->scb.command)
+ if (--boguscnt == 0)
+ {
+ printk("%s: close timed timed out with status %4.4x, cmd %4.4x.\n",
+ dev->name, lp->scb.status, lp->scb.command);
+ break;
+ }
free_irq(dev->irq);
irq2dev_map[dev->irq] = 0;
+ remove_rx_bufs(dev);
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+
return 0;
}
@@ -940,7 +995,7 @@ set_multicast_list(struct device *dev, int num_addrs, void *addrs)
i596_add_cmd(dev, cmd);
} else
{
- if (lp->set_conf.next != (struct i596_cmd * ) -1) return;
+ if (lp->set_conf.next != (struct i596_cmd * ) I596_NULL) return;
if (num_addrs == 0)
lp->i596_config[8] &= ~0x01;
else
@@ -954,9 +1009,39 @@ set_multicast_list(struct device *dev, int num_addrs, void *addrs)
#ifdef HAVE_DEVLIST
static unsigned int apricot_portlist[] = {0x300, 0};
-struct netdev_entry apricot_drv =
-{"apricot", apricot_init, APRICOT_TOTAL_SIZE, apricot_portlist};
+struct netdev_entry apricot_drv =
+{"apricot", apricot_probe, APRICOT_TOTAL_SIZE, apricot_portlist};
#endif
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+static struct device dev_apricot = {
+ " ", /* device name inserted by /linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0x300, 10,
+ 0, 0, 0, NULL, apricot_probe };
+
+int
+init_module(void)
+{
+ if (register_netdev(&dev_apricot) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ if (MOD_IN_USE)
+ printk("%s: device busy, remove delayed\n", dev_apricot.name);
+ else
+ {
+ unregister_netdev(&dev_apricot);
+ kfree_s((void *)dev_apricot.mem_start, sizeof(struct i596_private) + 0xf);
+ dev_apricot.priv = NULL;
+ }
+}
+#endif /* MODULE */
/*
* Local variables:
diff --git a/drivers/net/arcnet.c b/drivers/net/arcnet.c
new file mode 100644
index 000000000..1f0fad4bd
--- /dev/null
+++ b/drivers/net/arcnet.c
@@ -0,0 +1,2175 @@
+/* arcnet.c
+ Written 1994-95 by Avery Pennarun, derived from skeleton.c by
+ Donald Becker.
+
+ Contact Avery at: apenwarr@tourism.807-city.on.ca or
+ RR #5 Pole Line Road, Thunder Bay, ON, Canada P7C 5M9
+
+ **********************
+
+ skeleton.c Written 1993 by Donald Becker.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may only be used
+ and distributed according to the terms of the GNU Public License as
+ modified by SRC, incorporated herein by reference.
+
+ **********************
+
+ v1.01 (95/03/24)
+ - Fixed some IPX-related bugs. (Thanks to Tomasz Motylewski
+ <motyl@tichy.ch.uj.edu.pl> for the patches to make arcnet work
+ with dosemu!)
+ v1.0 (95/02/15)
+ - Initial non-alpha release.
+
+
+ TO DO:
+
+ - Test in systems with NON-ARCnet network cards, just to see if
+ autoprobe kills anything. With any luck, it won't. (It's pretty
+ careful.)
+ - Except some unfriendly NE2000's die. (as of 0.40-ALPHA)
+ - cards with shared memory that can be "turned off?"
+ - NFS mount freezes after several megabytes to SOSS for DOS.
+ unmount/remount works. Is this arcnet-specific? I don't know.
+ - Add support for the various stupid bugs ("I didn't read the RFC"
+ syndrome) in Windows for Workgroups and LanMan.
+ */
+
+/**************************************************************************/
+
+/* define this if you want to use the new but possibly dangerous ioprobe
+ * If you get lockups right after status5, you probably need
+ * to undefine this. It should make more cards probe correctly,
+ * I hope.
+ */
+#define DANGER_PROBE
+
+/* define this if you want to use the "extra delays" which were removed
+ * in 0.41 since they seemed needless.
+ */
+#undef EXTRA_DELAYS
+
+/* undefine this if you want to use the non-IRQ-driven transmitter. (possibly
+ * safer, although it takes more CPU time and IRQ_XMIT seems fine right now)
+ */
+#define IRQ_XMIT
+
+/* define this for "careful" transmitting. Try with and without if you have
+ * problems. If you use IRQ_XMIT, do NOT define this.
+ */
+#undef CAREFUL_XMIT
+
+/* define this for an extra-careful memory detect. This should work all
+ * the time now, but you never know.
+ */
+#define STRICT_MEM_DETECT
+
+/* define this to use the "old-style" limited MTU by default. It basically
+ * disables packet splitting. ifconfig can still be used to reset the MTU.
+ *
+ * leave this disabled if possible, so it will use ethernet defaults,
+ * which is our goal.
+ */
+#undef LIMIT_MTU
+
+/* define this if you have a problem with the card getting "stuck" now and
+ * then, which can only be fixed by a reboot or resetting the card manually
+ * via ifconfig up/down. ARCnet will set a timer function which is called
+ * 8 times every second.
+ *
+ * This should no longer be necessary. if you experience "stuck" ARCnet
+ * drivers, please email apenwarr@tourism.807-city.on.ca or I will remove
+ * this feature in a future release.
+ */
+#undef USE_TIMER_HANDLER
+
+/**************************************************************************/
+
+static char *version =
+ "arcnet.c:v1.01 95/03/24 Avery Pennarun <apenwarr@tourism.807-city.on.ca>\n";
+
+/*
+ Sources:
+ Crynwr arcnet.com/arcether.com packet drivers.
+ arcnet.c v0.00 dated 1/1/94 and apparently by
+ Donald Becker - it didn't work :)
+ skeleton.c v0.05 dated 11/16/93 by Donald Becker
+ (from Linux Kernel 1.1.45)
+ ...I sure wish I had the ARCnet data sheets right about now!
+ RFC's 1201 and 1051 (mostly 1201) - re: ARCnet IP packets
+ net/inet/eth.c (from kernel 1.1.50) for header-building info...
+ Alternate Linux ARCnet source by V.Shergin <vsher@sao.stavropol.su>
+ Textual information and more alternate source from Joachim Koenig
+ <jojo@repas.de>
+*/
+
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif /* MODULE */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <net/arp.h>
+
+
+/* debug levels:
+ * D_OFF production
+ * D_NORMAL verification
+ * D_INIT show init/detect messages
+ * D_DURING show messages during normal use (ie interrupts)
+ * D_DATA show packets data from skb's, not on Arcnet card
+ * D_TX show tx packets
+ * D_RX show tx+rx packets
+ */
+#define D_OFF 0
+#define D_NORMAL 1
+#define D_INIT 2
+#define D_EXTRA 3
+#define D_DURING 4
+#define D_DATA 6
+#define D_TX 8
+#define D_RX 9
+
+#ifndef NET_DEBUG
+#define NET_DEBUG D_INIT
+#endif
+static unsigned int net_debug = NET_DEBUG;
+
+#ifndef HAVE_AUTOIRQ
+/* From auto_irq.c, in ioport.h for later versions. */
+extern void autoirq_setup(int waittime);
+extern int autoirq_report(int waittime);
+/* The map from IRQ number (as passed to the interrupt handler) to
+ 'struct device'. */
+extern struct device *irq2dev_map[16];
+#endif
+
+#ifndef HAVE_PORTRESERVE
+#define check_region(ioaddr, size) 0
+#define request_region(ioaddr, size) do ; while (0)
+#endif
+
+/* macro to simplify debug checking */
+#define BUGLVL(x) if (net_debug>=x)
+
+/* The number of low I/O ports used by the ethercard. */
+#define ETHERCARD_TOTAL_SIZE 16
+
+
+/* Handy defines for ARCnet specific stuff */
+ /* COM 9026 (?) --> ARCnet register addresses */
+#define INTMASK (ioaddr+0) /* writable */
+#define STATUS (ioaddr+0) /* readable */
+#define COMMAND (ioaddr+1) /* writable, returns random vals on read (?) */
+#define RESET (ioaddr+8) /* software reset writable */
+
+ /* time needed for various things (in clock ticks, 1/100 sec) */
+#define RESETtime 40 /* reset */
+#define XMITtime 10 /* send (?) */
+#define ACKtime 10 /* acknowledge (?) */
+
+ /* these are the max/min lengths of packet data. (including
+ * ClientData header)
+ * note: packet sizes 250, 251, 252 are impossible (God knows why)
+ * so exception packets become necessary.
+ *
+ * These numbers are compared with the length of the full packet,
+ * including ClientData header.
+ */
+#define MTU (253+EXTRA_CLIENTDATA) /* normal packet max size */
+#define MinTU (257+EXTRA_CLIENTDATA) /* extended packet min size */
+#define XMTU (508+EXTRA_CLIENTDATA) /* extended packet max size */
+
+ /* status/interrupt mask bit fields */
+#define TXFREEflag 0x001 /* transmitter available */
+#define TXACKflag 0x002 /* transmitted msg. ackd */
+#define RECONflag 0x004 /* system reconfigured */
+#define TESTflag 0x008 /* test flag */
+#define RESETflag 0x010 /* power-on-reset */
+#define RES1flag 0x020 /* unused */
+#define RES2flag 0x040 /* unused */
+#define NORXflag 0x080 /* receiver inhibited */
+
+ /* in the command register, the following bits have these meanings:
+ * 0-2 command
+ * 3-4 page number (for enable rcv/xmt command)
+ * 7 receive broadcasts
+ */
+#define NOTXcmd 0x001 /* disable transmitter */
+#define NORXcmd 0x002 /* disable receiver */
+#define TXcmd 0x003 /* enable transmitter */
+#define RXcmd 0x004 /* enable receiver */
+#define CONFIGcmd 0x005 /* define configuration */
+#define CFLAGScmd 0x006 /* clear flags */
+#define TESTcmd 0x007 /* load test flags */
+
+ /* flags for "clear flags" command */
+#define RESETclear 0x008 /* power-on-reset */
+#define CONFIGclear 0x010 /* system reconfigured */
+
+ /* flags for "load test flags" command */
+#define TESTload 0x008 /* test flag (diagnostic) */
+
+ /* byte deposited into first address of buffers on reset */
+#define TESTvalue 0321 /* that's octal for 0xD1 :) */
+
+ /* for "enable receiver" command */
+#define RXbcasts 0x080 /* receive broadcasts */
+
+ /* flags for "define configuration" command */
+#define NORMALconf 0x000 /* 1-249 byte packets */
+#define EXTconf 0x008 /* 250-504 byte packets */
+
+ /* buffers (4 total) used for receive and xmit.
+ */
+#define EnableReceiver() outb(RXcmd|(recbuf<<3)|RXbcasts,COMMAND)
+/*#define TXbuf 2 (Obsoleted by ping-pong xmits) */
+
+ /* Protocol ID's */
+#define ARC_P_IP 212 /* 0xD4 */
+#define ARC_P_ARP 213 /* 0xD5 */
+#define ARC_P_RARP 214 /* 0xD6 */
+#define ARC_P_IPX 250 /* 0xFA */
+#define ARC_P_LANSOFT 251 /* 0xFB */
+#define ARC_P_ATALK 0xDD
+
+ /* Length of time between "stuck" checks */
+#define TIMERval (HZ/8) /* about 1/8 second */
+
+ /* these structures define the format of an arcnet packet. */
+#define NORMAL 0
+#define EXTENDED 1
+#define EXCEPTION 2
+
+ /* the header required by the card itself */
+struct HardHeader
+{
+ u_char source, /* source ARCnet - filled in automagically */
+ destination, /* destination ARCnet - 0 for broadcast */
+ offset1, /* offset of ClientData (256-byte packets) */
+ offset2; /* offset of ClientData (512-byte packets) */
+};
+
+ /* a complete ARCnet packet */
+union ArcPacket
+{
+ struct HardHeader hardheader; /* the hardware header */
+ u_char raw[512]; /* raw packet info, incl ClientData */
+};
+
+ /* the "client data" header - RFC-1201 information
+ * notice that this screws up if it's not an even number of bytes
+ * <sigh>
+ */
+struct ClientData
+{
+ /* data that's NOT part of real packet */
+ u_char daddr; /* Destination address - stored here,
+ * but WE MUST GET RID OF IT BEFORE SENDING A
+ * PACKET!!
+ */
+ u_char saddr; /* Source address - necessary for IPX protocol */
+
+ /* data that IS part of real packet */
+ u_char protocol_id, /* ARC_P_IP, ARC_P_ARP, or ARC_P_RARP */
+ split_flag; /* for use with split packets */
+ u_short sequence; /* sequence number (?) */
+};
+#define EXTRA_CLIENTDATA (sizeof(struct ClientData)-4)
+
+
+/* "Incoming" is information needed for each address that could be sending
+ * to us. Mostly for partially-received split packets.
+ */
+struct Incoming
+{
+ struct sk_buff *skb; /* packet data buffer */
+ unsigned char lastpacket, /* number of last packet (from 1) */
+ numpackets; /* number of packets in split */
+ u_short sequence; /* sequence number of assembly */
+};
+
+struct Outgoing
+{
+ struct sk_buff *skb; /* buffer from upper levels */
+ struct ClientData *hdr; /* clientdata of last packet */
+ u_char *data; /* pointer to data in packet */
+ short length, /* bytes total */
+ dataleft, /* bytes left */
+ segnum, /* segment being sent */
+ numsegs, /* number of segments */
+ seglen; /* length of segment */
+};
+
+
+/* Information that needs to be kept for each board. */
+struct arcnet_local {
+ struct enet_statistics stats;
+ u_char arcnum; /* arcnet number - our 8-bit address */
+ u_short sequence; /* sequence number (incs with each packet) */
+ u_char recbuf, /* receive buffer # (0 or 1) */
+ txbuf, /* transmit buffer # (2 or 3) */
+ txready; /* buffer where a packet is ready to send */
+ short intx, /* in TX routine? */
+ in_txhandler, /* in TX_IRQ handler? */
+ sending; /* transmit in progress? */
+ short tx_left; /* segments of split packet left to TX */
+ struct timer_list timer; /* the timer interrupt struct */
+ struct Incoming incoming[256]; /* one from each address */
+ struct Outgoing outgoing; /* packet currently being sent */
+};
+
+
+/* Index to functions, as function prototypes. */
+extern int arcnet_probe(struct device *dev);
+#ifndef MODULE
+static int arcnet_memprobe(struct device *dev,u_char *addr);
+static int arcnet_ioprobe(struct device *dev, short ioaddr);
+#endif
+
+static int arcnet_open(struct device *dev);
+static int arcnet_close(struct device *dev);
+
+static int arcnet_send_packet(struct sk_buff *skb, struct device *dev);
+#ifdef CAREFUL_XMIT
+ static void careful_xmit_wait(struct device *dev);
+#else
+ #define careful_xmit_wait(dev)
+#endif
+static void arcnet_continue_tx(struct device *dev);
+static void arcnet_prepare_tx(struct device *dev,struct ClientData *hdr,
+ short length,char *data);
+static void arcnet_go_tx(struct device *dev);
+
+static void arcnet_interrupt(int irq,struct pt_regs *regs);
+static void arcnet_inthandler(struct device *dev);
+static void arcnet_rx(struct device *dev,int recbuf);
+
+#ifdef USE_TIMER_HANDLER
+static void arcnet_timer(unsigned long arg);
+#endif
+
+static struct enet_statistics *arcnet_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+
+ /* annoying functions for header/arp/etc building */
+int arc_header(unsigned char *buff,struct device *dev,unsigned short type,
+ void *daddr,void *saddr,unsigned len,struct sk_buff *skb);
+int arc_rebuild_header(void *eth,struct device *dev,unsigned long raddr,
+ struct sk_buff *skb);
+unsigned short arc_type_trans(struct sk_buff *skb,struct device *dev);
+
+static int arcnet_reset(struct device *dev);
+
+#ifdef MODULE
+int init_module(void);
+void cleanup_module(void);
+#endif
+
+#define tx_done(dev) 1
+
+/*
+#define JIFFER(time) for (delayval=jiffies+(time); delayval>jiffies;);
+*/
+#define JIFFER(time) for (delayval=0; delayval<(time*10); delayval++) \
+ udelay(1000);
+
+#ifdef EXTRA_DELAYS
+ #define XJIFFER(time) JIFFER(time)
+#else
+ #define XJIFFER(time)
+#endif
+
+
+/* Check for a network adaptor of this type, and return '0' if one exists.
+ * If dev->base_addr == 0, probe all likely locations.
+ * If dev->base_addr == 1, always return failure.
+ * If dev->base_addr == 2, allocate space for the device and return success
+ * (detachable devices only).
+ */
+int
+arcnet_probe(struct device *dev)
+{
+#ifndef MODULE
+ /* I refuse to probe anything less than 0x200, because anyone using
+ * an address like that should probably be shot.
+ */
+ int *port, ports[] = {/* first the suggested values! */
+ 0x300,0x2E0,0x2F0,0x2D0,
+ /* ...now everything else possible. */
+ 0x200,0x210,0x220,0x230,0x240,0x250,0x260,0x270,
+ 0x280,0x290,0x2a0,0x2b0,0x2c0,
+ 0x310,0x320,0x330,0x340,0x350,0x360,0x370,
+ 0x380,0x390,0x3a0,/* video ports, */0x3e0,0x3f0,
+ /* a null ends the list */
+ 0};
+ /* I'm not going to probe below 0xA0000 either, for similar reasons.
+ */
+ unsigned long *addr, addrs[] = {0xD0000,0xE0000,0xA0000,0xB0000,
+ 0xC0000,0xF0000,
+ /* from <mdrejhon@magi.com> */
+ 0xE1000,
+ 0xDD000,0xDC000,
+ 0xD9000,0xD8000,0xD5000,0xD4000,0xD1000,
+ 0xCD000,0xCC000,
+ 0xC9000,0xC8000,0xC5000,0xC4000,
+ /* terminator */
+ 0};
+ int base_addr=dev->base_addr, status=0;
+#endif /* MODULE */
+ int delayval;
+ struct arcnet_local *lp;
+
+ if (net_debug)
+ {
+ printk(version);
+ printk("arcnet: ***\n");
+ printk("arcnet: * Read linux/drivers/net/README.arcnet for important release notes!\n");
+ printk("arcnet: *\n");
+ printk("arcnet: * This version should be stable, but e-mail me if you have any\n");
+ printk("arcnet: * questions, comments, or bug reports!\n");
+ printk("arcnet: ***\n");
+ }
+
+ BUGLVL(D_INIT)
+ printk("arcnet: given: base %lXh, IRQ %Xh, shmem %lXh\n",
+ dev->base_addr,dev->irq,dev->mem_start);
+
+#ifndef MODULE
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ status=arcnet_ioprobe(dev, base_addr);
+ else if (base_addr > 0) /* Don't probe at all. */
+ return ENXIO;
+ else for (port = &ports[0]; *port; port++)
+ {
+ int ioaddr = *port;
+ if (check_region(ioaddr, ETHERCARD_TOTAL_SIZE))
+ {
+ BUGLVL(D_INIT)
+ printk("arcnet: Skipping %Xh because of check_region...\n",
+ ioaddr);
+ continue;
+ }
+
+ status=arcnet_ioprobe(dev, ioaddr);
+ if (!status) break;
+ }
+
+ if (status) return status;
+
+ /* ioprobe turned out okay. Now give it a couple seconds to finish
+ * initializing...
+ */
+ BUGLVL(D_INIT)
+ printk("arcnet: ioprobe okay! Waiting for reset...\n");
+ JIFFER(100);
+
+ /* okay, now we have to find the shared memory area. */
+ BUGLVL(D_INIT)
+ printk("arcnet: starting memory probe, given %lXh\n",
+ dev->mem_start);
+ if (dev->mem_start) /* value given - probe just that one */
+ {
+ status=arcnet_memprobe(dev,(u_char *)dev->mem_start);
+ if (status) return status;
+ }
+ else /* no value given - probe everything */
+ {
+ for (addr = &addrs[0]; *addr; addr++) {
+ status=arcnet_memprobe(dev,(u_char *)(*addr));
+ if (!status) break;
+ }
+
+ if (status) return status;
+ }
+#else /* MODULE */
+ if (!dev->base_addr || !dev->irq || !dev->mem_start
+ || !dev->rmem_start)
+ {
+ printk("arcnet: loadable modules can't autoprobe!\n");
+ printk("arcnet: try using io=, irqnum=, and shmem= on the insmod line.\n");
+ printk("arcnet: you may also need num= to change the device name. (ie. num=1 for arc1)\n");
+ return ENODEV;
+ }
+#endif
+ /* now reserve the irq... */
+ {
+ int irqval = request_irq(dev->irq, &arcnet_interrupt, 0,
+ "arcnet");
+ if (irqval) {
+ printk("%s: unable to get IRQ %d (irqval=%d).\n",
+ dev->name,dev->irq, irqval);
+ return EAGAIN;
+ }
+ }
+
+ /* Grab the region so we can find another board if autoIRQ fails. */
+ request_region(dev->base_addr, ETHERCARD_TOTAL_SIZE,"arcnet");
+
+ printk("%s: ARCnet card found at %03lXh, IRQ %d, ShMem at %lXh.\n",
+ dev->name, dev->base_addr, dev->irq, dev->mem_start);
+
+ /* Initialize the device structure. */
+ dev->priv = kmalloc(sizeof(struct arcnet_local), GFP_KERNEL);
+ memset(dev->priv, 0, sizeof(struct arcnet_local));
+ lp=(struct arcnet_local *)(dev->priv);
+
+ dev->open = arcnet_open;
+ dev->stop = arcnet_close;
+ dev->hard_start_xmit = arcnet_send_packet;
+ dev->get_stats = arcnet_get_stats;
+#ifdef HAVE_MULTICAST
+ dev->set_multicast_list = &set_multicast_list;
+#endif
+
+ /* Fill in the fields of the device structure with ethernet-generic values. */
+ ether_setup(dev);
+
+ /* And now fill particular ones with arcnet values :) */
+
+ dev->type=ARPHRD_ARCNET;
+ dev->hard_header_len=sizeof(struct ClientData);
+ BUGLVL(D_EXTRA)
+ printk("arcnet: ClientData header size is %d.\narcnet: HardHeader size is %d.\n",
+ sizeof(struct ClientData),sizeof(struct HardHeader));
+#if LIMIT_MTU /* the old way - normally, now use ethernet default */
+ dev->mtu=512-sizeof(struct HardHeader)+EXTRA_CLIENTDATA;
+#endif
+ /* since we strip EXTRA_CLIENTDATA bytes off before sending,
+ * we let Linux add that many bytes to the packet data...
+ */
+ dev->addr_len=1;
+ dev->broadcast[0]=0x00;
+
+ BUGLVL(D_INIT) printk("arcnet: arcnet_probe: resetting card.\n");
+ arcnet_reset(dev);
+ JIFFER(50);
+ BUGLVL(D_NORMAL)
+ printk("arcnet: We appear to be station %d (%02Xh)\n",
+ lp->arcnum,lp->arcnum);
+ if (lp->arcnum==0)
+ printk("arcnet: WARNING! Station address 0 is reserved for broadcasts!\n");
+ if (lp->arcnum==255)
+ printk("arcnet: WARNING! Station address 255 may confuse DOS networking programs!\n");
+ dev->dev_addr[0]=lp->arcnum;
+ lp->sequence=1;
+ lp->recbuf=0;
+
+ dev->hard_header = arc_header;
+ dev->rebuild_header = arc_rebuild_header;
+
+ return 0;
+}
+
+#ifndef MODULE
+
+int arcnet_ioprobe(struct device *dev, short ioaddr)
+{
+ int delayval,airq;
+
+ BUGLVL(D_INIT)
+ printk("arcnet: probing address %Xh\n",ioaddr);
+
+ BUGLVL(D_INIT)
+ printk("arcnet: status1=%Xh\n",inb(STATUS));
+
+
+ /* very simple - all we have to do is reset the card, and if there's
+ * no irq, it's not an ARCnet. We can also kill two birds with
+ * one stone because we detect the IRQ at the same time :)
+ */
+
+ /* reset the card by reading the reset port */
+ inb(RESET);
+ JIFFER(RESETtime);
+
+ /* if status port is FF, there's certainly no arcnet... give up. */
+ if (inb(STATUS)==0xFF)
+ {
+ BUGLVL(D_INIT)
+ printk("arcnet: probe failed. Status port empty.\n");
+ return ENODEV;
+ }
+
+ /* we'll try to be reasonably sure it's an arcnet by making sure
+ * the value of the COMMAND port changes automatically once in a
+ * while. I have no idea what those values ARE, but at least
+ * they work.
+ */
+ {
+ int initval,curval;
+
+ curval=initval=inb(COMMAND);
+ delayval=jiffies+5;
+ while (delayval>=jiffies && curval==initval)
+ curval=inb(COMMAND);
+
+ if (curval==initval)
+ {
+ printk("arcnet: probe failed. never-changing command port (%02Xh).\n",
+ initval);
+ return ENODEV;
+ }
+ }
+
+ BUGLVL(D_INIT)
+ printk("arcnet: status2=%Xh\n",inb(STATUS));
+
+ /* now we turn the reset bit off so we can IRQ next reset... */
+ outb(CFLAGScmd|RESETclear|CONFIGclear,COMMAND);
+ XJIFFER(ACKtime);
+ if (inb(STATUS) & RESETflag) /* reset flag STILL on */
+ {
+ BUGLVL(D_INIT)
+ printk("arcnet: probe failed. eternal reset flag1...(status=%Xh)\n",
+ inb(STATUS));
+ return ENODEV;
+ }
+
+ /* set up automatic IRQ detection */
+ autoirq_setup(0);
+
+ /* enable reset IRQ's (shouldn't be necessary, but worth a try) */
+ outb(RESETflag,INTMASK);
+
+ /* now reset it again to generate an IRQ */
+ inb(RESET);
+ JIFFER(RESETtime);
+
+ BUGLVL(D_INIT)
+ printk("arcnet: status3=%Xh\n",inb(STATUS));
+
+ /* and turn the reset flag back off */
+ outb(CFLAGScmd|RESETclear|CONFIGclear,COMMAND);
+ XJIFFER(ACKtime);
+
+ BUGLVL(D_INIT)
+ printk("arcnet: status4=%Xh\n",inb(STATUS));
+
+ /* enable reset IRQ's again */
+ outb(RESETflag,INTMASK);
+
+ /* now reset it again to generate an IRQ */
+ inb(RESET);
+ JIFFER(RESETtime);
+
+ BUGLVL(D_INIT)
+ printk("arcnet: status5=%Xh\n",inb(STATUS));
+
+ /* if we do this, we're sure to get an IRQ since the card has
+ * just reset and the NORXflag is on until we tell it to start
+ * receiving.
+ *
+ * However, this could, theoretically, cause a lockup. Maybe I'm just
+ * not very good at theory! :)
+ */
+#ifdef DANGER_PROBE
+ outb(NORXflag,INTMASK);
+ JIFFER(RESETtime);
+ outb(0,INTMASK);
+#endif
+
+ /* and turn the reset flag back off */
+ outb(CFLAGScmd|RESETclear|CONFIGclear,COMMAND);
+ XJIFFER(ACKtime);
+
+ airq = autoirq_report(0);
+ if (net_debug>=D_INIT && airq)
+ printk("arcnet: autoirq is %d\n", airq);
+
+ /* if there was no autoirq AND the user hasn't set any defaults,
+ * give up.
+ */
+ if (!airq && !(dev->base_addr && dev->irq))
+ {
+ BUGLVL(D_INIT)
+ printk("arcnet: probe failed. no autoirq...\n");
+ return ENODEV;
+ }
+
+ /* otherwise we probably have a card. Let's make sure. */
+
+ if (inb(STATUS) & RESETflag) /* reset flag on */
+ {
+ /* now we turn the reset bit off */
+ outb(CFLAGScmd|RESETclear|CONFIGclear,COMMAND);
+ XJIFFER(ACKtime);
+ }
+
+ if (inb(STATUS) & RESETflag) /* reset flag STILL on */
+ {
+ BUGLVL(D_INIT)
+ printk("arcnet: probe failed. eternal reset flag...(status=%Xh)\n",
+ inb(STATUS));
+ return ENODEV;
+ }
+
+ /* okay, we've got a real, live ARCnet on our hands. */
+ if (!dev->base_addr) dev->base_addr=ioaddr;
+
+ if (dev->irq < 2) /* "Auto-IRQ" */
+ {
+ /* we already did the autoirq above, so store the values */
+ dev->irq=airq;
+ }
+ else if (dev->irq == 2)
+ {
+ if (net_debug)
+ printk("arcnet: IRQ2 == IRQ9, don't worry.\n");
+ dev->irq = 9;
+ }
+
+ BUGLVL(D_INIT)
+ printk("arcnet: irq and base address seem okay. (%lXh, IRQ %d)\n",
+ dev->base_addr,dev->irq);
+ return 0;
+}
+
+
+/* A memory probe that is called after the card is reset.
+ * It checks for the official TESTvalue in byte 0 and makes sure the buffer
+ * has certain characteristics of an ARCnet...
+ */
+int arcnet_memprobe(struct device *dev,u_char *addr)
+{
+ BUGLVL(D_INIT)
+ printk("arcnet: probing memory at %lXh\n",(u_long)addr);
+
+ dev->mem_start=0;
+
+#ifdef STRICT_MEM_DETECT /* probably better. */
+ /* ARCnet memory byte 0 is TESTvalue */
+ if (addr[0]!=TESTvalue)
+ {
+ BUGLVL(D_INIT)
+ printk("arcnet: probe failed. addr=%lXh, addr[0]=%Xh (not %Xh)\n",
+ (unsigned long)addr,addr[0],TESTvalue);
+ return ENODEV;
+ }
+
+ /* now verify the shared memory writability */
+ addr[0]=0x42;
+ if (addr[0]!=0x42)
+ {
+ BUGLVL(D_INIT)
+ printk("arcnet: probe failed. addr=%lXh, addr[0]=%Xh (not 42h)\n",
+ (unsigned long)addr,addr[0]);
+ return ENODEV;
+ }
+#else
+ if (addr[0]!=TESTvalue)
+ {
+ BUGLVL(D_INIT)
+ printk("arcnet: probe failed. addr=%lXh, addr[0]=%Xh (not %Xh)\n",
+ (unsigned long)addr,addr[0],TESTvalue);
+ return ENODEV;
+ }
+#endif
+
+ /* got it! fill in dev */
+ dev->mem_start=(unsigned long)addr;
+ dev->mem_end=dev->mem_start+512*4-1;
+ dev->rmem_start=dev->mem_start+512*0;
+ dev->rmem_end=dev->mem_start+512*2-1;
+
+ return 0;
+}
+
+#endif /* MODULE */
+
+
+/* Open/initialize the board. This is called (in the current kernel)
+ sometime after booting when the 'ifconfig' program is run.
+
+ This routine should set everything up anew at each open, even
+ registers that "should" only need to be set once at boot, so that
+ there is non-reboot way to recover if something goes wrong.
+ */
+static int
+arcnet_open(struct device *dev)
+{
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+/* int ioaddr = dev->base_addr;*/
+
+ if (dev->metric>=10)
+ {
+ net_debug=dev->metric-10;
+ dev->metric=1;
+ }
+
+ if (net_debug) printk(version);
+
+#if 0 /* Yup, they're hardwired in arcnets */
+ /* This is used if the interrupt line can turned off (shared).
+ See 3c503.c for an example of selecting the IRQ at config-time. */
+ if (request_irq(dev->irq, &arcnet_interrupt, 0, "arcnet")) {
+ return -EAGAIN;
+ }
+#endif
+
+ irq2dev_map[dev->irq] = dev;
+
+ /* Reset the hardware here. */
+ BUGLVL(D_EXTRA) printk("arcnet: arcnet_open: resetting card.\n");
+
+ /* try to reset - twice if it fails the first time */
+ if (arcnet_reset(dev) && arcnet_reset(dev))
+ return -ENODEV;
+
+/* chipset_init(dev, 1);*/
+/* outb(0x00, ioaddr);*/
+
+/* lp->open_time = jiffies;*/
+
+ dev->tbusy=0;
+ dev->interrupt=0;
+ dev->start=1;
+ lp->intx=0;
+ lp->in_txhandler=0;
+
+#ifdef USE_TIMER_HANDLER
+ /* grab a timer handler to recover from any missed IRQ's */
+ init_timer(&lp->timer);
+ lp->timer.expires = TIMERval; /* length of time */
+ lp->timer.data = (unsigned long)dev; /* pointer to "dev" structure */
+ lp->timer.function = &arcnet_timer; /* timer handler */
+ add_timer(&lp->timer);
+#endif
+
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+
+/* The inverse routine to arcnet_open(). */
+static int
+arcnet_close(struct device *dev)
+{
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+#ifdef EXTRA_DELAYS
+ int delayval;
+#endif
+
+/* lp->open_time = 0;*/
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* release the timer */
+ del_timer(&lp->timer);
+
+ /* Flush the Tx and disable Rx here. */
+ /* resetting the card should do the job. */
+ /*inb(RESET);*/
+
+ outb(0,INTMASK); /* no IRQ's */
+ outb(NOTXcmd,COMMAND); /* disable transmit */
+ XJIFFER(ACKtime);
+ outb(NORXcmd,COMMAND); /* disable receive */
+
+ /* Update the statistics here. */
+
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+
+static int
+arcnet_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ int ioaddr=dev->base_addr;
+/* short daddr;*/
+
+ lp->intx++;
+
+ BUGLVL(D_DURING)
+ printk("arcnet: transmit requested (status=%Xh, inTX=%d)\n",
+ inb(STATUS),lp->intx);
+
+ if (dev->tbusy || lp->in_txhandler)
+ {
+ /* If we get here, some higher level has decided we are broken.
+ There should really be a "kick me" function call instead. */
+ int tickssofar = jiffies - dev->trans_start;
+ int recbuf=lp->recbuf;
+ int status=inb(STATUS);
+
+ /* resume any stopped tx's */
+#if 0
+ if (lp->txready && (inb(STATUS)&TXFREEflag))
+ {
+ printk("arcnet: kickme: starting a TX (status=%Xh)\n",
+ inb(STATUS));
+ arcnet_go_tx(dev);
+ lp->intx--;
+ return 1;
+ }
+#endif
+
+ if (tickssofar < 5)
+ {
+ BUGLVL(D_DURING)
+ printk("arcnet: premature kickme! (status=%Xh ticks=%d o.skb=%ph numsegs=%d segnum=%d\n",
+ status,tickssofar,lp->outgoing.skb,
+ lp->outgoing.numsegs,
+ lp->outgoing.segnum);
+ lp->intx--;
+ return 1;
+ }
+
+ BUGLVL(D_INIT)
+ printk("arcnet: transmit timed out (status=%Xh, inTX=%d, tickssofar=%d)\n",
+ status,lp->intx,tickssofar);
+
+ /* Try to restart the adaptor. */
+ /*arcnet_reset(dev);*/
+
+ if (status&NORXflag) EnableReceiver();
+ if (!(status&TXFREEflag)) outb(NOTXcmd,COMMAND);
+ dev->trans_start = jiffies;
+
+ if (lp->outgoing.skb)
+ dev_kfree_skb(lp->outgoing.skb,FREE_WRITE);
+ lp->outgoing.skb=NULL;
+
+ dev->tbusy=0;
+ mark_bh(NET_BH);
+ lp->intx=0;
+ lp->in_txhandler=0;
+ lp->txready=0;
+ lp->sending=0;
+
+ return 1;
+ }
+
+ /* If some higher layer thinks we've missed a tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ BUGLVL(D_INIT)
+ printk("arcnet: tx passed null skb (status=%Xh, inTX=%d, tickssofar=%ld)\n",
+ inb(STATUS),lp->intx,jiffies-dev->trans_start);
+ dev_tint(dev);
+ lp->intx--;
+ return 0;
+ }
+
+ if (lp->txready) /* transmit already in progress! */
+ {
+ printk("arcnet: trying to start new packet while busy!\n");
+ printk("arcnet: marking as not ready.\n");
+ lp->txready=0;
+ return 1;
+ }
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ {
+ printk("arcnet: transmitter called with busy bit set! (status=%Xh, inTX=%d, tickssofar=%ld)\n",
+ inb(STATUS),lp->intx,jiffies-dev->trans_start);
+ lp->intx--;
+ return -EBUSY;
+ }
+ else {
+ struct Outgoing *out=&(lp->outgoing);
+ out->length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ out->hdr=(struct ClientData*)skb->data;
+ out->skb=skb;
+ BUGLVL( D_DATA ) {
+ short i;
+ for( i=0; i< skb->len; i++)
+ {
+ if( i%16 == 0 ) printk("\n[%04hX] ",i);
+ printk("%02hX ",((unsigned char*)skb->data)[i]);
+ }
+ printk("\n");
+ }
+
+#ifdef IRQ_XMIT
+ if (lp->txready && inb(STATUS)&TXFREEflag)
+ arcnet_go_tx(dev);
+#endif
+
+
+ if (out->length<=XMTU) /* fits in one packet? */
+ {
+ BUGLVL(D_TX) printk("arcnet: not splitting %d-byte packet. (split_flag=%d)\n",
+ out->length,out->hdr->split_flag);
+ BUGLVL(D_INIT) if (out->hdr->split_flag)
+ printk("arcnet: short packet has split_flag set?! (split_flag=%d)\n",
+ out->hdr->split_flag);
+ out->numsegs=1;
+ out->segnum=1;
+ arcnet_prepare_tx(dev,out->hdr,
+ out->length-sizeof(struct ClientData),
+ ((char *)skb->data)+sizeof(struct ClientData));
+ careful_xmit_wait(dev);
+
+ /* done right away */
+ dev_kfree_skb(out->skb,FREE_WRITE);
+ out->skb=NULL;
+
+ if (!lp->sending)
+ {
+ arcnet_go_tx(dev);
+
+ /* inform upper layers */
+ dev->tbusy=0;
+ mark_bh(NET_BH);
+ }
+ }
+ else /* too big for one - split it */
+ {
+ int maxsegsize=XMTU-sizeof(struct ClientData);
+
+ out->data=(u_char *)skb->data
+ + sizeof(struct ClientData);
+ out->dataleft=out->length-sizeof(struct ClientData);
+ out->numsegs=(out->dataleft+maxsegsize-1)/maxsegsize;
+
+ out->segnum=0;
+
+ BUGLVL(D_TX) printk("arcnet: packet (%d bytes) split into %d fragments:\n",
+ out->length,out->numsegs);
+
+#ifdef IRQ_XMIT
+ /* if a packet waiting, launch it */
+ if (lp->txready && inb(STATUS)&TXFREEflag)
+ arcnet_go_tx(dev);
+
+ if (!lp->txready)
+ {
+ /* prepare a packet, launch it and prepare
+ * another.
+ */
+ arcnet_continue_tx(dev);
+ if (!lp->sending)
+ {
+ arcnet_go_tx(dev);
+ arcnet_continue_tx(dev);
+ if (!lp->sending)
+ arcnet_go_tx(dev);
+ }
+ }
+
+ /* if segnum==numsegs, the transmission is finished;
+ * free the skb right away.
+ */
+ if (out->segnum==out->numsegs)
+ {
+ /* transmit completed */
+ out->segnum++;
+ if (out->skb)
+ dev_kfree_skb(out->skb,FREE_WRITE);
+ out->skb=NULL;
+#if 0
+ /* inform upper layers */
+ dev->tbusy=0;
+ mark_bh(NET_BH);
+#endif
+ }
+
+#else /* non-irq xmit */
+ while (out->segnum<out->numsegs)
+ {
+ arcnet_continue_tx(dev);
+ careful_xmit_wait(dev);
+ arcnet_go_tx(dev);
+ dev->trans_start=jiffies;
+ }
+
+ dev_kfree_skb(out->skb,FREE_WRITE);
+ out->skb=NULL;
+
+ /* inform upper layers */
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+#endif
+ }
+ }
+
+ lp->intx--;
+ lp->stats.tx_packets++;
+ dev->trans_start=jiffies;
+ return 0;
+}
+
+static void arcnet_continue_tx(struct device *dev)
+{
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ int maxsegsize=XMTU-sizeof(struct ClientData);
+ struct Outgoing *out=&(lp->outgoing);
+
+ if (lp->txready)
+ {
+ printk("arcnet: continue_tx: called with packet in buffer!\n");
+ return;
+ }
+
+ if (out->segnum>=out->numsegs)
+ {
+ printk("arcnet: continue_tx: building segment %d of %d!\n",
+ out->segnum+1,out->numsegs);
+ }
+
+ if (!out->segnum) /* first packet */
+ out->hdr->split_flag=((out->numsegs-2)<<1)+1;
+ else
+ out->hdr->split_flag=out->segnum<<1;
+
+ out->seglen=maxsegsize;
+ if (out->seglen>out->dataleft) out->seglen=out->dataleft;
+
+ BUGLVL(D_TX) printk("arcnet: building packet #%d (%d bytes) of %d (%d total), splitflag=%d\n",
+ out->segnum+1,out->seglen,out->numsegs,
+ out->length,out->hdr->split_flag);
+
+ arcnet_prepare_tx(dev,out->hdr,out->seglen,out->data);
+
+ out->dataleft-=out->seglen;
+ out->data+=out->seglen;
+ out->segnum++;
+}
+
+#ifdef CAREFUL_XMIT
+static void careful_xmit_wait(struct device *dev)
+{
+ int ioaddr=dev->base_addr;
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+
+ /* wait patiently for tx to become available again */
+ while ( !(inb(STATUS)&TXFREEflag) )
+ {
+ if (jiffies-dev->trans_start > 20 || !dev->tbusy)
+ {
+ BUGLVL(D_INIT)
+ printk("arcnet: CAREFUL_XMIT timeout. (busy=%d, status=%Xh)\n",
+ dev->tbusy,inb(STATUS));
+ lp->stats.tx_errors++;
+
+ outb(NOTXcmd,COMMAND);
+ return;
+ }
+ }
+ BUGLVL(D_TX) printk("arcnet: transmit completed successfully. (status=%Xh)\n",
+ inb(STATUS));
+}
+#endif
+
+static void
+arcnet_prepare_tx(struct device *dev,struct ClientData *hdr,short length,
+ char *data)
+{
+/* int ioaddr = dev->base_addr;*/
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ struct ClientData *arcsoft;
+ union ArcPacket *arcpacket =
+ (union ArcPacket *)(dev->mem_start+512*(lp->txbuf^1));
+ u_char pkttype;
+ int offset;
+ short daddr;
+
+ lp->txbuf=lp->txbuf^1; /* XOR with 1 to alternate between 2 and 3 */
+
+ length+=sizeof(struct ClientData);
+
+ BUGLVL(D_TX)
+ printk("arcnet: arcnet_prep_tx: hdr:%ph, length:%d, data:%ph\n",
+ hdr,length,data);
+
+ /* clean out the page to make debugging make more sense :) */
+ BUGLVL(D_DURING)
+ memset((void *)dev->mem_start+lp->txbuf*512,0x42,512);
+
+ daddr=arcpacket->hardheader.destination=hdr->daddr;
+
+ /* load packet into shared memory */
+ if (length<=MTU) /* Normal (256-byte) Packet */
+ {
+ pkttype=NORMAL;
+
+ arcpacket->hardheader.offset1=offset=256-length
+ + EXTRA_CLIENTDATA;
+ arcsoft=(struct ClientData *)
+ (&arcpacket->raw[offset-EXTRA_CLIENTDATA]);
+ }
+ else if (length>=MinTU) /* Extended (512-byte) Packet */
+ {
+ pkttype=EXTENDED;
+
+ arcpacket->hardheader.offset1=0;
+ arcpacket->hardheader.offset2=offset=512-length
+ + EXTRA_CLIENTDATA;
+ arcsoft=(struct ClientData *)
+ (&arcpacket->raw[offset-EXTRA_CLIENTDATA]);
+ }
+ else /* Exception Packet */
+ {
+ pkttype=EXCEPTION;
+
+ arcpacket->hardheader.offset1=0;
+ arcpacket->hardheader.offset2=offset=512-length-4
+ + EXTRA_CLIENTDATA;
+ arcsoft=(struct ClientData *)
+ (&arcpacket->raw[offset+4-EXTRA_CLIENTDATA]);
+
+ /* exception-specific stuff - these four bytes
+ * make the packet long enough to fit in a 512-byte
+ * frame.
+ */
+ arcpacket->raw[offset+0]=arcsoft->protocol_id;
+ arcpacket->raw[offset+1]=0xFF; /* FF flag */
+ arcpacket->raw[offset+2]=0xFF; /* FF padding */
+ arcpacket->raw[offset+3]=0xFF; /* FF padding */
+ }
+
+
+ /* copy the packet into ARCnet shmem
+ * - the first bytes of ClientData header are skipped
+ */
+ memcpy((u_char*)arcsoft+EXTRA_CLIENTDATA,
+ (u_char*)hdr+EXTRA_CLIENTDATA,
+ sizeof(struct ClientData)-EXTRA_CLIENTDATA);
+ memcpy((u_char*)arcsoft+sizeof(struct ClientData),
+ data,
+ length-sizeof(struct ClientData));
+
+ BUGLVL(D_DURING) printk("arcnet: transmitting packet to station %02Xh (%d bytes, type=%d)\n",
+ daddr,length,pkttype);
+
+ BUGLVL(D_TX)
+ {
+ int countx,county;
+
+ printk("arcnet: packet dump [tx] follows:");
+
+ for (county=0; county<16+(pkttype!=NORMAL)*16; county++)
+ {
+ printk("\n[%04X] ",county*16);
+ for (countx=0; countx<16; countx++)
+ printk("%02X ",
+ arcpacket->raw[county*16+countx]);
+ }
+
+ printk("\n");
+ }
+#ifdef CAREFUL_XMIT
+ #if 0
+ careful_xmit_wait(dev);
+
+ /* if we're not broadcasting, make sure the xmit was ack'd.
+ * if it wasn't, there is probably no card with that
+ * address... or else it missed our tx somehow.
+ */
+ if (daddr && !(inb(STATUS)&TXACKflag))
+ {
+ BUGLVL(D_INIT)
+ printk("arcnet: transmit not acknowledged. (status=%Xh, daddr=%02Xh)\n",
+ inb(STATUS),daddr);
+ lp->stats.tx_errors++;
+ return -ENONET; /* "machine is not on the network" */
+ }
+ #endif
+#endif
+ lp->txready=lp->txbuf; /* packet is ready for sending */
+
+#if 0
+#ifdef IRQ_XMIT
+ if (inb(STATUS)&TXFREEflag) arcnet_go_tx(dev);
+#endif
+#endif
+}
+
+static void
+arcnet_go_tx(struct device *dev)
+{
+ struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
+ int ioaddr=dev->base_addr;
+
+ BUGLVL(D_DURING)
+ printk("arcnet: go_tx: status=%Xh\n",
+ inb(STATUS));
+
+ if (!(inb(STATUS)&TXFREEflag) || !lp->txready) return;
+
+ /* start sending */
+ outb(TXcmd|(lp->txready<<3),COMMAND);
+
+#ifdef IRQ_XMIT
+ outb(TXFREEflag|NORXflag,INTMASK);
+#endif
+
+ dev->trans_start = jiffies;
+ lp->txready=0;
+ lp->sending++;
+}
+
+
+/* The typical workload of the driver:
+ Handle the network interface interrupts. */
+static void
+arcnet_interrupt(int irq,struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+
+ if (dev == NULL) {
+ if (net_debug >= D_DURING)
+ printk("arcnet: irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ arcnet_inthandler(dev);
+}
+
+static void
+arcnet_inthandler(struct device *dev)
+{
+ struct arcnet_local *lp;
+ int ioaddr, status, boguscount = 3, didsomething;
+
+ dev->interrupt = 1;
+ sti();
+
+ ioaddr = dev->base_addr;
+ lp = (struct arcnet_local *)dev->priv;
+
+#ifdef IRQ_XMIT
+ outb(0,INTMASK);
+#endif
+
+ BUGLVL(D_DURING)
+ printk("arcnet: in net_interrupt (status=%Xh)\n",inb(STATUS));
+
+ do
+ {
+ status = inb(STATUS);
+ didsomething=0;
+
+ if (!dev->start)
+ {
+ BUGLVL(D_EXTRA)
+ printk("arcnet: ARCnet not yet initialized. irq ignored. (status=%Xh)\n",
+ status);
+#ifdef IRQ_XMIT
+ if (!(status&NORXflag))
+ outb(NORXflag,INTMASK);
+#endif
+ dev->interrupt=0;
+ return;
+ }
+
+ /* RESET flag was enabled - card is resetting and if RX
+ * is disabled, it's NOT because we just got a packet.
+ */
+ if (status & RESETflag)
+ {
+ BUGLVL(D_INIT)
+ printk("arcnet: reset irq (status=%Xh)\n",
+ status);
+ dev->interrupt=0;
+ return;
+ }
+
+#if 1 /* yes, it's silly to disable this part but it makes good testing */
+ /* RX is inhibited - we must have received something. */
+ if (status & NORXflag)
+ {
+ int recbuf=lp->recbuf=!lp->recbuf;
+
+ BUGLVL(D_DURING)
+ printk("arcnet: receive irq (status=%Xh)\n",
+ status);
+
+ /* enable receive of our next packet */
+ EnableReceiver();
+
+ /* Got a packet. */
+ arcnet_rx(dev,!recbuf);
+
+ didsomething++;
+ }
+#endif
+#ifdef IRQ_XMIT
+ /* it can only be an xmit-done irq if we're xmitting :) */
+ if (status&TXFREEflag && !lp->in_txhandler && lp->sending)
+ {
+ struct Outgoing *out=&(lp->outgoing);
+
+ lp->in_txhandler++;
+ lp->sending--;
+
+ BUGLVL(D_DURING)
+ printk("arcnet: TX IRQ (stat=%Xh, numsegs=%d, segnum=%d, skb=%ph)\n",
+ status,out->numsegs,out->segnum,out->skb);
+
+ /* send packet if there is one */
+ if (lp->txready)
+ {
+ arcnet_go_tx(dev);
+ didsomething++;
+ }
+
+ if (lp->intx)
+ {
+ lp->in_txhandler--;
+ continue;
+ }
+
+ if (!lp->outgoing.skb)
+ {
+ BUGLVL(D_DURING)
+ printk("arcnet: TX IRQ done: no split to continue.\n");
+
+ /* inform upper layers */
+ if (!lp->txready && dev->tbusy)
+ {
+ dev->tbusy=0;
+ mark_bh(NET_BH);
+ }
+
+ lp->in_txhandler--;
+ continue;
+ }
+
+ /*lp->stats.tx_packets++;*/
+
+ /* if more than one segment, and not all segments
+ * are done, then continue xmit.
+ */
+ if (out->segnum<out->numsegs)
+ arcnet_continue_tx(dev);
+ if (lp->txready && !lp->sending)
+ arcnet_go_tx(dev);
+
+ /* if segnum==numsegs, the transmission is finished;
+ * free the skb.
+ */
+ if (out->segnum>=out->numsegs)
+ {
+ /* transmit completed */
+ out->segnum++;
+ if (out->skb)
+ dev_kfree_skb(out->skb,FREE_WRITE);
+ out->skb=NULL;
+
+ /* inform upper layers */
+ if (!lp->txready && dev->tbusy)
+ {
+ dev->tbusy=0;
+ mark_bh(NET_BH);
+ }
+ }
+ didsomething++;
+
+ lp->in_txhandler--;
+ }
+#endif /* IRQ_XMIT */
+ } while (--boguscount && didsomething);
+
+ BUGLVL(D_DURING)
+ printk("arcnet: net_interrupt complete (status=%Xh)\n",
+ inb(STATUS));
+
+#ifdef IRQ_XMIT
+ if (dev->start && lp->sending )
+ outb(NORXflag|TXFREEflag,INTMASK);
+ else
+ outb(NORXflag,INTMASK);
+#endif
+
+ dev->interrupt=0;
+}
+
+/* A packet has arrived; grab it from the buffers and possibly unsplit it.
+ */
+static void
+arcnet_rx(struct device *dev,int recbuf)
+{
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+/* int status = inb(STATUS);*/
+
+ struct sk_buff *skb;
+
+ union ArcPacket *arcpacket=
+ (union ArcPacket *)(dev->mem_start+recbuf*512);
+ struct ClientData *soft,*arcsoft;
+ short length,offset;
+ u_char pkttype,daddr,saddr;
+
+ daddr=arcpacket->hardheader.destination;
+ saddr=arcpacket->hardheader.source;
+
+ /* if source is 0, it's not a "used" packet! */
+ if (saddr==0)
+ {
+ /*BUGLVL(D_DURING)*/
+ printk("arcnet: discarding old packet. (status=%Xh)\n",
+ inb(STATUS));
+ lp->stats.rx_errors++;
+ return;
+ }
+ arcpacket->hardheader.source=0;
+
+ if (arcpacket->hardheader.offset1) /* Normal Packet */
+ {
+ offset=arcpacket->hardheader.offset1;
+ arcsoft=(struct ClientData *)
+ (&arcpacket->raw[offset-EXTRA_CLIENTDATA]);
+ length=256-offset+EXTRA_CLIENTDATA;
+ pkttype=NORMAL;
+ }
+ else /* ExtendedPacket or ExceptionPacket */
+ {
+ offset=arcpacket->hardheader.offset2;
+ arcsoft=(struct ClientData *)
+ (&arcpacket->raw[offset-EXTRA_CLIENTDATA]);
+
+ if (arcsoft->split_flag!=0xFF) /* Extended Packet */
+ {
+ length=512-offset+EXTRA_CLIENTDATA;
+ pkttype=EXTENDED;
+ }
+ else /* Exception Packet */
+ {
+ /* skip over 4-byte junkola */
+ arcsoft=(struct ClientData *)
+ ((u_char *)arcsoft + 4);
+ length=512-offset+EXTRA_CLIENTDATA-4;
+ pkttype=EXCEPTION;
+ }
+ }
+
+ if (!arcsoft->split_flag) /* not split */
+ {
+ struct Incoming *in=&lp->incoming[saddr];
+
+ BUGLVL(D_RX) printk("arcnet: incoming is not split (splitflag=%d)\n",
+ arcsoft->split_flag);
+
+ if (in->skb) /* already assembling one! */
+ {
+ BUGLVL(D_INIT) printk("arcnet: aborting assembly (seq=%d) for unsplit packet (splitflag=%d, seq=%d)\n",
+ in->sequence,arcsoft->split_flag,
+ arcsoft->sequence);
+ kfree_skb(in->skb,FREE_WRITE);
+ in->skb=NULL;
+ }
+
+ in->sequence=arcsoft->sequence;
+
+ skb = alloc_skb(length, GFP_ATOMIC);
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, dropping packet.\n",
+ dev->name);
+ lp->stats.rx_dropped++;
+ return;
+ }
+ soft=(struct ClientData *)skb->data;
+
+ skb->len = length;
+ skb->dev = dev;
+
+ memcpy((u_char *)soft+EXTRA_CLIENTDATA,
+ (u_char *)arcsoft+EXTRA_CLIENTDATA,
+ length-EXTRA_CLIENTDATA);
+ soft->daddr=daddr;
+ soft->saddr=saddr;
+
+ BUGLVL(D_DURING)
+ printk("arcnet: received packet from %02Xh to %02Xh (%d bytes, type=%d)\n",
+ saddr,daddr,length,pkttype);
+ BUGLVL(D_RX)
+ {
+ int countx,county;
+
+ printk("arcnet: packet dump [rx-unsplit] follows:");
+
+ for (county=0; county<16+(pkttype!=NORMAL)*16; county++)
+ {
+ printk("\n[%04X] ",county*16);
+ for (countx=0; countx<16; countx++)
+ printk("%02X ",
+ arcpacket->raw[county*16+countx]);
+ }
+
+ printk("\n");
+ }
+
+ /* ARP packets have problems when sent from DOS.
+ * source address is always 0! So we take the hardware
+ * source addr (which is impossible to fumble) and insert
+ * it ourselves.
+ */
+ if (soft->protocol_id == ARC_P_ARP)
+ {
+ struct arphdr *arp=(struct arphdr *)
+ ((char *)soft+sizeof(struct ClientData));
+
+ /* make sure addresses are the right length */
+ if (arp->ar_hln==1 && arp->ar_pln==4)
+ {
+ char *cptr=(char *)(arp)+sizeof(struct arphdr);
+
+ if (!*cptr) /* is saddr = 00? */
+ {
+ BUGLVL(D_DURING)
+ printk("arcnet: ARP source address was 00h, set to %02Xh.\n",
+ saddr);
+ *cptr=saddr;
+ }
+ else BUGLVL(D_DURING)
+ {
+ printk("arcnet: ARP source address (%Xh) is fine.\n",
+ *cptr);
+ }
+ }
+ else
+ {
+ printk("arcnet: funny-shaped ARP packet. (%Xh, %Xh)\n",
+ arp->ar_hln,arp->ar_pln);
+ }
+ }
+ skb->protocol=arc_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+ else /* split packet */
+ {
+ /* NOTE: MSDOS ARP packet correction should only need to
+ * apply to unsplit packets, since ARP packets are so short.
+ *
+ * My interpretation of the RFC1201 (ARCnet) document is that
+ * if a packet is received out of order, the entire assembly
+ * process should be aborted.
+ *
+ * The RFC also mentions "it is possible for successfully
+ * received packets to be retransmitted." As of 0.40 all
+ * previously received packets are allowed, not just the
+ * most recent one.
+ *
+ * We allow multiple assembly processes, one for each
+ * ARCnet card possible on the network. Seems rather like
+ * a waste of memory. Necessary?
+ */
+
+ struct Incoming *in=&lp->incoming[saddr];
+
+ BUGLVL(D_RX) printk("arcnet: packet is split (splitflag=%d, seq=%d)\n",
+ arcsoft->split_flag,in->sequence);
+
+ if (in->skb && in->sequence!=arcsoft->sequence)
+ {
+ BUGLVL(D_INIT) printk("arcnet: wrong seq number, aborting assembly (expected=%d, seq=%d, splitflag=%d)\n",
+ in->sequence,arcsoft->sequence,
+ arcsoft->split_flag);
+ kfree_skb(in->skb,FREE_WRITE);
+ in->skb=NULL;
+ in->lastpacket=in->numpackets=0;
+ }
+
+ if (arcsoft->split_flag & 1) /* first packet in split */
+ {
+ BUGLVL(D_RX) printk("arcnet: brand new splitpacket (splitflag=%d)\n",
+ arcsoft->split_flag);
+ if (in->skb) /* already assembling one! */
+ {
+ BUGLVL(D_INIT) printk("arcnet: aborting previous (seq=%d) assembly (splitflag=%d, seq=%d)\n",
+ in->sequence,arcsoft->split_flag,
+ arcsoft->sequence);
+ kfree_skb(in->skb,FREE_WRITE);
+ }
+
+ in->sequence=arcsoft->sequence;
+ in->numpackets=((unsigned)arcsoft->split_flag>>1)+2;
+ in->lastpacket=1;
+
+ if (in->numpackets>16)
+ {
+ printk("arcnet: incoming packet more than 16 segments; dropping. (splitflag=%d)\n",
+ arcsoft->split_flag);
+ lp->stats.rx_dropped++;
+ return;
+ }
+
+ in->skb=skb=alloc_skb(508*in->numpackets
+ + sizeof(struct ClientData),
+ GFP_ATOMIC);
+ if (skb == NULL) {
+ printk("%s: (split) memory squeeze, dropping packet.\n",
+ dev->name);
+ lp->stats.rx_dropped++;
+ return;
+ }
+
+ /* I don't know what this is for, but it DOES avoid
+ * warnings...
+ */
+ skb->free=1;
+
+ soft=(struct ClientData *)skb->data;
+
+ skb->len=sizeof(struct ClientData);
+ skb->dev=dev;
+
+ memcpy((u_char *)soft+EXTRA_CLIENTDATA,
+ (u_char *)arcsoft+EXTRA_CLIENTDATA,
+ sizeof(struct ClientData)-EXTRA_CLIENTDATA);
+ soft->split_flag=0; /* final packet won't be split */
+ }
+ else /* not first packet */
+ {
+ int packetnum=((unsigned)arcsoft->split_flag>>1) + 1;
+
+ /* if we're not assembling, there's no point
+ * trying to continue.
+ */
+ if (!in->skb)
+ {
+ BUGLVL(D_INIT) printk("arcnet: can't continue split without starting first! (splitflag=%d, seq=%d)\n",
+ arcsoft->split_flag,arcsoft->sequence);
+ return;
+ }
+
+ in->lastpacket++;
+ if (packetnum!=in->lastpacket) /* not the right flag! */
+ {
+ /* harmless duplicate? ignore. */
+ if (packetnum<=in->lastpacket-1)
+ {
+ BUGLVL(D_INIT) printk("arcnet: duplicate splitpacket ignored! (splitflag=%d)\n",
+ arcsoft->split_flag);
+ return;
+ }
+
+ /* "bad" duplicate, kill reassembly */
+ BUGLVL(D_INIT) printk("arcnet: out-of-order splitpacket, reassembly (seq=%d) aborted (splitflag=%d, seq=%d)\n",
+ in->sequence,arcsoft->split_flag,
+ arcsoft->sequence);
+ kfree_skb(in->skb,FREE_WRITE);
+ in->skb=NULL;
+ in->lastpacket=in->numpackets=0;
+ return;
+ }
+
+ soft=(struct ClientData *)in->skb->data;
+ }
+
+ skb=in->skb;
+
+ memcpy(skb->data+skb->len,
+ (u_char *)arcsoft+sizeof(struct ClientData),
+ length-sizeof(struct ClientData));
+
+ skb->len+=length-sizeof(struct ClientData);
+
+ soft->daddr=daddr;
+ soft->saddr=saddr;
+
+ BUGLVL(D_DURING)
+ printk("arcnet: received packet from %02Xh to %02Xh (%d bytes, type=%d)\n",
+ saddr,daddr,length,pkttype);
+ BUGLVL(D_RX)
+ {
+ int countx,county;
+
+ printk("arcnet: packet dump [rx-split] follows:");
+
+ for (county=0; county<16+(pkttype!=NORMAL)*16; county++)
+ {
+ printk("\n[%04X] ",county*16);
+ for (countx=0; countx<16; countx++)
+ printk("%02X ",
+ arcpacket->raw[county*16+countx]);
+ }
+
+ printk("\n");
+ }
+
+ /* are we done? */
+ if (in->lastpacket == in->numpackets)
+ {
+ if (!skb || !in->skb)
+ printk("arcnet: ?!? done reassembling packet, no skb? (skb=%ph, in->skb=%ph)\n",
+ skb,in->skb);
+ in->skb=NULL;
+ in->lastpacket=in->numpackets=0;
+ skb->protocol=arc_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+ }
+
+ /* If any worth-while packets have been received, netif_rx()
+ has done a mark_bh(NET_BH) for us and will work on them
+ when we get to the bottom-half routine. */
+}
+
+
+#ifdef USE_TIMER_HANDLER
+/* this function is called every once in a while to make sure the ARCnet
+ * isn't stuck.
+ *
+ * If we miss a receive IRQ, the receiver (and IRQ) is permanently disabled
+ * and we might never receive a packet again! This will check if this
+ * is the case, and if so, re-enable the receiver.
+ */
+static void
+arcnet_timer(unsigned long arg)
+{
+ struct device *dev=(struct device *)arg;
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ short ioaddr=dev->base_addr;
+ int status=inb(STATUS);
+
+ /* if we didn't interrupt the IRQ handler, and RX's are still
+ * disabled, and we're not resetting the card... then we're stuck!
+ */
+ if (!dev->interrupt && dev->start
+ && status&NORXflag && !status&RESETflag)
+ {
+ BUGLVL(D_INIT)
+ printk("arcnet: timer: ARCnet was stuck! (status=%Xh)\n",
+ status);
+
+ arcnet_inthandler(dev);
+ }
+
+ /* requeue ourselves */
+ init_timer(&lp->timer);
+ lp->timer.expires=TIMERval;
+ add_timer(&lp->timer);
+}
+#endif
+
+/* Get the current statistics. This may be called with the card open or
+ closed. */
+static struct enet_statistics *
+arcnet_get_stats(struct device *dev)
+{
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+/* short ioaddr = dev->base_addr;*/
+
+ return &lp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ num_addrs == -1 Promiscuous mode, receive all packets
+ num_addrs == 0 Normal mode, clear multicast list
+ num_addrs > 0 Multicast mode, receive normal and MC packets, and do
+ best-effort filtering.
+ */
+static void
+set_multicast_list(struct device *dev, int num_addrs, void *addrs)
+{
+#if 0 /* no promiscuous mode at all */
+ struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
+
+ short ioaddr = dev->base_addr;
+ if (num_addrs) {
+ outw(69, ioaddr); /* Enable promiscuous mode */
+ } else
+ outw(99, ioaddr); /* Disable promiscuous mode, use normal mode */
+#endif
+}
+
+int arcnet_reset(struct device *dev)
+{
+ struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
+ short ioaddr=dev->base_addr;
+ int delayval,recbuf=lp->recbuf;
+
+ outb(0,INTMASK); /* no IRQ's, please! */
+
+ BUGLVL(D_INIT)
+ printk("arcnet: Resetting %s (status=%Xh)\n",
+ dev->name,inb(STATUS));
+
+ inb(RESET); /* Reset by reading this port */
+ JIFFER(RESETtime);
+
+ outb(CFLAGScmd|RESETclear, COMMAND); /* clear flags & end reset */
+ outb(CFLAGScmd|CONFIGclear,COMMAND);
+
+ /* after a reset, the first byte of shared mem is TESTvalue and the
+ * second byte is our 8-bit ARCnet address
+ */
+ {
+ u_char *cardmem = (u_char *) dev->mem_start;
+ if (cardmem[0] != TESTvalue)
+ {
+ BUGLVL(D_INIT)
+ printk("arcnet: reset failed: TESTvalue not present.\n");
+ return 1;
+ }
+ lp->arcnum=cardmem[1]; /* save address for later use */
+ }
+
+ /* clear out status variables */
+ recbuf=lp->recbuf=0;
+ lp->txbuf=2;
+ /*dev->tbusy=0;*/
+
+ /* enable extended (512-byte) packets */
+ outb(CONFIGcmd|EXTconf,COMMAND);
+ XJIFFER(ACKtime);
+
+ /* clean out all the memory to make debugging make more sense :) */
+ BUGLVL(D_DURING)
+ memset((void *)dev->mem_start,0x42,2048);
+
+ /* and enable receive of our first packet to the first buffer */
+ EnableReceiver();
+
+ /* re-enable interrupts */
+ outb(NORXflag,INTMASK);
+
+ /* done! return success. */
+ return 0;
+}
+
+
+/*
+ * Create the ARCnet ClientData header for an arbitrary protocol layer
+ *
+ * saddr=NULL means use device source address (always will anyway)
+ * daddr=NULL means leave destination address (eg unresolved arp)
+ */
+int arc_header(unsigned char *buff,struct device *dev,unsigned short type,
+ void *daddr,void *saddr,unsigned len,struct sk_buff *skb)
+{
+ struct ClientData *head = (struct ClientData *)buff;
+ struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
+
+ /* set the protocol ID according to RFC-1201 */
+ switch(type)
+ {
+ case ETH_P_IP:
+ head->protocol_id=ARC_P_IP;
+ break;
+ case ETH_P_ARP:
+ head->protocol_id=ARC_P_ARP;
+ break;
+ case ETH_P_RARP:
+ head->protocol_id=ARC_P_RARP;
+ break;
+ case ETH_P_IPX:
+ head->protocol_id=ARC_P_IPX;
+ break;
+ case ETH_P_ATALK:
+ head->protocol_id=ARC_P_ATALK;
+ break;
+ default:
+ printk("arcnet: I don't understand protocol %d (%Xh)\n",
+ type,type);
+ return 0;
+ }
+
+#if 1
+ /*
+ * Set the source hardware address.
+ * AVE: we can't do this, so we don't. Code below is directly
+ * stolen from eth.c driver and won't work.
+ ** TM: but for debugging I would like to have saddr in the header
+ */
+ if(saddr)
+ head->saddr=((u_char*)saddr)[0];
+ else
+ head->saddr=((u_char*)(dev->dev_addr))[0];
+#endif
+
+#if 0
+ /*
+ * Anyway, the loopback-device should never use this function...
+ *
+ * And the chances of it using the ARCnet version of it are so
+ * tiny that I don't think we have to worry :)
+ */
+ if (dev->flags & IFF_LOOPBACK)
+ {
+ head->daddr=0;
+ return(dev->hard_header_len);
+ }
+#endif
+
+ head->split_flag=0; /* split packets are done elsewhere */
+ head->sequence=(lp->sequence++);
+
+ /* supposedly if daddr is NULL, we should ignore it... */
+ if(daddr)
+ {
+ head->daddr=((u_char*)daddr)[0];
+ return dev->hard_header_len;
+ }
+ else
+ head->daddr=0; /* better fill one in anyway */
+
+ return -dev->hard_header_len;
+}
+
+
+/*
+ * Rebuild the ARCnet ClientData header. This is called after an ARP
+ * (or in future other address resolution) has completed on this
+ * sk_buff. We now let ARP fill in the other fields.
+ */
+int arc_rebuild_header(void *buff,struct device *dev,unsigned long dst,
+ struct sk_buff *skb)
+{
+ struct ClientData *head = (struct ClientData *)buff;
+
+ /*
+ * Only ARP/IP is currently supported
+ */
+
+ if(head->protocol_id != ARC_P_IP)
+ {
+ printk("arcnet: I don't understand resolve type %d (%Xh) addresses!\n",
+ head->protocol_id,head->protocol_id);
+ head->daddr=0;
+ /*memcpy(eth->h_source, dev->dev_addr, dev->addr_len);*/
+ return 0;
+ }
+
+ /*
+ * Try and get ARP to resolve the header.
+ */
+#ifdef CONFIG_INET
+ return arp_find(&(head->daddr), dst, dev, dev->pa_addr, skb)? 1 : 0;
+#else
+ return 0;
+#endif
+}
+
+/*
+ * Determine the packet's protocol ID.
+ *
+ * With ARCnet we have to convert everything to Ethernet-style stuff.
+ */
+unsigned short arc_type_trans(struct sk_buff *skb,struct device *dev)
+{
+ struct ClientData *head = (struct ClientData *) skb->data;
+
+ if (head->daddr==0)
+ skb->pkt_type=PACKET_BROADCAST;
+ else if(dev->flags&IFF_PROMISC)
+ {
+ /* if we're not sending to ourselves :) */
+ if (head->daddr != dev->dev_addr[0])
+ skb->pkt_type=PACKET_OTHERHOST;
+ }
+
+ /* now return the protocol number */
+ switch (head->protocol_id)
+ {
+ case ARC_P_IP: return htons(ETH_P_IP);
+ case ARC_P_ARP: return htons(ETH_P_ARP);
+ case ARC_P_RARP: return htons(ETH_P_RARP);
+ case ARC_P_IPX: return htons(ETH_P_IPX);
+ case ARC_P_ATALK: return htons(ETH_P_ATALK); /* Doesn't work yet */
+ case ARC_P_LANSOFT: /* don't understand. fall through. */
+ default:
+ BUGLVL(D_DURING)
+ printk("arcnet: received packet of unknown protocol id %d (%Xh)\n",
+ head->protocol_id,head->protocol_id);
+ return 0;
+ }
+ return htons(ETH_P_IP);
+}
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+static struct device thisARCnet = {
+ " ",/* if blank, device name inserted by /linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0, /* I/O address, IRQ */
+ 0, 0, 0, NULL, arcnet_probe };
+
+
+int io=0x0; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
+int irqnum=0; /* or use the insmod io= irq= shmem= options */
+int shmem=0;
+int num=0; /* number of device (ie for arc0, arc1, arc2...) */
+
+int
+init_module(void)
+{
+ sprintf(thisARCnet.name,"arc%d",num);
+
+ thisARCnet.base_addr=io;
+
+ thisARCnet.irq=irqnum;
+ if (thisARCnet.irq==2) thisARCnet.irq=9;
+
+ if (shmem)
+ {
+ thisARCnet.mem_start=shmem;
+ thisARCnet.mem_end=thisARCnet.mem_start+512*4-1;
+ thisARCnet.rmem_start=thisARCnet.mem_start+512*0;
+ thisARCnet.rmem_end=thisARCnet.mem_start+512*2-1;
+ }
+
+ if (register_netdev(&thisARCnet) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ if (MOD_IN_USE) {
+ printk("%s: device busy, remove delayed\n",thisARCnet.name);
+ } else {
+ if (thisARCnet.start) arcnet_close(&thisARCnet);
+ if (thisARCnet.irq) free_irq(thisARCnet.irq);
+ if (thisARCnet.base_addr) release_region(thisARCnet.base_addr,
+ ETHERCARD_TOTAL_SIZE);
+ unregister_netdev(&thisARCnet);
+ }
+}
+
+#endif /* MODULE */
+
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c skeleton.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * End:
+ */
+
diff --git a/drivers/net/at1700.c b/drivers/net/at1700.c
index be2bf7ed3..d1d9b45c9 100644
--- a/drivers/net/at1700.c
+++ b/drivers/net/at1700.c
@@ -16,20 +16,20 @@
straight-forward Fujitsu MB86965 implementation.
Sources:
- The Fujitsu MB86695 datasheet.
+ The Fujitsu MB86965 datasheet.
- After the initial version of this driver was written Gerry Sockins of
+ After the initial version of this driver was written Gerry Sawkins of
ATI provided their EEPROM configuration code header file.
Thanks to NIIBE Yutaka <gniibe@mri.co.jp> for bug fixes.
Bugs:
- The MB86695 has a design flaw that makes all probes unreliable. Not
+ The MB86965 has a design flaw that makes all probes unreliable. Not
only is it difficult to detect, it also moves around in I/O space in
response to inb()s from other device probes!
*/
static char *version =
- "at1700.c:v1.10 9/24/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+ "at1700.c:v1.12 1/18/95 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
#include <linux/config.h>
@@ -70,7 +70,6 @@ typedef unsigned char uchar;
/* Information that need to be kept for each board. */
struct net_local {
struct enet_statistics stats;
- long open_time; /* Useless example local info. */
uint tx_started:1; /* Number of packet on the Tx queue. */
uchar tx_queue; /* Number of packet on the Tx queue. */
ushort tx_queue_len; /* Current length of the Tx queue. */
@@ -120,7 +119,7 @@ static int at1700_probe1(struct device *dev, short ioaddr);
static int read_eeprom(int ioaddr, int location);
static int net_open(struct device *dev);
static int net_send_packet(struct sk_buff *skb, struct device *dev);
-static void net_interrupt(int reg_ptr);
+static void net_interrupt(int irq, struct pt_regs *regs);
static void net_rx(struct device *dev);
static int net_close(struct device *dev);
static struct enet_statistics *net_get_stats(struct device *dev);
@@ -207,7 +206,7 @@ int at1700_probe1(struct device *dev, short ioaddr)
/* Grab the region so that we can find another board if the IRQ request
fails. */
- snarf_region(ioaddr, AT1700_IO_EXTENT);
+ request_region(ioaddr, AT1700_IO_EXTENT, "at1700");
printk("%s: AT1700 found at %#3x, IRQ %d, address ", dev->name,
ioaddr, irq);
@@ -345,12 +344,14 @@ static int net_open(struct device *dev)
/* Switch to register bank 2 for the run-time registers. */
outb(0xe8, ioaddr + CONFIG_1);
+ lp->tx_started = 0;
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+
/* Turn on Rx interrupts, leave Tx interrupts off until packet Tx. */
outb(0x00, ioaddr + TX_INTR);
outb(0x81, ioaddr + RX_INTR);
- lp->open_time = jiffies;
-
dev->tbusy = 0;
dev->interrupt = 0;
dev->start = 1;
@@ -385,6 +386,9 @@ net_send_packet(struct sk_buff *skb, struct device *dev)
outw(0x8100, ioaddr + TX_INTR);
dev->tbusy=0;
dev->trans_start = jiffies;
+ lp->tx_started = 0;
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
}
/* If some higher layer thinks we've missed an tx-done interrupt
@@ -435,9 +439,8 @@ net_send_packet(struct sk_buff *skb, struct device *dev)
/* The typical workload of the driver:
Handle the network interface interrupts. */
static void
-net_interrupt(int reg_ptr)
+net_interrupt(int irq, struct pt_regs *regs)
{
- int irq = pt_regs2irq(reg_ptr);
struct device *dev = (struct device *)(irq2dev_map[irq]);
struct net_local *lp;
int ioaddr, status;
@@ -493,6 +496,7 @@ net_rx(struct device *dev)
while ((inb(ioaddr + RX_MODE) & 0x40) == 0) {
ushort status = inw(ioaddr + DATAPORT);
+ ushort pkt_len = inw(ioaddr + DATAPORT);
if (net_debug > 4)
printk("%s: Rxing packet mode %02x status %04x.\n",
@@ -511,13 +515,14 @@ net_rx(struct device *dev)
if (status & 0x02) lp->stats.rx_crc_errors++;
if (status & 0x01) lp->stats.rx_over_errors++;
} else {
- ushort pkt_len = inw(ioaddr + DATAPORT);
/* Malloc up new buffer. */
struct sk_buff *skb;
if (pkt_len > 1550) {
printk("%s: The AT1700 claimed a very large packet, size %d.\n",
dev->name, pkt_len);
+ /* Prime the FIFO and then flush the packet. */
+ inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT);
outb(0x05, ioaddr + 14);
lp->stats.rx_errors++;
break;
@@ -526,6 +531,8 @@ net_rx(struct device *dev)
if (skb == NULL) {
printk("%s: Memory squeeze, dropping packet (len %d).\n",
dev->name, pkt_len);
+ /* Prime the FIFO and then flush the packet. */
+ inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT);
outb(0x05, ioaddr + 14);
lp->stats.rx_dropped++;
break;
@@ -534,15 +541,7 @@ net_rx(struct device *dev)
skb->dev = dev;
insw(ioaddr + DATAPORT, skb->data, (pkt_len + 1) >> 1);
-
- if (net_debug > 5) {
- int i;
- printk("%s: Rxed packet of length %d: ", dev->name, pkt_len);
- for (i = 0; i < 14; i++)
- printk(" %02x", skb->data[i]);
- printk(".\n");
- }
-
+ skb->protocol=eth_type_trans(skb, dev);
netif_rx(skb);
lp->stats.rx_packets++;
}
@@ -575,8 +574,6 @@ static int net_close(struct device *dev)
struct net_local *lp = (struct net_local *)dev->priv;
int ioaddr = dev->base_addr;
- lp->open_time = 0;
-
dev->tbusy = 1;
dev->start = 0;
diff --git a/drivers/net/atp.c b/drivers/net/atp.c
index 19520abf2..1e542fcfd 100644
--- a/drivers/net/atp.c
+++ b/drivers/net/atp.c
@@ -1,23 +1,30 @@
-/* atp.c: Attached (pocket) ethernet adaptor driver for linux. */
+/* atp.c: Attached (pocket) ethernet adapter driver for linux. */
/*
- Written 1993 by Donald Becker.
- Copyright 1993 United States Government as represented by the Director,
- National Security Agency. This software may only be used and distributed
- according to the terms of the GNU Public License as modified by SRC,
- incorporated herein by reference.
+ This is a driver for a commonly OEMed pocket (parallel port)
+ ethernet adapter.
- The author may be reached as becker@super.org or
- C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ Written 1993,1994,1995 by Donald Becker.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ The timer-based reset code was written by Bill Carlson, wwc@super.org.
*/
static char *version =
- "atp.c:v0.04 2/25/94 Donald Becker (becker@super.org)\n";
+ "atp.c:v1.01 1/18/95 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
/*
This file is a device driver for the RealTek (aka AT-Lan-Tec) pocket
- ethernet adaptor. This is a common low-cost OEM pocket ethernet
- adaptor, sold under many names.
+ ethernet adapter. This is a common low-cost OEM pocket ethernet
+ adapter, sold under many names.
Sources:
This driver was written from the packet driver assembly code provided by
@@ -28,7 +35,7 @@ static char *version =
Theory of Operation
- The RTL8002 adaptor seems to be built around a custom spin of the SEEQ
+ The RTL8002 adapter seems to be built around a custom spin of the SEEQ
controller core. It probably has a 16K or 64K internal packet buffer, of
which the first 4K is devoted to transmit and the rest to receive.
The controller maintains the queue of received packet and the packet buffer
@@ -40,7 +47,7 @@ static char *version =
The station address is stored in a standard bit-serial EEPROM which must be
read (ughh) by the device driver. (Provisions have been made for
substituting a 74S288 PROM, but I haven't gotten reports of any models
- using it.) Unlike built-in devices, a pocket adaptor can temporarily lose
+ using it.) Unlike built-in devices, a pocket adapter can temporarily lose
power without indication to the device driver. The major effect is that
the station address, receive filter (promiscuous, etc.) and transceiver
must be reset.
@@ -53,7 +60,7 @@ static char *version =
Since the bulk data transfer of the actual packets through the slow
parallel port dominates the driver's running time, four distinct data
- (non-register) transfer modes are provided by the adaptor, two in each
+ (non-register) transfer modes are provided by the adapter, two in each
direction. In the first mode timing for the nibble transfers is
provided through the data port. In the second mode the same timing is
provided through the control port. In either case the data is read from
@@ -74,7 +81,6 @@ static char *version =
interpretations of the device registers.
*/
-#include <linux/config.h> /* Used only to override default values. */
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
@@ -97,35 +103,26 @@ static char *version =
#include "atp.h"
-/* Compatibility definitions for earlier kernel versions. */
-#ifndef HAVE_AUTOIRQ
-/* From auto_irq.c, in ioport.h for later versions. */
-extern void autoirq_setup(int waittime);
-extern int autoirq_report(int waittime);
-/* The map from IRQ number (as passed to the interrupt handler) to
- 'struct device'. */
-extern struct device *irq2dev_map[16];
-#endif
-
-#ifndef HAVE_ALLOC_SKB
-#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
-#define kfree_skbmem(addr, size) kfree_s(addr,size);
-#endif
-
-#ifndef HAVE_PORTRESERVE
-#define check_region(ioaddr, size) 0
-#define snarf_region(ioaddr, size); do ; while (0)
-#endif
-
/* use 0 for production, 1 for verification, >2 for debug */
#ifndef NET_DEBUG
-#define NET_DEBUG 4
+#define NET_DEBUG 1
#endif
static unsigned int net_debug = NET_DEBUG;
/* The number of low I/O ports used by the ethercard. */
#define ETHERCARD_TOTAL_SIZE 3
+/* This code, written by wwc@super.org, resets the adapter every
+ TIMED_CHECKER ticks. This recovers from an unknown error which
+ hangs the device. */
+#define TIMED_CHECKER (HZ/4)
+#ifdef TIMED_CHECKER
+#include <linux/timer.h>
+static void atp_timed_checker(unsigned long ignored);
+static struct device *atp_timed_dev;
+static struct timer_list atp_timer = {NULL, NULL, 0, 0, atp_timed_checker};
+#endif
+
/* Index to functions, as function prototypes. */
extern int atp_probe(struct device *dev);
@@ -138,7 +135,7 @@ static void hardware_init(struct device *dev);
static void write_packet(short ioaddr, int length, unsigned char *packet, int mode);
static void trigger_send(short ioaddr, int length);
static int net_send_packet(struct sk_buff *skb, struct device *dev);
-static void net_interrupt(int reg_ptr);
+static void net_interrupt(int irq, struct pt_regs *regs);
static void net_rx(struct device *dev);
static void read_block(short ioaddr, int length, unsigned char *buffer, int data_mode);
static int net_close(struct device *dev);
@@ -146,7 +143,7 @@ static struct enet_statistics *net_get_stats(struct device *dev);
static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
-/* Check for a network adaptor of this type, and return '0' iff one exists.
+/* Check for a network adapter of this type, and return '0' iff one exists.
If dev->base_addr == 0, probe all likely locations.
If dev->base_addr == 1, always return failure.
If dev->base_addr == 2, allocate space for the device and return success
@@ -190,7 +187,7 @@ static int atp_probe1(struct device *dev, short ioaddr)
status = read_nibble(ioaddr, CMR1);
if ((status & 0x78) != 0x08) {
- /* The pocket adaptor probe failed, restore the control register. */
+ /* The pocket adapter probe failed, restore the control register. */
outb(saved_ctrl_reg, ioaddr + PAR_CONTROL);
return 1;
}
@@ -216,7 +213,7 @@ static int atp_probe1(struct device *dev, short ioaddr)
/* Read the station address PROM. */
get_node_ID(dev);
- printk("%s: Pocket adaptor found at %#3x, IRQ %d, SAPROM "
+ printk("%s: Pocket adapter found at %#3x, IRQ %d, SAPROM "
"%02X:%02X:%02X:%02X:%02X:%02X.\n", dev->name, dev->base_addr,
dev->irq, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
@@ -238,7 +235,7 @@ static int atp_probe1(struct device *dev, short ioaddr)
lp->addr_mode = CMR2h_Normal;
}
- /* For the ATP adaptor the "if_port" is really the data transfer mode. */
+ /* For the ATP adapter the "if_port" is really the data transfer mode. */
dev->if_port = (dev->mem_start & 0xf) ? dev->mem_start & 0x7 : 4;
if (dev->mem_end & 0xf)
net_debug = dev->mem_end & 7;
@@ -249,6 +246,12 @@ static int atp_probe1(struct device *dev, short ioaddr)
dev->get_stats = net_get_stats;
dev->set_multicast_list = &set_multicast_list;
+#ifdef TIMED_CHECKER
+ del_timer(&atp_timer);
+ atp_timer.expires = TIMED_CHECKER;
+ atp_timed_dev = dev;
+ add_timer(&atp_timer);
+#endif
return 0;
}
@@ -261,7 +264,7 @@ static void get_node_ID(struct device *dev)
write_reg(ioaddr, CMR2, CMR2_EEPROM); /* Point to the EEPROM control registers. */
- /* Some adaptors have the station address at offset 15 instead of offset
+ /* Some adapters have the station address at offset 15 instead of offset
zero. Check for it, and fix it if needed. */
if (eeprom_op(ioaddr, EE_READ(0)) == 0xffff)
sa_offset = 15;
@@ -323,7 +326,7 @@ static int net_open(struct device *dev)
port or interrupt may be shared. */
if (irq2dev_map[dev->irq] != 0
|| (irq2dev_map[dev->irq] = dev) == 0
- || request_irq(dev->irq, &net_interrupt, 0, "atp")) {
+ || request_irq(dev->irq, &net_interrupt, 0, "ATP")) {
return -EAGAIN;
}
@@ -423,7 +426,7 @@ net_send_packet(struct sk_buff *skb, struct device *dev)
inb(ioaddr + PAR_CONTROL) & 0x10 ? "network cable problem"
: "IRQ conflict");
lp->stats.tx_errors++;
- /* Try to restart the adaptor. */
+ /* Try to restart the adapter. */
hardware_init(dev);
dev->tbusy=0;
dev->trans_start = jiffies;
@@ -479,9 +482,8 @@ net_send_packet(struct sk_buff *skb, struct device *dev)
/* The typical workload of the driver:
Handle the network interface interrupts. */
static void
-net_interrupt(int reg_ptr)
+net_interrupt(int irq, struct pt_regs * regs)
{
- int irq = pt_regs2irq(reg_ptr);
struct device *dev = (struct device *)(irq2dev_map[irq]);
struct net_local *lp;
int ioaddr, status, boguscount = 20;
@@ -499,7 +501,7 @@ net_interrupt(int reg_ptr)
/* Disable additional spurious interrupts. */
outb(Ctrl_SelData, ioaddr + PAR_CONTROL);
- /* The adaptor's output is currently the IRQ line, switch it to data. */
+ /* The adapter's output is currently the IRQ line, switch it to data. */
write_reg(ioaddr, CMR2, CMR2_NULL);
write_reg(ioaddr, IMR, 0);
@@ -534,7 +536,7 @@ net_interrupt(int reg_ptr)
} else if (status & ((ISR_TxErr + ISR_TxOK)<<3)) {
if (net_debug > 6) printk("handling Tx done..");
/* Clear the Tx interrupt. We should check for too many failures
- and reinitialize the adaptor. */
+ and reinitialize the adapter. */
write_reg(ioaddr, ISR, ISR_TxErr + ISR_TxOK);
if (status & (ISR_TxErr<<3)) {
lp->stats.collisions++;
@@ -576,14 +578,19 @@ net_interrupt(int reg_ptr)
}
/* This following code fixes a rare (and very difficult to track down)
- problem where the adaptor forgets its ethernet address. */
+ problem where the adapter forgets its ethernet address. */
{
int i;
for (i = 0; i < 6; i++)
write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
+#ifdef TIMED_CHECKER
+ del_timer(&atp_timer);
+ atp_timer.expires = TIMED_CHECKER;
+ add_timer(&atp_timer);
+#endif
}
- /* Tell the adaptor that it can go back to using the output line as IRQ. */
+ /* Tell the adapter that it can go back to using the output line as IRQ. */
write_reg(ioaddr, CMR2, CMR2_IRQOUT);
/* Enable the physical interrupt line, which is sure to be low until.. */
outb(Ctrl_SelData + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
@@ -598,6 +605,41 @@ net_interrupt(int reg_ptr)
return;
}
+#ifdef TIMED_CHECKER
+/* This following code fixes a rare (and very difficult to track down)
+ problem where the adapter forgets its ethernet address. */
+static void atp_timed_checker(unsigned long ignored)
+{
+ int i;
+ int ioaddr = atp_timed_dev->base_addr;
+
+ if (!atp_timed_dev->interrupt)
+ {
+ for (i = 0; i < 6; i++)
+#if 0
+ if (read_cmd_byte(ioaddr, PAR0 + i) != atp_timed_dev->dev_addr[i])
+ {
+ struct net_local *lp = (struct net_local *)atp_timed_dev->priv;
+ write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]);
+ if (i == 2)
+ lp->stats.tx_errors++;
+ else if (i == 3)
+ lp->stats.tx_dropped++;
+ else if (i == 4)
+ lp->stats.collisions++;
+ else
+ lp->stats.rx_errors++;
+ }
+#else
+ write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]);
+#endif
+ }
+ del_timer(&atp_timer);
+ atp_timer.expires = TIMED_CHECKER;
+ add_timer(&atp_timer);
+}
+#endif
+
/* We have a good packet(s), get it/them out of the buffers. */
static void net_rx(struct device *dev)
{
@@ -648,6 +690,7 @@ static void net_rx(struct device *dev)
data[12], data[13]);
}
+ skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
lp->stats.rx_packets++;
}
@@ -710,7 +753,7 @@ net_get_stats(struct device *dev)
return &lp->stats;
}
-/* Set or clear the multicast filter for this adaptor.
+/* Set or clear the multicast filter for this adapter.
num_addrs == -1 Promiscuous mode, receive all packets
num_addrs == 0 Normal mode, clear multicast list
num_addrs > 0 Multicast mode, receive normal and MC packets, and do
diff --git a/drivers/net/auto_irq.c b/drivers/net/auto_irq.c
index 0bab58cb1..c050d204e 100644
--- a/drivers/net/auto_irq.c
+++ b/drivers/net/auto_irq.c
@@ -32,15 +32,12 @@ static char *version=
"auto_irq.c:v1.11 Donald Becker (becker@cesdis.gsfc.nasa.gov)";
#endif
-/*#include <linux/config.h>*/
-/*#include <linux/kernel.h>*/
#include <linux/sched.h>
#include <linux/delay.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/netdevice.h>
-/*#include <asm/system.h>*/
struct device *irq2dev_map[16] = {0, 0, /* ... zeroed */};
@@ -53,7 +50,7 @@ static volatile int irq_number; /* The latest irq number we actually found. */
static volatile int irq_bitmap; /* The irqs we actually found. */
static int irq_handled; /* The irq lines we have a handler on. */
-static void autoirq_probe(int irq)
+static void autoirq_probe(int irq, struct pt_regs * regs)
{
irq_number = irq;
set_bit(irq, (void *)&irq_bitmap); /* irq_bitmap |= 1 << irq; */
diff --git a/drivers/net/de4x5.c b/drivers/net/de4x5.c
new file mode 100644
index 000000000..6abd17d5e
--- /dev/null
+++ b/drivers/net/de4x5.c
@@ -0,0 +1,2513 @@
+/* de4x5.c: A DIGITAL DE425/DE434/DE435 ethernet driver for linux.
+
+ Copyright 1994, 1995 Digital Equipment Corporation.
+
+ This software may be used and distributed according to the terms of
+ the GNU Public License, incorporated herein by reference.
+
+ This driver is written for the Digital Equipment Corporation series
+ of EtherWORKS ethernet cards:
+
+ DE425 TP/COAX EISA
+ DE434 TP PCI
+ DE435 TP/COAX/AUI PCI
+ DE500 10/100 PCI Fasternet
+
+ The driver has been tested on a relatively busy network using the DE425,
+ DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred
+ 16M of data to a DECstation 5000/200 as follows:
+
+ TCP UDP
+ TX RX TX RX
+ DE425 1030k 997k 1170k 1128k
+ DE434 1063k 995k 1170k 1125k
+ DE435 1063k 995k 1170k 1125k
+ DE500 1063k 998k 1170k 1125k in 10Mb/s mode
+
+ All values are typical (in kBytes/sec) from a sample of 4 for each
+ measurement. Their error is +/-20k on a quiet (private) network and also
+ depend on what load the CPU has.
+
+ The author may be reached as davies@wanton.lkg.dec.com or Digital
+ Equipment Corporation, 550 King Street, Littleton MA 01460.
+
+ =========================================================================
+ This driver has been written substantially from scratch, although its
+ inheritance of style and stack interface from 'ewrk3.c' and in turn from
+ Donald Becker's 'lance.c' should be obvious.
+
+ Upto 15 EISA cards can be supported under this driver, limited primarily
+ by the available IRQ lines. I have checked different configurations of
+ multiple depca, EtherWORKS 3 cards and de4x5 cards and have not found a
+ problem yet (provided you have at least depca.c v0.38) ...
+
+ PCI support has been added to allow the driver to work with the DE434
+ and DE435 cards. The I/O accesses are a bit of a kludge due to the
+ differences in the EISA and PCI CSR address offsets from the base
+ address.
+
+ The ability to load this driver as a loadable module has been included
+ and used extensively during the driver development (to save those long
+ reboot sequences). Loadable module support under PCI has been achieved
+ by letting any I/O address less than 0x1000 be assigned as:
+
+ 0xghh
+
+ where g is the bus number (usually 0 until the BIOS's get fixed)
+ hh is the device number (max is 32 per bus).
+
+ Essentially, the I/O address and IRQ information are ignored and filled
+ in later by the PCI BIOS during the PCI probe. Note that the board
+ should be in the system at boot time so that its I/O address and IRQ are
+ allocated by the PCI BIOS automatically. The special case of device 0 on
+ bus 0 is not allowed as the probe will think you're autoprobing a
+ module.
+
+ To utilise this ability, you have to do 8 things:
+
+ 0) have a copy of the loadable modules code installed on your system.
+ 1) copy de4x5.c from the /linux/drivers/net directory to your favourite
+ temporary directory.
+ 2) edit the source code near line 1945 to reflect the I/O address and
+ IRQ you're using, or assign these when loading by:
+
+ insmod de4x5.o irq=x io=y
+
+ 3) compile de4x5.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 de4x5 configuration turned off and reboot.
+ 5) insmod de4x5.o
+ 6) run the net startup bits for your new 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.
+
+ To unload a module, turn off the associated interface
+ 'ifconfig eth?? down' then 'rmmod de4x5'.
+
+ Automedia detection is included so that in principal you can disconnect
+ from, e.g. TP, reconnect to BNC and things will still work (after a
+ pause whilst the driver figures out where its media went). My tests
+ using ping showed that it appears to work....
+
+ A compile time switch to allow Zynx recognition has been added. This
+ "feature" is in no way supported nor tested in this driver and the user
+ may use it at his/her sole discretion. I have had 2 conflicting reports
+ that my driver will or won't work with Zynx. Try Donald Becker's
+ 'tulip.c' if this driver doesn't work for you. I will not be supporting
+ Zynx cards since I have no information on them and can't test them in a
+ system.
+
+ TO DO:
+ ------
+
+
+ Revision History
+ ----------------
+
+ Version Date Description
+
+ 0.1 17-Nov-94 Initial writing. ALPHA code release.
+ 0.2 13-Jan-95 Added PCI support for DE435's.
+ 0.21 19-Jan-95 Added auto media detection.
+ 0.22 10-Feb-95 Fix interrupt handler call <chris@cosy.sbg.ac.at>.
+ Fix recognition bug reported by <bkm@star.rl.ac.uk>.
+ Add request/release_region code.
+ Add loadable modules support for PCI.
+ Clean up loadable modules support.
+ 0.23 28-Feb-95 Added DC21041 and DC21140 support.
+ Fix missed frame counter value and initialisation.
+ Fixed EISA probe.
+ 0.24 11-Apr-95 Change delay routine to use <linux/udelay>.
+ Change TX_BUFFS_AVAIL macro.
+ Change media autodetection to allow manual setting.
+ Completed DE500 (DC21140) support.
+ 0.241 18-Apr-95 Interim release without DE500 Autosense Algorithm.
+
+ =========================================================================
+*/
+
+static char *version = "de4x5.c:v0.241 4/18/95 davies@wanton.lkg.dec.com\n";
+
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif /* MODULE */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/segment.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/unistd.h>
+
+#include "de4x5.h"
+
+#ifdef DE4X5_DEBUG
+static int de4x5_debug = DE4X5_DEBUG;
+#else
+static int de4x5_debug = 1;
+#endif
+
+#ifdef DE4X5_AUTOSENSE /* Should be done on a per adapter basis */
+static int de4x5_autosense = DE4X5_AUTOSENSE;
+#else
+static int de4x5_autosense = AUTO; /* Do auto media/mode sensing */
+#endif
+
+/*
+** Ethernet PROM defines
+*/
+#define PROBE_LENGTH 32
+#define ETH_PROM_SIG 0xAA5500FFUL
+
+/*
+** Ethernet Info
+*/
+#define PKT_BUF_SZ 1544 /* Buffer size for each Tx/Rx buffer */
+#define MAX_PKT_SZ 1514 /* Maximum ethernet packet length */
+#define MAX_DAT_SZ 1500 /* Maximum ethernet data length */
+#define MIN_DAT_SZ 1 /* Minimum ethernet data length */
+#define PKT_HDR_LEN 14 /* Addresses and data length info */
+
+#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */
+#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */
+
+/*
+** EISA bus defines
+*/
+#define DE4X5_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */
+#define DE4X5_EISA_TOTAL_SIZE 0xfff /* I/O address extent */
+
+#define MAX_EISA_SLOTS 16
+#define EISA_SLOT_INC 0x1000
+
+#define DE4X5_SIGNATURE {"DE425",""}
+#define DE4X5_NAME_LENGTH 8
+
+/*
+** PCI Bus defines
+*/
+#define PCI_MAX_BUS_NUM 8
+#define DE4X5_PCI_TOTAL_SIZE 0x80 /* I/O address extent */
+
+/*
+** Memory Alignment. Each descriptor is 4 longwords long. To force a
+** particular alignment on the TX descriptor, adjust DESC_SKIP_LEN and
+** DESC_ALIGN. ALIGN aligns the start address of the private memory area
+** and hence the RX descriptor ring's first entry.
+*/
+#define ALIGN4 ((u_long)4 - 1) /* 1 longword align */
+#define ALIGN8 ((u_long)8 - 1) /* 2 longword align */
+#define ALIGN16 ((u_long)16 - 1) /* 4 longword align */
+#define ALIGN32 ((u_long)32 - 1) /* 8 longword align */
+#define ALIGN64 ((u_long)64 - 1) /* 16 longword align */
+#define ALIGN128 ((u_long)128 - 1) /* 32 longword align */
+
+#define ALIGN ALIGN32 /* Keep the DC21040 happy... */
+#define CACHE_ALIGN CAL_16LONG
+#define DESC_SKIP_LEN DSL_0 /* Must agree with DESC_ALIGN */
+/*#define DESC_ALIGN u_long dummy[4]; / * Must agree with DESC_SKIP_LEN */
+#define DESC_ALIGN
+
+#ifndef IS_NOT_DEC /* See README.de4x5 for using this */
+static int is_not_dec = 0;
+#else
+static int is_not_dec = 1;
+#endif
+
+/*
+** DE4X5 IRQ ENABLE/DISABLE
+*/
+static u_long irq_mask = IMR_SEM | IMR_RIM | IMR_RUM | IMR_TIM | IMR_TUM ;
+
+static u_long irq_en = IMR_NIM | IMR_AIM;
+
+#define ENABLE_IRQs { \
+ imr |= irq_en;\
+ outl(imr, DE4X5_IMR); /* Enable the IRQs */\
+}
+
+#define DISABLE_IRQs {\
+ imr = inl(DE4X5_IMR);\
+ imr &= ~irq_en;\
+ outl(imr, DE4X5_IMR); /* Disable the IRQs */\
+}
+
+#define UNMASK_IRQs {\
+ imr |= irq_mask;\
+ outl(imr, DE4X5_IMR); /* Unmask the IRQs */\
+}
+
+#define MASK_IRQs {\
+ imr = inl(DE4X5_IMR);\
+ imr &= ~irq_mask;\
+ outl(imr, DE4X5_IMR); /* Mask the IRQs */\
+}
+
+/*
+** DE4X5 START/STOP
+*/
+#define START_DE4X5 {\
+ omr = inl(DE4X5_OMR);\
+ omr |= OMR_ST | OMR_SR;\
+ outl(omr, DE4X5_OMR); /* Enable the TX and/or RX */\
+}
+
+#define STOP_DE4X5 {\
+ omr = inl(DE4X5_OMR);\
+ omr &= ~(OMR_ST|OMR_SR);\
+ outl(omr, DE4X5_OMR); /* Disable the TX and/or RX */ \
+}
+
+/*
+** DE4X5 SIA RESET
+*/
+#define RESET_SIA outl(0, DE4X5_SICR); /* Reset SIA connectivity regs */
+
+/*
+** SROM Structure
+*/
+struct de4x5_srom {
+ char reserved[18];
+ char version;
+ char num_adapters;
+ char ieee_addr[6];
+ char info[100];
+ short chksum;
+};
+
+/*
+** DE4X5 Descriptors. Make sure that all the RX buffers are contiguous
+** and have sizes of both a power of 2 and a multiple of 4.
+** A size of 256 bytes for each buffer was chosen because over 90% of
+** all packets in our network are <256 bytes long and 64 longword alignment
+** is possible.
+*/
+#define NUM_RX_DESC 8 /* Number of RX descriptors */
+#define NUM_TX_DESC 8 /* Number of TX descriptors */
+#define BUFF_ALLOC_RETRIES 10 /* In case of memory shortage */
+#define RX_BUFF_SZ 1536 /* Power of 2 for kmalloc and */
+ /* Multiple of 4 for DC21040 */
+
+struct de4x5_desc {
+ volatile long status;
+ u_long des1;
+ char *buf;
+ char *next;
+ DESC_ALIGN
+};
+
+/*
+** The DE4X5 private structure
+*/
+#define DE4X5_PKT_STAT_SZ 16
+#define DE4X5_PKT_BIN_SZ 128 /* Should be >=100 unless you
+ increase DE4X5_PKT_STAT_SZ */
+
+struct de4x5_private {
+ char adapter_name[80]; /* Adapter name */
+ struct de4x5_desc rx_ring[NUM_RX_DESC]; /* RX descriptor ring */
+ struct de4x5_desc tx_ring[NUM_TX_DESC]; /* TX descriptor ring */
+ struct sk_buff *skb[NUM_TX_DESC]; /* TX skb for freeing when sent */
+ int rx_new, rx_old; /* RX descriptor ring pointers */
+ int tx_new, tx_old; /* TX descriptor ring pointers */
+ char setup_frame[SETUP_FRAME_LEN]; /* Holds MCA and PA info. */
+ struct enet_statistics stats; /* Public stats */
+ struct {
+ unsigned long bins[DE4X5_PKT_STAT_SZ]; /* Private stats counters */
+ unsigned long unicast;
+ unsigned long multicast;
+ unsigned long broadcast;
+ unsigned long excessive_collisions;
+ unsigned long tx_underruns;
+ unsigned long excessive_underruns;
+ } pktStats;
+ char rxRingSize;
+ char txRingSize;
+ char bus; /* EISA or PCI */
+ u_short chipset; /* DC21040, DC21041 or DC21140 */
+ long media; /* Media (eg TP), mode (eg 100B)*/
+ int autosense; /* Allow/disallow autosensing */
+ int tx_enable; /* Enable descriptor polling */
+ char lostMedia; /* Possibly lost media */
+ int setup_f; /* Setup frame filtering type */
+};
+
+
+/*
+** The transmit ring full condition is described by the tx_old and tx_new
+** pointers by:
+** tx_old = tx_new Empty ring
+** tx_old = tx_new+1 Full ring
+** tx_old+txRingSize = tx_new+1 Full ring (wrapped condition)
+*/
+#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\
+ lp->tx_old+lp->txRingSize-lp->tx_new-1:\
+ lp->tx_old -lp->tx_new-1)
+/*#define TX_BUFFS_AVAIL ((lp->tx_ring[lp->tx_new].status)>=0 ? 1 : 0)*/
+
+/*
+** Public Functions
+*/
+static int de4x5_open(struct device *dev);
+static int de4x5_queue_pkt(struct sk_buff *skb, struct device *dev);
+static void de4x5_interrupt(int irq, struct pt_regs *regs);
+static int de4x5_close(struct device *dev);
+static struct enet_statistics *de4x5_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+static int de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+
+/*
+** Private functions
+*/
+static int de4x5_hw_init(struct device *dev, short iobase);
+static int de4x5_init(struct device *dev);
+static int de4x5_rx(struct device *dev);
+static int de4x5_tx(struct device *dev);
+
+static int autoconf_media(struct device *dev);
+static void create_packet(struct device *dev, char *frame, int len);
+static void dce_us_delay(u_long usec);
+static void dce_ms_delay(u_long msec);
+static void load_packet(struct device *dev, char *buf, u_long flags, struct sk_buff *skb);
+static void dc21040_autoconf(struct device *dev);
+static void dc21041_autoconf(struct device *dev);
+static void dc21140_autoconf(struct device *dev);
+static long test_media(struct device *dev, long irqs, long irq_mask, long csr13, long csr14, long csr15, long msec);
+static long ping_media(struct device *dev);
+static void reset_init_sia(struct device *dev, long sicr, long strr, long sigr);
+static int test_ans(struct device *dev, long irqs, long irq_mask, long msec);
+static void load_ms_timer(struct device *dev, u_long msec);
+static int EISA_signature(char *name, long eisa_id);
+static int DevicePresent(short iobase);
+static short srom_rd(u_short address, u_char offset);
+static void srom_latch(u_long command, u_short address);
+static void srom_command(u_long command, u_short address);
+static void srom_address(u_long command, u_short address, u_char offset);
+static short srom_data(u_long command, u_short address);
+/*static void srom_busy(u_long command, u_short address);*/
+static void sendto_srom(u_long command, u_short addr);
+static long getfrom_srom(u_short addr);
+static void SetMulticastFilter(struct device *dev, int num_addrs, char *addrs);
+static int aprom_crc (struct device *dev);
+
+static void eisa_probe(struct device *dev, short iobase);
+static void pci_probe(struct device *dev, short iobase);
+static struct device *alloc_device(struct device *dev, int iobase);
+static char *build_setup_frame(struct device *dev, int mode);
+
+#ifdef MODULE
+int init_module(void);
+void cleanup_module(void);
+static int autoprobed = 1, loading_module = 1;
+# else
+static unsigned char de4x5_irq[] = {5,9,10,11};
+static int autoprobed = 0, loading_module = 0;
+#endif /* MODULE */
+
+static char name[DE4X5_NAME_LENGTH + 1];
+static int num_de4x5s = 0, num_eth = 0;
+
+/*
+** Kludge to get around the fact that the CSR addresses have different
+** offsets in the PCI and EISA boards. Also note that the ethernet address
+** PROM is accessed differently.
+*/
+static struct bus_type {
+ int bus;
+ int device;
+ int chipset;
+ struct de4x5_srom srom;
+ int autosense;
+} bus;
+
+/*
+** Miscellaneous defines...
+*/
+#define RESET_DE4X5 {\
+ long i;\
+ i=inl(DE4X5_BMR);\
+ outl(i | BMR_SWR, DE4X5_BMR);\
+ outl(i, DE4X5_BMR);\
+ for (i=0;i<5;i++) inl(DE4X5_BMR);\
+}
+
+
+
+int de4x5_probe(struct device *dev)
+{
+ int tmp = num_de4x5s, iobase = dev->base_addr;
+ int status = -ENODEV;
+
+ if ((iobase == 0) && loading_module){
+ printk("Autoprobing is not supported when loading a module based driver.\n");
+ status = -EIO;
+ } else { /* First probe for the Ethernet */
+ /* Address PROM pattern */
+ eisa_probe(dev, iobase);
+ pci_probe(dev, iobase);
+
+ if ((tmp == num_de4x5s) && (iobase != 0)) {
+ printk("%s: de4x5_probe() cannot find device at 0x%04x.\n", dev->name,
+ iobase);
+ }
+
+ /*
+ ** Walk the device list to check that at least one device
+ ** initialised OK
+ */
+ for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next);
+
+ if (dev->priv) status = 0;
+ if (iobase == 0) autoprobed = 1;
+ }
+
+ return status;
+}
+
+static int
+de4x5_hw_init(struct device *dev, short iobase)
+{
+ struct bus_type *lp = &bus;
+ int tmpbus, tmpchs, i, j, status=0;
+ char *tmp;
+ u_long nicsr;
+
+ /* Ensure we're not sleeping */
+ if (lp->chipset == DC21041) {
+ outl(0, PCI_CFDA);
+ dce_ms_delay(10);
+ }
+
+ RESET_DE4X5;
+
+ if (((nicsr=inl(DE4X5_STS)) & (STS_TS | STS_RS)) == 0) {
+ /*
+ ** Now find out what kind of DC21040/DC21041/DC21140 board we have.
+ */
+ if (lp->bus == PCI) {
+ if (!is_not_dec) {
+ if ((lp->chipset == DC21040) || (lp->chipset == DC21041)) {
+ strcpy(name, "DE435");
+ } else if (lp->chipset == DC21140) {
+ strcpy(name, "DE500"); /* Must read the SROM here! */
+ }
+ } else {
+ strcpy(name, "UNKNOWN");
+ }
+ } else {
+ EISA_signature(name, EISA_ID0);
+ }
+
+ if (*name != '\0') { /* found a board signature */
+ dev->base_addr = iobase;
+
+ if (lp->bus == EISA) {
+ printk("%s: %s at %#3x (EISA slot %d)",
+ dev->name, name, (u_short)iobase, (((u_short)iobase>>12)&0x0f));
+ } else { /* PCI port address */
+ printk("%s: %s at %#3x (PCI device %d)", dev->name, name, (u_short)iobase,lp->device);
+ }
+
+ printk(", h/w address ");
+ status = aprom_crc(dev);
+ for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet addr. */
+ printk("%2.2x:", dev->dev_addr[i]);
+ }
+ printk("%2.2x,\n", dev->dev_addr[i]);
+
+ tmpbus = lp->bus;
+ tmpchs = lp->chipset;
+
+ if (status == 0) {
+ struct de4x5_private *lp;
+
+ /*
+ ** Reserve a section of kernel memory for the adapter
+ ** private area and the TX/RX descriptor rings.
+ */
+ dev->priv = (void *) kmalloc(sizeof(struct de4x5_private) + ALIGN,
+ GFP_KERNEL);
+ /*
+ ** Align to a longword boundary
+ */
+ dev->priv = (void *)(((u_long)dev->priv + ALIGN) & ~ALIGN);
+ lp = (struct de4x5_private *)dev->priv;
+ memset(dev->priv, 0, sizeof(struct de4x5_private));
+ lp->bus = tmpbus;
+ lp->chipset = tmpchs;
+
+ /*
+ ** Choose autosensing
+ */
+ if (de4x5_autosense & AUTO) {
+ lp->autosense = AUTO;
+ } else {
+ if (lp->chipset != DC21140) {
+ if ((lp->chipset == DC21040) && (de4x5_autosense & TP_NW)) {
+ de4x5_autosense = TP;
+ }
+ if ((lp->chipset == DC21041) && (de4x5_autosense & BNC_AUI)) {
+ de4x5_autosense = BNC;
+ }
+ lp->autosense = de4x5_autosense & 0x001f;
+ } else {
+ lp->autosense = de4x5_autosense & 0x00c0;
+ }
+ }
+
+ sprintf(lp->adapter_name,"%s (%s)", name, dev->name);
+ request_region(iobase, (lp->bus == PCI ? DE4X5_PCI_TOTAL_SIZE :
+ DE4X5_EISA_TOTAL_SIZE),
+ lp->adapter_name);
+
+ /*
+ ** Allocate contiguous receive buffers, long word aligned.
+ ** This could be a possible memory leak if the private area
+ ** is ever hosed.
+ */
+ for (tmp=NULL, j=0; (j<BUFF_ALLOC_RETRIES) && (tmp==NULL); j++) {
+ if ((tmp = (void *)kmalloc(RX_BUFF_SZ * NUM_RX_DESC + ALIGN,
+ GFP_KERNEL)) != NULL) {
+ tmp = (char *)(((u_long) tmp + ALIGN) & ~ALIGN);
+ for (i=0; i<NUM_RX_DESC; i++) {
+ lp->rx_ring[i].status = 0;
+ lp->rx_ring[i].des1 = RX_BUFF_SZ;
+ lp->rx_ring[i].buf = tmp + i * RX_BUFF_SZ;
+ lp->rx_ring[i].next = NULL;
+ }
+ }
+ }
+
+ if (tmp != NULL) {
+ lp->rxRingSize = NUM_RX_DESC;
+ lp->txRingSize = NUM_TX_DESC;
+
+ /* Write the end of list marker to the descriptor lists */
+ lp->rx_ring[lp->rxRingSize - 1].des1 |= RD_RER;
+ lp->tx_ring[lp->txRingSize - 1].des1 |= TD_TER;
+
+ /* Tell the adapter where the TX/RX rings are located. */
+ outl((long)lp->rx_ring, DE4X5_RRBA);
+ outl((long)lp->tx_ring, DE4X5_TRBA);
+
+ lp->tx_enable = TRUE;
+
+ if (dev->irq < 2) {
+#ifndef MODULE
+ unsigned char irqnum;
+ u_long omr;
+ autoirq_setup(0);
+
+ omr = inl(DE4X5_OMR);
+ outl(IMR_AIM|IMR_RUM, DE4X5_IMR); /* Unmask RUM interrupt */
+ outl(OMR_SR | omr, DE4X5_OMR); /* Start RX w/no descriptors */
+
+ irqnum = autoirq_report(1);
+ if (!irqnum) {
+ printk(" and failed to detect IRQ line.\n");
+ status = -ENXIO;
+ } else {
+ for (dev->irq=0,i=0; (i<sizeof(de4x5_irq)) && (!dev->irq); i++) {
+ if (irqnum == de4x5_irq[i]) {
+ dev->irq = irqnum;
+ printk(" and uses IRQ%d.\n", dev->irq);
+ }
+ }
+
+ if (!dev->irq) {
+ printk(" but incorrect IRQ line detected.\n");
+ status = -ENXIO;
+ }
+ }
+
+ outl(0, DE4X5_IMR); /* Re-mask RUM interrupt */
+
+#endif /* MODULE */
+ } else {
+ printk(" and requires IRQ%d (not probed).\n", dev->irq);
+ }
+ } else {
+ printk("%s: Kernel could not allocate RX buffer memory.\n",
+ dev->name);
+ status = -ENXIO;
+ }
+ if (status) release_region(iobase, (lp->bus == PCI ?
+ DE4X5_PCI_TOTAL_SIZE :
+ DE4X5_EISA_TOTAL_SIZE));
+ } else {
+ printk(" which has an Ethernet PROM CRC error.\n");
+ status = -ENXIO;
+ }
+ } else {
+ status = -ENXIO;
+ }
+ } else {
+ status = -ENXIO;
+ }
+
+ if (!status) {
+ if (de4x5_debug > 0) {
+ printk(version);
+ }
+
+ /* The DE4X5-specific entries in the device structure. */
+ dev->open = &de4x5_open;
+ dev->hard_start_xmit = &de4x5_queue_pkt;
+ dev->stop = &de4x5_close;
+ dev->get_stats = &de4x5_get_stats;
+#ifdef HAVE_MULTICAST
+ dev->set_multicast_list = &set_multicast_list;
+#endif
+ dev->do_ioctl = &de4x5_ioctl;
+
+ dev->mem_start = 0;
+
+ /* Fill in the generic field of the device structure. */
+ ether_setup(dev);
+
+ /* Let the adapter sleep to save power */
+ if (lp->chipset == DC21041) {
+ outl(0, DE4X5_SICR);
+ outl(CFDA_PSM, PCI_CFDA);
+ }
+ } else { /* Incorrectly initialised hardware */
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ if (lp) {
+ kfree_s(lp->rx_ring[0].buf, RX_BUFF_SZ * NUM_RX_DESC + ALIGN);
+ }
+ if (dev->priv) {
+ kfree_s(dev->priv, sizeof(struct de4x5_private) + ALIGN);
+ dev->priv = NULL;
+ }
+ }
+
+ return status;
+}
+
+
+static int
+de4x5_open(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ short iobase = dev->base_addr;
+ int i, status = 0;
+ long imr, omr, sts;
+
+ /*
+ ** Wake up the adapter
+ */
+ if (lp->chipset == DC21041) {
+ outl(0, PCI_CFDA);
+ dce_ms_delay(10);
+ }
+
+ if (request_irq(dev->irq, (void *)de4x5_interrupt, 0, lp->adapter_name)) {
+ printk("de4x5_open(): Requested IRQ%d is busy\n",dev->irq);
+ status = -EAGAIN;
+ } else {
+
+ irq2dev_map[dev->irq] = dev;
+ /*
+ ** Re-initialize the DE4X5...
+ */
+ status = de4x5_init(dev);
+
+ if (de4x5_debug > 1){
+ printk("%s: de4x5 open with irq %d\n",dev->name,dev->irq);
+ printk("\tphysical address: ");
+ for (i=0;i<6;i++){
+ printk("%2.2x:",(short)dev->dev_addr[i]);
+ }
+ printk("\n");
+ printk("Descriptor head addresses:\n");
+ printk("\t0x%8.8lx 0x%8.8lx\n",(long)lp->rx_ring,(long)lp->tx_ring);
+ printk("Descriptor addresses:\nRX: ");
+ for (i=0;i<lp->rxRingSize-1;i++){
+ if (i < 3) {
+ printk("0x%8.8lx ",(long)&lp->rx_ring[i].status);
+ }
+ }
+ printk("...0x%8.8lx\n",(long)&lp->rx_ring[i].status);
+ printk("TX: ");
+ for (i=0;i<lp->txRingSize-1;i++){
+ if (i < 3) {
+ printk("0x%8.8lx ", (long)&lp->tx_ring[i].status);
+ }
+ }
+ printk("...0x%8.8lx\n", (long)&lp->tx_ring[i].status);
+ printk("Descriptor buffers:\nRX: ");
+ for (i=0;i<lp->rxRingSize-1;i++){
+ if (i < 3) {
+ printk("0x%8.8lx ",(long)lp->rx_ring[i].buf);
+ }
+ }
+ printk("...0x%8.8lx\n",(long)lp->rx_ring[i].buf);
+ printk("TX: ");
+ for (i=0;i<lp->txRingSize-1;i++){
+ if (i < 3) {
+ printk("0x%8.8lx ", (long)lp->tx_ring[i].buf);
+ }
+ }
+ printk("...0x%8.8lx\n", (long)lp->tx_ring[i].buf);
+ printk("Ring size: \nRX: %d\nTX: %d\n",
+ (short)lp->rxRingSize,
+ (short)lp->txRingSize);
+ printk("\tstatus: %d\n", status);
+ }
+
+ if (!status) {
+ dev->tbusy = 0;
+ dev->start = 1;
+ dev->interrupt = UNMASK_INTERRUPTS;
+ dev->trans_start = jiffies;
+
+ START_DE4X5;
+
+ /* Unmask and enable DE4X5 board interrupts */
+ imr = 0;
+ UNMASK_IRQs;
+
+ /* Reset any pending (stale) interrupts */
+ sts = inl(DE4X5_STS);
+ outl(sts, DE4X5_STS);
+
+ ENABLE_IRQs;
+ }
+ if (de4x5_debug > 1) {
+ printk("\tsts: 0x%08x\n", inl(DE4X5_STS));
+ printk("\tbmr: 0x%08x\n", inl(DE4X5_BMR));
+ printk("\timr: 0x%08x\n", inl(DE4X5_IMR));
+ printk("\tomr: 0x%08x\n", inl(DE4X5_OMR));
+ printk("\tsisr: 0x%08x\n", inl(DE4X5_SISR));
+ printk("\tsicr: 0x%08x\n", inl(DE4X5_SICR));
+ printk("\tstrr: 0x%08x\n", inl(DE4X5_STRR));
+ printk("\tsigr: 0x%08x\n", inl(DE4X5_SIGR));
+ }
+ }
+
+ MOD_INC_USE_COUNT;
+
+ return status;
+}
+
+/*
+** Initialize the DE4X5 operating conditions. NB: a chip problem with the
+** DC21140 requires using perfect filtering mode for that chip. Since I can't
+** see why I'd want > 14 multicast addresses, I may change all chips to use
+** the perfect filtering mode. Keep the DMA burst length at 8: there seems
+** to be data corruption problems if it is larger (UDP errors seen from a
+** ttcp source).
+*/
+static int
+de4x5_init(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ short iobase = dev->base_addr;
+ int status = 0;
+ long i, j, bmr, omr;
+
+ /* Lock out other processes whilst setting up the hardware */
+ set_bit(0, (void *)&dev->tbusy);
+
+ RESET_DE4X5;
+
+ bmr = inl(DE4X5_BMR);
+ bmr |= PBL_8 | DESC_SKIP_LEN | CACHE_ALIGN;
+ outl(bmr, DE4X5_BMR);
+
+ if (lp->chipset != DC21140) {
+ omr = TR_96;
+ lp->setup_f = HASH_PERF;
+ } else {
+ omr = OMR_SDP | OMR_SF;
+ lp->setup_f = PERFECT;
+ }
+ outl((long)lp->rx_ring, DE4X5_RRBA);
+ outl((long)lp->tx_ring, DE4X5_TRBA);
+
+ lp->rx_new = lp->rx_old = 0;
+ lp->tx_new = lp->tx_old = 0;
+
+ for (i = 0; i < lp->rxRingSize; i++) {
+ lp->rx_ring[i].status = R_OWN;
+ }
+
+ for (i = 0; i < lp->txRingSize; i++) {
+ lp->tx_ring[i].status = 0;
+ }
+
+ /* Build the setup frame depending on filtering mode */
+ SetMulticastFilter(dev, 0, NULL);
+
+ if (lp->chipset != DC21140) {
+ load_packet(dev, lp->setup_frame, HASH_F|TD_SET|SETUP_FRAME_LEN, NULL);
+ } else {
+ load_packet(dev, lp->setup_frame, PERFECT_F|TD_SET|SETUP_FRAME_LEN, NULL);
+ }
+ outl(omr|OMR_ST, DE4X5_OMR);
+
+ /* Poll for completion of setup frame (interrupts are disabled for now) */
+ for (j=0, i=0;(i<100) && (j==0);i++) {
+ if (lp->tx_ring[lp->tx_new].status >= 0) j=1;
+ }
+ outl(omr, DE4X5_OMR); /* Stop everything! */
+
+ if (i == 100) {
+ printk("%s: Setup frame timed out, status %08x\n", dev->name,
+ inl(DE4X5_STS));
+ status = -EIO;
+ }
+
+ lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+ lp->tx_old = lp->tx_new;
+
+ /* Autoconfigure the connected port */
+ if (autoconf_media(dev) == 0) {
+ status = -EIO;
+ }
+
+ return 0;
+}
+
+/*
+** Writes a socket buffer address to the next available transmit descriptor
+*/
+static int
+de4x5_queue_pkt(struct sk_buff *skb, struct device *dev)
+{
+ volatile struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int iobase = dev->base_addr;
+ int status = 0;
+ u_long imr, omr, sts;
+
+ sts = inl(DE4X5_STS);
+
+ /*
+ ** Transmitter timeout, possibly serious problems.
+ ** The 'lostMedia' threshold accounts for transient errors that
+ ** were noticed when switching media.
+ */
+ if (dev->tbusy || (lp->lostMedia > LOST_MEDIA_THRESHOLD)) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 10 && (lp->lostMedia <= LOST_MEDIA_THRESHOLD)) {
+ status = -1;
+ } else {
+ printk("%s: transmit timed out, status %08x, tbusy:%d, lostMedia:%d tickssofar:%d, resetting.\n",dev->name, inl(DE4X5_STS), dev->tbusy, lp->lostMedia, tickssofar);
+
+ /* Stop and reset the TX and RX... */
+ STOP_DE4X5;
+ status = de4x5_init(dev);
+
+ /* Unmask DE4X5 board interrupts */
+ if (!status) {
+ /* Start here to clean stale interrupts later */
+ dev->interrupt = UNMASK_INTERRUPTS;
+ dev->start = 1;
+ dev->tbusy = 0;
+ dev->trans_start = jiffies;
+
+ START_DE4X5;
+
+ /* Unmask DE4X5 board interrupts */
+ imr = 0;
+ UNMASK_IRQs;
+
+ /* Clear any pending (stale) interrupts */
+ sts = inl(DE4X5_STS);
+ outl(sts, DE4X5_STS);
+
+ ENABLE_IRQs;
+ } else {
+ printk("%s: hardware initialisation failure, status %08x.\n",
+ dev->name, inl(DE4X5_STS));
+ }
+ }
+ } else if (skb == NULL) {
+ dev_tint(dev);
+ } else if (skb->len > 0) {
+
+ /*
+ ** Block a timer-based transmit from overlapping. This could better be
+ ** done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
+ */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+
+ if (TX_BUFFS_AVAIL) { /* Fill in a Tx ring entry */
+ load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb);
+ if (lp->tx_enable) {
+ outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */
+ }
+
+ lp->tx_new = (++lp->tx_new) % lp->txRingSize; /* Ensure a wrap */
+
+ dev->trans_start = jiffies;
+ }
+
+ if (TX_BUFFS_AVAIL) {
+ dev->tbusy = 0; /* Another pkt may be queued */
+ }
+ }
+
+ return status;
+}
+
+/*
+** The DE4X5 interrupt handler.
+*/
+static void
+de4x5_interrupt(int irq, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct de4x5_private *lp;
+ int iobase;
+ u_long imr, omr, sts;
+
+ if (dev == NULL) {
+ printk ("de4x5_interrupt(): irq %d for unknown device.\n", irq);
+ } else {
+ lp = (struct de4x5_private *)dev->priv;
+ iobase = dev->base_addr;
+
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+ dev->interrupt = MASK_INTERRUPTS;
+
+ /*
+ ** Get the interrupt information and disable them.
+ ** The device read will ensure pending buffers are flushed
+ ** in intermediate PCI bridges, so that the posted interrupt
+ ** has some real data to work with.
+ */
+ sts = inl(DE4X5_STS);
+ MASK_IRQs;
+
+ outl(sts, DE4X5_STS); /* Reset the board interrupts */
+
+ if (sts & (STS_RI | STS_RU)) /* Rx interrupt (packet[s] arrived) */
+ de4x5_rx(dev);
+
+ if (sts & (STS_TI | STS_TU)) /* Tx interrupt (packet sent) */
+ de4x5_tx(dev);
+
+ if (sts & STS_SE) { /* Bus Error */
+ STOP_DE4X5;
+ printk("%s: Fatal bus error occurred, sts=0x%08lx, device stopped.\n",
+ dev->name, sts);
+ }
+
+ if (TX_BUFFS_AVAIL && dev->tbusy) {/* Any resources available? */
+ dev->tbusy = 0; /* Clear TX busy flag */
+ mark_bh(NET_BH);
+ }
+
+ dev->interrupt = UNMASK_INTERRUPTS;
+
+ UNMASK_IRQs;
+ }
+
+ return;
+}
+
+static int
+de4x5_rx(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int i, entry;
+ volatile long status;
+ char *buf;
+
+ for (entry = lp->rx_new; lp->rx_ring[entry].status >= 0;entry = lp->rx_new) {
+ status = lp->rx_ring[entry].status;
+
+ if (status & RD_FS) { /* Remember the start of frame */
+ lp->rx_old = entry;
+ }
+
+ if (status & RD_LS) { /* Valid frame status */
+ if (status & RD_ES) { /* There was an error. */
+ lp->stats.rx_errors++; /* Update the error stats. */
+ if (status & (RD_RF | RD_TL)) lp->stats.rx_frame_errors++;
+ if (status & RD_CE) lp->stats.rx_crc_errors++;
+ if (status & RD_OF) lp->stats.rx_fifo_errors++;
+ } else { /* A valid frame received */
+ struct sk_buff *skb;
+ short pkt_len = (short)(lp->rx_ring[entry].status >> 16) - 4;
+
+ if ((skb = alloc_skb(pkt_len, GFP_ATOMIC)) != NULL) {
+ skb->len = pkt_len;
+ skb->dev = dev;
+
+ if (entry < lp->rx_old) { /* Wrapped buffer */
+ short len = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ;
+ memcpy(skb->data, lp->rx_ring[lp->rx_old].buf, len);
+ memcpy(skb->data + len, lp->rx_ring[0].buf, pkt_len - len);
+ } else { /* Linear buffer */
+ memcpy(skb->data, lp->rx_ring[lp->rx_old].buf, pkt_len);
+ }
+
+ /*
+ ** Notify the upper protocol layers that there is another
+ ** packet to handle
+ */
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+
+ /*
+ ** Update stats
+ */
+ lp->stats.rx_packets++;
+ for (i=1; i<DE4X5_PKT_STAT_SZ-1; i++) {
+ if (pkt_len < (i*DE4X5_PKT_BIN_SZ)) {
+ lp->pktStats.bins[i]++;
+ i = DE4X5_PKT_STAT_SZ;
+ }
+ }
+ buf = skb->data; /* Look at the dest addr */
+ if (buf[0] & 0x01) { /* Multicast/Broadcast */
+ if ((*(long *)&buf[0] == -1) && (*(short *)&buf[4] == -1)) {
+ lp->pktStats.broadcast++;
+ } else {
+ lp->pktStats.multicast++;
+ }
+ } else if ((*(long *)&buf[0] == *(long *)&dev->dev_addr[0]) &&
+ (*(short *)&buf[4] == *(short *)&dev->dev_addr[4])) {
+ lp->pktStats.unicast++;
+ }
+
+ lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */
+ if (lp->pktStats.bins[0] == 0) { /* Reset counters */
+ memset((char *)&lp->pktStats, 0, sizeof(lp->pktStats));
+ }
+ } else {
+ printk("%s: Insufficient memory; nuking packet.\n", dev->name);
+ lp->stats.rx_dropped++; /* Really, deferred. */
+ break;
+ }
+ }
+
+ /* Change buffer ownership for this last frame, back to the adapter */
+ for (; lp->rx_old!=entry; lp->rx_old=(++lp->rx_old)%lp->rxRingSize) {
+ lp->rx_ring[lp->rx_old].status = R_OWN;
+ }
+ lp->rx_ring[entry].status = R_OWN;
+ }
+
+ /*
+ ** Update entry information
+ */
+ lp->rx_new = (++lp->rx_new) % lp->rxRingSize;
+ }
+
+ return 0;
+}
+
+/*
+** Buffer sent - check for TX buffer errors.
+*/
+static int
+de4x5_tx(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int entry, iobase = dev->base_addr;
+ long status;
+
+ for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) {
+ status = lp->tx_ring[entry].status;
+ if (status < 0) { /* Buffer not sent yet */
+ break;
+ } else if (status & TD_ES) { /* An error happened */
+ lp->stats.tx_errors++;
+ if (status & TD_NC) lp->stats.tx_carrier_errors++;
+ if (status & TD_LC) lp->stats.tx_window_errors++;
+ if (status & TD_UF) lp->stats.tx_fifo_errors++;
+ if (status & TD_LC) lp->stats.collisions++;
+ if (status & TD_EC) lp->pktStats.excessive_collisions++;
+ if (status & TD_DE) lp->stats.tx_aborted_errors++;
+
+ if (status & (TD_LO | TD_NC | TD_EC | TD_LF)) {
+ lp->lostMedia++;
+ } else {
+ outl(POLL_DEMAND, DE4X5_TPD); /* Restart a stalled TX */
+ }
+ } else { /* Packet sent */
+ lp->stats.tx_packets++;
+ lp->lostMedia = 0; /* Remove transient problem */
+ }
+ /* Free the buffer if it's not a setup frame. */
+ if (lp->skb[entry] != NULL) {
+ dev_kfree_skb(lp->skb[entry], FREE_WRITE);
+ }
+
+ /* Update all the pointers */
+ lp->tx_old = (++lp->tx_old) % lp->txRingSize;
+ }
+
+ return 0;
+}
+
+static int
+de4x5_close(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int iobase = dev->base_addr;
+ u_long imr, omr;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (de4x5_debug > 1) {
+ printk("%s: Shutting down ethercard, status was %8.8x.\n",
+ dev->name, inl(DE4X5_STS));
+ }
+
+ /*
+ ** We stop the DE4X5 here... mask interrupts and stop TX & RX
+ */
+ DISABLE_IRQs;
+
+ STOP_DE4X5;
+
+ /*
+ ** Free the associated irq
+ */
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+
+ MOD_DEC_USE_COUNT;
+
+ /* Put the adapter to sleep to save power */
+ if (lp->chipset == DC21041) {
+ outl(0, DE4X5_SICR);
+ outl(CFDA_PSM, PCI_CFDA);
+ }
+
+ return 0;
+}
+
+static struct enet_statistics *
+de4x5_get_stats(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int iobase = dev->base_addr;
+
+ lp->stats.rx_missed_errors = (int) (inl(DE4X5_MFC) & (MFC_OVFL | MFC_CNTR));
+
+ return &lp->stats;
+}
+
+static void load_packet(struct device *dev, char *buf, u_long flags, struct sk_buff *skb)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+
+ lp->tx_ring[lp->tx_new].buf = buf;
+ lp->tx_ring[lp->tx_new].des1 &= TD_TER;
+ lp->tx_ring[lp->tx_new].des1 |= flags;
+ lp->skb[lp->tx_new] = skb;
+ lp->tx_ring[lp->tx_new].status = T_OWN;
+
+ return;
+}
+/*
+** Set or clear the multicast filter for this adaptor.
+** num_addrs == -1 Promiscuous mode, receive all packets - not supported.
+** Use the ioctls.
+** num_addrs == 0 Normal mode, clear multicast list
+** num_addrs > 0 Multicast mode, receive normal and MC packets, and do
+** best-effort filtering.
+** num_addrs == HASH_TABLE_LEN
+** Set all multicast bits (pass all multicasts).
+*/
+static void
+set_multicast_list(struct device *dev, int num_addrs, void *addrs)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int iobase = dev->base_addr;
+
+ /* First, double check that the adapter is open */
+ if (irq2dev_map[dev->irq] != NULL) {
+ if (num_addrs >= 0) {
+ SetMulticastFilter(dev, num_addrs, (char *)addrs);
+ if (lp->setup_f == HASH_PERF) {
+ load_packet(dev, lp->setup_frame, TD_IC | HASH_F | TD_SET |
+ SETUP_FRAME_LEN, NULL);
+ } else {
+ load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET |
+ SETUP_FRAME_LEN, NULL);
+ }
+
+ lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+ outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */
+ dev->trans_start = jiffies;
+ }
+ }
+
+ return;
+}
+
+/*
+** Calculate the hash code and update the logical address filter
+** from a list of ethernet multicast addresses.
+** Little endian crc one liner from Matt Thomas, DEC.
+*/
+static void SetMulticastFilter(struct device *dev, int num_addrs, char *addrs)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int i, j, bit, byte, iobase = dev->base_addr;
+ u_short hashcode;
+ u_long crc, omr, poly = CRC_POLYNOMIAL_LE;
+ char *pa;
+
+ omr = inl(DE4X5_OMR);
+ pa = build_setup_frame(dev, ALL); /* Build the basic frame */
+
+ if (lp->setup_f == HASH_PERF) {
+ if (num_addrs == HASH_TABLE_LEN) { /* Pass all multicasts */
+ omr |= OMR_PM;
+ } else {
+ omr &= ~OMR_PM;
+ /* Now update the MCA table */
+ for (i=0;i<num_addrs;i++) { /* for each address in the list */
+ if ((*addrs & 0x01) == 1) { /* multicast address? */
+ crc = 0xffffffff; /* init CRC for each address */
+ for (byte=0;byte<ETH_ALEN;byte++) {/* for each address byte */
+ /* process each address bit */
+ for (bit = *addrs++,j=0;j<8;j++, bit>>=1) {
+ crc = (crc >> 1) ^ (((crc ^ bit) & 0x01) ? poly : 0);
+ }
+ }
+ hashcode = crc & HASH_BITS; /* hashcode is 9 LSb of CRC */
+
+ byte = hashcode >> 3; /* bit[3-8] -> byte in filter */
+ bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */
+
+ byte <<= 1; /* calc offset into setup frame */
+ if (byte & 0x02) {
+ byte -= 1;
+ }
+ lp->setup_frame[byte] |= bit;
+
+ } else { /* skip this address */
+ addrs += ETH_ALEN;
+ }
+ }
+ }
+ } else { /* Perfect filtering */
+ omr &= ~OMR_PM;
+ for (j=0; j<num_addrs; j++) {
+ for (i=0; i<ETH_ALEN; i++) {
+ *(pa + (i&1)) = *addrs++;
+ if (i & 0x01) pa += 4;
+ }
+ }
+ }
+ outl(omr, DE4X5_OMR);
+
+ return;
+}
+
+/*
+** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually
+** the motherboard. Upto 15 EISA devices are supported.
+*/
+static void eisa_probe(struct device *dev, short ioaddr)
+{
+ int i, maxSlots;
+ int status;
+ u_short vendor, device, iobase;
+ struct bus_type *lp = &bus;
+ char name[DE4X5_STRLEN];
+ long cfid;
+
+ if (!ioaddr && autoprobed) return ; /* Been here before ! */
+ if ((ioaddr < 0x1000) && (ioaddr > 0)) return; /* PCI MODULE special */
+
+ lp->bus = EISA;
+
+ if (ioaddr == 0) { /* Autoprobing */
+ iobase = EISA_SLOT_INC; /* Get the first slot address */
+ i = 1;
+ maxSlots = MAX_EISA_SLOTS;
+ } else { /* Probe a specific location */
+ iobase = ioaddr;
+ i = (ioaddr >> 12);
+ maxSlots = i + 1;
+ }
+
+ for (status = -ENODEV; (i<maxSlots) && (dev!=NULL); i++, iobase+=EISA_SLOT_INC) {
+ if (EISA_signature(name, EISA_ID)) {
+ cfid = inl(PCI_CFID);
+ device = (u_short)(cfid >> 16);
+ vendor = (u_short) cfid;
+
+ lp->bus = EISA;
+ lp->chipset = device;
+ if (DevicePresent(EISA_APROM) == 0) {
+ /* Write the PCI Configuration Registers */
+ outl(PCI_COMMAND_IO | PCI_COMMAND_MASTER, PCI_CFCS);
+ outl(0x00004000, PCI_CFLT);
+ outl((u_long)iobase, PCI_CBIO);
+
+ if (check_region(iobase, DE4X5_EISA_TOTAL_SIZE) == 0) {
+ if ((dev = alloc_device(dev, iobase)) != NULL) {
+ if ((status = de4x5_hw_init(dev, iobase)) == 0) {
+ num_de4x5s++;
+ }
+ num_eth++;
+ }
+ } else if (autoprobed) {
+ printk("%s: region already allocated at 0x%04x.\n", dev->name, iobase);
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+/*
+** PCI bus I/O device probe
+*/
+#define PCI_DEVICE (dev_num << 3)
+#define PCI_LAST_DEV 32
+
+static void pci_probe(struct device *dev, short ioaddr)
+
+{
+ u_char irq;
+ u_short pb, dev_num, dev_last;
+ u_short vendor, device, status;
+ u_long class, iobase;
+ struct bus_type *lp = &bus;
+
+ if (!ioaddr && autoprobed) return ; /* Been here before ! */
+
+ if (pcibios_present()) {
+ lp->bus = PCI;
+
+ if (ioaddr < 0x1000) {
+ pb = (u_short)(ioaddr >> 8);
+ dev_num = (u_short)(ioaddr & 0xff);
+ } else {
+ pb = 0;
+ dev_num = 0;
+ }
+ if (ioaddr > 0) {
+ dev_last = (dev_num < PCI_LAST_DEV) ? dev_num + 1 : PCI_LAST_DEV;
+ } else {
+ dev_last = PCI_LAST_DEV;
+ }
+
+ for (; (dev_num < dev_last) && (dev != NULL); dev_num++) {
+ pcibios_read_config_dword(pb, PCI_DEVICE, PCI_CLASS_REVISION, &class);
+ if (class != 0xffffffff) {
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &device);
+ if (is_DC21040 || is_DC21041 || is_DC21140) {
+ /* Set the device number information */
+ lp->device = dev_num;
+
+ /* Set the chipset information */
+ lp->chipset = device;
+
+ /* Get the board I/O address */
+ pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase);
+ iobase &= CBIO_MASK;
+
+ /* Fetch the IRQ to be used */
+ pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &irq);
+
+ /* Enable I/O Accesses and Bus Mastering */
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
+ status |= PCI_COMMAND_IO | PCI_COMMAND_MASTER;
+ pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status);
+
+ /* If there is a device and I/O region is open, initialise dev. */
+ if ((DevicePresent(DE4X5_APROM) == 0) || is_not_dec) {
+ if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) {
+ if ((dev = alloc_device(dev, iobase)) != NULL) {
+ dev->irq = irq;
+ if ((status = de4x5_hw_init(dev, iobase)) == 0) {
+ num_de4x5s++;
+ }
+ num_eth++;
+ }
+ } else if (autoprobed) {
+ printk("%s: region already allocated at 0x%04x.\n", dev->name, (u_short)iobase);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+/*
+** Allocate the device by pointing to the next available space in the
+** device structure. Should one not be available, it is created.
+*/
+static struct device *alloc_device(struct device *dev, int iobase)
+{
+ int addAutoProbe = 0;
+ struct device *tmp = NULL, *ret;
+ int (*init)(struct device *) = NULL;
+
+ /*
+ ** Check the device structures for an end of list or unused device
+ */
+ if (!loading_module) {
+ while (dev->next != NULL) {
+ if ((dev->base_addr == 0xffe0) || (dev->base_addr == 0)) break;
+ dev = dev->next; /* walk through eth device list */
+ num_eth++; /* increment eth device number */
+ }
+
+ /*
+ ** If an autoprobe is requested for another device, we must re-insert
+ ** the request later in the list. Remember the current position first.
+ */
+ if ((dev->base_addr == 0) && (num_de4x5s > 0)) {
+ addAutoProbe++;
+ tmp = dev->next; /* point to the next device */
+ init = dev->init; /* remember the probe function */
+ }
+
+ /*
+ ** If at end of list and can't use current entry, malloc one up.
+ ** If memory could not be allocated, print an error message.
+ */
+ if ((dev->next == NULL) &&
+ !((dev->base_addr == 0xffe0) || (dev->base_addr == 0))){
+ dev->next = (struct device *)kmalloc(sizeof(struct device) + 8,
+ GFP_KERNEL);
+
+ dev = dev->next; /* point to the new device */
+ if (dev == NULL) {
+ printk("eth%d: Device not initialised, insufficient memory\n",
+ num_eth);
+ } else {
+ /*
+ ** If the memory was allocated, point to the new memory area
+ ** and initialize it (name, I/O address, next device (NULL) and
+ ** initialisation probe routine).
+ */
+ dev->name = (char *)(dev + sizeof(struct device));
+ if (num_eth > 9999) {
+ sprintf(dev->name,"eth????"); /* New device name */
+ } else {
+ sprintf(dev->name,"eth%d", num_eth);/* New device name */
+ }
+ dev->base_addr = iobase; /* assign the io address */
+ dev->next = NULL; /* mark the end of list */
+ dev->init = &de4x5_probe; /* initialisation routine */
+ num_de4x5s++;
+ }
+ }
+ ret = dev; /* return current struct, or NULL */
+
+ /*
+ ** Now figure out what to do with the autoprobe that has to be inserted.
+ ** Firstly, search the (possibly altered) list for an empty space.
+ */
+ if (ret != NULL) {
+ if (addAutoProbe) {
+ for (; (tmp->next!=NULL) && (tmp->base_addr!=0xffe0); tmp=tmp->next);
+
+ /*
+ ** If no more device structures and can't use the current one, malloc
+ ** one up. If memory could not be allocated, print an error message.
+ */
+ if ((tmp->next == NULL) && !(tmp->base_addr == 0xffe0)) {
+ tmp->next = (struct device *)kmalloc(sizeof(struct device) + 8,
+ GFP_KERNEL);
+ tmp = tmp->next; /* point to the new device */
+ if (tmp == NULL) {
+ printk("%s: Insufficient memory to extend the device list.\n",
+ dev->name);
+ } else {
+ /*
+ ** If the memory was allocated, point to the new memory area
+ ** and initialize it (name, I/O address, next device (NULL) and
+ ** initialisation probe routine).
+ */
+ tmp->name = (char *)(tmp + sizeof(struct device));
+ if (num_eth > 9999) {
+ sprintf(tmp->name,"eth????"); /* New device name */
+ } else {
+ sprintf(tmp->name,"eth%d", num_eth);/* New device name */
+ }
+ tmp->base_addr = 0; /* re-insert the io address */
+ tmp->next = NULL; /* mark the end of list */
+ tmp->init = init; /* initialisation routine */
+ }
+ } else { /* structure already exists */
+ tmp->base_addr = 0; /* re-insert the io address */
+ }
+ }
+ }
+ } else {
+ ret = dev;
+ }
+
+ return ret;
+}
+
+/*
+** Auto configure the media here rather than setting the port at compile
+** time. This routine is called by de4x5_init() when a loss of media is
+** detected (excessive collisions, loss of carrier, no carrier or link fail
+** [TP]) to check whether the user has been sneaky and changed the port on us.
+*/
+static int autoconf_media(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int iobase = dev->base_addr;
+
+ if (lp->chipset == DC21040) {
+ lp->media = (lp->autosense == AUTO ? TP : lp->autosense);
+ dc21040_autoconf(dev);
+ } else if (lp->chipset == DC21041) {
+ lp->media = (lp->autosense == AUTO ? TP_NW : lp->autosense);
+ dc21041_autoconf(dev);
+ } else if (lp->chipset == DC21140) {
+ /* Force 10Mb/s (_100Mb for 100Mb/s) */
+ lp->media = (lp->autosense == AUTO ? _10Mb : lp->autosense);
+ dc21140_autoconf(dev);
+ }
+
+ if (de4x5_debug >= 1 ) {
+ if (lp->chipset != DC21140) {
+ printk("%s: Media is %s\n",dev->name,
+ (lp->media == NC ? "unconnected!" :
+ (lp->media == TP ? "TP." :
+ (lp->media == ANS ? "TP/Nway." :
+ (lp->media == BNC ? "BNC." :
+ (lp->media == AUI ? "AUI." :
+ "BNC/AUI."
+ ))))));
+ } else {
+ printk("%s: Mode is forced to %s\n",dev->name,
+ (lp->media == NC ? "link down.":
+ (lp->media == _100Mb ? "100Mb/s." :
+ (lp->media == _10Mb ? "10Mb/s." :
+ "\?\?\?"
+ ))));
+ }
+ }
+
+ if (lp->media) {
+ lp->lostMedia = 0;
+ inl(DE4X5_MFC); /* Zero the lost frames counter */
+ }
+ dce_ms_delay(10);
+
+ return (lp->media);
+}
+
+static void dc21040_autoconf(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int iobase = dev->base_addr;
+ u_long i, sisr = 0, linkBad;
+ u_long t_3s = 3000;
+
+ switch (lp->media) {
+ case TP:
+ reset_init_sia(dev, 0x8f01, 0xffff, 0x0000);
+ for (linkBad=1,i=0;(i<t_3s) && linkBad && !(sisr & SISR_NCR);i++) {
+ if (((sisr = inl(DE4X5_SISR)) & SISR_LKF) == 0) linkBad = 0;
+ dce_ms_delay(1);
+ }
+ if (linkBad && (lp->autosense == AUTO)) {
+ lp->media = BNC_AUI;
+ dc21040_autoconf(dev);
+ }
+ break;
+
+ case BNC:
+ case AUI:
+ case BNC_AUI:
+ reset_init_sia(dev, 0x8f09, 0x0705, 0x0006);
+ dce_ms_delay(330);
+ linkBad = ping_media(dev);
+ if (linkBad && (lp->autosense == AUTO)) {
+ lp->media = NC;
+ dc21040_autoconf(dev);
+ }
+ break;
+
+ case NC:
+ reset_init_sia(dev, 0x8f01, 0xffff, 0x0000);
+ break;
+ }
+
+ return;
+}
+
+/*
+** Autoconfigure the media when using the DC21041. AUI needs to be tested
+** before BNC, because the BNC port will indicate activity if it's not
+** terminated correctly. The only way to test for that is to place a loopback
+** packet onto the network and watch for errors.
+*/
+static void dc21041_autoconf(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int iobase = dev->base_addr;
+ u_long sts, irqs, irq_mask, omr;
+
+ switch (lp->media) {
+ case TP_NW:
+ omr = inl(DE4X5_OMR); /* Set up full duplex for the autonegotiate */
+ outl(omr | OMR_FD, DE4X5_OMR);
+ irqs = STS_LNF | STS_LNP;
+ irq_mask = IMR_LFM | IMR_LPM;
+ sts = test_media(dev, irqs, irq_mask, 0xef01, 0xffff, 0x0008, 2400);
+ if (sts & STS_LNP) {
+ lp->media = ANS;
+ } else {
+ lp->media = AUI;
+ }
+ dc21041_autoconf(dev);
+ break;
+
+ case ANS:
+ irqs = STS_LNP;
+ irq_mask = IMR_LPM;
+ sts = test_ans(dev, irqs, irq_mask, 3000);
+ if (!(sts & STS_LNP) && (lp->autosense == AUTO)) {
+ lp->media = TP;
+ dc21041_autoconf(dev);
+ }
+ break;
+
+ case TP:
+ omr = inl(DE4X5_OMR); /* Set up half duplex for TP */
+ outl(omr & ~OMR_FD, DE4X5_OMR);
+ irqs = STS_LNF | STS_LNP;
+ irq_mask = IMR_LFM | IMR_LPM;
+ sts = test_media(dev, irqs, irq_mask, 0xef01, 0xff3f, 0x0008, 2400);
+ if (!(sts & STS_LNP) && (lp->autosense == AUTO)) {
+ if (inl(DE4X5_SISR) & SISR_NRA) { /* Non selected port activity */
+ lp->media = AUI;
+ } else {
+ lp->media = BNC;
+ }
+ dc21041_autoconf(dev);
+ }
+ break;
+
+ case AUI:
+ omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */
+ outl(omr & ~OMR_FD, DE4X5_OMR);
+ irqs = 0;
+ irq_mask = 0;
+ sts = test_media(dev, irqs, irq_mask, 0xef09, 0xf7fd, 0x000e, 1000);
+ if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) {
+ lp->media = BNC;
+ dc21041_autoconf(dev);
+ }
+ break;
+
+ case BNC:
+ omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */
+ outl(omr & ~OMR_FD, DE4X5_OMR);
+ irqs = 0;
+ irq_mask = 0;
+ sts = test_media(dev, irqs, irq_mask, 0xef09, 0xf7fd, 0x0006, 1000);
+ if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) {
+ lp->media = NC;
+ } else { /* Ensure media connected */
+ if (ping_media(dev)) lp->media = NC;
+ }
+ break;
+
+ case NC:
+ omr = inl(DE4X5_OMR); /* Set up full duplex for the autonegotiate */
+ outl(omr | OMR_FD, DE4X5_OMR);
+ reset_init_sia(dev, 0xef01, 0xffff, 0x0008);/* Initialise the SIA */
+ break;
+ }
+
+ return;
+}
+
+/*
+** Reduced feature version (temporary I hope)
+*/
+static void dc21140_autoconf(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int iobase = dev->base_addr;
+ u_long omr;
+
+ switch(lp->media) {
+ case _100Mb: /* Set 100Mb/s, MII Port with PCS Function and Scrambler */
+ omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR));
+ outl(omr | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);
+ outl(GEP_FDXD | GEP_MODE, DE4X5_GEP);
+ break;
+
+ case _10Mb: /* Set conventional 10Mb/s ENDEC interface */
+ omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR));
+ outl(omr | OMR_TTM, DE4X5_OMR);
+ outl(GEP_FDXD, DE4X5_GEP);
+ break;
+ }
+
+ return;
+}
+
+static long test_media(struct device *dev, long irqs, long irq_mask, long csr13, long csr14, long csr15, long msec)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int iobase = dev->base_addr;
+ long sts, time, csr12;
+
+ reset_init_sia(dev, csr13, csr14, csr15);
+
+ /* Set link_fail_inhibit_timer */
+ load_ms_timer(dev, msec);
+
+ /* clear all pending interrupts */
+ sts = inl(DE4X5_STS);
+ outl(sts, DE4X5_STS);
+
+ /* clear csr12 NRA and SRA bits */
+ csr12 = inl(DE4X5_SISR);
+ outl(csr12, DE4X5_SISR);
+
+ /* Poll for timeout - timer interrupt doesn't work correctly */
+ do {
+ time = inl(DE4X5_GPT) & GPT_VAL;
+ sts = inl(DE4X5_STS);
+ } while ((time != 0) && !(sts & irqs));
+
+ sts = inl(DE4X5_STS);
+
+ return sts;
+}
+
+/*
+** Send a packet onto the media and watch for send errors that indicate the
+** media is bad or unconnected.
+*/
+static long ping_media(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int entry, iobase = dev->base_addr;
+ char frame[64];
+ long i, linkBad, omr;
+ u_long t_3s = 3000;
+
+ create_packet(dev, frame, sizeof(frame));
+
+ entry = lp->tx_new; /* Remember the ring position */
+ load_packet(dev, frame, TD_LS | TD_FS | sizeof(frame),NULL);
+
+ omr = inl(DE4X5_OMR);
+ outl(omr|OMR_ST, DE4X5_OMR);
+
+ lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+ lp->tx_old = lp->tx_new;
+
+ /* Poll for completion of frame (interrupts are disabled for now)... */
+ for (linkBad=1,i=0;(i<t_3s) && linkBad;i++) {
+ if ((inl(DE4X5_SISR) & SISR_NCR) == 1) break;
+ if (lp->tx_ring[entry].status >= 0) linkBad=0;
+ dce_ms_delay(1);
+ }
+ outl(omr, DE4X5_OMR);
+
+ return ((linkBad || (lp->tx_ring[entry].status & TD_ES)) ? 1 : 0);
+}
+
+/*
+** Check the Auto Negotiation State. Return OK when a link pass interrupt
+** is received and the auto-negotiation status is NWAY OK.
+*/
+static int test_ans(struct device *dev, long irqs, long irq_mask, long msec)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int iobase = dev->base_addr;
+ long sts, ans;
+
+ outl(irq_mask, DE4X5_IMR);
+
+ /* Set timeout limit */
+ load_ms_timer(dev, msec);
+
+ /* clear all pending interrupts */
+ sts = inl(DE4X5_STS);
+ outl(sts, DE4X5_STS);
+
+ /* Poll for interrupts */
+ do {
+ ans = inl(DE4X5_SISR) & SISR_ANS;
+ sts = inl(DE4X5_STS);
+ } while (!(sts & irqs) && (ans ^ ANS_NWOK) != 0);
+
+ return ((sts & STS_LNP) && ((ans ^ ANS_NWOK) == 0) ? STS_LNP : 0);
+}
+
+/*
+**
+*/
+static void reset_init_sia(struct device *dev, long sicr, long strr, long sigr)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int iobase = dev->base_addr;
+
+ RESET_SIA;
+ outl(sigr, DE4X5_SIGR);
+ outl(strr, DE4X5_STRR);
+ outl(sicr, DE4X5_SICR);
+
+ return;
+}
+
+/*
+** Load the timer on the DC21041 and 21140. Max time is 13.42 secs.
+*/
+static void load_ms_timer(struct device *dev, u_long msec)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int iobase = dev->base_addr;
+
+ outl((long)(msec * 10000)/2048, DE4X5_GPT);
+
+ return;
+}
+
+/*
+** Create an Ethernet packet with an invalid CRC
+*/
+static void create_packet(struct device *dev, char *frame, int len)
+{
+ int i;
+ char *buf = frame;
+
+ for (i=0; i<ETH_ALEN; i++) { /* Use this source address */
+ *buf++ = dev->dev_addr[i];
+ }
+ for (i=0; i<ETH_ALEN; i++) { /* Use this destination address */
+ *buf++ = dev->dev_addr[i];
+ }
+
+ *buf++ = 0; /* Packet length (2 bytes) */
+ *buf++ = 1;
+
+ return;
+}
+
+/*
+** Known delay in microseconds
+*/
+static void dce_us_delay(u_long usec)
+{
+ udelay(usec);
+
+ return;
+}
+
+/*
+** Known delay in milliseconds, in millisecond steps.
+*/
+static void dce_ms_delay(u_long msec)
+{
+ u_long i;
+
+ for (i=0; i<msec; i++) {
+ dce_us_delay(1000);
+ }
+
+ return;
+}
+
+
+/*
+** Look for a particular board name in the EISA configuration space
+*/
+int EISA_signature(char *name, long eisa_id)
+{
+ unsigned long i;
+ char *signatures[] = DE4X5_SIGNATURE;
+ char ManCode[DE4X5_STRLEN];
+ union {
+ long ID;
+ char Id[4];
+ } Eisa;
+ int status = 0;
+
+ *name = '\0';
+ Eisa.ID = inl(eisa_id);
+
+ ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40);
+ ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40);
+ ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30);
+ ManCode[3]=((Eisa.Id[2]&0x0f)+0x30);
+ ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30);
+ ManCode[5]='\0';
+
+ for (i=0;(*signatures[i] != '\0') && (*name == '\0');i++) {
+ if (strstr(ManCode, signatures[i]) != NULL) {
+ strcpy(name,ManCode);
+ status = 1;
+ }
+ }
+
+ return status; /* return the device name string */
+}
+
+/*
+** Look for a special sequence in the Ethernet station address PROM that
+** is common across all DIGITAL network adapter products.
+**
+** Search the Ethernet address ROM for the signature. Since the ROM address
+** counter can start at an arbitrary point, the search must include the entire
+** probe sequence length plus the (length_of_the_signature - 1).
+** Stop the search IMMEDIATELY after the signature is found so that the
+** PROM address counter is correctly positioned at the start of the
+** ethernet address for later read out.
+*/
+
+static int DevicePresent(short aprom_addr)
+{
+ union {
+ struct {
+ u_long a;
+ u_long b;
+ } llsig;
+ char Sig[sizeof(long) << 1];
+ } dev;
+ char data;
+ long i, j, tmp;
+ short sigLength;
+ int status = 0;
+ struct bus_type *lp = &bus;
+
+ dev.llsig.a = ETH_PROM_SIG;
+ dev.llsig.b = ETH_PROM_SIG;
+ sigLength = sizeof(long) << 1;
+
+ if (lp->chipset == DC21040) {
+ for (i=0,j=0;(j<sigLength) && (i<PROBE_LENGTH+sigLength-1);i++) {
+ if (lp->bus == PCI) {
+ while ((tmp = inl(aprom_addr)) < 0);
+ data = (char)tmp;
+ } else {
+ data = inb(aprom_addr);
+ }
+ if (dev.Sig[j] == data) { /* track signature */
+ j++;
+ } else { /* lost signature; begin search again */
+ if (data == dev.Sig[0]) {
+ j=1;
+ } else {
+ j=0;
+ }
+ }
+ }
+
+ if (j!=sigLength) {
+ status = -ENODEV; /* search failed */
+ }
+
+ } else { /* use new srom */
+ short *p = (short *)&lp->srom;
+ for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) {
+ *p++ = srom_rd(aprom_addr, i);
+ }
+ }
+
+ return status;
+}
+
+static int aprom_crc(struct device *dev)
+{
+ int iobase = dev->base_addr;
+ long i, k, tmp;
+ unsigned short j,chksum;
+ unsigned char status = 0;
+ struct bus_type *lp = &bus;
+
+ for (i=0,k=0,j=0;j<3;j++) {
+ k <<= 1 ;
+ if (k > 0xffff) k-=0xffff;
+
+ if (lp->bus == PCI) {
+ if (lp->chipset == DC21040) {
+ while ((tmp = inl(DE4X5_APROM)) < 0);
+ k += (u_char) tmp;
+ dev->dev_addr[i++] = (u_char) tmp;
+ while ((tmp = inl(DE4X5_APROM)) < 0);
+ k += (u_short) (tmp << 8);
+ dev->dev_addr[i++] = (u_char) tmp;
+ } else {
+ dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++;
+ dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++;
+ }
+ } else {
+ k += (u_char) (tmp = inb(EISA_APROM));
+ dev->dev_addr[i++] = (u_char) tmp;
+ k += (u_short) ((tmp = inb(EISA_APROM)) << 8);
+ dev->dev_addr[i++] = (u_char) tmp;
+ }
+
+ if (k > 0xffff) k-=0xffff;
+ }
+ if (k == 0xffff) k=0;
+
+ if (lp->bus == PCI) {
+ if (lp->chipset == DC21040) {
+ while ((tmp = inl(DE4X5_APROM)) < 0);
+ chksum = (u_char) tmp;
+ while ((tmp = inl(DE4X5_APROM)) < 0);
+ chksum |= (u_short) (tmp << 8);
+ if (k != chksum) status = -1;
+ }
+ } else {
+ chksum = (u_char) inb(EISA_APROM);
+ chksum |= (u_short) (inb(EISA_APROM) << 8);
+ if (k != chksum) status = -1;
+ }
+
+
+ return status;
+}
+
+/*
+** SROM Read
+*/
+static short srom_rd(u_short addr, u_char offset)
+{
+ sendto_srom(SROM_RD | SROM_SR, addr);
+
+ srom_latch(SROM_RD | SROM_SR | DT_CS, addr);
+ srom_command(SROM_RD | SROM_SR | DT_IN | DT_CS, addr);
+ srom_address(SROM_RD | SROM_SR | DT_CS, addr, offset);
+
+ return srom_data(SROM_RD | SROM_SR | DT_CS, addr);
+}
+
+static void srom_latch(u_long command, u_short addr)
+{
+ sendto_srom(command, addr);
+ sendto_srom(command | DT_CLK, addr);
+ sendto_srom(command, addr);
+
+ return;
+}
+
+static void srom_command(u_long command, u_short addr)
+{
+ srom_latch(command, addr);
+ srom_latch(command, addr);
+ srom_latch((command & 0x0000ff00) | DT_CS, addr);
+
+ return;
+}
+
+static void srom_address(u_long command, u_short addr, u_char offset)
+{
+ long i;
+ char a;
+
+ a = (char)(offset << 2);
+ for (i=0; i<6; i++, a <<= 1) {
+ srom_latch(command | ((a < 0) ? DT_IN : 0), addr);
+ }
+ dce_us_delay(1);
+
+ i = (getfrom_srom(addr) >> 3) & 0x01;
+ if (i != 0) {
+ printk("Bad SROM address phase.....\n");
+ }
+
+ return;
+}
+
+static short srom_data(u_long command, u_short addr)
+{
+ int i;
+ short word = 0;
+ long tmp;
+
+ for (i=0; i<16; i++) {
+ sendto_srom(command | DT_CLK, addr);
+ tmp = getfrom_srom(addr);
+ sendto_srom(command, addr);
+
+ word = (word << 1) | ((tmp >> 3) & 0x01);
+ }
+
+ sendto_srom(command & 0x0000ff00, addr);
+
+ return word;
+}
+
+/*
+static void srom_busy(u_long command, u_short addr)
+{
+ sendto_srom((command & 0x0000ff00) | DT_CS, addr);
+
+ while (!((getfrom_srom(addr) >> 3) & 0x01)) {
+ dce_ms_delay(1);
+ }
+
+ sendto_srom(command & 0x0000ff00, addr);
+
+ return;
+}
+*/
+
+static void sendto_srom(u_long command, u_short addr)
+{
+ outl(command, addr);
+ dce_us_delay(1);
+
+ return;
+}
+
+static long getfrom_srom(u_short addr)
+{
+ long tmp;
+
+ tmp = inl(addr);
+ dce_us_delay(1);
+
+ return tmp;
+}
+
+static char *build_setup_frame(struct device *dev, int mode)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int i;
+ char *pa = lp->setup_frame;
+
+ /* Initialise the setup frame */
+ if (mode == ALL) {
+ memset(lp->setup_frame, 0, SETUP_FRAME_LEN);
+ }
+
+ if (lp->setup_f == HASH_PERF) {
+ for (pa=lp->setup_frame+IMPERF_PA_OFFSET, i=0; i<ETH_ALEN; i++) {
+ *(pa + i) = dev->dev_addr[i]; /* Host address */
+ if (i & 0x01) pa += 2;
+ }
+ *(lp->setup_frame + (HASH_TABLE_LEN >> 3) - 3) = 0x80; /* B'cast address */
+ } else {
+ for (i=0; i<ETH_ALEN; i++) { /* Host address */
+ *(pa + (i&1)) = dev->dev_addr[i];
+ if (i & 0x01) pa += 4;
+ }
+ for (i=0; i<ETH_ALEN; i++) { /* Broadcast address */
+ *(pa + (i&1)) = (char) 0xff;
+ if (i & 0x01) pa += 4;
+ }
+ }
+
+ return pa; /* Points to the next entry */
+}
+
+/*
+** Perform IOCTL call functions here. Some are privileged operations and the
+** effective uid is checked in those cases.
+*/
+static int de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ struct de4x5_ioctl *ioc = (struct de4x5_ioctl *) &rq->ifr_data;
+ int i, j, iobase = dev->base_addr, status = 0;
+ u_long omr;
+ union {
+ unsigned char addr[(HASH_TABLE_LEN * ETH_ALEN)];
+ unsigned short sval[(HASH_TABLE_LEN * ETH_ALEN) >> 1];
+ unsigned long lval[(HASH_TABLE_LEN * ETH_ALEN) >> 2];
+ } tmp;
+
+ switch(ioc->cmd) {
+ case DE4X5_GET_HWADDR: /* Get the hardware address */
+ for (i=0; i<ETH_ALEN; i++) {
+ tmp.addr[i] = dev->dev_addr[i];
+ }
+ ioc->len = ETH_ALEN;
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+
+ break;
+ case DE4X5_SET_HWADDR: /* Set the hardware address */
+ if (suser()) {
+ memcpy_fromfs(tmp.addr,ioc->data,ETH_ALEN);
+ for (i=0; i<ETH_ALEN; i++) {
+ dev->dev_addr[i] = tmp.addr[i];
+ }
+ build_setup_frame(dev, PHYS_ADDR_ONLY);
+ /* Set up the descriptor and give ownership to the card */
+ while (set_bit(0, (void *)&dev->tbusy) != 0); /* Wait for lock to free */
+ if (lp->setup_f == HASH_PERF) {
+ load_packet(dev, lp->setup_frame, TD_IC | HASH_F | TD_SET |
+ SETUP_FRAME_LEN, NULL);
+ } else {
+ load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET |
+ SETUP_FRAME_LEN, NULL);
+ }
+ lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+ outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */
+ dev->tbusy = 0; /* Unlock the TX ring */
+
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_SET_PROM: /* Set Promiscuous Mode */
+ if (suser()) {
+ omr = inl(DE4X5_OMR);
+ omr |= OMR_PR;
+ outl(omr, DE4X5_OMR);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_CLR_PROM: /* Clear Promiscuous Mode */
+ if (suser()) {
+ omr = inl(DE4X5_OMR);
+ omr &= ~OMR_PR;
+ outb(omr, DE4X5_OMR);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_SAY_BOO: /* Say "Boo!" to the kernel log file */
+ printk("%s: Boo!\n", dev->name);
+
+ break;
+ case DE4X5_GET_MCA: /* Get the multicast address table */
+ ioc->len = (HASH_TABLE_LEN >> 3);
+ memcpy_tofs(ioc->data, lp->setup_frame, 192);
+
+ break;
+ case DE4X5_SET_MCA: /* Set a multicast address */
+ if (suser()) {
+ if (ioc->len != HASH_TABLE_LEN) { /* MCA changes */
+ memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len);
+ }
+ set_multicast_list(dev, ioc->len, tmp.addr);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_CLR_MCA: /* Clear all multicast addresses */
+ if (suser()) {
+ set_multicast_list(dev, 0, NULL);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_MCA_EN: /* Enable pass all multicast addressing */
+ if (suser()) {
+ omr = inl(DE4X5_OMR);
+ omr |= OMR_PM;
+ outl(omr, DE4X5_OMR);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_GET_STATS: /* Get the driver statistics */
+ cli();
+ memcpy_tofs(ioc->data, &lp->pktStats, sizeof(lp->pktStats));
+ ioc->len = DE4X5_PKT_STAT_SZ;
+ sti();
+
+ break;
+ case DE4X5_CLR_STATS: /* Zero out the driver statistics */
+ if (suser()) {
+ cli();
+ memset(&lp->pktStats, 0, sizeof(lp->pktStats));
+ sti();
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_GET_OMR: /* Get the OMR Register contents */
+ tmp.addr[0] = inl(DE4X5_OMR);
+ memcpy_tofs(ioc->data, tmp.addr, 1);
+
+ break;
+ case DE4X5_SET_OMR: /* Set the OMR Register contents */
+ if (suser()) {
+ memcpy_fromfs(tmp.addr, ioc->data, 1);
+ outl(tmp.addr[0], DE4X5_OMR);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_GET_REG: /* Get the DE4X5 Registers */
+ tmp.lval[0] = inl(DE4X5_STS);
+ tmp.lval[1] = inl(DE4X5_BMR);
+ tmp.lval[2] = inl(DE4X5_IMR);
+ tmp.lval[3] = inl(DE4X5_OMR);
+ tmp.lval[4] = inl(DE4X5_SISR);
+ tmp.lval[5] = inl(DE4X5_SICR);
+ tmp.lval[6] = inl(DE4X5_STRR);
+ tmp.lval[7] = inl(DE4X5_SIGR);
+ memcpy_tofs(ioc->data, tmp.addr, 32);
+
+ break;
+
+#define DE4X5_DUMP 0x0f /* Dump the DE4X5 Status */
+
+ case DE4X5_DUMP:
+ j = 0;
+ tmp.addr[j++] = dev->irq;
+ for (i=0; i<ETH_ALEN; i++) {
+ tmp.addr[j++] = dev->dev_addr[i];
+ }
+ tmp.addr[j++] = lp->rxRingSize;
+ tmp.lval[j>>2] = (long)lp->rx_ring; j+=4;
+ tmp.lval[j>>2] = (long)lp->tx_ring; j+=4;
+
+ for (i=0;i<lp->rxRingSize-1;i++){
+ if (i < 3) {
+ tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4;
+ }
+ }
+ tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4;
+ for (i=0;i<lp->txRingSize-1;i++){
+ if (i < 3) {
+ tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4;
+ }
+ }
+ tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4;
+
+ for (i=0;i<lp->rxRingSize-1;i++){
+ if (i < 3) {
+ tmp.lval[j>>2] = (long)lp->rx_ring[i].buf; j+=4;
+ }
+ }
+ tmp.lval[j>>2] = (long)lp->rx_ring[i].buf; j+=4;
+ for (i=0;i<lp->txRingSize-1;i++){
+ if (i < 3) {
+ tmp.lval[j>>2] = (long)lp->tx_ring[i].buf; j+=4;
+ }
+ }
+ tmp.lval[j>>2] = (long)lp->tx_ring[i].buf; j+=4;
+
+ for (i=0;i<lp->rxRingSize;i++){
+ tmp.lval[j>>2] = lp->rx_ring[i].status; j+=4;
+ }
+ for (i=0;i<lp->txRingSize;i++){
+ tmp.lval[j>>2] = lp->tx_ring[i].status; j+=4;
+ }
+
+ tmp.lval[j>>2] = inl(DE4X5_STS); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_BMR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_IMR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_OMR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_SISR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_SICR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_STRR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_SIGR); j+=4;
+
+ tmp.addr[j++] = lp->txRingSize;
+ tmp.addr[j++] = dev->tbusy;
+
+ ioc->len = j;
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+
+ break;
+ default:
+ status = -EOPNOTSUPP;
+ }
+
+ return status;
+}
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+static struct device thisDE4X5 = {
+ " ", /* device name inserted by /linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0x2000, 10, /* I/O address, IRQ */
+ 0, 0, 0, NULL, de4x5_probe };
+
+int io=0x000b; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
+int irq=10; /* or use the insmod io= irq= options */
+
+int
+init_module(void)
+{
+ thisDE4X5.base_addr=io;
+ thisDE4X5.irq=irq;
+ if (register_netdev(&thisDE4X5) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ struct de4x5_private *lp = (struct de4x5_private *) thisDE4X5.priv;
+
+ if (MOD_IN_USE) {
+ printk("%s: device busy, remove delayed\n",thisDE4X5.name);
+ } else {
+ release_region(thisDE4X5.base_addr, (lp->bus == PCI ?
+ DE4X5_PCI_TOTAL_SIZE :
+ DE4X5_EISA_TOTAL_SIZE));
+ if (lp) {
+ kfree_s(lp->rx_ring[0].buf, RX_BUFF_SZ * NUM_RX_DESC + ALIGN);
+ }
+ kfree_s(thisDE4X5.priv, sizeof(struct de4x5_private) + ALIGN);
+ thisDE4X5.priv = NULL;
+
+ unregister_netdev(&thisDE4X5);
+ }
+}
+#endif /* MODULE */
+
+
+/*
+ * Local variables:
+ * kernel-compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de4x5.c"
+ *
+ * module-compile-command: "gcc -D__KERNEL__ -DMODULE -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de4x5.c"
+ * End:
+ */
+
+
diff --git a/drivers/net/de4x5.h b/drivers/net/de4x5.h
new file mode 100644
index 000000000..ee5c41b0a
--- /dev/null
+++ b/drivers/net/de4x5.h
@@ -0,0 +1,647 @@
+/*
+ Copyright 1994 Digital Equipment Corporation.
+
+ This software may be used and distributed according to the terms of the
+ GNU Public License, incorporated herein by reference.
+
+ The author may be reached as davies@wanton.lkg.dec.com or Digital
+ Equipment Corporation, 550 King Street, Littleton MA 01460.
+
+ =========================================================================
+*/
+
+/*
+** DC21040 CSR<1..15> Register Address Map
+*/
+#define DE4X5_BMR iobase+(0x000 << lp->bus) /* Bus Mode Register */
+#define DE4X5_TPD iobase+(0x008 << lp->bus) /* Transmit Poll Demand Reg */
+#define DE4X5_RPD iobase+(0x010 << lp->bus) /* Receive Poll Demand Reg */
+#define DE4X5_RRBA iobase+(0x018 << lp->bus) /* RX Ring Base Address Reg */
+#define DE4X5_TRBA iobase+(0x020 << lp->bus) /* TX Ring Base Address Reg */
+#define DE4X5_STS iobase+(0x028 << lp->bus) /* Status Register */
+#define DE4X5_OMR iobase+(0x030 << lp->bus) /* Operation Mode Register */
+#define DE4X5_IMR iobase+(0x038 << lp->bus) /* Interrupt Mask Register */
+#define DE4X5_MFC iobase+(0x040 << lp->bus) /* Missed Frame Counter */
+#define DE4X5_APROM iobase+(0x048 << lp->bus) /* Ethernet Address PROM */
+#define DE4X5_BROM iobase+(0x048 << lp->bus) /* Boot ROM Register */
+#define DE4X5_SROM iobase+(0x048 << lp->bus) /* Serial ROM Register */
+#define DE4X5_DDR iobase+(0x050 << lp->bus) /* Data Diagnostic Register */
+#define DE4X5_FDR iobase+(0x058 << lp->bus) /* Full Duplex Register */
+#define DE4X5_GPT iobase+(0x058 << lp->bus) /* General Purpose Timer Reg.*/
+#define DE4X5_GEP iobase+(0x060 << lp->bus) /* General Purpose Register */
+#define DE4X5_SISR iobase+(0x060 << lp->bus) /* SIA Status Register */
+#define DE4X5_SICR iobase+(0x068 << lp->bus) /* SIA Connectivity Register */
+#define DE4X5_STRR iobase+(0x070 << lp->bus) /* SIA TX/RX Register */
+#define DE4X5_SIGR iobase+(0x078 << lp->bus) /* SIA General Register */
+
+/*
+** EISA Register Address Map
+*/
+#define EISA_ID iobase+0x0c80 /* EISA ID Registers */
+#define EISA_ID0 iobase+0x0c80 /* EISA ID Register 0 */
+#define EISA_ID1 iobase+0x0c81 /* EISA ID Register 1 */
+#define EISA_ID2 iobase+0x0c82 /* EISA ID Register 2 */
+#define EISA_ID3 iobase+0x0c83 /* EISA ID Register 3 */
+#define EISA_CR iobase+0x0c84 /* EISA Control Register */
+#define EISA_REG0 iobase+0x0c88 /* EISA Configuration Register 0 */
+#define EISA_REG1 iobase+0x0c89 /* EISA Configuration Register 1 */
+#define EISA_REG2 iobase+0x0c8a /* EISA Configuration Register 2 */
+#define EISA_REG3 iobase+0x0c8f /* EISA Configuration Register 3 */
+#define EISA_APROM iobase+0x0c90 /* Ethernet Address PROM */
+
+/*
+** PCI/EISA Configuration Registers Address Map
+*/
+#define PCI_CFID iobase+0x0008 /* PCI Configuration ID Register */
+#define PCI_CFCS iobase+0x000c /* PCI Command/Status Register */
+#define PCI_CFRV iobase+0x0018 /* PCI Revision Register */
+#define PCI_CFLT iobase+0x001c /* PCI Latency Timer Register */
+#define PCI_CBIO iobase+0x0028 /* PCI Base I/O Register */
+#define PCI_CBMA iobase+0x002c /* PCI Base Memory Address Register */
+#define PCI_CBER iobase+0x0030 /* PCI Expansion ROM Base Address Reg. */
+#define PCI_CFIT iobase+0x003c /* PCI Configuration Interrupt Register */
+#define PCI_CFDA iobase+0x0040 /* PCI Driver Area Register */
+
+/*
+** EISA Configuration Register 0 bit definitions
+*/
+#define ER0_BSW 0x80 /* EISA Bus Slave Width, 1: 32 bits */
+#define ER0_BMW 0x40 /* EISA Bus Master Width, 1: 32 bits */
+#define ER0_EPT 0x20 /* EISA PREEMPT Time, 0: 23 BCLKs */
+#define ER0_ISTS 0x10 /* Interrupt Status (X) */
+#define ER0_LI 0x08 /* Latch Interrupts */
+#define ER0_INTL 0x06 /* INTerrupt Level */
+#define ER0_INTT 0x01 /* INTerrupt Type, 0: Level, 1: Edge */
+
+/*
+** EISA Configuration Register 1 bit definitions
+*/
+#define ER1_IAM 0xe0 /* ISA Address Mode */
+#define ER1_IAE 0x10 /* ISA Addressing Enable */
+#define ER1_UPIN 0x0f /* User Pins */
+
+/*
+** EISA Configuration Register 2 bit definitions
+*/
+#define ER2_BRS 0xc0 /* Boot ROM Size */
+#define ER2_BRA 0x3c /* Boot ROM Address <16:13> */
+
+/*
+** EISA Configuration Register 3 bit definitions
+*/
+#define ER3_BWE 0x40 /* Burst Write Enable */
+#define ER3_BRE 0x04 /* Burst Read Enable */
+#define ER3_LSR 0x02 /* Local Software Reset */
+
+/*
+** PCI Configuration ID Register (PCI_CFID)
+*/
+#define CFID_DID 0xff00 /* Device ID */
+#define CFID_VID 0x00ff /* Vendor ID */
+#define DC21040_DID 0x0002 /* Unique Device ID # */
+#define DC21040_VID 0x1011 /* DC21040 Manufacturer */
+#define DC21041_DID 0x0014 /* Unique Device ID # */
+#define DC21041_VID 0x1011 /* DC21041 Manufacturer */
+#define DC21140_DID 0x0009 /* Unique Device ID # */
+#define DC21140_VID 0x1011 /* DC21140 Manufacturer */
+
+/*
+** Chipset defines
+*/
+#define DC21040 DC21040_DID
+#define DC21041 DC21041_DID
+#define DC21140 DC21140_DID
+
+#define is_DC21040 ((vendor == DC21040_VID) && (device == DC21040_DID))
+#define is_DC21041 ((vendor == DC21041_VID) && (device == DC21041_DID))
+#define is_DC21140 ((vendor == DC21140_VID) && (device == DC21140_DID))
+
+/*
+** PCI Configuration Command/Status Register (PCI_CFCS)
+*/
+#define CFCS_DPE 0x80000000 /* Detected Parity Error (S) */
+#define CFCS_SSE 0x40000000 /* Signal System Error (S) */
+#define CFCS_RMA 0x20000000 /* Receive Master Abort (S) */
+#define CFCS_RTA 0x10000000 /* Receive Target Abort (S) */
+#define CFCS_DST 0x06000000 /* DEVSEL Timing (S) */
+#define CFCS_DPR 0x01000000 /* Data Parity Report (S) */
+#define CFCS_FBB 0x00800000 /* Fast Back-To-Back (S) */
+#define CFCS_SLE 0x00000100 /* System Error Enable (C) */
+#define CFCS_PER 0x00000040 /* Parity Error Response (C) */
+#define CFCS_MO 0x00000004 /* Master Operation (C) */
+#define CFCS_MSA 0x00000002 /* Memory Space Access (C) */
+#define CFCS_IOSA 0x00000001 /* I/O Space Access (C) */
+
+/*
+** PCI Configuration Revision Register (PCI_CFRV)
+*/
+#define CFRV_BC 0xff000000 /* Base Class */
+#define CFRV_SC 0x00ff0000 /* Subclass */
+#define CFRV_SN 0x000000f0 /* Step Number */
+#define CFRV_RN 0x0000000f /* Revision Number */
+#define BASE_CLASS 0x02000000 /* Indicates Network Controller */
+#define SUB_CLASS 0x00000000 /* Indicates Ethernet Controller */
+#define STEP_NUMBER 0x00000020 /* Increments for future chips */
+#define REV_NUMBER 0x00000003 /* 0x00, 0x01, 0x02, 0x03: Rev in Step */
+#define CFRV_MASK 0xffff0000 /* Register mask */
+
+/*
+** PCI Configuration Latency Timer Register (PCI_CFLT)
+*/
+#define CFLT_BC 0x0000ff00 /* Latency Timer bits */
+
+/*
+** PCI Configuration Base I/O Address Register (PCI_CBIO)
+*/
+#define CBIO_MASK 0x0000ff80 /* Base I/O Address Mask */
+#define CBIO_IOSI 0x00000001 /* I/O Space Indicator (RO, value is 1) */
+
+/*
+** PCI Configuration Expansion ROM Base Address Register (PCI_CBER)
+*/
+#define CBER_MASK 0xfffffc00 /* Expansion ROM Base Address Mask */
+#define CBER_ROME 0x00000001 /* ROM Enable */
+
+/*
+** PCI Configuration Driver Area Register (PCI_CFDA)
+*/
+#define CFDA_PSM 0x80000000 /* Power Saving Mode */
+
+/*
+** DC21040 Bus Mode Register (DE4X5_BMR)
+*/
+#define BMR_DBO 0x00100000 /* Descriptor Byte Ordering (Endian) */
+#define BMR_TAP 0x000e0000 /* Transmit Automatic Polling */
+#define BMR_DAS 0x00010000 /* Diagnostic Address Space */
+#define BMR_CAL 0x0000c000 /* Cache Alignment */
+#define BMR_PBL 0x00003f00 /* Programmable Burst Length */
+#define BMR_BLE 0x00000080 /* Big/Little Endian */
+#define BMR_DSL 0x0000007c /* Descriptor Skip Length */
+#define BMR_BAR 0x00000002 /* Bus ARbitration */
+#define BMR_SWR 0x00000001 /* Software Reset */
+
+#define TAP_NOPOLL 0x00000000 /* No automatic polling */
+#define TAP_200US 0x00020000 /* TX automatic polling every 200us */
+#define TAP_800US 0x00040000 /* TX automatic polling every 800us */
+#define TAP_1_6MS 0x00060000 /* TX automatic polling every 1.6ms */
+#define TAP_12_8US 0x00080000 /* TX automatic polling every 12.8us */
+#define TAP_25_6US 0x000a0000 /* TX automatic polling every 25.6us */
+#define TAP_51_2US 0x000c0000 /* TX automatic polling every 51.2us */
+#define TAP_102_4US 0x000e0000 /* TX automatic polling every 102.4us */
+
+#define CAL_NOUSE 0x00000000 /* Not used */
+#define CAL_8LONG 0x00004000 /* 8-longword alignment */
+#define CAL_16LONG 0x00008000 /* 16-longword alignment */
+#define CAL_32LONG 0x0000c000 /* 32-longword alignment */
+
+#define PBL_0 0x00000000 /* DMA burst length = amount in RX FIFO */
+#define PBL_1 0x00000100 /* 1 longword DMA burst length */
+#define PBL_2 0x00000200 /* 2 longwords DMA burst length */
+#define PBL_4 0x00000400 /* 4 longwords DMA burst length */
+#define PBL_8 0x00000800 /* 8 longwords DMA burst length */
+#define PBL_16 0x00001000 /* 16 longwords DMA burst length */
+#define PBL_32 0x00002000 /* 32 longwords DMA burst length */
+
+#define DSL_0 0x00000000 /* 0 longword / descriptor */
+#define DSL_1 0x00000004 /* 1 longword / descriptor */
+#define DSL_2 0x00000008 /* 2 longwords / descriptor */
+#define DSL_4 0x00000010 /* 4 longwords / descriptor */
+#define DSL_8 0x00000020 /* 8 longwords / descriptor */
+#define DSL_16 0x00000040 /* 16 longwords / descriptor */
+#define DSL_32 0x00000080 /* 32 longwords / descriptor */
+
+/*
+** DC21040 Transmit Poll Demand Register (DE4X5_TPD)
+*/
+#define TPD 0x00000001 /* Transmit Poll Demand */
+
+/*
+** DC21040 Receive Poll Demand Register (DE4X5_RPD)
+*/
+#define RPD 0x00000001 /* Receive Poll Demand */
+
+/*
+** DC21040 Receive Ring Base Address Register (DE4X5_RRBA)
+*/
+#define RRBA 0xfffffffc /* RX Descriptor List Start Address */
+
+/*
+** DC21040 Transmit Ring Base Address Register (DE4X5_TRBA)
+*/
+#define TRBA 0xfffffffc /* TX Descriptor List Start Address */
+
+/*
+** DC21040 Status Register (DE4X5_STS)
+*/
+#define STS_BE 0x03800000 /* Bus Error Bits */
+#define STS_TS 0x00700000 /* Transmit Process State */
+#define STS_RS 0x000e0000 /* Receive Process State */
+#define STS_NIS 0x00010000 /* Normal Interrupt Summary */
+#define STS_AIS 0x00008000 /* Abnormal Interrupt Summary */
+#define STS_ER 0x00004000 /* Early Receive */
+#define STS_SE 0x00002000 /* System Error */
+#define STS_LNF 0x00001000 /* Link Fail */
+#define STS_FD 0x00000800 /* Full-Duplex Short Frame Received */
+#define STS_TM 0x00000800 /* Timer Expired (DC21041) */
+#define STS_AT 0x00000400 /* AUI/TP Pin */
+#define STS_RWT 0x00000200 /* Receive Watchdog Time-Out */
+#define STS_RPS 0x00000100 /* Receive Process Stopped */
+#define STS_RU 0x00000080 /* Receive Buffer Unavailable */
+#define STS_RI 0x00000040 /* Receive Interrupt */
+#define STS_UNF 0x00000020 /* Transmit Underflow */
+#define STS_LNP 0x00000010 /* Link Pass */
+#define STS_TJT 0x00000008 /* Transmit Jabber Time-Out */
+#define STS_TU 0x00000004 /* Transmit Buffer Unavailable */
+#define STS_TPS 0x00000002 /* Transmit Process Stopped */
+#define STS_TI 0x00000001 /* Transmit Interrupt */
+
+#define EB_PAR 0x00000000 /* Parity Error */
+#define EB_MA 0x00800000 /* Master Abort */
+#define EB_TA 0x01000000 /* Target Abort */
+#define EB_RES0 0x01800000 /* Reserved */
+#define EB_RES1 0x02000000 /* Reserved */
+
+#define TS_STOP 0x00000000 /* Stopped */
+#define TS_FTD 0x00100000 /* Fetch Transmit Descriptor */
+#define TS_WEOT 0x00200000 /* Wait for End Of Transmission */
+#define TS_QDAT 0x00300000 /* Queue skb data into TX FIFO */
+#define TS_RES 0x00400000 /* Reserved */
+#define TS_SPKT 0x00500000 /* Setup Packet */
+#define TS_SUSP 0x00600000 /* Suspended */
+#define TS_CLTD 0x00700000 /* Close Transmit Descriptor */
+
+#define RS_STOP 0x00000000 /* Stopped */
+#define RS_FRD 0x00020000 /* Fetch Receive Descriptor */
+#define RS_CEOR 0x00040000 /* Check for End of Receive Packet */
+#define RS_WFRP 0x00060000 /* Wait for Receive Packet */
+#define RS_SUSP 0x00080000 /* Suspended */
+#define RS_CLRD 0x000a0000 /* Close Receive Descriptor */
+#define RS_FLUSH 0x000c0000 /* Flush RX FIFO */
+#define RS_QRFS 0x000e0000 /* Queue RX FIFO into RX Skb */
+
+#define INT_CANCEL 0x0001ffff /* For zeroing all interrupt sources */
+
+/*
+** DC21040 Operation Mode Register (DE4X5_OMR)
+*/
+#define OMR_SDP 0x02000000 /* SD Polarity - MUST BE ASSERTED */
+#define OMR_SCR 0x01000000 /* Scrambler Mode */
+#define OMR_PCS 0x00800000 /* PCS Function */
+#define OMR_TTM 0x00400000 /* Transmit Threshold Mode */
+#define OMR_SF 0x00200000 /* Store and Forward */
+#define OMR_HBD 0x00080000 /* HeartBeat Disable */
+#define OMR_PS 0x00040000 /* Port Select */
+#define OMR_CA 0x00020000 /* Capture Effect Enable */
+#define OMR_BP 0x00010000 /* Back Pressure */
+#define OMR_TR 0x0000c000 /* Threshold Control Bits */
+#define OMR_ST 0x00002000 /* Start/Stop Transmission Command */
+#define OMR_FC 0x00001000 /* Force Collision Mode */
+#define OMR_OM 0x00000c00 /* Operating Mode */
+#define OMR_FD 0x00000200 /* Full Duplex Mode */
+#define OMR_FKD 0x00000100 /* Flaky Oscillator Disable */
+#define OMR_PM 0x00000080 /* Pass All Multicast */
+#define OMR_PR 0x00000040 /* Promiscuous Mode */
+#define OMR_SB 0x00000020 /* Start/Stop Backoff Counter */
+#define OMR_IF 0x00000010 /* Inverse Filtering */
+#define OMR_PB 0x00000008 /* Pass Bad Frames */
+#define OMR_HO 0x00000004 /* Hash Only Filtering Mode */
+#define OMR_SR 0x00000002 /* Start/Stop Receive */
+#define OMR_HP 0x00000001 /* Hash/Perfect Receive Filtering Mode */
+
+#define TR_72 0x00000000 /* Threshold set to 72 bytes */
+#define TR_96 0x00004000 /* Threshold set to 96 bytes */
+#define TR_128 0x00008000 /* Threshold set to 128 bytes */
+#define TR_160 0x0000c000 /* Threshold set to 160 bytes */
+
+/*
+** DC21040 Interrupt Mask Register (DE4X5_IMR)
+*/
+#define IMR_NIM 0x00010000 /* Normal Interrupt Summary Mask */
+#define IMR_AIM 0x00008000 /* Abnormal Interrupt Summary Mask */
+#define IMR_ERM 0x00004000 /* Early Receive Mask */
+#define IMR_SEM 0x00002000 /* System Error Mask */
+#define IMR_LFM 0x00001000 /* Link Fail Mask */
+#define IMR_FDM 0x00000800 /* Full-Duplex (Short Frame) Mask */
+#define IMR_TMM 0x00000800 /* Timer Expired Mask (DC21041) */
+#define IMR_ATM 0x00000400 /* AUI/TP Switch Mask */
+#define IMR_RWM 0x00000200 /* Receive Watchdog Time-Out Mask */
+#define IMR_RSM 0x00000100 /* Receive Stopped Mask */
+#define IMR_RUM 0x00000080 /* Receive Buffer Unavailable Mask */
+#define IMR_RIM 0x00000040 /* Receive Interrupt Mask */
+#define IMR_UNM 0x00000020 /* Underflow Interrupt Mask */
+#define IMR_LPM 0x00000010 /* Link Pass */
+#define IMR_TJM 0x00000008 /* Transmit Time-Out Jabber Mask */
+#define IMR_TUM 0x00000004 /* Transmit Buffer Unavailable Mask */
+#define IMR_TSM 0x00000002 /* Transmission Stopped Mask */
+#define IMR_TIM 0x00000001 /* Transmit Interrupt Mask */
+
+/*
+** DC21040 Missed Frame Counter (DE4X5_MFC)
+*/
+#define MFC_OVFL 0x00010000 /* Counter Overflow Bit */
+#define MFC_CNTR 0x0000ffff /* Counter Bits */
+
+/*
+** DC21040 Ethernet Address PROM (DE4X5_APROM)
+*/
+#define APROM_DN 0x80000000 /* Data Not Valid */
+#define APROM_DT 0x000000ff /* Address Byte */
+
+/*
+** DC21041 Boot/Ethernet Address ROM (DE4X5_BROM)
+*/
+#define BROM_MODE 0x00008000 /* MODE_1: 0, MODE_0: 1 (read only) */
+#define BROM_RD 0x00004000 /* Read from Boot ROM */
+#define BROM_WR 0x00002000 /* Write to Boot ROM */
+#define BROM_BR 0x00001000 /* Select Boot ROM when set */
+#define BROM_SR 0x00000800 /* Select Serial ROM when set */
+#define BROM_REG 0x00000400 /* External Register Select */
+#define BROM_DT 0x000000ff /* Data Byte */
+
+/*
+** DC21041 Serial/Ethernet Address ROM (DE4X5_SROM)
+*/
+#define SROM_MODE 0x00008000 /* MODE_1: 0, MODE_0: 1 (read only) */
+#define SROM_RD 0x00004000 /* Read from Boot ROM */
+#define SROM_WR 0x00002000 /* Write to Boot ROM */
+#define SROM_BR 0x00001000 /* Select Boot ROM when set */
+#define SROM_SR 0x00000800 /* Select Serial ROM when set */
+#define SROM_REG 0x00000400 /* External Register Select */
+#define SROM_DT 0x000000ff /* Data Byte */
+
+#define DT_OUT 0x00000008 /* Serial Data Out */
+#define DT_IN 0x00000004 /* Serial Data In */
+#define DT_CLK 0x00000002 /* Serial ROM Clock */
+#define DT_CS 0x00000001 /* Serial ROM Chip Select */
+
+/*
+** DC21040 Full Duplex Register (DE4X5_FDR)
+*/
+#define FDR_FDACV 0x0000ffff /* Full Duplex Auto Configuration Value */
+
+/*
+** DC21041 General Purpose Timer Register (DE4X5_GPT)
+*/
+#define GPT_CON 0x00010000 /* One shot: 0, Continuous: 1 */
+#define GPT_VAL 0x0000ffff /* Timer Value */
+
+/*
+** DC21140 General Purpose Register (DE4X5_GEP) (hardware dependent bits)
+*/
+/* Valid ONLY for DE500 hardware */
+#define GEP_LNP 0x00000080 /* Link Pass (input) */
+#define GEP_SLNK 0x00000040 /* SYM LINK (input) */
+#define GEP_SDET 0x00000020 /* Signal Detect (input) */
+#define GEP_FDXD 0x00000008 /* Full Duplex Disable (output) */
+#define GEP_PHYL 0x00000004 /* PHY Loopback (output) */
+#define GEP_FLED 0x00000002 /* Force Activity LED on (output) */
+#define GEP_MODE 0x00000001 /* 0: 10Mb/s, 1: 100Mb/s */
+#define GEP_INIT 0x0000010f /* Setup inputs (0) and outputs (1) */
+
+
+/*
+** DC21040 SIA Status Register (DE4X5_SISR)
+*/
+#define SISR_LPC 0xffff0000 /* Link Partner's Code Word */
+#define SISR_LPN 0x00008000 /* Link Partner Negotiable */
+#define SISR_ANS 0x00007000 /* Auto Negotiation Arbitration State */
+#define SISR_NSN 0x00000800 /* Non Stable NLPs Detected */
+#define SISR_ANR_FDS 0x00000400 /* Auto Negotiate Restart/Full Duplex Sel.*/
+#define SISR_NRA 0x00000200 /* Non Selected Port Receive Activity */
+#define SISR_SRA 0x00000100 /* Selected Port Receive Activity */
+#define SISR_DAO 0x00000080 /* PLL All One */
+#define SISR_DAZ 0x00000040 /* PLL All Zero */
+#define SISR_DSP 0x00000020 /* PLL Self-Test Pass */
+#define SISR_DSD 0x00000010 /* PLL Self-Test Done */
+#define SISR_APS 0x00000008 /* Auto Polarity State */
+#define SISR_LKF 0x00000004 /* Link Fail Status */
+#define SISR_NCR 0x00000002 /* Network Connection Error */
+#define SISR_PAUI 0x00000001 /* AUI_TP Indication */
+#define SIA_RESET 0x00000000 /* SIA Reset */
+
+#define ANS_NDIS 0x00000000 /* Nway disable */
+#define ANS_TDIS 0x00001000 /* Transmit Disable */
+#define ANS_ADET 0x00002000 /* Ability Detect */
+#define ANS_ACK 0x00003000 /* Acknowledge */
+#define ANS_CACK 0x00004000 /* Complete Acknowledge */
+#define ANS_NWOK 0x00005000 /* Nway OK - FLP Link Good */
+#define ANS_LCHK 0x00006000 /* Link Check */
+
+/*
+** DC21040 SIA Connectivity Register (DE4X5_SICR)
+*/
+#define SICR_SDM 0xffff0000 /* SIA Diagnostics Mode */
+#define SICR_OE57 0x00008000 /* Output Enable 5 6 7 */
+#define SICR_OE24 0x00004000 /* Output Enable 2 4 */
+#define SICR_OE13 0x00002000 /* Output Enable 1 3 */
+#define SICR_IE 0x00001000 /* Input Enable */
+#define SICR_EXT 0x00000000 /* SIA MUX Select External SIA Mode */
+#define SICR_D_SIA 0x00000400 /* SIA MUX Select Diagnostics - SIA Sigs */
+#define SICR_DPLL 0x00000800 /* SIA MUX Select Diagnostics - DPLL Sigs*/
+#define SICR_APLL 0x00000a00 /* SIA MUX Select Diagnostics - DPLL Sigs*/
+#define SICR_D_RxM 0x00000c00 /* SIA MUX Select Diagnostics - RxM Sigs */
+#define SICR_M_RxM 0x00000d00 /* SIA MUX Select Diagnostics - RxM Sigs */
+#define SICR_LNKT 0x00000e00 /* SIA MUX Select Diagnostics - Link Test*/
+#define SICR_SEL 0x00000f00 /* SIA MUX Select AUI or TP with LEDs */
+#define SICR_ASE 0x00000080 /* APLL Start Enable*/
+#define SICR_SIM 0x00000040 /* Serial Interface Input Multiplexer */
+#define SICR_ENI 0x00000020 /* Encoder Input Multiplexer */
+#define SICR_EDP 0x00000010 /* SIA PLL External Input Enable */
+#define SICR_AUI 0x00000008 /* 10Base-T or AUI */
+#define SICR_CAC 0x00000004 /* CSR Auto Configuration */
+#define SICR_PS 0x00000002 /* Pin AUI/TP Selection */
+#define SICR_SRL 0x00000001 /* SIA Reset */
+#define SICR_RESET 0xffff0000 /* Reset value for SICR */
+
+/*
+** DC21040 SIA Transmit and Receive Register (DE4X5_STRR)
+*/
+#define STRR_TAS 0x00008000 /* 10Base-T/AUI Autosensing Enable */
+#define STRR_SPP 0x00004000 /* Set Polarity Plus */
+#define STRR_APE 0x00002000 /* Auto Polarity Enable */
+#define STRR_LTE 0x00001000 /* Link Test Enable */
+#define STRR_SQE 0x00000800 /* Signal Quality Enable */
+#define STRR_CLD 0x00000400 /* Collision Detect Enable */
+#define STRR_CSQ 0x00000200 /* Collision Squelch Enable */
+#define STRR_RSQ 0x00000100 /* Receive Squelch Enable */
+#define STRR_ANE 0x00000080 /* Auto Negotiate Enable */
+#define STRR_HDE 0x00000040 /* Half Duplex Enable */
+#define STRR_CPEN 0x00000030 /* Compensation Enable */
+#define STRR_LSE 0x00000008 /* Link Pulse Send Enable */
+#define STRR_DREN 0x00000004 /* Driver Enable */
+#define STRR_LBK 0x00000002 /* Loopback Enable */
+#define STRR_ECEN 0x00000001 /* Encoder Enable */
+#define STRR_RESET 0xffffffff /* Reset value for STRR */
+
+/*
+** DC21040 SIA General Register (DE4X5_SIGR)
+*/
+#define SIGR_LV2 0x00008000 /* General Purpose LED2 value */
+#define SIGR_LE2 0x00004000 /* General Purpose LED2 enable */
+#define SIGR_FRL 0x00002000 /* Force Receiver Low */
+#define SIGR_DPST 0x00001000 /* PLL Self Test Start */
+#define SIGR_LSD 0x00000800 /* LED Stretch Disable */
+#define SIGR_FLF 0x00000400 /* Force Link Fail */
+#define SIGR_FUSQ 0x00000200 /* Force Unsquelch */
+#define SIGR_TSCK 0x00000100 /* Test Clock */
+#define SIGR_LV1 0x00000080 /* General Purpose LED1 value */
+#define SIGR_LE1 0x00000040 /* General Purpose LED1 enable */
+#define SIGR_RWR 0x00000020 /* Receive Watchdog Release */
+#define SIGR_RWD 0x00000010 /* Receive Watchdog Disable */
+#define SIGR_ABM 0x00000008 /* BNC: 0, AUI:1 */
+#define SIGR_JCK 0x00000004 /* Jabber Clock */
+#define SIGR_HUJ 0x00000002 /* Host Unjab */
+#define SIGR_JBD 0x00000001 /* Jabber Disable */
+#define SIGR_RESET 0xffff0000 /* Reset value for SIGR */
+
+/*
+** Receive Descriptor Bit Summary
+*/
+#define R_OWN 0x80000000 /* Own Bit */
+#define RD_FL 0x7fff0000 /* Frame Length */
+#define RD_ES 0x00008000 /* Error Summary */
+#define RD_LE 0x00004000 /* Length Error */
+#define RD_DT 0x00003000 /* Data Type */
+#define RD_RF 0x00000800 /* Runt Frame */
+#define RD_MF 0x00000400 /* Multicast Frame */
+#define RD_FS 0x00000200 /* First Descriptor */
+#define RD_LS 0x00000100 /* Last Descriptor */
+#define RD_TL 0x00000080 /* Frame Too Long */
+#define RD_CS 0x00000040 /* Collision Seen */
+#define RD_FT 0x00000020 /* Frame Type */
+#define RD_RJ 0x00000010 /* Receive Watchdog */
+#define RD_DB 0x00000004 /* Dribbling Bit */
+#define RD_CE 0x00000002 /* CRC Error */
+#define RD_OF 0x00000001 /* Overflow */
+
+#define RD_RER 0x02000000 /* Receive End Of Ring */
+#define RD_RCH 0x01000000 /* Second Address Chained */
+#define RD_RBS2 0x003ff800 /* Buffer 2 Size */
+#define RD_RBS1 0x000007ff /* Buffer 1 Size */
+
+/*
+** Transmit Descriptor Bit Summary
+*/
+#define T_OWN 0x80000000 /* Own Bit */
+#define TD_ES 0x00008000 /* Error Summary */
+#define TD_TO 0x00004000 /* Transmit Jabber Time-Out */
+#define TD_LO 0x00000800 /* Loss Of Carrier */
+#define TD_NC 0x00000400 /* No Carrier */
+#define TD_LC 0x00000200 /* Late Collision */
+#define TD_EC 0x00000100 /* Excessive Collisions */
+#define TD_HF 0x00000080 /* Heartbeat Fail */
+#define TD_CC 0x00000078 /* Collision Counter */
+#define TD_LF 0x00000004 /* Link Fail */
+#define TD_UF 0x00000002 /* Underflow Error */
+#define TD_DE 0x00000001 /* Deferred */
+
+#define TD_IC 0x80000000 /* Interrupt On Completion */
+#define TD_LS 0x40000000 /* Last Segment */
+#define TD_FS 0x20000000 /* First Segment */
+#define TD_FT1 0x10000000 /* Filtering Type */
+#define TD_SET 0x08000000 /* Setup Packet */
+#define TD_AC 0x04000000 /* Add CRC Disable */
+#define TD_TER 0x02000000 /* Transmit End Of Ring */
+#define TD_TCH 0x01000000 /* Second Address Chained */
+#define TD_DPD 0x00800000 /* Disabled Padding */
+#define TD_FT0 0x00400000 /* Filtering Type */
+#define TD_RBS2 0x003ff800 /* Buffer 2 Size */
+#define TD_RBS1 0x000007ff /* Buffer 1 Size */
+
+#define PERFECT_F 0x00000000
+#define HASH_F TD_FT0
+#define INVERSE_F TD_FT1
+#define HASH_O_F TD_FT1| TD_F0
+
+/*
+** Media / mode state machine definitions
+*/
+#define NC 0x0000 /* No Connection */
+#define TP 0x0001 /* 10Base-T */
+#define TP_NW 0x0002 /* 10Base-T with Nway */
+#define BNC 0x0004 /* Thinwire */
+#define AUI 0x0008 /* Thickwire */
+#define BNC_AUI 0x0010 /* BNC/AUI on DC21040 indistinguishable */
+#define ANS 0x0020 /* Intermediate AutoNegotiation State */
+
+#define _10Mb 0x0040 /* 10Mb/s Ethernet */
+#define _100Mb 0x0080 /* 100Mb/s Ethernet */
+#define SYM_WAIT 0x0100 /* Wait for SYM_LINK */
+#define INIT 0x0200 /* Initial state */
+
+#define AUTO 0x4000 /* Auto sense the media or speed */
+
+/*
+** Miscellaneous
+*/
+#define PCI 0
+#define EISA 1
+
+#define HASH_TABLE_LEN 512 /* Bits */
+#define HASH_BITS 0x01ff /* 9 LS bits */
+
+#define SETUP_FRAME_LEN 192 /* Bytes */
+#define IMPERF_PA_OFFSET 156 /* Bytes */
+
+#define POLL_DEMAND 1
+
+#define LOST_MEDIA_THRESHOLD 3
+
+#define MASK_INTERRUPTS 1
+#define UNMASK_INTERRUPTS 0
+
+#define DE4X5_STRLEN 8
+
+/*
+** Address Filtering Modes
+*/
+#define PERFECT 0 /* 16 perfect physical addresses */
+#define HASH_PERF 1 /* 1 perfect, 512 multicast addresses */
+#define PERFECT_REJ 2 /* Reject 16 perfect physical addresses */
+#define ALL_HASH 3 /* Hashes all physical & multicast addrs */
+
+#define ALL 0 /* Clear out all the setup frame */
+#define PHYS_ADDR_ONLY 1 /* Update the physical address only */
+
+/*
+** Booleans
+*/
+#define NO 0
+#define FALSE 0
+
+#define YES !0
+#define TRUE !0
+
+/*
+** Include the IOCTL stuff
+*/
+#include <linux/sockios.h>
+
+#define DE4X5IOCTL SIOCDEVPRIVATE
+
+struct de4x5_ioctl {
+ unsigned short cmd; /* Command to run */
+ unsigned short len; /* Length of the data buffer */
+ unsigned char *data; /* Pointer to the data buffer */
+};
+
+/*
+** Recognised commands for the driver
+*/
+#define DE4X5_GET_HWADDR 0x01 /* Get the hardware address */
+#define DE4X5_SET_HWADDR 0x02 /* Get the hardware address */
+#define DE4X5_SET_PROM 0x03 /* Set Promiscuous Mode */
+#define DE4X5_CLR_PROM 0x04 /* Clear Promiscuous Mode */
+#define DE4X5_SAY_BOO 0x05 /* Say "Boo!" to the kernel log file */
+#define DE4X5_GET_MCA 0x06 /* Get a multicast address */
+#define DE4X5_SET_MCA 0x07 /* Set a multicast address */
+#define DE4X5_CLR_MCA 0x08 /* Clear a multicast address */
+#define DE4X5_MCA_EN 0x09 /* Enable a multicast address group */
+#define DE4X5_GET_STATS 0x0a /* Get the driver statistics */
+#define DE4X5_CLR_STATS 0x0b /* Zero out the driver statistics */
+#define DE4X5_GET_OMR 0x0c /* Get the OMR Register contents */
+#define DE4X5_SET_OMR 0x0d /* Set the OMR Register contents */
+#define DE4X5_GET_REG 0x0e /* Get the DE4X5 Registers */
+
+
+
diff --git a/drivers/net/de600.c b/drivers/net/de600.c
index a8867c2d9..1c56ea203 100644
--- a/drivers/net/de600.c
+++ b/drivers/net/de600.c
@@ -90,13 +90,18 @@ static char *version =
#endif
unsigned int de600_debug = DE600_DEBUG;
-#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/string.h>
#include <linux/interrupt.h>
+#include <linux/ioport.h>
#include <asm/io.h>
#include <linux/in.h>
#include <linux/ptrace.h>
@@ -108,14 +113,9 @@ unsigned int de600_debug = DE600_DEBUG;
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
-#ifdef MODULE
-#include <linux/module.h>
-#include "../../tools/version.h"
-#endif
-
#ifdef FAKE_SMALL_MAX
static unsigned long de600_rspace(struct sock *sk);
-#include "../../net/inet/sock.h"
+#include <net/sock.h>
#endif
#define netstats enet_statistics
@@ -250,7 +250,7 @@ static struct netstats *get_stats(struct device *dev);
static int de600_start_xmit(struct sk_buff *skb, struct device *dev);
/* Dispatch from interrupts. */
-static void de600_interrupt(int reg_ptr);
+static void de600_interrupt(int irq, struct pt_regs *regs);
static int de600_tx_intr(struct device *dev, int irq_status);
static void de600_rx_intr(struct device *dev);
@@ -262,7 +262,6 @@ static int adapter_init(struct device *dev);
/*
* D-Link driver variables:
*/
-extern struct device *irq2dev_map[16];
static volatile int rx_page = 0;
#define TX_PAGES 2
@@ -496,9 +495,8 @@ de600_start_xmit(struct sk_buff *skb, struct device *dev)
* Handle the network interface interrupts.
*/
static void
-de600_interrupt(int reg_ptr)
+de600_interrupt(int irq, struct pt_regs * regs)
{
- int irq = pt_regs2irq(reg_ptr);
struct device *dev = irq2dev_map[irq];
byte irq_status;
int retrig = 0;
@@ -631,6 +629,7 @@ de600_rx_intr(struct device *dev)
((struct netstats *)(dev->priv))->rx_packets++; /* count all receives */
+ skb->protocol=eth_type_trans(skb,dev);
if (dev_rint((unsigned char *)skb, size, IN_SKBUFF, dev))
printk("%s: receive buffers full.\n", dev->name);
/*
@@ -688,6 +687,14 @@ de600_probe(struct device *dev)
return ENODEV;
}
+#if 0 /* Not yet */
+ if (check_region(DE600_IO, 3)) {
+ printk(", port 0x%x busy\n", DE600_IO);
+ return EBUSY;
+ }
+#endif
+ request_region(DE600_IO, 3, "de600");
+
printk(", Ethernet Address: %02X", dev->dev_addr[0]);
for (i = 1; i < ETH_ALEN; i++)
printk(":%02X",dev->dev_addr[i]);
@@ -836,9 +843,7 @@ init_module(void)
void
cleanup_module(void)
{
- if (MOD_IN_USE)
- printk("de600: device busy, remove delayed\n");
- else
- unregister_netdev(&de600_dev);
+ unregister_netdev(&de600_dev);
+ release_region(DE600_IO, 3);
}
#endif /* MODULE */
diff --git a/drivers/net/de620.c b/drivers/net/de620.c
index 28124eb3c..68f85e33c 100644
--- a/drivers/net/de620.c
+++ b/drivers/net/de620.c
@@ -100,14 +100,26 @@ static char *version =
#define COUNT_LOOPS
*/
#endif
+static int bnc, utp;
+/*
+ * Force media with insmod:
+ * insmod de620.o bnc=1
+ * or
+ * insmod de620.o utp=1
+ */
-#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/string.h>
#include <linux/interrupt.h>
+#include <linux/ioport.h>
#include <asm/io.h>
#include <linux/in.h>
#include <linux/ptrace.h>
@@ -119,11 +131,6 @@ static char *version =
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
-#ifdef MODULE
-#include <linux/module.h>
-#include "../../tools/version.h"
-#endif
-
/* Constant definitions for the DE-620 registers, commands and bits */
#include "de620.h"
@@ -176,7 +183,7 @@ static void de620_set_multicast_list(struct device *, int, void *);
static int de620_start_xmit(struct sk_buff *, struct device *);
/* Dispatch from interrupts. */
-static void de620_interrupt(int);
+static void de620_interrupt(int, struct pt_regs *);
static int de620_rx_intr(struct device *);
/* Initialization */
@@ -193,7 +200,6 @@ static int read_eeprom(void);
#define DE620_RX_START_PAGE 12 /* 12 pages (=3k) reserved for tx */
#define DEF_NIC_CMD IRQEN | ICEN | DS1
-extern struct device *irq2dev_map[16];
unsigned int de620_debug = DE620_DEBUG;
static volatile byte NIC_Cmd;
@@ -566,9 +572,8 @@ de620_start_xmit(struct sk_buff *skb, struct device *dev)
*
*/
static void
-de620_interrupt(int reg_ptr)
+de620_interrupt(int irq, struct pt_regs *regs)
{
- int irq = pt_regs2irq(reg_ptr);
struct device *dev = irq2dev_map[irq];
byte irq_status;
int bogus_count = 0;
@@ -686,6 +691,7 @@ de620_rx_intr(struct device *dev)
/* copy the packet into the buffer */
de620_read_block(buffer, size);
PRINTK(("Read %d bytes\n", size));
+ skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb); /* deliver it "upstairs" */
/* count all receives */
((struct netstats *)(dev->priv))->rx_packets++;
@@ -721,6 +727,11 @@ adapter_init(struct device *dev)
EIPRegister = NCTL0 | NIS0;
}
+ if (utp)
+ EIPRegister = NCTL0 | NIS0;
+ if (bnc)
+ EIPRegister = NCTL0;
+
de620_send_command(W_CR | RNOP | CLEAR);
de620_send_command(W_CR | RNOP);
@@ -814,6 +825,14 @@ de620_probe(struct device *dev)
return ENODEV;
}
+#if 0 /* Not yet */
+ if (check_region(DE620_IO, 3)) {
+ printk(", port 0x%x busy\n", DE620_IO);
+ return EBUSY;
+ }
+#endif
+ request_region(DE620_IO, 3, "de620");
+
/* else, got it! */
printk(", Ethernet Address: %2.2X",
dev->dev_addr[0] = nic_data.NodeID[0]);
@@ -971,10 +990,8 @@ init_module(void)
void
cleanup_module(void)
{
- if (MOD_IN_USE)
- printk("de620: device busy, remove delayed\n");
- else
- unregister_netdev(&de620_dev);
+ unregister_netdev(&de620_dev);
+ release_region(DE620_IO, 3);
}
#endif /* MODULE */
diff --git a/drivers/net/depca.c b/drivers/net/depca.c
index c3fdccaf6..354eab0c3 100644
--- a/drivers/net/depca.c
+++ b/drivers/net/depca.c
@@ -17,6 +17,7 @@
DEPCA (the original)
DE100
+ DE101
DE200 Turbo
DE201 Turbo
DE202 Turbo (TP BNC)
@@ -137,6 +138,11 @@
To unload a module, turn off the associated interface
'ifconfig eth?? down' then 'rmmod depca'.
+ [Alan Cox: Changed to split off the module values as ints for insmod
+
+ you can now do insmod depca.c irq=7 io=0x200 ]
+
+
TO DO:
------
@@ -147,29 +153,41 @@
Version Date Description
- 0.1 25-jan-94 Initial writing.
- 0.2 27-jan-94 Added LANCE TX hardware buffer chaining.
- 0.3 1-feb-94 Added multiple DEPCA support.
- 0.31 4-feb-94 Added DE202 recognition.
- 0.32 19-feb-94 Tidy up. Improve multi-DEPCA support.
- 0.33 25-feb-94 Fix DEPCA ethernet ROM counter enable.
+ 0.1 25-jan-94 Initial writing.
+ 0.2 27-jan-94 Added LANCE TX hardware buffer chaining.
+ 0.3 1-feb-94 Added multiple DEPCA support.
+ 0.31 4-feb-94 Added DE202 recognition.
+ 0.32 19-feb-94 Tidy up. Improve multi-DEPCA support.
+ 0.33 25-feb-94 Fix DEPCA ethernet ROM counter enable.
Add jabber packet fix from murf@perftech.com
and becker@super.org
- 0.34 7-mar-94 Fix DEPCA max network memory RAM & NICSR access.
- 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
- 0.38 15-aug-94 Added DBR ROM switch in depca_close().
+ 0.34 7-mar-94 Fix DEPCA max network memory RAM & NICSR access.
+ 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
+ 0.38 15-aug-94 Added DBR ROM switch in depca_close().
Multi DEPCA bug fix.
+ 0.38axp 15-sep-94 Special version for Alpha AXP Linux V1.0.
+ 0.381 12-dec-94 Added DE101 recognition, fix multicast bug.
+ 0.382 9-feb-95 Fix recognition bug reported by <bkm@star.rl.ac.uk>.
+ 0.383 22-feb-95 Fix for conflict with VESA SCSI reported by
+ <stromain@alf.dec.com>
=========================================================================
*/
-static char *version = "depca.c:v0.38 8/15/94 davies@wanton.lkg.dec.com\n";
+static char *version = "depca.c:v0.383 2/22/94 davies@wanton.lkg.dec.com\n";
-#include <stdarg.h>
#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif /* MODULE */
+
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
@@ -186,11 +204,6 @@ static char *version = "depca.c:v0.38 8/15/94 davies@wanton.lkg.dec.com\n";
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
-#ifdef MODULE
-#include <linux/module.h>
-#include "/linux/tools/version.h"
-#endif /* MODULE */
-
#include "depca.h"
#ifdef DEPCA_DEBUG
@@ -203,13 +216,13 @@ static int depca_debug = 1;
#define PROBE_LENGTH 32
#endif
-#ifndef PROBE_SEQUENCE
-#define PROBE_SEQUENCE "FF0055AAFF0055AA"
-#endif
+#define ETH_PROM_SIG 0xAA5500FFUL
#ifndef DEPCA_SIGNATURE
-#define DEPCA_SIGNATURE {"DEPCA","DE100",\
- "DE200","DE201","DE202","DE210",\
+#define DEPCA_SIGNATURE {"DEPCA",\
+ "DE100","DE101",\
+ "DE200","DE201","DE202",\
+ "DE210",\
"DE422",\
""}
#define DEPCA_NAME_LENGTH 8
@@ -265,13 +278,13 @@ static short mem_chkd = 0; /* holds which base addrs have been */
** The DEPCA Rx and Tx ring descriptors.
*/
struct depca_rx_head {
- long base;
+ volatile long base;
short buf_length; /* This length is negative 2's complement! */
short msg_length; /* This length is "normal". */
};
struct depca_tx_head {
- long base;
+ volatile long base;
short length; /* This length is negative 2's complement! */
short misc; /* Errors and TDR info */
};
@@ -312,7 +325,7 @@ struct depca_private {
*/
static int depca_open(struct device *dev);
static int depca_start_xmit(struct sk_buff *skb, struct device *dev);
-static void depca_interrupt(int reg_ptr);
+static void depca_interrupt(int irq, struct pt_regs * regs);
static int depca_close(struct device *dev);
static struct enet_statistics *depca_get_stats(struct device *dev);
#ifdef HAVE_MULTICAST
@@ -322,15 +335,16 @@ static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
/*
** Private functions
*/
-static int depca_probe1(struct device *dev, short ioaddr);
+static int depca_probe1(struct device *dev, short ioaddr);
static void depca_init_ring(struct device *dev);
-static int depca_rx(struct device *dev);
-static int depca_tx(struct device *dev);
+static int depca_rx(struct device *dev);
+static int depca_tx(struct device *dev);
static void LoadCSRs(struct device *dev);
-static int InitRestartDepca(struct device *dev);
+static int InitRestartDepca(struct device *dev);
static char *DepcaSignature(unsigned long mem_addr);
-static int DevicePresent(short ioaddr);
+static int DevicePresent(short ioaddr);
+static int EISA_signature(short iobase);
#ifdef HAVE_MULTICAST
static void SetMulticastFilter(int num_addrs, char *addrs, char *multicast_table);
#endif
@@ -470,9 +484,7 @@ depca_probe1(struct device *dev, short ioaddr)
j=inb(DEPCA_PROM);
}
-#ifdef HAVE_PORTRESERVE
- snarf_region(ioaddr, DEPCA_TOTAL_SIZE);
-#endif
+ request_region(ioaddr, DEPCA_TOTAL_SIZE, dev->name);
/*
** Set up the maximum amount of network RAM(kB)
@@ -573,7 +585,7 @@ depca_probe1(struct device *dev, short ioaddr)
memset(lp->tx_ring, 0, sizeof(struct depca_tx_head)*j);
/* This should never happen. */
- if ((int)(lp->rx_ring) & 0x07) {
+ if ((long)(lp->rx_ring) & 0x07) {
printk("\n **ERROR** DEPCA Rx and Tx descriptor rings not on a quadword boundary.\n");
return -ENXIO;
}
@@ -753,9 +765,7 @@ depca_open(struct device *dev)
printk("nicsr: 0x%02x\n",inb(DEPCA_NICSR));
}
-#ifdef MODULE
- MOD_INC_USE_COUNT;
-#endif
+ MOD_INC_USE_COUNT;
return 0; /* Always succeed */
}
@@ -946,9 +956,8 @@ depca_start_xmit(struct sk_buff *skb, struct device *dev)
** The DEPCA interrupt handler.
*/
static void
-depca_interrupt(int reg_ptr)
+depca_interrupt(int irq, struct pt_regs * regs)
{
- int irq = pt_regs2irq(reg_ptr);
struct device *dev = (struct device *)(irq2dev_map[irq]);
struct depca_private *lp;
int csr0, ioaddr, nicsr;
@@ -1063,6 +1072,7 @@ depca_rx(struct device *dev)
** Notify the upper protocol layers that there is another
** packet to handle
*/
+ skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
lp->stats.rx_packets++;
}
@@ -1168,9 +1178,7 @@ depca_close(struct device *dev)
irq2dev_map[dev->irq] = 0;
-#ifdef MODULE
MOD_DEC_USE_COUNT;
-#endif
return 0;
}
@@ -1237,35 +1245,28 @@ depca_get_stats(struct device *dev)
** num_addrs > 0 Multicast mode, receive normal and MC packets, and do
** best-effort filtering.
*/
+#define hash_filter lp->init_block.filter
+
static void
set_multicast_list(struct device *dev, int num_addrs, void *addrs)
{
short ioaddr = dev->base_addr;
struct depca_private *lp = (struct depca_private *)dev->priv;
- /* We take the simple way out and always enable promiscuous mode. */
- STOP_DEPCA; /* Temporarily stop the depca. */
-
- lp->init_block.mode = PROM; /* Set promiscuous mode */
- if (num_addrs >= 0) {
- short multicast_table[4];
- int i;
-
- SetMulticastFilter(num_addrs, (char *)addrs, (char *)multicast_table);
+ if (irq2dev_map[dev->irq] != NULL) {
+ STOP_DEPCA; /* Temporarily stop the depca. */
+ depca_init_ring(dev); /* Initialize the descriptor rings */
- /* We don't use the multicast table, but rely on upper-layer filtering. */
- memset(multicast_table, (num_addrs==0) ? 0 : -1, sizeof(multicast_table));
-
- for (i = 0; i < 4; i++) {
- lp->init_block.filter[i] = multicast_table[i];
+ if (num_addrs >= 0) {
+ SetMulticastFilter(num_addrs, (char *)addrs, (char *)hash_filter);
+ lp->init_block.mode &= ~PROM; /* Unset promiscuous mode */
+ } else {
+ lp->init_block.mode |= PROM; /* Set promiscuous mode */
}
- lp->init_block.mode &= ~PROM; /* Unset promiscuous mode */
- } else {
- lp->init_block.mode |= PROM; /* Set promiscuous mode */
- }
- outw(CSR0, DEPCA_ADDR);
- outw(IDON|INEA|STRT, DEPCA_DATA); /* Resume normal operation. */
+ LoadCSRs(dev); /* Reload CSR3 */
+ InitRestartDepca(dev); /* Resume normal operation. */
+ }
}
/*
@@ -1349,7 +1350,7 @@ static struct device *eisa_probe(struct device *dev)
ioaddr+=0x1000; /* get the first slot address */
for (status = -ENODEV, i=1; i<MAX_EISA_SLOTS; i++, ioaddr+=0x1000) {
- if (DevicePresent(ioaddr) == 0) {
+ if (EISA_signature(DEPCA_EISA_ID) == 0) {
if (num_depcas > 0) { /* only gets here in autoprobe */
dev = alloc_device(dev, ioaddr);
} else {
@@ -1451,96 +1452,93 @@ static char *DepcaSignature(unsigned long mem_addr)
** its ROM address counter to be initialized and enabled. Only enable
** if the first address octet is a 0x08 - this minimises the chances of
** messing around with some other hardware, but it assumes that this DEPCA
-** card initialized itself correctly. It also assumes that all past and
-** future DEPCA/EtherWORKS cards will have ethernet addresses beginning with
-** a 0x08.
+** card initialized itself correctly.
+**
+** Search the Ethernet address ROM for the signature. Since the ROM address
+** counter can start at an arbitrary point, the search must include the entire
+** probe sequence length plus the (length_of_the_signature - 1).
+** Stop the search IMMEDIATELY after the signature is found so that the
+** PROM address counter is correctly positioned at the start of the
+** ethernet address for later read out.
*/
-
static int DevicePresent(short ioaddr)
{
- static short fp=1,sigLength=0;
- static char devSig[] = PROBE_SEQUENCE;
+ union {
+ struct {
+ u_long a;
+ u_long b;
+ } llsig;
+ char Sig[sizeof(long) << 1];
+ } dev;
+ short sigLength=0;
char data;
int i, j, nicsr, status = 0;
- static char asc2hex(char value);
-/*
-** Initialize the counter on a DEPCA card. Two reads to ensure DEPCA ethernet
-** address counter is a) cleared and b) the correct data read.
-*/
- data = inb(DEPCA_PROM); /* clear counter */
+ data = inb(DEPCA_PROM); /* clear counter on DEPCA */
data = inb(DEPCA_PROM); /* read data */
-/*
-** Enable counter
-*/
- if (data == 0x08) {
+ if (data == 0x08) { /* Enable counter on DEPCA */
nicsr = inb(DEPCA_NICSR);
nicsr |= AAC;
outb(nicsr, DEPCA_NICSR);
}
-/*
-** Convert the ascii signature to a hex equivalent & pack in place
-*/
- if (fp) { /* only do this once!... */
- for (i=0,j=0;devSig[i] != '\0' && !status;i+=2,j++) {
- if ((devSig[i]=asc2hex(devSig[i]))>=0) {
- devSig[i]<<=4;
- if((devSig[i+1]=asc2hex(devSig[i+1]))>=0){
- devSig[j]=devSig[i]+devSig[i+1];
- } else {
- status= -1;
- }
+ dev.llsig.a = ETH_PROM_SIG;
+ dev.llsig.b = ETH_PROM_SIG;
+ sigLength = sizeof(long) << 1;
+
+ for (i=0,j=0;j<sigLength && i<PROBE_LENGTH+sigLength-1;i++) {
+ data = inb(DEPCA_PROM);
+ if (dev.Sig[j] == data) { /* track signature */
+ j++;
+ } else { /* lost signature; begin search again */
+ if (data == dev.Sig[0]) { /* rare case.... */
+ j=1;
} else {
- status= -1;
- }
- }
- sigLength=j;
- fp = 0;
- }
-
-/*
-** Search the Ethernet address ROM for the signature. Since the ROM address
-** counter can start at an arbitrary point, the search must include the entire
-** probe sequence length plus the (length_of_the_signature - 1).
-** Stop the search IMMEDIATELY after the signature is found so that the
-** PROM address counter is correctly positioned at the start of the
-** ethernet address for later read out.
-*/
- if (!status) {
- for (i=0,j=0;j<sigLength && i<PROBE_LENGTH+sigLength-1;i++) {
- data = inb(DEPCA_PROM);
- if (devSig[j] == data) { /* track signature */
- j++;
- } else { /* lost signature; begin search again */
j=0;
}
}
+ }
- if (j!=sigLength) {
- status = -ENODEV; /* search failed */
- }
+ if (j!=sigLength) {
+ status = -ENODEV; /* search failed */
}
return status;
}
-static char asc2hex(char value)
+/*
+** Look for a particular board name in the EISA configuration space
+*/
+static int EISA_signature(short iobase)
{
- value -= 0x30; /* normalise to 0..9 range */
- if (value >= 0) {
- if (value > 9) { /* but may not be 10..15 */
- value &= 0x1f; /* make A..F & a..f be the same */
- value -= 0x07; /* normalise to 10..15 range */
- if ((value < 0x0a) || (value > 0x0f)) { /* if outside range then... */
- value = -1; /* ...signal error */
- }
+ unsigned long i;
+ int status;
+ char *signatures[] = DEPCA_SIGNATURE;
+ char ManCode[8];
+ union {
+ u_long ID;
+ u_char Id[4];
+ } Eisa;
+
+ for (i=0; i<4; i++) {
+ Eisa.Id[i] = inb(iobase + i);
+ }
+
+ ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40);
+ ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40);
+ ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30);
+ ManCode[3]=((Eisa.Id[2]&0x0f)+0x30);
+ ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30);
+ ManCode[5]='\0';
+
+ for (status = -ENXIO, i=0;*signatures[i] != '\0' && status;i++) {
+ if (strstr(ManCode, signatures[i]) != NULL) {
+ status = 0;
}
- } else { /* outside 0..9 range... */
- value = -1; /* ...signal error */
}
- return value; /* return hex char or error */
+
+ return status; /* return the device name string */
}
#ifdef MODULE
@@ -1548,12 +1546,22 @@ 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 */
+ 0x200, 7, /* I/O address, IRQ */
0, 0, 0, NULL, depca_probe };
+
+/*
+ * This is a tweak to keep the insmod program happy. It can only
+ * set int values with var=value so we split these out.
+ */
+
+int irq=7; /* EDIT THESE LINE FOR YOUR CONFIGURATION */
+int io=0x200; /* Or use the irq= io= options to insmod */
int
init_module(void)
{
+ thisDepca.irq=irq;
+ thisDepca.base_addr=io;
if (register_netdev(&thisDepca) != 0)
return -EIO;
return 0;
@@ -1565,6 +1573,7 @@ cleanup_module(void)
if (MOD_IN_USE) {
printk("%s: device busy, remove delayed\n",thisDepca.name);
} else {
+ release_region(thisDepca.base_addr, DEPCA_TOTAL_SIZE);
unregister_netdev(&thisDepca);
}
}
diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c
index c70629d73..24c410297 100644
--- a/drivers/net/dummy.c
+++ b/drivers/net/dummy.c
@@ -31,7 +31,11 @@
/* To have statistics (just packets sent) define this */
#undef DUMMY_STATS
-#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
@@ -57,8 +61,23 @@ static int dummy_xmit(struct sk_buff *skb, struct device *dev);
static struct enet_statistics *dummy_get_stats(struct device *dev);
#endif
-int
-dummy_init(struct device *dev)
+#ifdef MODULE
+static int dummy_open(struct device *dev)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int dummy_close(struct device *dev)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+#endif
+
+
+int dummy_init(struct device *dev)
{
/* I commented this out as bootup is noisy enough anyway and this driver
seems pretty reliable 8) 8) 8) */
@@ -72,9 +91,14 @@ dummy_init(struct device *dev)
memset(dev->priv, 0, sizeof(struct enet_statistics));
dev->get_stats = dummy_get_stats;
#endif
+#ifdef MODULE
+ dev->open = &dummy_open;
+ dev->stop = &dummy_close;
+#endif
/* Fill in the fields of the device structure with ethernet-generic values. */
ether_setup(dev);
+ dev->flags |= IFF_NOARP;
return 0;
}
@@ -107,3 +131,45 @@ dummy_get_stats(struct device *dev)
return stats;
}
#endif
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+
+static int dummy_probe(struct device *dev)
+{
+ dummy_init(dev);
+ return 0;
+}
+
+static struct device dev_dummy = {
+ "dummy0\0 ",
+ 0, 0, 0, 0,
+ 0x0, 0,
+ 0, 0, 0, NULL, dummy_probe };
+
+int init_module(void)
+{
+ /* Find a name for this unit */
+ int ct= 1;
+
+ while(dev_get(dev_dummy.name)!=NULL && ct<100)
+ {
+ sprintf(dev_dummy.name,"dummy%d",ct);
+ ct++;
+ }
+
+ if (register_netdev(&dev_dummy) != 0)
+ return -EIO;
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ if (MOD_IN_USE)
+ printk("dummy: device busy, remove delayed\n");
+ else
+ {
+ unregister_netdev(&dev_dummy);
+ }
+}
+#endif /* MODULE */
diff --git a/drivers/net/e2100.c b/drivers/net/e2100.c
index 42df4d1ed..94471dc4a 100644
--- a/drivers/net/e2100.c
+++ b/drivers/net/e2100.c
@@ -34,7 +34,6 @@
static char *version =
"e2100.c:v1.01 7/21/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
@@ -148,7 +147,7 @@ int e21_probe1(struct device *dev, int ioaddr)
return ENODEV;
/* Grab the region so we can find a different board if IRQ select fails. */
- snarf_region(ioaddr, E21_IO_EXTENT);
+ request_region(ioaddr, E21_IO_EXTENT,"e2100");
/* Read the station address PROM. */
for (i = 0; i < 6; i++)
diff --git a/drivers/net/eexpress.c b/drivers/net/eexpress.c
index f1e041fd5..ea7729782 100644
--- a/drivers/net/eexpress.c
+++ b/drivers/net/eexpress.c
@@ -18,7 +18,8 @@
Rework the board error reset
The statistics need to be updated correctly.
- Modularized my Pauline Middelink <middelin@polyware.iaf.nl>
+ Modularized by Pauline Middelink <middelin@polyware.iaf.nl>
+ Changed to support io= irq= by Alan Cox <Alan.Cox@linux.org>
*/
static char *version =
@@ -37,6 +38,11 @@ static char *version =
info that the casual reader might think that it documents the i82586.
*/
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
@@ -55,11 +61,6 @@ static char *version =
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
-#ifdef MODULE
-#include <linux/module.h>
-#include "../../tools/version.h"
-#endif
-
#include <linux/malloc.h>
/* use 0 for production, 1 for verification, 2..7 for debug */
@@ -289,7 +290,7 @@ extern int express_probe(struct device *dev); /* Called from Space.c */
static int eexp_probe1(struct device *dev, short ioaddr);
static int eexp_open(struct device *dev);
static int eexp_send_packet(struct sk_buff *skb, struct device *dev);
-static void eexp_interrupt(int reg_ptr);
+static void eexp_interrupt(int irq, struct pt_regs *regs);
static void eexp_rx(struct device *dev);
static int eexp_close(struct device *dev);
static struct enet_statistics *eexp_get_stats(struct device *dev);
@@ -362,7 +363,7 @@ int eexp_probe1(struct device *dev, short ioaddr)
}
/* We've committed to using the board, and can start filling in *dev. */
- snarf_region(ioaddr, 16);
+ request_region(ioaddr, 16,"eexpress");
dev->base_addr = ioaddr;
for (i = 0; i < 6; i++) {
@@ -514,9 +515,8 @@ eexp_send_packet(struct sk_buff *skb, struct device *dev)
/* The typical workload of the driver:
Handle the network interface interrupts. */
static void
-eexp_interrupt(int reg_ptr)
+eexp_interrupt(int irq, struct pt_regs *regs)
{
- int irq = pt_regs2irq(reg_ptr);
struct device *dev = (struct device *)(irq2dev_map[irq]);
struct net_local *lp;
int ioaddr, status, boguscount = 0;
@@ -654,6 +654,9 @@ eexp_close(struct device *dev)
irq2dev_map[dev->irq] = 0;
+ /* release the ioport-region */
+ release_region(ioaddr, 16);
+
/* Update the statistics here. */
#ifdef MODULE
@@ -683,6 +686,8 @@ eexp_get_stats(struct device *dev)
static void
set_multicast_list(struct device *dev, int num_addrs, void *addrs)
{
+/* This doesn't work yet */
+#if 0
short ioaddr = dev->base_addr;
if (num_addrs < 0) {
/* Not written yet, this requires expanding the init_words config
@@ -699,6 +704,7 @@ set_multicast_list(struct device *dev, int num_addrs, void *addrs)
cmd. */
outw(99, ioaddr); /* Disable promiscuous mode, use normal mode */
}
+#endif
}
/* The horrible routine to read a word from the serial EEPROM. */
@@ -948,6 +954,7 @@ eexp_rx(struct device *dev)
insw(ioaddr, skb->data, (pkt_len + 1) >> 1);
+ skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
lp->stats.rx_packets++;
}
@@ -997,10 +1004,16 @@ eexp_rx(struct device *dev)
char kernel_version[] = UTS_RELEASE;
static struct device dev_eexpress = {
" " /*"eexpress"*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, express_probe };
+
+
+int irq=0;
+int io=0;
int
init_module(void)
{
+ dev_eexpress.base_addr=io;
+ dev_eexpress.irq=irq;
if (register_netdev(&dev_eexpress) != 0)
return -EIO;
return 0;
diff --git a/drivers/net/eql.c b/drivers/net/eql.c
new file mode 100644
index 000000000..c11876cdb
--- /dev/null
+++ b/drivers/net/eql.c
@@ -0,0 +1,1153 @@
+/*
+ * Equalizer Load-balancer for serial network interfaces.
+ *
+ * (c) Copyright 1995 Simon "Guru Aleph-Null" Janes
+ * NCM: Network and Communications Mangement, Inc.
+ *
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ * The author may be reached as simon@ncm.com, or C/O
+ * NCM
+ * Attn: Simon Janes
+ * 6803 Whittier Ave
+ * McLean VA 22101
+ * Phone: 1-703-847-0040 ext 103
+ */
+
+static char *version =
+ "Equalizer: $Revision: 3.12 $ $Date: 1995/01/19 $ Simon Janes (simon@ncm.com)\n";
+
+#include <linux/config.h>
+
+/*
+ * Sources:
+ * skeleton.c by Donald Becker.
+ * Inspirations:
+ * The Harried and Overworked Alan Cox
+ * Conspiracies:
+ * The Alan Cox and Arisian plot to get someone else to do the code, which
+ * turned out to be me.
+ */
+
+/*
+ * $Log: eql.c,v $
+ * Revision 3.12 1995/03/22 21:07:51 anarchy
+ * Added suser() checks on configuration.
+ * Moved header file.
+ *
+ * Revision 3.11 1995/01/19 23:14:31 guru
+ * slave_load = (ULONG_MAX - (ULONG_MAX / 2)) -
+ * (priority_Bps) + bytes_queued * 8;
+ *
+ * Revision 3.10 1995/01/19 23:07:53 guru
+ * back to
+ * slave_load = (ULONG_MAX - (ULONG_MAX / 2)) -
+ * (priority_Bps) + bytes_queued;
+ *
+ * Revision 3.9 1995/01/19 22:38:20 guru
+ * slave_load = (ULONG_MAX - (ULONG_MAX / 2)) -
+ * (priority_Bps) + bytes_queued * 4;
+ *
+ * Revision 3.8 1995/01/19 22:30:55 guru
+ * slave_load = (ULONG_MAX - (ULONG_MAX / 2)) -
+ * (priority_Bps) + bytes_queued * 2;
+ *
+ * Revision 3.7 1995/01/19 21:52:35 guru
+ * printk's trimmed out.
+ *
+ * Revision 3.6 1995/01/19 21:49:56 guru
+ * This is working pretty well. I gained 1 K/s in speed.. now its just
+ * robustness and printk's to be diked out.
+ *
+ * Revision 3.5 1995/01/18 22:29:59 guru
+ * still crashes the kernel when the lock_wait thing is woken up.
+ *
+ * Revision 3.4 1995/01/18 21:59:47 guru
+ * Broken set-bit locking snapshot
+ *
+ * Revision 3.3 1995/01/17 22:09:18 guru
+ * infinite sleep in a lock somewhere..
+ *
+ * Revision 3.2 1995/01/15 16:46:06 guru
+ * Log trimmed of non-pertinant 1.x branch messages
+ *
+ * Revision 3.1 1995/01/15 14:41:45 guru
+ * New Scheduler and timer stuff...
+ *
+ * Revision 1.15 1995/01/15 14:29:02 guru
+ * Will make 1.14 (now 1.15) the 3.0 branch, and the 1.12 the 2.0 branch, the one
+ * with the dumber scheduler
+ *
+ * Revision 1.14 1995/01/15 02:37:08 guru
+ * shock.. the kept-new-versions could have zonked working
+ * stuff.. shudder
+ *
+ * Revision 1.13 1995/01/15 02:36:31 guru
+ * big changes
+ *
+ * scheduler was torn out and replaced with something smarter
+ *
+ * global names not prefixed with eql_ were renamed to protect
+ * against namespace collisions
+ *
+ * a few more abstract interfaces were added to facilitate any
+ * potential change of datastructure. the driver is still using
+ * a linked list of slaves. going to a heap would be a bit of
+ * an overkill.
+ *
+ * this compiles fine with no warnings.
+ *
+ * the locking mechanism and timer stuff must be written however,
+ * this version will not work otherwise
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/if.h>
+#include <linux/timer.h>
+
+#include <linux/if_eql.h>
+
+#ifndef EQL_DEBUG
+/* #undef EQL_DEBUG -* print nothing at all, not even a boot-banner */
+/* #define EQL_DEBUG 1 -* print only the boot-banner */
+/* #define EQL_DEBUG 5 -* print major function entries */
+/* #define EQL_DEBUG 20 -* print subfunction entries */
+/* #define EQL_DEBUG 50 -* print utility entries */
+/* #define EQL_DEBUG 100 -* print voluminous function entries */
+#define EQL_DEBUG 1
+#endif
+static unsigned int eql_debug = EQL_DEBUG;
+
+int eql_init(struct device *dev); /* */
+static int eql_open(struct device *dev); /* */
+static int eql_close(struct device *dev); /* */
+static int eql_ioctl(struct device *dev, struct ifreq *ifr, int cmd); /* */
+static int eql_slave_xmit(struct sk_buff *skb, struct device *dev); /* */
+
+static struct enet_statistics *eql_get_stats(struct device *dev); /* */
+static int eql_header(unsigned char *buff, struct device *dev,
+ unsigned short type, void *daddr, void *saddr,
+ unsigned len, struct sk_buff *skb); /* */
+static int eql_rebuild_header(void *buff, struct device *dev,
+ unsigned long raddr, struct sk_buff *skb); /* */
+static unsigned short eql_type_trans (struct sk_buff *skb,
+ struct device *dev); /* */
+
+/* ioctl() handlers
+ ---------------- */
+static int eql_enslave(struct device *dev, slaving_request_t *srq); /* */
+static int eql_emancipate(struct device *dev, slaving_request_t *srq); /* */
+
+static int eql_g_slave_cfg(struct device *dev, slave_config_t *sc); /* */
+static int eql_s_slave_cfg(struct device *dev, slave_config_t *sc); /* */
+
+static int eql_g_master_cfg(struct device *dev, master_config_t *mc); /* */
+static int eql_s_master_cfg(struct device *dev, master_config_t *mc); /* */
+
+static inline int eql_is_slave(struct device *dev); /* */
+static inline int eql_is_master(struct device *dev); /* */
+
+static slave_t *eql_new_slave(void); /* */
+static void eql_delete_slave(slave_t *slave); /* */
+
+/* static long eql_slave_priority(slave_t *slave); -* */
+static inline int eql_number_slaves(slave_queue_t *queue); /* */
+
+static inline int eql_is_empty(slave_queue_t *queue); /* */
+static inline int eql_is_full(slave_queue_t *queue); /* */
+
+static slave_queue_t *eql_new_slave_queue(struct device *dev); /* */
+static void eql_delete_slave_queue(slave_queue_t *queue); /* */
+
+static int eql_insert_slave(slave_queue_t *queue, slave_t *slave); /* */
+static slave_t *eql_remove_slave(slave_queue_t *queue, slave_t *slave); /* */
+
+/* static int eql_insert_slave_dev(slave_queue_t *queue, struct device *dev); -* */
+static int eql_remove_slave_dev(slave_queue_t *queue, struct device *dev); /* */
+
+static inline struct device *eql_best_slave_dev(slave_queue_t *queue); /* */
+static inline slave_t *eql_best_slave(slave_queue_t *queue); /* */
+static inline slave_t *eql_first_slave(slave_queue_t *queue); /* */
+static inline slave_t *eql_next_slave(slave_queue_t *queue, slave_t *slave); /* */
+
+static inline void eql_set_best_slave(slave_queue_t *queue, slave_t *slave); /* */
+static inline void eql_schedule_slaves(slave_queue_t *queue); /* */
+
+static slave_t *eql_find_slave_dev(slave_queue_t *queue, struct device *dev); /* */
+
+/* static inline eql_lock_slave_queue(slave_queue_t *queue); -* */
+/* static inline eql_unlock_slave_queue(slave_queue_t *queue); -* */
+
+static void eql_timer(unsigned long param); /* */
+
+/* struct device * interface functions
+ ---------------------------------------------------------
+ */
+
+int
+eql_init(struct device *dev)
+{
+ static unsigned version_printed = 0;
+ /* static unsigned num_masters = 0; */
+ equalizer_t *eql = 0;
+ int i;
+
+ if ( version_printed++ == 0 && eql_debug > 0)
+ printk(version);
+
+ /* Initialize the device structure. */
+ dev->priv = kmalloc (sizeof (equalizer_t), GFP_KERNEL);
+ memset (dev->priv, 0, sizeof (equalizer_t));
+ eql = (equalizer_t *) dev->priv;
+
+ eql->stats = kmalloc (sizeof (struct enet_statistics), GFP_KERNEL);
+ memset (eql->stats, 0, sizeof (struct enet_statistics));
+
+ init_timer (&eql->timer);
+ eql->timer.data = (unsigned long) dev->priv;
+ eql->timer.expires = EQL_DEFAULT_RESCHED_IVAL;
+ eql->timer.function = &eql_timer;
+ eql->timer_on = 0;
+
+ dev->open = eql_open;
+ dev->stop = eql_close;
+ dev->do_ioctl = eql_ioctl;
+ dev->hard_start_xmit = eql_slave_xmit;
+ dev->get_stats = eql_get_stats;
+
+ /* Fill in the fields of the device structure with ethernet-generic values.
+ This should be in a common file instead of per-driver. */
+
+ for (i = 0; i < DEV_NUMBUFFS; i++)
+ skb_queue_head_init(&dev->buffs[i]);
+
+ dev->hard_header = eql_header;
+ dev->rebuild_header = eql_rebuild_header;
+
+ /* now we undo some of the things that eth_setup does that we don't like */
+ dev->mtu = EQL_DEFAULT_MTU; /* set to 576 in eql.h */
+ dev->flags = IFF_MASTER;
+
+ dev->family = AF_INET;
+ dev->pa_addr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ dev->pa_alen = sizeof (unsigned long);
+
+ dev->type = ARPHRD_SLIP;
+
+ return 0;
+}
+
+
+static
+int
+eql_open(struct device *dev)
+{
+ equalizer_t *eql = (equalizer_t *) dev->priv;
+ slave_queue_t *new_queue;
+
+#ifdef EQL_DEBUG
+ if (eql_debug >= 5)
+ printk ("%s: open\n", dev->name);
+#endif
+
+ new_queue = eql_new_slave_queue (dev);
+
+ if (new_queue != 0)
+ {
+ new_queue->master_dev = dev;
+ eql->queue = new_queue;
+ eql->queue->lock = 0;
+ eql->min_slaves = 1;
+ eql->max_slaves = EQL_DEFAULT_MAX_SLAVES; /* 4 usually... */
+
+ printk ("%s: adding timer\n", dev->name);
+ eql->timer_on = 1;
+ add_timer (&eql->timer);
+
+ return 0;
+ }
+ return 1;
+}
+
+
+static
+int
+eql_close(struct device *dev)
+{
+ equalizer_t *eql = (equalizer_t *) dev->priv;
+
+#ifdef EQL_DEBUG
+ if ( eql_debug >= 5)
+ printk ("%s: close\n", dev->name);
+#endif
+ /* The timer has to be stopped first before we start hacking away
+ at the data structure it scans every so often... */
+ printk ("%s: stopping timer\n", dev->name);
+ eql->timer_on = 0;
+ del_timer (&eql->timer);
+
+ eql_delete_slave_queue (eql->queue);
+
+ return 0;
+}
+
+
+static
+int
+eql_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+ if(!suser() && cmd!=EQL_GETMASTRCFG && cmd!=EQL_GETSLAVECFG)
+ return -EPERM;
+ switch (cmd)
+ {
+ case EQL_ENSLAVE:
+ return eql_enslave (dev, (slaving_request_t *) ifr->ifr_data);
+ case EQL_EMANCIPATE:
+ return eql_emancipate (dev, (slaving_request_t *) ifr->ifr_data);
+
+ case EQL_GETSLAVECFG:
+ return eql_g_slave_cfg (dev, (slave_config_t *) ifr->ifr_data);
+ case EQL_SETSLAVECFG:
+ return eql_s_slave_cfg (dev, (slave_config_t *) ifr->ifr_data);
+
+ case EQL_GETMASTRCFG:
+ return eql_g_master_cfg (dev, (master_config_t *) ifr->ifr_data);
+ case EQL_SETMASTRCFG:
+ return eql_s_master_cfg (dev, (master_config_t *) ifr->ifr_data);
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+
+static
+int
+eql_slave_xmit(struct sk_buff *skb, struct device *dev)
+{
+ equalizer_t *eql = (equalizer_t *) dev->priv;
+ struct device *slave_dev = 0;
+ slave_t *slave;
+
+ if (skb == NULL)
+ {
+ return 0;
+ }
+
+ eql_schedule_slaves (eql->queue);
+
+ slave_dev = eql_best_slave_dev (eql->queue);
+ slave = eql_best_slave (eql->queue);
+
+ if ( slave_dev != 0 )
+ {
+#ifdef EQL_DEBUG
+ if (eql_debug >= 100)
+ printk ("%s: %d slaves xmitng %ld B %s\n",
+ dev->name, eql_number_slaves (eql->queue), skb->len,
+ slave_dev->name);
+#endif
+
+ dev_queue_xmit (skb, slave_dev, 1);
+ eql->stats->tx_packets++;
+ slave->bytes_queued += skb->len;
+ }
+ else
+ {
+ /* The alternative for this is the return 1 and have
+ dev_queue_xmit just queue it up on the eql's queue. */
+
+ eql->stats->tx_dropped++;
+ dev_kfree_skb(skb, FREE_WRITE);
+ }
+ return 0;
+}
+
+
+static
+struct enet_statistics *
+eql_get_stats(struct device *dev)
+{
+ equalizer_t *eql = (equalizer_t *) dev->priv;
+
+ return eql->stats;
+}
+
+
+static
+int
+eql_header(unsigned char *buff, struct device *dev,
+ unsigned short type, void *daddr, void *saddr,
+ unsigned len, struct sk_buff *skb)
+{
+ return 0;
+}
+
+
+static
+int
+eql_rebuild_header(void *buff, struct device *dev,
+ unsigned long raddr, struct sk_buff *skb)
+{
+ return 0;
+}
+
+
+static
+unsigned short
+eql_type_trans (struct sk_buff *skb, struct device *dev)
+{
+ return htons (ETH_P_IP);
+}
+
+
+/* private ioctl functions
+ -----------------------------------------------------------------
+ */
+
+static int
+eql_enslave(struct device *dev, slaving_request_t *srqp)
+{
+ struct device *master_dev;
+ struct device *slave_dev;
+ slaving_request_t srq;
+
+ memcpy_fromfs (&srq, srqp, sizeof (slaving_request_t));
+
+#ifdef EQL_DEBUG
+ if (eql_debug >= 20)
+ printk ("%s: enslave '%s' %ld bps\n", dev->name,
+ srq.slave_name, srq.priority);
+#endif
+
+ master_dev = dev; /* for "clarity" */
+ slave_dev = dev_get (srq.slave_name);
+
+ if (master_dev != 0 && slave_dev != 0)
+ {
+ if (! eql_is_master (slave_dev) && /* slave is not a master */
+ ! eql_is_slave (slave_dev) ) /* slave is not already a slave */
+ {
+ slave_t *s = eql_new_slave ();
+ equalizer_t *eql = (equalizer_t *) master_dev->priv;
+
+ s->dev = slave_dev;
+ s->priority = srq.priority;
+ s->priority_bps = srq.priority;
+ s->priority_Bps = srq.priority / 8;
+
+ slave_dev->flags |= IFF_SLAVE;
+
+ eql_insert_slave (eql->queue, s);
+
+ return 0;
+ }
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+
+
+static
+int
+eql_emancipate(struct device *dev, slaving_request_t *srqp)
+{
+ struct device *master_dev;
+ struct device *slave_dev;
+ slaving_request_t srq;
+
+ memcpy_fromfs (&srq, srqp, sizeof (slaving_request_t));
+
+#ifdef EQL_DEBUG
+ if (eql_debug >= 20)
+ printk ("%s: emancipate `%s`\n", dev->name, srq.slave_name);
+#endif
+
+
+ master_dev = dev; /* for "clarity" */
+ slave_dev = dev_get (srq.slave_name);
+
+ if ( eql_is_slave (slave_dev) ) /* really is a slave */
+ {
+ equalizer_t *eql = (equalizer_t *) master_dev->priv;
+ slave_dev->flags = slave_dev->flags & ~IFF_SLAVE;
+
+ eql_remove_slave_dev (eql->queue, slave_dev);
+
+ return 0;
+ }
+ return -EINVAL;
+}
+
+
+static
+int
+eql_g_slave_cfg(struct device *dev, slave_config_t *scp)
+{
+ slave_t *slave;
+ equalizer_t *eql;
+ struct device *slave_dev;
+ slave_config_t sc;
+
+ memcpy_fromfs (&sc, scp, sizeof (slave_config_t));
+
+#ifdef EQL_DEBUG
+ if (eql_debug >= 20)
+ printk ("%s: get config for slave `%s'\n", dev->name, sc.slave_name);
+#endif
+
+ eql = (equalizer_t *) dev->priv;
+ slave_dev = dev_get (sc.slave_name);
+
+ if ( eql_is_slave (slave_dev) )
+ {
+ slave = eql_find_slave_dev (eql->queue, slave_dev);
+ if (slave != 0)
+ {
+ sc.priority = slave->priority;
+ memcpy_tofs (scp, &sc, sizeof (slave_config_t));
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+
+static
+int
+eql_s_slave_cfg(struct device *dev, slave_config_t *scp)
+{
+ slave_t *slave;
+ equalizer_t *eql;
+ struct device *slave_dev;
+ slave_config_t sc;
+
+#ifdef EQL_DEBUG
+ if (eql_debug >= 20)
+ printk ("%s: set config for slave `%s'\n", dev->name, sc.slave_name);
+#endif
+
+ memcpy_fromfs (&sc, scp, sizeof (slave_config_t));
+
+ eql = (equalizer_t *) dev->priv;
+ slave_dev = dev_get (sc.slave_name);
+
+ if ( eql_is_slave (slave_dev) )
+ {
+ slave = eql_find_slave_dev (eql->queue, slave_dev);
+ if (slave != 0)
+ {
+ slave->priority = sc.priority;
+ slave->priority_bps = sc.priority;
+ slave->priority_Bps = sc.priority / 8;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+
+static
+int
+eql_g_master_cfg(struct device *dev, master_config_t *mcp)
+{
+ equalizer_t *eql;
+ master_config_t mc;
+
+#if EQL_DEBUG
+ if (eql_debug >= 20)
+ printk ("%s: get master config\n", dev->name);
+#endif
+
+ if ( eql_is_master (dev) )
+ {
+ eql = (equalizer_t *) dev->priv;
+ mc.max_slaves = eql->max_slaves;
+ mc.min_slaves = eql->min_slaves;
+ memcpy_tofs (mcp, &mc, sizeof (master_config_t));
+ return 0;
+ }
+ return -EINVAL;
+}
+
+
+static
+int
+eql_s_master_cfg(struct device *dev, master_config_t *mcp)
+{
+ equalizer_t *eql;
+ master_config_t mc;
+
+#if EQL_DEBUG
+ if (eql_debug >= 20)
+ printk ("%s: set master config\n", dev->name);
+#endif
+
+ memcpy_fromfs (&mc, mcp, sizeof (master_config_t));
+
+ if ( eql_is_master (dev) )
+ {
+ eql = (equalizer_t *) dev->priv;
+ eql->max_slaves = mc.max_slaves;
+ eql->min_slaves = mc.min_slaves;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/* private device support functions
+ ------------------------------------------------------------------
+ */
+
+static inline
+int
+eql_is_slave(struct device *dev)
+{
+ if (dev)
+ {
+ if ((dev->flags & IFF_SLAVE) == IFF_SLAVE)
+ return 1;
+ }
+ return 0;
+}
+
+
+static inline
+int
+eql_is_master(struct device *dev)
+{
+ if (dev)
+ {
+ if ((dev->flags & IFF_MASTER) == IFF_MASTER)
+ return 1;
+ }
+ return 0;
+}
+
+
+static
+slave_t *
+eql_new_slave(void)
+{
+ slave_t *slave;
+
+ slave = (slave_t *) kmalloc (sizeof (slave_t), GFP_KERNEL);
+ if (slave)
+ {
+ memset(slave, 0, sizeof (slave_t));
+ return slave;
+ }
+ return 0;
+}
+
+
+static
+void
+eql_delete_slave(slave_t *slave)
+{
+ kfree (slave);
+}
+
+
+#if 0 /* not currently used, will be used
+ when we realy use a priority queue */
+static
+long
+slave_Bps(slave_t *slave)
+{
+ return (slave->priority_Bps);
+}
+
+static
+long
+slave_bps(slave_t *slave)
+{
+ return (slave->priority_bps);
+}
+#endif
+
+
+static inline
+int
+eql_number_slaves(slave_queue_t *queue)
+{
+ return queue->num_slaves;
+}
+
+
+static inline
+int
+eql_is_empty(slave_queue_t *queue)
+{
+ if (eql_number_slaves (queue) == 0)
+ return 1;
+ return 0;
+}
+
+
+static inline
+int
+eql_is_full(slave_queue_t *queue)
+{
+ equalizer_t *eql = (equalizer_t *) queue->master_dev->priv;
+
+ if (eql_number_slaves (queue) == eql->max_slaves)
+ return 1;
+ return 0;
+}
+
+
+static
+slave_queue_t *
+eql_new_slave_queue(struct device *dev)
+{
+ slave_queue_t *queue;
+ slave_t *head_slave;
+ slave_t *tail_slave;
+
+ queue = (slave_queue_t *) kmalloc (sizeof (slave_queue_t), GFP_KERNEL);
+ memset (queue, 0, sizeof (slave_queue_t));
+
+ head_slave = eql_new_slave ();
+ tail_slave = eql_new_slave ();
+
+ if ( head_slave != 0 &&
+ tail_slave != 0 )
+ {
+ head_slave->next = tail_slave;
+ tail_slave->next = 0;
+ queue->head = head_slave;
+ queue->num_slaves = 0;
+ queue->master_dev = dev;
+ }
+ else
+ {
+ kfree (queue);
+ return 0;
+ }
+ return queue;
+}
+
+
+static
+void
+eql_delete_slave_queue(slave_queue_t *queue)
+{
+ slave_t *zapped;
+
+ /* this should only be called when there isn't a timer running that scans
+ the data periodicaly.. dev_close stops the timer... */
+
+ while ( ! eql_is_empty (queue) )
+ {
+ zapped = eql_remove_slave (queue, queue->head->next);
+ eql_delete_slave (zapped);
+ }
+ kfree (queue->head->next);
+ kfree (queue->head);
+ kfree (queue);
+}
+
+
+static
+int
+eql_insert_slave(slave_queue_t *queue, slave_t *slave)
+{
+ cli ();
+
+ if ( ! eql_is_full (queue) )
+ {
+ slave_t *duplicate_slave = 0;
+
+ duplicate_slave = eql_find_slave_dev (queue, slave->dev);
+
+ if (duplicate_slave != 0)
+ {
+/* printk ("%s: found a duplicate, killing it and replacing\n",
+ queue->master_dev->name); */
+ eql_delete_slave (eql_remove_slave (queue, duplicate_slave));
+ }
+
+ slave->next = queue->head->next;
+ queue->head->next = slave;
+ queue->num_slaves++;
+ sti ();
+ return 0;
+ }
+
+ sti ();
+
+ return 1;
+}
+
+
+static
+slave_t *
+eql_remove_slave(slave_queue_t *queue, slave_t *slave)
+{
+ slave_t *prev;
+ slave_t *current;
+
+ cli ();
+
+ prev = queue->head;
+ current = queue->head->next;
+ while (current != slave &&
+ current->dev != 0 )
+ {
+/* printk ("%s: remove_slave; searching...\n", queue->master_dev->name); */
+ prev = current;
+ current = current->next;
+ }
+
+ if (current == slave)
+ {
+ prev->next = current->next;
+ queue->num_slaves--;
+ return current;
+ }
+
+ sti ();
+
+ return 0; /* not found */
+}
+
+
+#if 0
+static
+int
+eql_insert_slave_dev(slave_queue_t *queue, struct device *dev)
+{
+ slave_t *slave;
+
+ cli ();
+
+ if ( ! eql_is_full (queue) )
+ {
+ slave = eql_new_slave ();
+ slave->dev = dev;
+ slave->priority = EQL_DEFAULT_SLAVE_PRIORITY;
+ slave->priority_bps = EQL_DEFAULT_SLAVE_PRIORITY;
+ slave->priority_Bps = EQL_DEFAULT_SLAVE_PRIORITY / 8;
+ slave->next = queue->head->next;
+ queue->head->next = slave;
+ sti ();
+ return 0;
+ }
+ sti ();
+ return 1;
+}
+#endif
+
+
+static
+int
+eql_remove_slave_dev(slave_queue_t *queue, struct device *dev)
+{
+ slave_t *prev;
+ slave_t *current;
+ slave_t *target;
+
+ target = eql_find_slave_dev (queue, dev);
+
+ if (target != 0)
+ {
+ cli ();
+
+ prev = queue->head;
+ current = prev->next;
+ while (current != target)
+ {
+ prev = current;
+ current = current->next;
+ }
+ prev->next = current->next;
+ queue->num_slaves--;
+
+ sti ();
+
+ eql_delete_slave (current);
+ return 0;
+ }
+ return 1;
+}
+
+
+static inline
+struct device *
+eql_best_slave_dev(slave_queue_t *queue)
+{
+ if (queue->best_slave != 0)
+ {
+ if (queue->best_slave->dev != 0)
+ return queue->best_slave->dev;
+ else
+ return 0;
+ }
+ else
+ return 0;
+}
+
+
+static inline
+slave_t *
+eql_best_slave(slave_queue_t *queue)
+{
+ return queue->best_slave;
+}
+
+static inline
+void
+eql_schedule_slaves(slave_queue_t *queue)
+{
+ struct device *master_dev = queue->master_dev;
+ slave_t *best_slave = 0;
+ slave_t *slave_corpse = 0;
+
+#ifdef EQL_DEBUG
+ if (eql_debug >= 100)
+ printk ("%s: schedule %d slaves\n",
+ master_dev->name, eql_number_slaves (queue));
+#endif
+
+ if ( eql_is_empty (queue) )
+ {
+ /* no slaves to play with */
+ eql_set_best_slave (queue, (slave_t *) 0);
+ return;
+ }
+ else
+ { /* make a pass to set the best slave */
+ unsigned long best_load = (unsigned long) ULONG_MAX;
+ slave_t *slave = 0;
+ int i;
+
+ cli ();
+
+ for (i = 1, slave = eql_first_slave (queue);
+ i <= eql_number_slaves (queue);
+ i++, slave = eql_next_slave (queue, slave))
+ {
+ /* go through the slave list once, updating best_slave
+ whenever a new best_load is found, whenever a dead
+ slave is found, it is marked to be pulled out of the
+ queue */
+
+ unsigned long slave_load;
+ unsigned long bytes_queued;
+ unsigned long priority_Bps;
+
+ if (slave != 0)
+ {
+ bytes_queued = slave->bytes_queued;
+ priority_Bps = slave->priority_Bps;
+
+ if ( slave->dev != 0)
+ {
+ if ( slave->dev->flags & IFF_UP == IFF_UP )
+ {
+ slave_load = (ULONG_MAX - (ULONG_MAX / 2)) -
+ (priority_Bps) + bytes_queued * 8;
+
+ if (slave_load < best_load)
+ {
+ best_load = slave_load;
+ best_slave = slave;
+ }
+ }
+ else /* we found a dead slave */
+ {
+ /* we only bury one slave at a time, if more than
+ one slave dies, we will bury him on the next
+ reschedule. slaves don't die all at once that much
+ anyway */
+ slave_corpse = slave;
+ }
+ }
+ }
+ } /* for */
+
+ sti ();
+
+ eql_set_best_slave (queue, best_slave);
+ } /* else */
+
+ if (slave_corpse != 0)
+ {
+ printk ("eql: scheduler found dead slave, burying...\n");
+ eql_delete_slave (eql_remove_slave (queue, slave_corpse));
+ }
+
+ return;
+}
+
+
+static
+slave_t *
+eql_find_slave_dev(slave_queue_t *queue, struct device *dev)
+{
+ slave_t *slave = 0;
+
+ slave = eql_first_slave(queue);
+
+ while (slave != 0 && slave->dev != dev && slave != 0)
+ {
+#if 0
+ if (slave->dev != 0)
+ printk ("eql: find_slave_dev; looked at '%s'...\n", slave->dev->name);
+ else
+ printk ("eql: find_slave_dev; looked at nothing...\n");
+#endif
+
+ slave = slave->next;
+ }
+
+ return slave;
+}
+
+
+static inline
+slave_t *
+eql_first_slave(slave_queue_t *queue)
+{
+ return queue->head->next;
+}
+
+
+static inline
+slave_t *
+eql_next_slave(slave_queue_t *queue, slave_t *slave)
+{
+ return slave->next;
+}
+
+
+static inline
+void
+eql_set_best_slave(slave_queue_t *queue, slave_t *slave)
+{
+ queue->best_slave = slave;
+}
+
+
+#if 0
+static inline
+int
+eql_lock_slave_queue(slave_queue_t *queue)
+{
+ int result = 0;
+
+ printk ("eql: lock == %d\n", queue->lock);
+ if (queue->lock)
+ {
+ printk ("eql: lock_slave-q sleeping for lock\n");
+ sleep_on (&eql_queue_lock);
+ printk ("eql: lock_slave-q woken up\n");
+ queue->lock = 1;
+ }
+ queue->lock = 1;
+ return result;
+}
+
+static inline
+int
+eql_unlock_slave_queue(slave_queue_t *queue)
+{
+ int result = 0;
+
+ if (queue->lock != 0)
+ {
+ queue->lock = 0;
+ printk ("eql: unlock_slave-q waking up lock waiters\n");
+ wake_up (&eql_queue_lock);
+ }
+ return result;
+}
+#endif
+
+static inline
+int
+eql_is_locked_slave_queue(slave_queue_t *queue)
+{
+ return test_bit(1, (void *) &queue->lock);
+}
+
+static
+void
+eql_timer(unsigned long param)
+{
+ equalizer_t *eql = (equalizer_t *) param;
+ slave_t *slave;
+ slave_t *slave_corpse = 0;
+ int i;
+
+ if ( ! eql_is_empty (eql->queue) )
+ {
+ cli ();
+
+ for (i = 1, slave = eql_first_slave (eql->queue);
+ i <= eql_number_slaves (eql->queue);
+ i++, slave = eql_next_slave (eql->queue, slave))
+ {
+ if (slave != 0)
+ {
+ if ( slave->dev->flags & IFF_UP == IFF_UP )
+ {
+ slave->bytes_queued -= slave->priority_Bps;
+
+ if (slave->bytes_queued < 0)
+ slave->bytes_queued = 0;
+ }
+ else
+ {
+ slave_corpse = slave;
+ }
+ }
+ }
+
+ sti ();
+
+ if (slave_corpse != 0)
+ {
+ printk ("eql: timer found dead slave, burying...\n");
+ eql_delete_slave (eql_remove_slave (eql->queue, slave_corpse));
+ }
+
+ }
+
+ if (eql->timer_on != 0)
+ {
+ eql->timer.expires = EQL_DEFAULT_RESCHED_IVAL;
+ add_timer (&eql->timer);
+ }
+}
+
+/*
+ * Local Variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c eql.c"
+ * version-control: t
+ * kept-new-versions: 20
+ * End:
+ */
diff --git a/drivers/net/ewrk3.c b/drivers/net/ewrk3.c
index 0442249d5..bc357c80e 100644
--- a/drivers/net/ewrk3.c
+++ b/drivers/net/ewrk3.c
@@ -68,7 +68,7 @@
0) have a copy of the loadable modules code installed on your system.
1) copy ewrk3.c from the /linux/drivers/net directory to your favourite
temporary directory.
- 2) edit the source code near line 1340 to reflect the I/O address and
+ 2) edit the source code near line 1830 to reflect the I/O address and
IRQ you're using.
3) compile ewrk3.c, but include -DMODULE in the command line to ensure
that the correct bits are compiled (see end of source code).
@@ -79,6 +79,8 @@
(usually /etc/rc.inet[12] at boot time).
7) enjoy!
+ [Alan Cox: Changed this so you can insmod ewrk3.o irq=x io=y]
+
Note that autoprobing is not allowed in loadable modules - the system is
already up and running and you're messing with interrupts.
@@ -118,14 +120,20 @@
0.23 21-sep-94 Added transmit cut through
0.24 31-oct-94 Added uid checks in some ioctls
0.30 1-nov-94 BETA code release
+ 0.31 5-dec-94 Added check/snarf_region code.
+ 0.32 16-jan-95 Broadcast packet fix
=========================================================================
*/
-static char *version = "ewrk3.c:v0.30 11/1/94 davies@wanton.lkg.dec.com\n";
+static char *version = "ewrk3.c:v0.32 1/16/95 davies@wanton.lkg.dec.com\n";
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif /* MODULE */
#include <stdarg.h>
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
@@ -147,11 +155,6 @@ static char *version = "ewrk3.c:v0.30 11/1/94 davies@wanton.lkg.dec.com\n";
#include <linux/types.h>
#include <linux/unistd.h>
-#ifdef MODULE
-#include <linux/module.h>
-#include "/linux/tools/version.h"
-#endif /* MODULE */
-
#include "ewrk3.h"
#ifdef EWRK3_DEBUG
@@ -193,6 +196,7 @@ static int ewrk3_debug = 1;
#define EWRK3_IO_BASE 0x100 /* Start address for probe search */
#define EWRK3_IOP_INC 0x20 /* I/O address increment */
+#define EWRK3_TOTAL_SIZE 0x20 /* required I/O address space */
#define EWRK3_IO_SEARCH 0x007dff7f /* probe search mask */
static long mem_chkd = EWRK3_IO_SEARCH; /* holds which I/O addrs should be */
/* checked, for multi-EWRK3 case */
@@ -210,9 +214,8 @@ static long mem_chkd = EWRK3_IO_SEARCH; /* holds which I/O addrs should be */
#define EISA_SLOT_INC 0x1000
#endif
-#ifndef CRC_POLYNOMIAL
-#define CRC_POLYNOMIAL 0x04c11db7 /* Ethernet CRC polynomial */
-#endif /* CRC_POLYNOMIAL */
+#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */
+#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */
/*
** EtherWORKS 3 shared memory window sizes
@@ -287,7 +290,7 @@ struct ewrk3_private {
*/
static int ewrk3_open(struct device *dev);
static int ewrk3_queue_pkt(struct sk_buff *skb, struct device *dev);
-static void ewrk3_interrupt(int reg_ptr);
+static void ewrk3_interrupt(int irq, struct pt_regs *regs);
static int ewrk3_close(struct device *dev);
static struct enet_statistics *ewrk3_get_stats(struct device *dev);
static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
@@ -314,7 +317,7 @@ 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 iobase);
-static int num_ewrk3s = 0, num_eth = 0, autoprobed = 0;
+static int num_ewrk3s = 0, num_eth = 0;
static unsigned char irq[] = {5,0,10,3,11,9,15,12};
#else
@@ -323,6 +326,8 @@ void cleanup_module(void);
#endif /* MODULE */
+static int autoprobed = 0;
+
/*
** Miscellaneous defines...
*/
@@ -344,14 +349,24 @@ int ewrk3_probe(struct device *dev)
#endif
if (base_addr > 0x0ff) { /* Check a single specified location. */
- if (((mem_chkd >> ((base_addr - EWRK3_IO_BASE)/ EWRK3_IOP_INC))&0x01)==0) {
- if (DevicePresent(base_addr) == 0) { /* Is EWRK3 really here? */
- status = ewrk3_hw_init(dev, base_addr);
+ if (!autoprobed) { /* Module or fixed location */
+ if (!check_region(base_addr, EWRK3_TOTAL_SIZE)) {
+ if (((mem_chkd >> ((base_addr - EWRK3_IO_BASE)/ EWRK3_IOP_INC))&0x01)==1) {
+ if (DevicePresent(base_addr) == 0) { /* Is EWRK3 really here? */
+ /* Register I/O Region */
+ request_region(base_addr, EWRK3_IOP_INC, "ewrk3");
+ status = ewrk3_hw_init(dev, base_addr);
+ } else {
+ printk("ewrk3_probe(): No device found\n");
+ mem_chkd &= ~(0x01 << ((base_addr - EWRK3_IO_BASE)/EWRK3_IOP_INC));
+ }
+ }
} else {
- printk("ewrk3_probe(): No device found\n");
+ printk("%s: ewrk3_probe(): Detected a device already registered at 0x%02x\n", dev->name, base_addr);
+ mem_chkd &= ~(0x01 << ((base_addr - EWRK3_IO_BASE)/EWRK3_IOP_INC));
}
- } else {
- status = ewrk3_hw_init(dev, base_addr); /* Yes there is h/w */
+ } else { /* already know what ewrk3 h/w is here */
+ status = ewrk3_hw_init(dev, base_addr);
}
} else if (base_addr > 0) { /* Don't probe at all. */
status = -ENXIO;
@@ -891,9 +906,8 @@ ewrk3_queue_pkt(struct sk_buff *skb, struct device *dev)
** The EWRK3 interrupt handler.
*/
static void
-ewrk3_interrupt(int reg_ptr)
+ewrk3_interrupt(int irq, struct pt_regs * regs)
{
- int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
struct device *dev = (struct device *)(irq2dev_map[irq]);
struct ewrk3_private *lp;
int iobase;
@@ -1040,6 +1054,8 @@ ewrk3_rx(struct device *dev)
** Notify the upper protocol layers that there is another
** packet to handle
*/
+
+ skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
/*
@@ -1227,20 +1243,19 @@ set_multicast_list(struct device *dev, int num_addrs, void *addrs)
/*
** Calculate the hash code and update the logical address filter
** from a list of ethernet multicast addresses.
-** Derived from a 'C' program in the AMD data book:
-** "Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE)",
-** Pub #17781, Rev. A, May 1993
+** Little endian crc one liner from Matt Thomas, DEC.
**
+** Note that when clearing the table, the broadcast bit must remain asserted
+** to receive broadcast messages.
*/
static void SetMulticastFilter(struct device *dev, int num_addrs, char *addrs, char *multicast_table)
{
struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
- int iobase = dev->base_addr;
- char j, ctrl, bit, octet;
+ int i, iobase = dev->base_addr;
+ char j, bit, byte;
short *p = (short *) multicast_table;
- unsigned short hashcode;
- int i;
- long int crc, poly = (long int) CRC_POLYNOMIAL;
+ u_short hashcode;
+ u_long crc, poly = CRC_POLYNOMIAL_LE;
while (set_bit(0, (void *)&lp->lock) != 0); /* Wait for lock to free */
@@ -1260,47 +1275,49 @@ static void SetMulticastFilter(struct device *dev, int num_addrs, char *addrs, c
i++;
}
}
- } else if (num_addrs == 0) {
+ } else {
+ /* Clear table except for broadcast bit */
if (lp->shmem_length == IO_ONLY) {
- for (i=0; i<(HASH_TABLE_LEN >> 3); i++) {
+ for (i=0; i<(HASH_TABLE_LEN >> 4) - 1; i++) {
+ outb(0x00, EWRK3_DATA);
+ }
+ outb(0x80, EWRK3_DATA); i++; /* insert the broadcast bit */
+ for (; i<(HASH_TABLE_LEN >> 3); i++) {
outb(0x00, EWRK3_DATA);
}
} else {
memset(multicast_table, 0, (HASH_TABLE_LEN >> 3));
+ *(multicast_table + (HASH_TABLE_LEN >> 4) - 1) = 0x80;
}
- } else {
+
+ /* Update table */
for (i=0;i<num_addrs;i++) { /* for each address in the list */
- if (((char) *(addrs+ETH_ALEN*i) & 0x01) == 1) {/* multicast address? */
- crc = (long int) 0xffffffff; /* init CRC for each address */
- for (octet=0;octet<ETH_ALEN;octet++) { /* for each address octet */
- for(j=0;j<8;j++) { /* process each address bit */
- bit = (((char)* (addrs+ETH_ALEN*i+octet)) >> j) & 0x01;
- ctrl = ((crc < 0) ? 1 : 0); /* shift the control bit */
- crc <<= 1; /* shift the CRC */
- if (bit ^ ctrl) { /* (bit) XOR (control bit) */
- crc ^= poly; /* (CRC) XOR (polynomial) */
- }
+ if ((*addrs & 0x01) == 1) { /* multicast address? */
+ crc = 0xffffffff; /* init CRC for each address */
+ for (byte=0;byte<ETH_ALEN;byte++) { /* for each address byte */
+ /* process each address bit */
+ for (bit = *addrs++,j=0;j<8;j++, bit>>=1) {
+ crc = (crc >> 1) ^ (((crc ^ bit) & 0x01) ? poly : 0);
}
}
- hashcode = (crc & 0x01); /* hashcode is 9 LSb of CRC ... */
- for (j=0;j<8;j++) { /* ... in reverse order. */
- hashcode <<= 1;
- crc >>= 1;
- hashcode |= (crc & 0x01);
- }
- octet = hashcode >> 3; /* bit[3-8] -> octet in filter */
- /* bit[0-2] -> bit in octet */
+ hashcode = crc & ((1 << 9) - 1); /* hashcode is 9 LSb of CRC */
+
+ byte = hashcode >> 3; /* bit[3-8] -> byte in filter */
+ bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */
+
if (lp->shmem_length == IO_ONLY) {
unsigned char tmp;
- outw((short)((long)multicast_table) + octet, EWRK3_PIR1);
+ outw((short)((long)multicast_table) + byte, EWRK3_PIR1);
tmp = inb(EWRK3_DATA);
- tmp |= (1 << (hashcode & 0x07));
- outw((short)((long)multicast_table) + octet, EWRK3_PIR1);
+ tmp |= bit;
+ outw((short)((long)multicast_table) + byte, EWRK3_PIR1);
outb(tmp, EWRK3_DATA);
} else {
- multicast_table[octet] |= (1 << (hashcode & 0x07));
+ multicast_table[byte] |= bit;
}
+ } else { /* skip this address */
+ addrs += ETH_ALEN;
}
}
}
@@ -1323,20 +1340,27 @@ static struct device *isa_probe(struct device *dev)
i < 24;
iobase += EWRK3_IOP_INC, i++) {
if (tmp & 0x01) {
- if (DevicePresent(iobase) == 0) {
+ /* Anything else registered here? */
+ if (!check_region(iobase, EWRK3_TOTAL_SIZE)) {
+ if (DevicePresent(iobase) == 0) {
/*
** Device found. Mark its (I/O) location for future reference. Only 24
** EtherWORKS devices can exist between 0x100 and 0x3e0.
*/
- if (num_ewrk3s > 0) { /* only gets here in autoprobe */
- dev = alloc_device(dev, iobase);
- } else {
- if ((status = ewrk3_hw_init(dev, iobase)) == 0) {
- num_ewrk3s++;
+ request_region(iobase, EWRK3_IOP_INC, "ewrk3");
+ if (num_ewrk3s > 0) { /* only gets here in autoprobe */
+ dev = alloc_device(dev, iobase);
+ } else {
+ if ((status = ewrk3_hw_init(dev, iobase)) == 0) {
+ num_ewrk3s++;
+ }
}
+ num_eth++;
+ } else {
+ mem_chkd &= ~(0x01 << ((iobase - EWRK3_IO_BASE)/EWRK3_IOP_INC));
}
- num_eth++;
} else {
+ printk("%s: ewrk3_probe(): Detected a device already registered at 0x%02x\n", dev->name, iobase);
mem_chkd &= ~(0x01 << ((iobase - EWRK3_IO_BASE)/EWRK3_IOP_INC));
}
}
@@ -1358,20 +1382,24 @@ static struct device *eisa_probe(struct device *dev)
iobase+=EISA_SLOT_INC; /* get the first slot address */
for (status = -ENODEV, i=1; i<MAX_EISA_SLOTS; i++, iobase+=EISA_SLOT_INC) {
- if (DevicePresent(iobase) == 0) {
+ /* Anything else registered here? */
+ if (!check_region(iobase, EWRK3_TOTAL_SIZE)) {
+ if (DevicePresent(iobase) == 0) {
/*
** Device found. Mark its slot location for future reference. Only 7
** EtherWORKS devices can exist in EISA space....
*/
- mem_chkd |= (0x01 << (i + 24));
- if (num_ewrk3s > 0) { /* only gets here in autoprobe */
- dev = alloc_device(dev, iobase);
- } else {
- if ((status = ewrk3_hw_init(dev, iobase)) == 0) {
- num_ewrk3s++;
+ mem_chkd |= (0x01 << (i + 24));
+ request_region(iobase, EWRK3_IOP_INC, "ewrk3");
+ if (num_ewrk3s > 0) { /* only gets here in autoprobe */
+ dev = alloc_device(dev, iobase);
+ } else {
+ if ((status = ewrk3_hw_init(dev, iobase)) == 0) {
+ num_ewrk3s++;
+ }
}
+ num_eth++;
}
- num_eth++;
}
}
return dev;
@@ -1800,12 +1828,17 @@ char kernel_version[] = UTS_RELEASE;
static struct device thisEthwrk = {
" ", /* device name inserted by /linux/drivers/net/net_init.c */
0, 0, 0, 0,
- 0x300, 5, /* I/O address, IRQ <--- EDIT THIS LINE FOR YOUR CONFIGURATION */
+ 0x300, 5, /* I/O address, IRQ */
0, 0, 0, NULL, ewrk3_probe };
+int io=0x300; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
+int irq=5; /* or use the insmod io= irq= options */
+
int
init_module(void)
{
+ thisEthwrk.base_addr=io;
+ thisEthwrk.irq=irq;
if (register_netdev(&thisEthwrk) != 0)
return -EIO;
return 0;
diff --git a/drivers/net/hp-plus.c b/drivers/net/hp-plus.c
index dfecd2436..694b3c0d5 100644
--- a/drivers/net/hp-plus.c
+++ b/drivers/net/hp-plus.c
@@ -21,21 +21,17 @@
static char *version =
"hp-plus.c:v1.10 9/24/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
-#include <linux/config.h>
#include <linux/string.h> /* Important -- this inlines word moves. */
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
#include <asm/system.h>
#include <asm/io.h>
-#ifdef start_bh_atomic /* This checks for kernels >= 1.1.0. */
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#else
-#include "dev.h"
-#endif
#include "8390.h"
extern struct device *init_etherdev(struct device *dev, int sizeof_private,
@@ -182,7 +178,7 @@ int hpp_probe1(struct device *dev, int ioaddr)
}
/* Grab the region so we can find another board if something fails. */
- snarf_region(ioaddr, HP_IO_EXTENT);
+ request_region(ioaddr, HP_IO_EXTENT,"hp-plus");
/* Read the IRQ line. */
outw(HW_Page, ioaddr + HP_PAGING);
diff --git a/drivers/net/hp.c b/drivers/net/hp.c
index 76f33f4b6..393b1a1d9 100644
--- a/drivers/net/hp.c
+++ b/drivers/net/hp.c
@@ -21,7 +21,6 @@
static char *version =
"hp.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
@@ -126,7 +125,7 @@ int hp_probe1(struct device *dev, int ioaddr)
dev = init_etherdev(0, sizeof(struct ei_device), 0);
/* Grab the region so we can find another board if something fails. */
- snarf_region(ioaddr, HP_IO_EXTENT);
+ request_region(ioaddr, HP_IO_EXTENT,"hp");
printk("%s: %s (ID %02x) at %#3x,", dev->name, name, board_id, ioaddr);
diff --git a/drivers/net/i82586.h b/drivers/net/i82586.h
new file mode 100644
index 000000000..e65c3f30f
--- /dev/null
+++ b/drivers/net/i82586.h
@@ -0,0 +1,410 @@
+#if defined(CONFIG_WAVELAN)
+/*
+ * Intel 82586 IEEE 802.3 Ethernet LAN Coprocessor.
+ *
+ * See:
+ * Intel Microcommunications 1991
+ * p1-1 to p1-37
+ * Intel order No. 231658
+ * ISBN 1-55512-119-5
+ *
+ * Unfortunately, the above chapter mentions neither
+ * the System Configuration Pointer (SCP) nor the
+ * Intermediate System Configuration Pointer (ISCP),
+ * so we probably need to look elsewhere for the
+ * whole story -- some recommend the "Intel LAN
+ * Components manual" but I have neither a copy
+ * nor a full reference. But "elsewhere" may be
+ * in the same publication...
+ * The description of a later device, the
+ * "82596CA High-Performance 32-Bit Local Area Network
+ * Coprocessor", (ibid. p1-38 to p1-109) does mention
+ * the SCP and ISCP and also has an i82586 compatibility
+ * mode. Even more useful is "AP-235 An 82586 Data Link
+ * Driver" (ibid. p1-337 to p1-417).
+ */
+
+#define I82586_MEMZ (64 * 1024)
+
+#define I82586_SCP_ADDR (I82586_MEMZ - sizeof(scp_t))
+
+#define ADDR_LEN 6
+#define I82586NULL 0xFFFF
+
+#define toff(t,p,f) (unsigned short)((void *)(&((t *)((void *)0 + (p)))->f) - (void *)0)
+
+/*
+ * System Configuration Pointer (SCP).
+ */
+typedef struct scp_t scp_t;
+struct scp_t
+{
+ unsigned short scp_sysbus; /* 82586 bus width: */
+#define SCP_SY_16BBUS (0x0 << 0) /* 16 bits */
+#define SCP_SY_8BBUS (0x1 << 0) /* 8 bits. */
+ unsigned short scp_junk[2]; /* Unused */
+ unsigned short scp_iscpl; /* lower 16 bits of ISCP_ADDR */
+ unsigned short scp_iscph; /* upper 16 bits of ISCP_ADDR */
+};
+
+/*
+ * Intermediate System Configuration Pointer (ISCP).
+ */
+typedef struct iscp_t iscp_t;
+struct iscp_t
+{
+ unsigned short iscp_busy; /* set by CPU before first CA, */
+ /* cleared by 82586 after read. */
+ unsigned short iscp_offset; /* offset of SCB */
+ unsigned short iscp_basel; /* base of SCB */
+ unsigned short iscp_baseh; /* " */
+};
+
+/*
+ * System Control Block (SCB).
+ * The 82586 writes its status to scb_status and then
+ * raises an interrupt to alert the CPU.
+ * The CPU writes a command to scb_command and
+ * then issues a Channel Attention (CA) to alert the 82586.
+ */
+typedef struct scb_t scb_t;
+struct scb_t
+{
+ unsigned short scb_status; /* Status of 82586 */
+#define SCB_ST_INT (0xF << 12) /* Some of: */
+#define SCB_ST_CX (0x1 << 15) /* Cmd completed */
+#define SCB_ST_FR (0x1 << 14) /* Frame received */
+#define SCB_ST_CNA (0x1 << 13) /* Cmd unit not active */
+#define SCB_ST_RNR (0x1 << 12) /* Rcv unit not ready */
+#define SCB_ST_JUNK0 (0x1 << 11) /* 0 */
+#define SCB_ST_CUS (0x7 << 8) /* Cmd unit status */
+#define SCB_ST_CUS_IDLE (0 << 8) /* Idle */
+#define SCB_ST_CUS_SUSP (1 << 8) /* Suspended */
+#define SCB_ST_CUS_ACTV (2 << 8) /* Active */
+#define SCB_ST_JUNK1 (0x1 << 7) /* 0 */
+#define SCB_ST_RUS (0x7 << 4) /* Rcv unit status */
+#define SCB_ST_RUS_IDLE (0 << 4) /* Idle */
+#define SCB_ST_RUS_SUSP (1 << 4) /* Suspended */
+#define SCB_ST_RUS_NRES (2 << 4) /* No resources */
+#define SCB_ST_RUS_RDY (4 << 4) /* Ready */
+ unsigned short scb_command; /* Next command */
+#define SCB_CMD_ACK_CX (0x1 << 15) /* Ack cmd completion */
+#define SCB_CMD_ACK_FR (0x1 << 14) /* Ack frame received */
+#define SCB_CMD_ACK_CNA (0x1 << 13) /* Ack CU not active */
+#define SCB_CMD_ACK_RNR (0x1 << 12) /* Ack RU not ready */
+#define SCB_CMD_JUNKX (0x1 << 11) /* Unused */
+#define SCB_CMD_CUC (0x7 << 8) /* Command Unit command */
+#define SCB_CMD_CUC_NOP (0 << 8) /* Nop */
+#define SCB_CMD_CUC_GO (1 << 8) /* Start cbl_offset */
+#define SCB_CMD_CUC_RES (2 << 8) /* Resume execution */
+#define SCB_CMD_CUC_SUS (3 << 8) /* Suspend " */
+#define SCB_CMD_CUC_ABT (4 << 8) /* Abort " */
+#define SCB_CMD_RESET (0x1 << 7) /* Reset chip (hardware) */
+#define SCB_CMD_RUC (0x7 << 4) /* Receive Unit command */
+#define SCB_CMD_RUC_NOP (0 << 4) /* Nop */
+#define SCB_CMD_RUC_GO (1 << 4) /* Start rfa_offset */
+#define SCB_CMD_RUC_RES (2 << 4) /* Resume reception */
+#define SCB_CMD_RUC_SUS (3 << 4) /* Suspend " */
+#define SCB_CMD_RUC_ABT (4 << 4) /* Abort " */
+ unsigned short scb_cbl_offset; /* Offset of first command unit */
+ /* Action Command */
+ unsigned short scb_rfa_offset; /* Offset of first Receive */
+ /* Frame Descriptor in the */
+ /* Receive Frame Area */
+ unsigned short scb_crcerrs; /* Properly aligned frames */
+ /* received with a CRC error */
+ unsigned short scb_alnerrs; /* Misaligned frames received */
+ /* with a CRC error */
+ unsigned short scb_rscerrs; /* Frames lost due to no space */
+ unsigned short scb_ovrnerrs; /* Frames lost due to slow bus */
+};
+
+#define scboff(p,f) toff(scb_t, p, f)
+
+/*
+ * The eight Action Commands.
+ */
+typedef enum acmd_e acmd_e;
+enum acmd_e
+{
+ acmd_nop = 0, /* Do nothing */
+ acmd_ia_setup = 1, /* Load an (ethernet) address into the */
+ /* 82586 */
+ acmd_configure = 2, /* Update the 82586 operating parameters */
+ acmd_mc_setup = 3, /* Load a list of (ethernet) multicast */
+ /* addresses into the 82586 */
+ acmd_transmit = 4, /* Transmit a frame */
+ acmd_tdr = 5, /* Perform a Time Domain Reflectometer */
+ /* test on the serial link */
+ acmd_dump = 6, /* Copy 82586 registers to memory */
+ acmd_diagnose = 7, /* Run an internal self test */
+};
+
+/*
+ * Generic Action Command header.
+ */
+typedef struct ach_t ach_t;
+struct ach_t
+{
+ unsigned short ac_status; /* Command status: */
+#define AC_SFLD_C (0x1 << 15) /* Command completed */
+#define AC_SFLD_B (0x1 << 14) /* Busy executing */
+#define AC_SFLD_OK (0x1 << 13) /* Completed error free */
+#define AC_SFLD_A (0x1 << 12) /* Command aborted */
+#define AC_SFLD_FAIL (0x1 << 11) /* Selftest failed */
+#define AC_SFLD_S10 (0x1 << 10) /* No carrier sense */
+ /* during transmission */
+#define AC_SFLD_S9 (0x1 << 9) /* Tx unsuccessful: */
+ /* (stopped) lost CTS */
+#define AC_SFLD_S8 (0x1 << 8) /* Tx unsuccessful: */
+ /* (stopped) slow DMA */
+#define AC_SFLD_S7 (0x1 << 7) /* Tx deferred: */
+ /* other link traffic */
+#define AC_SFLD_S6 (0x1 << 6) /* Heart Beat: collision */
+ /* detect after last tx */
+#define AC_SFLD_S5 (0x1 << 5) /* Tx stopped: */
+ /* excessive collisions */
+#define AC_SFLD_MAXCOL (0xF << 0) /* Collision count */
+ unsigned short ac_command; /* Command specifier: */
+#define AC_CFLD_EL (0x1 << 15) /* End of command list */
+#define AC_CFLD_S (0x1 << 14) /* Suspend on completion */
+#define AC_CFLD_I (0x1 << 13) /* Interrupt on completion */
+#define AC_CFLD_CMD (0x7 << 0) /* acmd_e */
+ unsigned short ac_link; /* Next Action Command */
+};
+
+#define acoff(p,f) toff(ach_t, p, f)
+
+/*
+ * The Nop Action Command.
+ */
+typedef struct ac_nop_t ac_nop_t;
+struct ac_nop_t
+{
+ ach_t nop_h;
+};
+
+/*
+ * The IA-Setup Action Command.
+ */
+typedef struct ac_ias_t ac_ias_t;
+struct ac_ias_t
+{
+ ach_t ias_h;
+ unsigned char ias_addr[ADDR_LEN]; /* The (ethernet) address */
+};
+
+/*
+ * The Configure Action Command.
+ */
+typedef struct ac_cfg_t ac_cfg_t;
+struct ac_cfg_t
+{
+ ach_t cfg_h;
+ unsigned char cfg_byte_cnt; /* Size foll data: 4-12 */
+#define AC_CFG_BYTE_CNT(v) (((v) & 0xF) << 0)
+ unsigned char cfg_fifolim; /* FIFO threshold */
+#define AC_CFG_FIFOLIM(v) (((v) & 0xF) << 0)
+ unsigned char cfg_byte8;
+#define AC_CFG_SAV_BF(v) (((v) & 0x1) << 7) /* Save rxd bad frames */
+#define AC_CFG_SRDY(v) (((v) & 0x1) << 6) /* SRDY/ARDY pin means */
+ /* external sync. */
+ unsigned char cfg_byte9;
+#define AC_CFG_ELPBCK(v) (((v) & 0x1) << 7) /* External loopback */
+#define AC_CFG_ILPBCK(v) (((v) & 0x1) << 6) /* Internal loopback */
+#define AC_CFG_PRELEN(v) (((v) & 0x3) << 4) /* Preamble length */
+#define AC_CFG_PLEN_2 0 /* 2 bytes */
+#define AC_CFG_PLEN_4 1 /* 4 bytes */
+#define AC_CFG_PLEN_8 2 /* 8 bytes */
+#define AC_CFG_PLEN_16 3 /* 16 bytes */
+#define AC_CFG_ALOC(v) (((v) & 0x1) << 3) /* Addr/len data is */
+ /* explicit in buffers */
+#define AC_CFG_ADDRLEN(v) (((v) & 0x7) << 0) /* Bytes per address */
+ unsigned char cfg_byte10;
+#define AC_CFG_BOFMET(v) (((v) & 0x1) << 7) /* Use alternate expo. */
+ /* backoff method */
+#define AC_CFG_ACR(v) (((v) & 0x7) << 4) /* Accelerated cont. res. */
+#define AC_CFG_LINPRIO(v) (((v) & 0x7) << 0) /* Linear priority */
+ unsigned char cfg_ifs; /* Interframe spacing */
+ unsigned char cfg_slotl; /* Slot time (low byte) */
+ unsigned char cfg_byte13;
+#define AC_CFG_RETRYNUM(v) (((v) & 0xF) << 4) /* Max. collision retry */
+#define AC_CFG_SLTTMHI(v) (((v) & 0x7) << 0) /* Slot time (high bits) */
+ unsigned char cfg_byte14;
+#define AC_CFG_FLGPAD(v) (((v) & 0x1) << 7) /* Pad with HDLC flags */
+#define AC_CFG_BTSTF(v) (((v) & 0x1) << 6) /* Do HDLC bitstuffing */
+#define AC_CFG_CRC16(v) (((v) & 0x1) << 5) /* 16 bit CCITT CRC */
+#define AC_CFG_NCRC(v) (((v) & 0x1) << 4) /* Insert no CRC */
+#define AC_CFG_TNCRS(v) (((v) & 0x1) << 3) /* Tx even if no carrier */
+#define AC_CFG_MANCH(v) (((v) & 0x1) << 2) /* Manchester coding */
+#define AC_CFG_BCDIS(v) (((v) & 0x1) << 1) /* Disable broadcast */
+#define AC_CFG_PRM(v) (((v) & 0x1) << 0) /* Promiscuous mode */
+ unsigned char cfg_byte15;
+#define AC_CFG_ICDS(v) (((v) & 0x1) << 7) /* Internal collision */
+ /* detect source */
+#define AC_CFG_CDTF(v) (((v) & 0x7) << 4) /* Collision detect */
+ /* filter in bit times */
+#define AC_CFG_ICSS(v) (((v) & 0x1) << 3) /* Internal carrier */
+ /* sense source */
+#define AC_CFG_CSTF(v) (((v) & 0x7) << 0) /* Carrier sense */
+ /* filter in bit times */
+ unsigned short cfg_min_frm_len;
+#define AC_CFG_MNFRM(v) (((v) & 0xFF) << 0) /* Min. bytes/frame (<= 255) */
+};
+
+/*
+ * The MC-Setup Action Command.
+ */
+typedef struct ac_mcs_t ac_mcs_t;
+struct ac_mcs_t
+{
+ ach_t mcs_h;
+ unsigned short mcs_cnt; /* No. of bytes of MC addresses */
+ unsigned short mcs_data[3]; /* The first MC address .. */
+};
+
+/*
+ * The Transmit Action Command.
+ */
+typedef struct ac_tx_t ac_tx_t;
+struct ac_tx_t
+{
+ ach_t tx_h;
+ unsigned short tx_tbd_offset; /* Address of list of buffers. */
+#if 0
+Linux packets are passed down with the destination MAC address
+and length/type field already prepended to the data,
+so we do not need to insert it. Consistent with this
+we must also set the AC_CFG_ALOC(..) flag during the
+ac_cfg_t action command.
+ unsigned char tx_addr[ADDR_LEN]; /* The frame dest. address */
+ unsigned short tx_length; /* The frame length */
+#endif /* 0 */
+};
+
+/*
+ * The Time Domain Reflectometer Action Command.
+ */
+typedef struct ac_tdr_t ac_tdr_t;
+struct ac_tdr_t
+{
+ ach_t tdr_h;
+ unsigned short tdr_result; /* Result. */
+#define AC_TDR_LNK_OK (0x1 << 15) /* No link problem */
+#define AC_TDR_XCVR_PRB (0x1 << 14) /* Txcvr cable problem */
+#define AC_TDR_ET_OPN (0x1 << 13) /* Open on the link */
+#define AC_TDR_ET_SRT (0x1 << 12) /* Short on the link */
+#define AC_TDR_TIME (0x7FF << 0) /* Distance to problem */
+ /* site in transmit */
+ /* clock cycles */
+};
+
+/*
+ * The Dump Action Command.
+ */
+typedef struct ac_dmp_t ac_dmp_t;
+struct ac_dmp_t
+{
+ ach_t dmp_h;
+ unsigned short dmp_offset; /* Result. */
+};
+
+/*
+ * Size of the result of the dump command.
+ */
+#define DUMPBYTES 170
+
+/*
+ * The Diagnose Action Command.
+ */
+typedef struct ac_dgn_t ac_dgn_t;
+struct ac_dgn_t
+{
+ ach_t dgn_h;
+};
+
+/*
+ * Transmit Buffer Descriptor (TBD).
+ */
+typedef struct tbd_t tbd_t;
+struct tbd_t
+{
+ unsigned short tbd_status; /* Written by the CPU */
+#define TBD_STATUS_EOF (0x1 << 15) /* This TBD is the */
+ /* last for this frame */
+#define TBD_STATUS_ACNT (0x3FFF << 0) /* Actual count of data */
+ /* bytes in this buffer */
+ unsigned short tbd_next_bd_offset; /* Next in list */
+ unsigned short tbd_bufl; /* Buffer address (low) */
+ unsigned short tbd_bufh; /* " " (high) */
+};
+
+/*
+ * Receive Buffer Descriptor (RBD).
+ */
+typedef struct rbd_t rbd_t;
+struct rbd_t
+{
+ unsigned short rbd_status; /* Written by the 82586 */
+#define RBD_STATUS_EOF (0x1 << 15) /* This RBD is the */
+ /* last for this frame */
+#define RBD_STATUS_F (0x1 << 14) /* ACNT field is valid */
+#define RBD_STATUS_ACNT (0x3FFF << 0) /* Actual no. of data */
+ /* bytes in this buffer */
+ unsigned short rbd_next_rbd_offset; /* Next rbd in list */
+ unsigned short rbd_bufl; /* Data pointer (low) */
+ unsigned short rbd_bufh; /* " " (high) */
+ unsigned short rbd_el_size; /* EL+Data buf. size */
+#define RBD_EL (0x1 << 15) /* This BD is the */
+ /* last in the list */
+#define RBD_SIZE (0x3FFF << 0) /* No. of bytes the */
+ /* buffer can hold */
+};
+
+#define rbdoff(p,f) toff(rbd_t, p, f)
+
+/*
+ * Frame Descriptor (FD).
+ */
+typedef struct fd_t fd_t;
+struct fd_t
+{
+ unsigned short fd_status; /* Written by the 82586 */
+#define FD_STATUS_C (0x1 << 15) /* Completed storing frame */
+#define FD_STATUS_B (0x1 << 14) /* FD was consumed by RU */
+#define FD_STATUS_OK (0x1 << 13) /* Frame rxd successfully */
+#define FD_STATUS_S11 (0x1 << 11) /* CRC error */
+#define FD_STATUS_S10 (0x1 << 10) /* Alignment error */
+#define FD_STATUS_S9 (0x1 << 9) /* Ran out of resources */
+#define FD_STATUS_S8 (0x1 << 8) /* Rx DMA overrun */
+#define FD_STATUS_S7 (0x1 << 7) /* Frame too short */
+#define FD_STATUS_S6 (0x1 << 6) /* No EOF flag */
+ unsigned short fd_command; /* Command */
+#define FD_COMMAND_EL (0x1 << 15) /* Last FD in list */
+#define FD_COMMAND_S (0x1 << 14) /* Suspend RU after rx */
+ unsigned short fd_link_offset; /* Next FD */
+ unsigned short fd_rbd_offset; /* First RBD (data) */
+ /* Prepared by CPU, */
+ /* updated by 82586 */
+#if 0
+I think the rest is unused since we
+have set AC_CFG_ALOC(..). However, just
+in case, we leave the space.
+#endif /* 0 */
+ unsigned char fd_dest[ADDR_LEN]; /* Destination address */
+ /* Written by 82586 */
+ unsigned char fd_src[ADDR_LEN]; /* Source address */
+ /* Written by 82586 */
+ unsigned short fd_length; /* Frame length or type */
+ /* Written by 82586 */
+};
+
+#define fdoff(p,f) toff(fd_t, p, f)
+
+/*
+ * This software may only be used and distributed
+ * according to the terms of the GNU Public License.
+ *
+ * For more details, see wavelan.c.
+ */
+#endif /* defined(CONFIG_WAVELAN) */
diff --git a/drivers/net/ibmtr.c b/drivers/net/ibmtr.c
new file mode 100644
index 000000000..70f524225
--- /dev/null
+++ b/drivers/net/ibmtr.c
@@ -0,0 +1,1180 @@
+/* ibmtr.c: A shared-memory IBM Token Ring 16/4 driver for linux */
+/*
+ Written 1993 by Mark Swanson and Peter De Schrijver.
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ This device driver should work with Any IBM Token Ring Card that does
+ not use DMA.
+
+ I used Donald Becker's (becker@super.org) device driver work
+ as a base for most of my initial work.
+*/
+
+/*
+ Changes by Peter De Schrijver (stud11@cc4.kuleuven.ac.be) :
+
+ + changed name to ibmtr.c in anticipation of other tr boards.
+ + changed reset code and adapter open code.
+ + added SAP open code.
+ + a first attempt to write interrupt, transmit and receive routines.
+
+ Changes by David W. Morris (dwm@shell.portal.com) :
+ 941003 dwm: - Restructure tok_probe for multiple adapters, devices
+ - Add comments, misc reorg for clarity
+ - Flatten interrupt handler levels
+
+ Warnings !!!!!!!!!!!!!!
+ This driver is only partially sanitized for support of multiple
+ adapters. It will almost definately fail if more than one
+ active adapter is identified.
+*/
+
+#define NO_AUTODETECT 1
+#undef NO_AUTODETECT
+#undef ENABLE_PAGING
+
+#define FALSE 0
+#define TRUE (!FALSE)
+
+static char *version = "ibmtr.c:v1.1.48 8/7/94 Peter De Schrijver and Mark Swanson\n"
+ " modified 10/3/94 David W. Morris\n";
+
+static char pcchannelid[]={0x05, 0x00, 0x04, 0x09,
+ 0x04, 0x03, 0x04, 0x0f,
+ 0x03, 0x06, 0x03, 0x01,
+ 0x03, 0x01, 0x03, 0x00,
+ 0x03, 0x09, 0x03, 0x09,
+ 0x03, 0x00, 0x02, 0x00};
+static char mcchannelid[]={0x04, 0x0d, 0x04, 0x01,
+ 0x05, 0x02, 0x05, 0x03,
+ 0x03, 0x06, 0x03, 0x03,
+ 0x05, 0x08, 0x03, 0x04,
+ 0x03, 0x05, 0x03, 0x01,
+ 0x03, 0x08, 0x02, 0x00};
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/in.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/trdevice.h>
+#include <stddef.h>
+#include "ibmtr.h"
+
+
+#define DPRINTK(format, args...) printk("%s: " format, dev->name , ## args)
+#define DPRINTD(format, args...) DummyCall("%s: " format, dev->name , ## args)
+
+
+#if 0
+struct tok_info tok_info1; /* WARNING: this area must be replicated
+ or 'malloced' to support > 1 adapter */
+
+static struct wait_queue *wait_for_tok_int=NULL, *wait_for_reset;
+void (*do_tok_int)(struct device *dev)=NULL;
+#endif
+
+unsigned char ibmtr_debug_trace=1; /* Patch or otherwise alter to
+ control tokenring tracing. */
+#define TRC_INIT 0x01 /* Trace initialization & PROBEs */
+#define TRC_INITV 0x02 /* verbose init trace points */
+
+static short TokBaseAddrs[]={MMIOStartLocP, /* Addr-s to scan */
+ MMIOStartLocA};
+
+
+int tok_probe(struct device *dev);
+unsigned char get_sram_size(struct tok_info *adapt_info);
+
+static void tok_init_card(unsigned long dev_addr);
+static void tok_interrupt(int irq, struct pt_regs *regs);
+
+static void initial_tok_int(struct device *dev);
+
+static void open_sap(unsigned char type,struct device *dev);
+void tok_open_adapter(unsigned long dev_addr);
+static void tr_rx(struct device *dev);
+static void tr_tx(struct device *dev);
+
+static int tok_open(struct device *dev);
+static int tok_close(struct device *dev);
+static int tok_send_packet(struct sk_buff *skb, struct device *dev);
+static struct enet_statistics * tok_get_stats(struct device *dev);
+
+static struct timer_list tr_timer={NULL,NULL,0,0L,tok_open_adapter};
+
+int DummyCallCount=0;
+
+/* This routine combined with the #DEFINE DPRINTD serves
+ to workaround the gcc apparent bug. in tr_tx() */
+
+static void DummyCall(char * fmt,...) {DummyCallCount++;return;}
+
+static void PrtChanID(char *pcid, short stride) {
+ short i, j;
+ for (i=0,j=0;i<24;i++,j=j+stride) printk("%1x",((int) pcid[j])&0x0f);
+ printk("\n");
+}
+
+/* tok_probe(): Routine specified in the network device structure
+ to probe for an IBM Token Ring Adapter. Routine outline:
+ I. Interrogate hardware to determine if an adapter exists
+ and what the speeds and feeds are
+ II. Setup data structures to control execution based upon
+ adapter characteristics.
+ III. Initialize adapter operation
+ We expect tok_probe to be called once for each device entry
+ which references it.
+ */
+
+int tok_probe(struct device *dev) {
+
+ unsigned char segment=0, intr=0, irq=0, i=0, j=0,
+ cardpresent=NOTOK,temp=0;
+ unsigned char *t_mmio=0;
+ short PIOaddr=0, iAddr;
+ struct tok_info *ti=0;
+ static struct tok_info *badti=0; /* if fail after kmalloc, reuse */
+
+ static unsigned char Shared_Ram_Base = IBMTR_SHARED_RAM_BASE;
+
+ /* this is the major adapter probe loop. For each call to tok_probe,
+ we try each remaining entry in TokBaseAddrs[] as a possible
+ adapter. Once an entry is rejected or assigned, we zero it to
+ avoid duplicate use or worthless trial for the tok probe call*/
+
+ for (iAddr=0;
+ iAddr < (sizeof(TokBaseAddrs)/sizeof(short))&&PIOaddr==0;
+ iAddr++) { char *tchanid, *cd_chanid, ctemp;
+ PIOaddr=TokBaseAddrs[iAddr]; /* address to try */
+ TokBaseAddrs[iAddr] = 0; /* (and marked already used */
+ if (PIOaddr == 0) continue; /* already tried this addr */
+ if ( check_region(PIOaddr,4) ) { /* Make sure PIO address not
+ already assigned elsewhere before
+ we muck with I/O addresses */
+ if (ibmtr_debug_trace & TRC_INIT)
+ DPRINTK("check_region(%4hx,4) failed.\n",PIOaddr);
+ PIOaddr = 0; continue; /* clear to flag fail and try next */
+ }
+ /* Query the adapter PIO base port which will return
+ indication of where MMIO was placed (per tech ref
+ this assignment is done by BIOS - what is rational for
+ where it is?). We also have a coded interrupt address.*/
+
+ segment = inb(PIOaddr);
+ if (segment < 0x40 || segment > 0xe0) { /* out of range values
+ so we will assume non-existant IO dev */
+ PIOaddr = 0; continue; /* clear to flag fail and try next */
+ }
+
+ /* Compute the linear base address of the MMIO area
+ as LINUX doesn't care about segments */
+ t_mmio=(char *) (((segment & 0xfc) << 11) + 0x80000);
+ intr = segment & 0x03; /* low bits is coded interrupt # */
+ if (ibmtr_debug_trace & TRC_INIT)
+ DPRINTK("PIOaddr: %4hx seg/intr: %2x mmio base: %p intr: %d\n",
+ PIOaddr, (int) segment,t_mmio,(int) intr);
+ /* Now we will compare expected 'channelid' strings with
+ what we is there to learn of ISA/MCA or not TR card */
+ /* !!!WARNING:!!!! It seems pretty silly to blunder ahead
+ w/o verification that the mmio address we have found
+ is valid storage -- perhaps this is tolerable for current
+ hardware state??? */
+ cd_chanid = (char *)(CHANNEL_ID + t_mmio); /* for efficiency */
+ tchanid=pcchannelid; cardpresent=TR_ISA; /* try ISA ? */
+ /* suboptimize knowing first byte different */
+ ctemp = (* cd_chanid) & 0x0f;
+ if ( ctemp != *tchanid) { /* NOT ISA card, try MCA */
+ tchanid=mcchannelid; cardpresent=TR_MCA;
+ if ( ctemp != *tchanid) /* Neither ISA nor MCA */
+ cardpresent=NOTOK;
+ }
+ if (cardpresent != NOTOK) { /* know presumed type, try rest of ID */
+ for (i=2,j=1; i<=46; i=i+2,j++) {
+ if ( (cd_chanid[i] & 0x0f) != tchanid[j]) {
+ cardpresent=NOTOK; /* match failed, not TR card */
+ break;
+ }
+ }
+ }
+
+ /* If we have an ISA board check for the ISA P&P version, as it has
+ different IRQ settings */
+ if (cardpresent == TR_ISA && (*(AIPFID + t_mmio)==0x0e))
+ cardpresent=TR_ISAPNP;
+
+ if (cardpresent == NOTOK) { /* "channel_id" did not match, report */
+ if (ibmtr_debug_trace & TRC_INIT) {
+ DPRINTK("Channel ID string not found for PIOaddr: %4hx\n",
+ PIOaddr);
+ DPRINTK("Expected for ISA: "); PrtChanID(pcchannelid,1);
+ DPRINTK(" found: "); PrtChanID(cd_chanid,2);
+ DPRINTK("Expected for MCA: "); PrtChanID(mcchannelid,1);
+ }
+ PIOaddr = 0; /* all to know not found yet */
+ continue;
+ }
+
+ /* !!!! we could tighten validation by checking the HW Address
+ against the 1-s complement.. Move the get HW logic to here */
+
+ }
+
+ /* The search loop has either completed with a presumed TR adapter
+ or none found. Check situation ... march on if possible */
+
+ if (PIOaddr == 0) { /* failed to find a valid TR adapter */
+ if (ibmtr_debug_trace & TRC_INIT)
+ DPRINTK("Unable to assign adapter to device.\n");
+ return ENODEV;
+ }
+
+ /*?? Now, allocate some of the pl0 buffers for this driver.. */
+ /*?? Now, allocate some of the PIO PORTs for this driver.. */
+ request_region(PIOaddr,4,"ibmtr"); /* record PIOaddr range as busy */
+
+ if (!badti)
+ ti = (struct tok_info *)kmalloc(sizeof(struct tok_info), GFP_KERNEL);
+ else { ti = badti; badti = NULL; }/*?? dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); */
+ memset(ti,0,sizeof(struct tok_info));
+
+ ti->mmio= t_mmio;
+
+ dev->priv = ti; /* this seems like the logical use of the
+ field ... lets try some empirical tests
+ using the token-info structure -- that
+ should fit with out future hope of multiple
+ adapter support as well /dwm */
+
+ switch (cardpresent) {
+ case TR_ISA:
+ if (intr==0) irq=9; /* irq2 really is irq9 */
+ if (intr==1) irq=3;
+ if (intr==2) irq=6;
+ if (intr==3) irq=7;
+ ti->global_int_enable=GLOBAL_INT_ENABLE+((irq==9) ? 2 : irq);
+ ti->sram=NULL;
+ DPRINTK("ti->global_int_enable: %04X\n",ti->global_int_enable);
+ break;
+ case TR_MCA:
+ if (intr==0) irq=9;
+ if (intr==1) irq=3;
+ if (intr==2) irq=10;
+ if (intr==3) irq=11;
+ ti->global_int_enable=0;
+ ti->sram=(unsigned char *)((inb(PIOaddr+ADAPTRESETREL) & 0xfe)
+ << 12);
+ break;
+ case TR_ISAPNP:
+ if (intr==0) irq=9;
+ if (intr==1) irq=3;
+ if (intr==2) irq=10;
+ if (intr==3) irq=11;
+ while(!(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN));
+ ti->sram=(unsigned char *)((unsigned long)(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN) <<12);
+ ti->global_int_enable=PIOaddr+ADAPTINTREL;
+ break;
+
+ }
+
+ if (ibmtr_debug_trace & TRC_INIT) { /* just report int */
+ DPRINTK("irq=%d",irq);
+ if (ibmtr_debug_trace & TRC_INITV) { /* full chat in verbose only */
+ DPRINTK(", ti->mmio=%p",ti->mmio);
+ printk(", segment=%02X",segment);
+ }
+ printk(".\n");
+ }
+
+ DPRINTK("hw address: ");
+ /* Get hw address of token ring card */
+ j=0;
+ for (i=0; i<0x18; i=i+2) {
+ temp = *(char *)((ulong)AIP + (ulong)i + ti->mmio) & 0x0f; /* Tech ref states must do this */
+ printk("%1X",ti->hw_address[j]=temp);
+ if(j&1)
+ dev->dev_addr[(j/2)]=ti->hw_address[j]+(ti->hw_address[j-1]<<4);
+ ++j;
+ }
+ printk("\n");
+
+ /* get Adapter type: 'F' = Adapter/A, 'E' = 16/4 Adapter II,...*/
+ ti->adapter_type = *(char *)(ti->mmio + AIPADAPTYPE);
+
+ /* get Data Rate: F=4Mb, E=16Mb, D=4Mb & 16Mb ?? */
+ ti->data_rate = *(char *)(ti->mmio + AIPDATARATE);
+
+ /* Get Early Token Release support?: F=no, E=4Mb, D=16Mb, C=4&16Mb */
+ ti->token_release = *(char *)(ti->mmio + AIPEARLYTOKEN);
+
+ /* How much shared RAM is on adapter ? */
+ ti->avail_shared_ram = get_sram_size(ti);
+
+ /* We need to set or do a bunch of work here based on previous results.. */
+ /* Support paging? What sizes?: F=no, E=16k, D=32k, C=16 & 32k */
+ ti->shared_ram_paging = *(char *)(ti->mmio + AIPSHRAMPAGE);
+
+ /* Available DHB 4Mb size: F=2048, E=4096, D=4464 */
+ ti->dhb_size4mb = *(char *) (ti->mmio + AIP4MBDHB);
+
+ /* Available DHB 16Mb size: F=2048, E=4096, D=8192, C=16384, B=17960 */
+ ti->dhb_size16mb = *(char *)(ti->mmio + AIP16MBDHB);
+
+ DPRINTK("atype=%x, drate=%x, trel=%x, asram=%dK, srp=%x, dhb(4mb=%x, 16mb=%x)\n",ti->adapter_type,
+ ti->data_rate, ti->token_release, ti->avail_shared_ram/2, ti->shared_ram_paging, ti->dhb_size4mb,
+ ti->dhb_size16mb);
+
+ /* We must figure out how much shared memory space this adapter
+ will occupy so that if there are two adapters we can fit both
+ in. Given a choice, we will limit this adapter to 32K. The
+ maximum space will will use for two adapters is 64K so if the
+ adapter we are working on demands 64K (it also doesn't support
+ paging), then only one adapter can be supported. */
+
+ /* determine how much of total RAM is mapped into PC space */
+ ti->mapped_ram_size=1<<(((*(unsigned char *)
+ (ti->mmio+ ACA_OFFSET + ACA_RW + RRR_ODD))>>2)+4);
+ ti->page_mask=0;
+ if (ti->shared_ram_paging == 0xf) { /* No paging in adapter */
+ ti->mapped_ram_size = ti->avail_shared_ram;
+ } else { unsigned char pg_size;
+
+ DPRINTK("shared ram page size: %dK\n",ti->mapped_ram_size/2);
+#ifdef ENABLE_PAGING
+ switch(ti->shared_ram_paging) {
+ case 0xf: break;
+ case 0xe: ti->page_mask=(ti->mapped_ram_size==32) ? 0xc0 : 0;
+ pg_size=32; /* 16KB page size */
+ break;
+ case 0xd: ti->page_mask=(ti->mapped_ram_size==64) ? 0x80 : 0;
+ pg_size=64; /* 32KB page size */
+ break;
+ case 0xc: ti->page_mask=(ti->mapped_ram_size==32) ? 0xc0 : 0;
+ ti->page_mask=(ti->mapped_ram_size==64) ? 0x80 : 0;
+ DPRINTK("Dual size shared RAM page (code=0xC), don't support it!\n");
+ /* nb/dwm: I did this because RRR (3,2) bits are documented as
+ R/O and I can't find how to select which page size */
+ /* Also, the above conditional statement sequence is invalid */
+ /* as page_mask will always be set by the second stmt */
+ badti=ti;
+ break;
+ default: DPRINTK("Unknown shared ram paging info %01X\n",ti->shared_ram_paging);
+ badti=ti; /* bail out if bad code */
+ break;
+ }
+ if(ti->page_mask) {
+ if(pg_size > ti->mapped_ram_size) {
+ DPRINTK("Page size (%d) > mapped ram window (%d), can't page.\n",
+ pg_size, ti->mapped_ram_size);
+ ti->page_mask = 0; /* reset paging */
+ } else {
+ ti->mapped_ram_size=ti->avail_shared_ram; /****** ?????????? *******/
+ DPRINTK("Shared RAM paging enabled. Page size : %uK\n",((ti->page_mask^ 0xff)+1)>>2);
+ }
+ }
+#else
+#endif
+ }
+
+ if (cardpresent==TR_ISA) { /* finish figuring the shared RAM address */
+ static unsigned char ram_bndry_mask[]={0xfe, 0xfc, 0xf8, 0xf0};
+ unsigned char new_base, rrr_32, chk_base, rbm;
+ rrr_32 = (*(unsigned char *)
+ (ti->mmio+ ACA_OFFSET + ACA_RW + RRR_ODD))>>2;
+ rbm = ram_bndry_mask[rrr_32];
+ new_base = (Shared_Ram_Base + (~rbm)) & rbm; /* up to boundary */
+ chk_base = new_base + (ti->mapped_ram_size>>3);
+ if (chk_base > (IBMTR_SHARED_RAM_BASE+IBMTR_SHARED_RAM_SIZE)) {
+ DPRINTK("Shared RAM for this adapter (%05x) exceeds driver"
+ " limit (%05x), adapter not started.\n",
+ chk_base<<12, (IBMTR_SHARED_RAM_BASE+
+ IBMTR_SHARED_RAM_SIZE)<<12);
+ badti=ti;
+ } else { /* seems cool, record what we have figured out */
+ ti->sram_base = new_base;
+ Shared_Ram_Base = new_base;
+ }
+ }
+
+ /* dwm: irq and other final setup moved here so if we find other
+ unrecognized values OR shared ram conflicts, we can still
+ bail out in a rather benign fashion. */
+
+ if (badti) return ENODEV;
+
+ DPRINTK("Using %dK shared RAM\n",ti->mapped_ram_size/2);
+
+ if (request_irq (dev->irq = irq, &tok_interrupt,0,"IBM TR") != 0) {
+ DPRINTK("Could not grab irq %d. Halting Token Ring driver.\n",irq);
+ badti = ti; /* keep track of unused tok_info */
+ return ENODEV;
+ }
+ irq2dev_map[irq]=dev;
+
+ DPRINTK("%s",version); /* As we have passed card identification,
+ let the world know we're here! */
+ dev->base_addr=PIOaddr; /* set the value for device */
+
+ dev->open=tok_open;
+ dev->stop=tok_close;
+ dev->hard_start_xmit=tok_send_packet;
+ dev->get_stats = NULL;
+ dev->get_stats = tok_get_stats;
+ dev->set_multicast_list = NULL;
+ tr_setup(dev);
+ tok_init_card((unsigned long)dev);
+
+ return 0; /* Return 0 to indicate we have found a Token Ring card. */
+}
+
+/* query the adapter for the size of shared RAM */
+
+unsigned char get_sram_size(struct tok_info *adapt_info) {
+
+ unsigned char avail_sram_code;
+ static unsigned char size_code[]={ 0,16,32,64,127,128 };
+
+ /* Adapter gives
+ 'F' -- use RRR bits 3,2
+ 'E' -- 8kb 'D' -- 16kb
+ 'C' -- 32kb 'A' -- 64KB
+ 'B' - 64KB less 512 bytes at top
+ (WARNING ... must zero top bytes in INIT */
+
+ avail_sram_code=0xf-*(adapt_info->mmio + AIPAVAILSHRAM);
+ if(avail_sram_code)
+ return size_code[avail_sram_code];
+ else /* for code 'F', must compute size from RRR(3,2) bits */
+
+ return 1<<(((*(unsigned char *)
+ (adapt_info->mmio+ ACA_OFFSET + ACA_RW + RRR_ODD))>>2)+4);
+}
+
+static int tok_open(struct device *dev) {
+
+ struct tok_info *ti=(struct tok_info *)dev->priv;
+
+ if(ti->open_status==CLOSED) {
+ tok_init_card((unsigned long)dev);
+ }
+
+ if(ti->open_status==IN_PROGRESS) {
+ sleep_on(&ti->wait_for_reset);
+ }
+
+ if(ti->open_status==SUCCES) {
+ dev->tbusy=0;
+ dev->interrupt=0;
+ dev->start=1;
+ /* NEED to see smem size *AND* reset high 512 bytes if
+ needed */
+ return 0;
+ }
+ else
+ return -EAGAIN;
+
+}
+
+static int tok_close(struct device *dev) {
+
+ struct tok_info *ti=(struct tok_info *) dev->priv;
+
+ struct srb_close_adapter *close_adapter=(struct srb_close_adapter *)ti->srb;
+
+ close_adapter->command=DIR_CLOSE_ADAPTER;
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)=CMD_IN_SRB;
+
+ ti->open_status=CLOSED;
+
+ sleep_on(&ti->wait_for_tok_int);
+
+ if(close_adapter->ret_code)
+ DPRINTK("close adapter failed: %02X\n",close_adapter->ret_code);
+
+ return 0;
+}
+
+static void tok_interrupt (int irq, struct pt_regs *regs)
+{
+
+ unsigned char status;
+ struct tok_info *ti;
+/* int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2); */
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+#if 0
+ DPRINTK("Int from tok_driver, dev : %p\n",dev);
+#endif
+ ti=(struct tok_info *) dev->priv;
+
+ switch (ti->do_tok_int) {
+ case NOT_FIRST:
+
+ /* Begin the regular interrupt handler HERE inline to avoid
+ the extra levels of logic and call depth for the
+ original solution. */
+
+
+ dev->interrupt=1;
+
+ /* Disable interrupts till processing is finished */
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_EVEN)=(~INT_ENABLE);
+
+ /* Reset interrupt for ISA boards */
+ if(ti->global_int_enable)
+ outb(0,ti->global_int_enable);
+
+ status=*(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RW + ISRP_ODD);
+
+ if(status & ADAP_CHK_INT) {
+ int i;
+ unsigned char *check_reason=ti->mmio + ntohs(*(unsigned short *)(ti->mmio + ACA_OFFSET + ACA_RW +WWCR_EVEN));
+
+ DPRINTK("adapter check interrupt\n");
+
+ DPRINTK("8 reason bytes follow: ");
+ for(i=0;i< 8;i++,check_reason++)
+ printk("%02X ",*check_reason);
+ printk("\n");
+
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD)=(~ADAP_CHK_INT);
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN)=INT_ENABLE;
+ dev->interrupt=0;
+ }
+
+ else if((*(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RW + ISRP_EVEN)) & (TCR_INT + ERR_INT + ACCESS_INT)) {
+
+ DPRINTK("adapter error: ISRP_EVEN : %02x\n",
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RW + ISRP_EVEN));
+
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_EVEN)=~(TCR_INT + ERR_INT + ACCESS_INT);
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN)=INT_ENABLE;
+ dev->interrupt=0;
+ }
+
+ else if(status & (SRB_RESP_INT + ASB_FREE_INT + ARB_CMD_INT + SSB_RESP_INT)) {
+
+ if(status & SRB_RESP_INT) {
+ switch(*ti->srb) {
+ case XMIT_DIR_FRAME: {
+ struct srb_xmit *xmit=(struct srb_xmit *)(ti->srb);
+ if(xmit->ret_code!=0xff) {
+ DPRINTK("error on xmit_dir_frame request: %02X\n",xmit->ret_code);
+ if(ti->current_skb) {
+ dev_kfree_skb(ti->current_skb, FREE_WRITE);
+ ti->current_skb=NULL;
+ }
+ dev->tbusy=0;
+ }
+ }
+ break;
+
+ case XMIT_UI_FRAME: {
+ struct srb_xmit *xmit=(struct srb_xmit *)(ti->srb);
+ if(xmit->ret_code!=0xff) {
+ DPRINTK("error on xmit_ui_frame request: %02X\n",xmit->ret_code);
+ if(ti->current_skb) {
+ dev_kfree_skb(ti->current_skb, FREE_WRITE);
+ ti->current_skb=NULL;
+ }
+ dev->tbusy=0;
+ }
+ }
+ break;
+
+ case DIR_OPEN_ADAPTER: {
+ struct srb_open_response *open_response=(struct srb_open_response *)(ti->init_srb);
+
+ ti->srb=ti->sram+ntohs(open_response->srb_addr);
+ ti->ssb=ti->sram+ntohs(open_response->ssb_addr);
+ ti->arb=ti->sram+ntohs(open_response->arb_addr);
+ ti->asb=ti->sram+ntohs(open_response->asb_addr);
+ ti->current_skb=NULL;
+
+ if(open_response->ret_code==7) {
+ if(!ti->auto_ringspeedsave && (open_response->error_code==0x24)) {
+ DPRINTK("open failed: Adapter speed must match ring speed if Automatic Ring Speed Save is disabled\n");
+ ti->open_status=FAILURE;
+ wake_up(&ti->wait_for_reset);
+ }
+ else if(open_response->error_code==0x24)
+ DPRINTK("retrying open to adjust to ring speed\n");
+
+ else if((open_response->error_code==0x2d) && ti->auto_ringspeedsave)
+ DPRINTK("No signal detected for Auto Speed Detection\n");
+ else DPRINTK("Unrecoverable error: error code = %02X\n",open_response->error_code);
+ }
+ else if(!open_response->ret_code) {
+ DPRINTK("board opened...\n");
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD)=~(SRB_RESP_INT);
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RESET + ISRA_ODD)=~(CMD_IN_SRB);
+ open_sap(EXTENDED_SAP,dev);
+/* YdW probably hates me */
+ goto skip_reset;
+ }
+ else {
+ DPRINTK("open failed: ret_code = %02X, retrying\n",open_response->ret_code);
+ }
+ if(ti->open_status!=FAILURE) {
+ tr_timer.expires=TR_RETRY_INTERVAL;
+ tr_timer.data=(unsigned long)dev;
+ tr_timer.next=tr_timer.prev=NULL;
+ add_timer(&tr_timer);
+ }
+ }
+ break;
+
+ case DIR_CLOSE_ADAPTER:
+ wake_up(&ti->wait_for_tok_int);
+ break;
+ case DLC_OPEN_SAP: {
+ struct dlc_open_sap *open_sap=(struct dlc_open_sap *)ti->srb;
+ if(open_sap->ret_code) {
+ DPRINTK("open_sap failed: ret_code = %02X,retrying\n",open_sap->ret_code);
+ tr_timer.expires=TR_RETRY_INTERVAL;
+ tr_timer.data=(unsigned long)dev;
+ tr_timer.next=tr_timer.prev=NULL;
+ add_timer(&tr_timer);
+ }
+ else {
+ ti->exsap_station_id=open_sap->station_id;
+ ti->open_status=SUCCES; /* TR adapter is now available */
+ wake_up(&ti->wait_for_reset);
+ }
+ }
+ break;
+
+ case DIR_INTERRUPT:
+ case DIR_MOD_OPEN_PARAMS:
+ case DIR_SET_GRP_ADDR:
+ case DIR_SET_FUNC_ADDR:
+ case DLC_CLOSE_SAP: {
+ struct srb_interrupt *intr=(struct srb_interrupt *)(ti->srb);
+ if(intr->ret_code)
+ DPRINTK("error on %02X: %02X\n",intr->command,intr->ret_code);
+ }
+ break;
+
+ case DIR_READ_LOG: {
+ struct srb_read_log *read_log=(struct srb_read_log *)(ti->srb);
+ if(read_log->ret_code)
+ DPRINTK("error on dir_read_log: %02X\n",read_log->ret_code);
+ else {
+ DPRINTK("Line errors %02X, Internal errors %02X, Burst errors %02X\n",
+ read_log->line_errors,read_log->internal_errors,read_log->burst_errors);
+ DPRINTK("A/C errors %02X, Abort delimiters %02X, Lost frames %02X\n",
+ read_log->A_C_errors,read_log->abort_delimiters,read_log->lost_frames);
+ DPRINTK("Receive congestion count %02X, Frame copied errors %02X, Frequency errors %02X\n",
+ read_log->recv_congest_count,read_log->frame_copied_errors,read_log->frequency_errors);
+ DPRINTK("Token errors %02X\n",read_log->token_errors);
+ }
+ dev->tbusy=0;
+ }
+ break;
+
+ default:
+ DPRINTK("Unknown command %02X encountered\n",*(ti->srb));
+ }
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RESET + ISRA_ODD)=~(CMD_IN_SRB);
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD)=~(SRB_RESP_INT);
+skip_reset:
+ }
+
+ if(status & ASB_FREE_INT) {
+ switch(*ti->asb) {
+ case REC_DATA:
+ case XMIT_UI_FRAME:
+ case XMIT_DIR_FRAME:
+ if(*(ti->asb+2)!=0xff)
+ DPRINTK("ASB error %02X in cmd %02X\n", *(ti->asb+2),*(ti->asb));
+ break;
+ default:
+ DPRINTK("unknown command in asb %02X\n",*ti->asb);
+ }
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD)=~(ASB_FREE_INT);
+ }
+
+ if(status & ARB_CMD_INT) {
+ switch(*ti->arb) {
+ case DLC_STATUS: {
+ struct arb_dlc_status *dlc_status=(struct arb_dlc_status *)(ti->arb);
+ DPRINTK("DLC_STATUS new status: %02X on station %02X\n",ntohs(dlc_status->status),ntohs(dlc_status->station_id));
+ }
+ break;
+
+ case REC_DATA:
+ tr_rx(dev);
+ break;
+
+ case RING_STAT_CHANGE: {
+ struct arb_ring_stat_change *ring_stat_change=(struct arb_ring_stat_change *)(ti->arb);
+ unsigned short ring_status=ntohs(ring_stat_change->ring_status);
+
+ if(ring_status & (SIGNAL_LOSS + LOBE_FAULT)) {
+ DPRINTK("Signal loss/Lobe fault\n");
+ DPRINTK("We try to reopen the adapter.\n");
+ tr_timer.expires=TR_RETRY_INTERVAL;
+ tr_timer.data=(unsigned long)dev;
+ tr_timer.next=tr_timer.prev=NULL;
+ add_timer(&tr_timer);
+ } else if (ring_status & (HARD_ERROR + XMIT_BEACON +
+ AUTO_REMOVAL + REMOVE_RECV + RING_RECOVER))
+ DPRINTK("New ring status: %02X\n",ring_status);
+
+ if(ring_status & LOG_OVERFLOW) {
+ *(ti->srb)=DIR_READ_LOG;
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN)=INT_ENABLE;
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)=CMD_IN_SRB;
+ dev->tbusy=1; /* really srb busy... */
+ }
+ }
+ break;
+
+ case XMIT_DATA_REQ:
+ tr_tx(dev);
+ break;
+
+ default:
+ DPRINTK("Unknown command %02X in arb\n",*(ti->arb));
+ break;
+ }
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD)=~(ARB_CMD_INT);
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)=ARB_FREE;
+ }
+
+ if(status & SSB_RESP_INT) {
+ switch(*ti->ssb) {
+ case XMIT_DIR_FRAME:
+ case XMIT_UI_FRAME:
+ if(*(ti->ssb+2))
+ DPRINTK("xmit ret_code: %02X xmit error code: %02X\n",*(ti->ssb+2),*(ti->ssb+6));
+ else
+ ti->tr_stats.tx_packets++;
+ break;
+
+ case XMIT_XID_CMD:
+ DPRINTK("xmit xid ret_code: %02X\n",*(ti->ssb+2));
+
+ default:
+ DPRINTK("Unknown command %02X in ssb\n",*(ti->ssb));
+ }
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD)=~(SSB_RESP_INT);
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)=SSB_FREE;
+ }
+ }
+
+ dev->interrupt=0;
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN)=INT_ENABLE;
+
+ return;
+
+ break;
+ case FIRST_INT:
+ initial_tok_int(dev);
+ break;
+ default:
+ DPRINTK("Unexpected interrupt from tr adapter\n");
+ }
+
+}
+
+static void initial_tok_int(struct device *dev) {
+
+ int i;
+ unsigned char *encoded_addr;
+ struct tok_info *ti;
+
+ ti=(struct tok_info *) dev->priv;
+
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_EVEN)=(~INT_ENABLE);
+
+ /* Reset interrupt for ISA boards */
+ if(ti->global_int_enable)
+ outb(0,ti->global_int_enable);
+
+ ti->do_tok_int=NOT_FIRST;
+
+ DPRINTK("Initial tok int received\n");
+
+ if(!ti->sram) { /* we assign the address for ISA devices */
+ /* set RRR even to D000 for shared ram address */
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN)=
+ ti->sram_base;
+ ti->sram=(char *)(ti->sram_base << 12);
+ }
+ ti->init_srb=ti->sram+ntohs(*(unsigned short *)(ti->mmio+ ACA_OFFSET + WRBR_EVEN));
+ SET_PAGE(ntohs(*(unsigned short *)(ti->mmio+ ACA_OFFSET + WRBR_EVEN)));
+
+#if 1
+ DPRINTK("init_srb(%p):",ti->init_srb);
+ for(i=0;i<17;i++)
+ printk("%02X ",*(ti->init_srb+i));
+ printk("\n");
+#endif
+
+
+ DPRINTK("srb_init_response->encoded_address: %04X\n",((struct srb_init_response *)ti->init_srb)->encoded_address);
+ DPRINTK("ntohs(srb_init_response->encoded_address): %04X\n",ntohs(((struct srb_init_response *)ti->init_srb)->encoded_address));
+ encoded_addr=(unsigned char *)(ti->sram + ntohs(((struct srb_init_response *)ti->init_srb)->encoded_address));
+
+ DPRINTK("encoded addr (%04X,%04X,%p): ",
+ ((struct srb_init_response *)ti->init_srb)->encoded_address,
+ ntohs(((struct srb_init_response *)ti->init_srb)->encoded_address),
+ encoded_addr);
+ ti->auto_ringspeedsave=((struct srb_init_response *)ti->init_srb)->init_status_2 & 0x4 ? TRUE : FALSE;
+
+ for(i=0;i<TR_ALEN;i++)
+ printk("%02X%s",dev->dev_addr[i]=encoded_addr[i],(i==TR_ALEN-1) ? "" : ":" );
+ printk("\n");
+
+ tok_open_adapter((unsigned long)dev);
+
+}
+
+static void tok_init_card(unsigned long dev_addr) {
+
+ struct tok_info *ti;
+ short PIOaddr;
+ int i;
+ struct device *dev=(struct device *)dev_addr;
+ PIOaddr = dev->base_addr;
+ ti=(struct tok_info *) dev->priv;
+
+ /* Special processing for first interrupt after reset */
+ ti->do_tok_int=FIRST_INT;
+
+ /* Reset adapter */
+
+ dev->tbusy=1; /* nothing can be done before reset and open completed */
+
+#ifdef ENABLE_PAGING
+ if(ti->page_mask) {
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RW + SRPR_EVEN)=SRPR_ENABLE_PAGING;
+ }
+#endif
+
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_EVEN)=~(INT_ENABLE);
+ DPRINTK("resetting card\n");
+ outb(0,PIOaddr+ADAPTRESET);
+ for(i=jiffies+5;jiffies<=i;); /* wait 50ms */
+ outb(0,PIOaddr+ADAPTRESETREL);
+ DPRINTK("card reset\n");
+
+ ti->open_status=IN_PROGRESS;
+
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN)=INT_ENABLE;
+
+}
+
+static void open_sap(unsigned char type,struct device *dev) {
+
+ struct tok_info *ti=(struct tok_info *) dev->priv;
+ struct dlc_open_sap *open_sap=(struct dlc_open_sap *)ti->srb;
+
+ SET_PAGE(ti->srb);
+ memset(open_sap,0,sizeof(struct dlc_open_sap));
+
+ open_sap->command=DLC_OPEN_SAP;
+ open_sap->max_i_field=htons(MAX_I_FIELD);
+ open_sap->sap_options=SAP_OPEN_IND_SAP | SAP_OPEN_PRIORITY;
+ open_sap->station_count=SAP_OPEN_STATION_CNT;
+ open_sap->sap_value=type;
+
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)=CMD_IN_SRB;
+
+}
+
+void tok_open_adapter(unsigned long dev_addr) {
+
+ struct device *dev=(struct device *)dev_addr;
+ struct dir_open_adapter *open_adapter;
+ struct tok_info *ti;
+ ti=(struct tok_info *) dev->priv;
+
+ DPRINTK("now opening the board...\n");
+
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RESET + ISRP_ODD)=~(SRB_RESP_INT);
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_RESET + ISRA_ODD)=~(CMD_IN_SRB);
+
+ open_adapter=(struct dir_open_adapter *)(ti->init_srb);
+ memset(open_adapter,0,sizeof(struct dir_open_adapter));
+
+ open_adapter->command=DIR_OPEN_ADAPTER;
+ open_adapter->open_options=htons(OPEN_PASS_BCON_MAC);
+ open_adapter->num_rcv_buf=htons(NUM_RCV_BUF);
+ open_adapter->rcv_buf_len=htons(RCV_BUF_LEN);
+ open_adapter->dhb_length=htons(DHB_LENGTH);
+ open_adapter->num_dhb=NUM_DHB;
+ open_adapter->dlc_max_sap=DLC_MAX_SAP;
+ open_adapter->dlc_max_sta=DLC_MAX_STA;
+
+ ti->srb=ti->init_srb; /* We use this one in the interrupt handler */
+
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_SET + ISRP_EVEN)=INT_ENABLE;
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)=CMD_IN_SRB;
+
+}
+
+static void tr_tx(struct device *dev) {
+
+ struct tok_info *ti=(struct tok_info *) dev->priv;
+ struct asb_xmit_resp *xmit_resp=(struct asb_xmit_resp *)ti->asb;
+ struct arb_xmit_req *xmit_req=(struct arb_xmit_req *)ti->arb;
+ struct srb_xmit *xmit=(struct srb_xmit *)ti->srb;
+ unsigned int hdr_len;
+ unsigned char *dhb;
+
+/* */
+ DPRINTD("ti=%p asb=(%p,%p) arb=(%p,%p) srb=(%p,%p)\n",
+ ti , ti->asb, xmit_resp, ti->arb, xmit_req, ti->srb, xmit);
+/* */
+
+#if 0
+DPRINTK("transmitting...\n");
+#endif
+
+ if(xmit_resp->ret_code!=0xff) DPRINTK("ASB not free !!!\n");
+
+ /* in providing the transmit interrupts,
+ is telling us it is ready for data and
+ providing a shared memory address for us
+ to stuff with data. Here we compute the
+ effective address where we will place data.*/
+ dhb=ti->sram+ntohs(xmit_req->dhb_address);
+
+ xmit_resp->command=xmit->command;
+ xmit_resp->station_id=xmit_req->station_id;
+ xmit_resp->rsap_value=EXTENDED_SAP;
+ xmit_resp->cmd_corr=xmit_req->cmd_corr;
+ xmit_resp->ret_code=0;
+
+ if((xmit->command==XMIT_XID_CMD) || (xmit->command==XMIT_TEST_CMD)) {
+ xmit_resp->frame_length=htons(0x11);
+ xmit_resp->hdr_length=0x0e;
+ dhb[0]=AC;
+ dhb[1]=LLC_FRAME;
+ memset(dhb+2,(int)0x0ff,TR_ALEN);
+ memset(dhb+2+TR_ALEN,0,TR_ALEN);
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_SET
+ + ISRA_ODD)=RESP_IN_ASB;
+ return;
+ }
+
+ /* the token ring packet is copied from sk_buff to the adapter
+ buffer identified in the command data received with the
+ interrupt. The sk_buff area was set up with a maximum
+ sized route information field so here we must compress
+ out the extra (all) rif fields. */
+ /* nb/dwm .... I re-arranged code here to avoid copy of extra
+ bytes, ended up with fewer statements as well */
+
+ /* TR arch. identifies if RIF present by high bit of source
+ address. So here we check if RIF present */
+ if(!(((struct trh_hdr *)(&ti->current_skb->data))->saddr[0] & 0x80)) {
+ hdr_len=sizeof(struct trh_hdr)-18;
+#if 0
+DPRINTK(("hdr_length: %d, frame length: %ld\n",hdr_len,
+ ti->current_skb->len-18));
+#endif
+ } /* TR packet includes RIF data ... preserve it */
+ else {
+ hdr_len=((ntohs(((struct trh_hdr *)(&ti->current_skb->data))->rcf)
+ & TR_RCF_LEN_MASK)>>8)+sizeof(struct trh_hdr)-18;
+#if 0
+/* rework the following if activated, hdr_len <> rif_len */
+DPRINTK("rcf: %02X rif_len: %d\n",((struct trh_hdr *)&ti->current_skb->data)->rcf,wrk_len);
+DPRINTK("hdr_length: %d, frame length: %ld\n",sizeof(struct trh_hdr)-18+hdr_len,
+ ti->current_skb->len-18+hdr_len);
+#endif
+ }
+
+ /* header length including rif is computed above, now move the data
+ and set fields appropriately. */
+
+ memcpy(dhb,ti->current_skb->data,hdr_len);
+ dhb+=hdr_len;
+ xmit_resp->hdr_length= hdr_len;
+ xmit_resp->frame_length=htons(ti->current_skb->len
+ -sizeof(struct trh_hdr)+hdr_len);
+
+ /* now copy the actual packet data next to hdr */
+ memcpy(dhb,ti->current_skb->data+sizeof(struct trh_hdr),
+ ti->current_skb->len-sizeof(struct trh_hdr));
+
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)
+ =RESP_IN_ASB;
+ dev->tbusy=0;
+ dev_kfree_skb(ti->current_skb,FREE_WRITE);
+ ti->current_skb=NULL;
+ mark_bh(NET_BH);
+}
+
+static void tr_rx(struct device *dev) {
+
+ struct tok_info *ti=(struct tok_info *) dev->priv;
+
+ struct arb_rec_req *rec_req=(struct arb_rec_req *)ti->arb;
+ struct asb_rec *rec_resp=(struct asb_rec *)ti->asb;
+ struct rec_buf *rbuffer;
+ struct trllc *llc;
+ unsigned char *data;
+ unsigned int rbuffer_len,lan_hdr_len;
+ struct sk_buff *skb;
+
+ rbuffer=(struct rec_buf *)(ti->sram+ntohs(rec_req->rec_buf_addr));
+
+ if(rec_resp->ret_code!=0xff) DPRINTK("ASB not free !!!\n");
+
+ rec_resp->command=REC_DATA;
+ rec_resp->station_id=rec_req->station_id;
+ rec_resp->rec_buf_addr=rec_req->rec_buf_addr;
+
+ lan_hdr_len=rec_req->lan_hdr_len;
+
+ llc=(struct trllc *)((unsigned char *)rbuffer+offsetof(struct rec_buf,data)+lan_hdr_len);
+
+#if 0
+DPRINTK("offsetof data: %02X lan_hdr_len: %02X\n",offsetof(struct rec_buf,data),lan_hdr_len);
+DPRINTK("llc: %p rec_buf_addr: %04X ti->sram: %p\n",llc,ntohs(rec_req->rec_buf_addr),ti->sram);
+DPRINTK("dsap: %02X, ssap: %02X, llc: %02X, protid: %02X%02X%02X, ethertype: %04X\n",
+ llc->dsap,llc->ssap,llc->llc,llc->protid[0],llc->protid[1],llc->protid[2],llc->ethertype);
+#endif
+
+ if(llc->llc!=UI_CMD) {
+
+ DPRINTK("non-UI frame arrived. dropped. llc= %02X\n",llc->llc);
+ rec_resp->ret_code=DATA_LOST;
+ ti->tr_stats.rx_dropped++;
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)=RESP_IN_ASB;
+ return;
+ }
+
+#if 0
+ if((llc->dsap!=0xaa) || (llc->ssap!=0xaa)) {
+
+ struct trh_hdr *trhdr=(struct trh_hdr *)((unsigned char *)rbuffer+offsetof(struct rec_buf,data));
+
+DPRINTK("Probably non-IP frame received.\n");
+DPRINTK("ssap: %02X dsap: %02X saddr: %02X:%02X:%02X:%02X:%02X:%02X daddr: %02X:%02X:%02X:%02X:%02X:%02X\n",
+ llc->ssap,llc->dsap,trhdr->saddr[0],trhdr->saddr[1],trhdr->saddr[2],trhdr->saddr[3],trhdr->saddr[4],trhdr->saddr[5],
+ trhdr->daddr[0],trhdr->daddr[1],trhdr->daddr[2],trhdr->daddr[3],trhdr->daddr[4],trhdr->daddr[5]);
+ }
+#endif
+
+
+ if(!(skb=alloc_skb(ntohs(rec_req->frame_len)-lan_hdr_len+sizeof(struct trh_hdr), GFP_ATOMIC))) {
+ DPRINTK("out of memory. frame dropped.\n");
+ ti->tr_stats.rx_dropped++;
+ rec_resp->ret_code=DATA_LOST;
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)=RESP_IN_ASB;
+ return;
+ }
+
+ skb->len=ntohs(rec_req->frame_len)-lan_hdr_len+sizeof(struct trh_hdr);
+ skb->dev=dev;
+
+#if 0
+DPRINTK("Now copying data...\n");
+#endif
+
+
+ data=skb->data;
+ memcpy(data,&(rbuffer->data),lan_hdr_len);
+
+
+ if(lan_hdr_len<sizeof(struct trh_hdr))
+ memset(data+lan_hdr_len,0,sizeof(struct trh_hdr)-lan_hdr_len);
+
+ data+=sizeof(struct trh_hdr);
+ rbuffer_len=ntohs(rbuffer->buf_len)-lan_hdr_len;
+#if 0
+DPRINTK("rbuffer_len: %d, data: %p\n",rbuffer_len,data);
+#endif
+ memcpy(data,(unsigned char *)(&(rbuffer->data))+lan_hdr_len,rbuffer_len);
+ data+=rbuffer_len;
+
+
+ if(rbuffer->buf_ptr)
+ for(rbuffer=(struct rec_buf *)(ti->sram+ntohs(rbuffer->buf_ptr)-2);
+ memcpy(data,&(rbuffer->data),rbuffer_len=ntohs(rbuffer->buf_len)),rbuffer->buf_ptr;
+ data+=rbuffer_len,rbuffer=(struct rec_buf *)(ti->sram+ntohs(rbuffer->buf_ptr)-2))
+#if 0
+ DPRINTK("buf_ptr: %d,data =%p\n",ntohs(rbuffer->buf_ptr),data);
+#endif
+
+ rec_resp->ret_code=0;
+
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)=RESP_IN_ASB;
+
+ ti->tr_stats.rx_packets++;
+
+ skb->protocol=tr_type_trans(skb,dev);
+ netif_rx(skb);
+
+ return;
+}
+
+static int tok_send_packet(struct sk_buff *skb, struct device *dev) {
+
+ struct tok_info *ti=(struct tok_info *) dev->priv;
+
+#if 0
+DPRINTK("tada: sending packet...\n");
+#endif
+
+ if (dev->tbusy) {
+ int ticks_waited=jiffies - dev->trans_start;
+ if(ticks_waited<5)
+ return 1;
+ DPRINTK("Arrg. Transmitter busy for more than 50 msec. Donald resets adapter, but resetting\n \
+the IBM tokenring adapter takes a long time. It might not even help when the\n \
+ring is very busy, so we just wait a little longer and hope for the best.\n");
+ dev->trans_start+=5; /* we fake the transmission start time... */
+ return 1;
+ }
+
+ /* Donald does this, so we do too. */
+
+ if(skb==NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ if(set_bit(0,(void *)&dev->tbusy)!=0)
+ DPRINTK("Transmitter access conflict\n");
+ else {
+ struct srb_xmit *xmit=(struct srb_xmit *)ti->srb;
+
+ ti->current_skb=skb; /* save skb. We will need it when the adapter
+ asks for the data */
+ xmit->command=XMIT_UI_FRAME;
+ xmit->station_id=ti->exsap_station_id;
+ *(unsigned char *)(ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD)=CMD_IN_SRB;
+ dev->trans_start=jiffies;
+ }
+
+ return 0;
+}
+
+/* tok_get_stats(): Basically a scaffold routine which will return
+ the address of the tr_statistics structure associated with
+ this device -- the tr.... structure is a ethnet look-alike
+ so at least for this iteration may suffice. */
+
+static struct enet_statistics * tok_get_stats(struct device *dev) {
+
+ struct tok_info *toki;
+ toki=(struct tok_info *) dev->priv;
+ return (struct enet_statistics *) &toki->tr_stats;
+}
diff --git a/drivers/net/ibmtr.h b/drivers/net/ibmtr.h
new file mode 100644
index 000000000..e392a852b
--- /dev/null
+++ b/drivers/net/ibmtr.h
@@ -0,0 +1,426 @@
+/* Definitions for an IBM Token Ring card. */
+/* This file is distributed under the GNU GPL */
+
+#define TR_RETRY_INTERVAL 500
+#define TR_ISA 1
+#define TR_MCA 2
+#define TR_ISAPNP 3
+#define NOTOK 0
+#define TOKDEBUG 1
+
+#ifndef IBMTR_SHARED_RAM_BASE
+#define IBMTR_SHARED_RAM_BASE 0xD0
+#define IBMTR_SHARED_RAM_SIZE 0x10
+#endif
+
+#define CHANNEL_ID 0X1F30
+#define AIP 0X1F00
+#define AIPCHKSUM1 0X1F60
+#define AIPCHKSUM2 0X1FF0
+#define AIPADAPTYPE 0X1FA0
+#define AIPDATARATE 0X1FA2
+#define AIPEARLYTOKEN 0X1FA4
+#define AIPAVAILSHRAM 0X1FA6
+#define AIPSHRAMPAGE 0X1FA8
+#define AIP4MBDHB 0X1FAA
+#define AIP16MBDHB 0X1FAC
+#define AIPFID 0X1FBA
+
+/* Note, 0xA20 == 0x220 since motherboard decodes 10 bits. I left everything
+ the way my documentation had it, ie: 0x0A20. */
+#define ADAPTINTCNTRL 0x02f0 /* Adapter interrupt control */
+#define ADAPTRESET 0x1 /* Control Adapter reset (add to base) */
+#define ADAPTRESETREL 0x2 /* Release Adapter from reset ( """) */
+#define ADAPTINTREL 0x3 /* Adapter interrupt release */
+
+#define MMIOStartLocP 0x0a20 /* Primary adapter's starting MMIO area */
+#define MMIOStartLocA 0x0a24 /* Alternate adapter's starting MMIO area */
+
+#define GLOBAL_INT_ENABLE 0x02f0
+
+/* MMIO bits 0-4 select register */
+#define RRR_EVEN 0x00 /* Shared RAM relocation registers - even and odd */
+/* Used to set the starting address of shared RAM */
+/* Bits 1 through 7 of this register map to bits 13 through 19 of the shared RAM address.*/
+/* ie: 0x02 sets RAM address to ...ato! issy su wazzoo !! GODZILLA!!! */
+#define RRR_ODD 0x01
+/* Bits 2 and 3 of this register can be read to determine shared RAM size */
+/* 00 for 8k, 01 for 16k, 10 for 32k, 11 for 64k */
+#define WRBR_EVEN 0x02 /* Write region base registers - even and odd */
+#define WRBR_ODD 0x03
+#define WWCR_EVEN 0x04 /* Write window close registers - even and odd */
+#define WWCR_ODD 0x05
+#define WWOR_EVEN 0x06 /* Write window open registers - even and odd */
+#define WWOR_ODD 0x07
+
+/* Interrupt status registers - PC system - even and odd */
+#define ISRP_EVEN 0x08
+
+#define TCR_INT 0x10 /* Bit 4 - Timer interrupt. The TVR_EVEN timer has
+ expired. */
+#define ERR_INT 0x08 /* Bit 3 - Error interrupt. The adapter has had an
+ internal error. */
+#define ACCESS_INT 0x04 /* Bit 2 - Access interrupt. You have attempted to
+ write to an invalid area of shared RAM or an invalid
+ register within the MMIO. */
+/* In addition, the following bits within ISRP_EVEN can be turned on or off by you */
+/* to control the interrupt processing: */
+#define INT_IRQ 0x80 /* Bit 7 - If 0 the adapter will issue a CHCK, if 1 and
+ IRQ. This should normally be set (by you) to 1. */
+#define INT_ENABLE 0x40 /* Bit 6 - Interrupt enable. If 0, no interrupts will
+ occur. If 1, interrupts will occur normally.
+ Normally set to 1. */
+/* Bit 0 - Primary or alternate adapter. Set to zero if this adapter is the primary adapter,*/
+/* 1 if this adapter is the alternate adapter. */
+
+
+#define ISRP_ODD 0x09
+
+#define ADAP_CHK_INT 0x40 /* Bit 6 - Adapter check. the adapter has
+ encountered a serious problem and has closed
+ itself. Whoa. */
+#define SRB_RESP_INT 0x20 /* Bit 5 - SRB response. The adapter has accepted
+ an SRB request and set the return code withing
+ the SRB. */
+#define ASB_FREE_INT 0x10 /* Bit 4 - ASB free. The adapter has read the ASB
+ and this area can be safely reused. This interrupt
+ is only used if your application has set the ASB
+ free request bit in ISRA_ODD or if an error was
+ detected in your response. */
+#define ARB_CMD_INT 0x08 /* Bit 3 - ARB command. The adapter has given you a
+ command for action. The command is located in the
+ ARB area of shared memory. */
+#define SSB_RESP_INT 0x04 /* Bit 2 - SSB response. The adapter has posted a
+ response to your SRB (the response is located in
+ the SSB area of shared memory). */
+/* Bit 1 - Bridge frame forward complete. */
+
+
+
+#define ISRA_EVEN 0x0A /* Interrupt status registers - adapter - even and odd */
+/* Bit 7 - Internal parity error (on adapter's internal bus) */
+/* Bit 6 - Timer interrupt pending */
+/* Bit 5 - Access interrupt (attempt by adapter to access illegal address) */
+/* Bit 4 - Adapter microcode problem (microcode dead-man timer expired) */
+/* Bit 3 - Adapter processor check status */
+/* Bit 2 - Reserved */
+/* Bit 1 - Adapter hardware interrupt mask (prevents internal interrupts) */
+/* Bit 0 - Adapter software interrupt mask (prevents internal software interrupts) */
+
+#define ISRA_ODD 0x0B
+#define CMD_IN_SRB 0x20 /* Bit 5 - Indicates that you have placed a new
+ command in the SRB and are ready for the adapter to
+ process the command. */
+#define RESP_IN_ASB 0x10 /* Bit 4 - Indicates that you have placed a response
+ (an ASB) in the shared RAM which is available for
+ the adapter's use. */
+/* Bit 3 - Indicates that you are ready to ut an SRB in the shared RAM, but that a previous */
+/* command is still pending. The adapter will then interrupt you when the previous */
+/* command is completed */
+/* Bit 2 - Indicates that you are ready to put an ASB in the shared RAM, but that a previous */
+/* ASB is still pending. The adapter will then interrupt you when the previous ASB */
+/* is copied. */
+#define ARB_FREE 0x2
+#define SSB_FREE 0x1
+
+#define TCR_EVEN 0x0C /* Timer control registers - even and odd */
+#define TCR_ODD 0x0D
+#define TVR_EVEN 0x0E /* Timer value registers - even and odd */
+#define TVR_ODD 0x0F
+#define SRPR_EVEN 0x10 /* Shared RAM paging registers - even and odd */
+#define SRPR_ENABLE_PAGING 0xc0
+#define SRPR_ODD 0x11 /* Not used. */
+#define TOKREAD 0x60
+#define TOKOR 0x40
+#define TOKAND 0x20
+#define TOKWRITE 0x00
+
+/* MMIO bits 5-6 select operation */
+/* 00 is used to write to a register */
+/* 01 is used to bitwise AND a byte with a register */
+/* 10 is used to bitwise OR a byte with a register */
+/* 11 is used to read from a register */
+
+/* MMIO bits 7-8 select area of interest.. see below */
+/* 00 selects attachment control area. */
+/* 01 is reserved. */
+/* 10 selects adapter identification area A containing the adapter encoded address. */
+/* 11 selects the adapter identification area B containing test patterns. */
+
+#define PCCHANNELID 5049434F3631313039393020
+#define MCCHANNELID 4D4152533633583435313820
+
+#define ACA_OFFSET 0x1e00
+#define ACA_SET 0x40
+#define ACA_RESET 0x20
+#define ACA_RW 0x00
+
+#ifdef ENABLE_PAGING
+#define SET_PAGE(x) (*(unsigned char *) \
+ (ti->mmio + ACA_OFFSET + ACA_RW + SRPR_EVEN)\
+ = (x>>8)&ti.page_mask)
+#else
+#define SET_PAGE(x)
+#endif
+
+typedef enum { IN_PROGRESS, SUCCES, FAILURE, CLOSED } open_state;
+
+struct tok_info {
+ unsigned char irq;
+ unsigned char *mmio;
+ unsigned char hw_address[32];
+ unsigned char adapter_type;
+ unsigned char data_rate;
+ unsigned char token_release;
+ unsigned char avail_shared_ram;
+ unsigned char shared_ram_paging;
+ unsigned char dhb_size4mb;
+ unsigned char dhb_size16mb;
+/* Additions by David Morris */
+ unsigned char do_tok_int;
+#define FIRST_INT 1
+#define NOT_FIRST 2
+ struct wait_queue *wait_for_tok_int;
+ struct wait_queue *wait_for_reset;
+ unsigned char sram_base;
+/* Additions by Peter De Schrijver */
+ unsigned char page_mask; /* mask to select RAM page to Map*/
+ unsigned char mapped_ram_size; /* size of RAM page */
+ unsigned char *sram; /* Shared memory base address */
+ unsigned char *init_srb; /* Initial System Request Block address */
+ unsigned char *srb; /* System Request Block address */
+ unsigned char *ssb; /* System Status Block address */
+ unsigned char *arb; /* Adapter Request Block address */
+ unsigned char *asb; /* Adapter Status Block address */
+ unsigned short exsap_station_id;
+ unsigned short global_int_enable;
+ struct sk_buff *current_skb;
+ struct tr_statistics tr_stats;
+ unsigned char auto_ringspeedsave;
+ open_state open_status;
+
+};
+
+struct srb_init_response {
+ unsigned char command;
+ unsigned char init_status;
+ unsigned char init_status_2;
+ unsigned char reserved[3];
+ unsigned short bring_up_code;
+ unsigned short encoded_address;
+ unsigned short level_address;
+ unsigned short adapter_address;
+ unsigned short parms_address;
+ unsigned short mac_address;
+};
+
+#define DIR_OPEN_ADAPTER 0x03
+
+struct dir_open_adapter {
+ unsigned char command;
+ char reserved[7];
+ unsigned short open_options;
+ unsigned char node_address[6];
+ unsigned char group_address[4];
+ unsigned char funct_address[4];
+ unsigned short num_rcv_buf;
+ unsigned short rcv_buf_len;
+ unsigned short dhb_length;
+ unsigned char num_dhb;
+ char reserved2;
+ unsigned char dlc_max_sap;
+ unsigned char dlc_max_sta;
+ unsigned char dlc_max_gsap;
+ unsigned char dlc_max_gmem;
+ unsigned char dlc_t1_tick_1;
+ unsigned char dlc_t2_tick_1;
+ unsigned char dlc_ti_tick_1;
+ unsigned char dlc_t1_tick_2;
+ unsigned char dlc_t2_tick_2;
+ unsigned char dlc_ti_tick_2;
+ unsigned char product_id[18];
+};
+
+struct srb_open_response {
+ unsigned char command;
+ unsigned char reserved1;
+ unsigned char ret_code;
+ unsigned char reserved2[3];
+ unsigned short error_code;
+ unsigned short asb_addr;
+ unsigned short srb_addr;
+ unsigned short arb_addr;
+ unsigned short ssb_addr;
+};
+
+/* DIR_OPEN_ADAPTER options */
+
+#define OPEN_PASS_BCON_MAC 0x0100
+#define NUM_RCV_BUF 16
+#define RCV_BUF_LEN 136
+#define DHB_LENGTH 2048
+#define NUM_DHB 2
+#define DLC_MAX_SAP 2
+#define DLC_MAX_STA 1
+
+#define DLC_OPEN_SAP 0x15
+
+struct dlc_open_sap {
+ unsigned char command;
+ unsigned char reserved1;
+ unsigned char ret_code;
+ unsigned char reserved2;
+ unsigned short station_id;
+ unsigned char timer_t1;
+ unsigned char timer_t2;
+ unsigned char timer_ti;
+ unsigned char maxout;
+ unsigned char maxin;
+ unsigned char maxout_incr;
+ unsigned char max_retry_count;
+ unsigned char gsap_max_mem;
+ unsigned short max_i_field;
+ unsigned char sap_value;
+ unsigned char sap_options;
+ unsigned char station_count;
+ unsigned char sap_gsap_mem;
+ unsigned char gsap[0];
+};
+
+/* DLC_OPEN_SAP options */
+
+#define MAX_I_FIELD 0x0088
+#define SAP_OPEN_IND_SAP 0x04
+#define SAP_OPEN_PRIORITY 0x20
+#define SAP_OPEN_STATION_CNT 0x1
+
+#define XMIT_DIR_FRAME 0x0a
+#define XMIT_UI_FRAME 0x0d
+#define XMIT_XID_CMD 0x0e
+#define XMIT_TEST_CMD 0x11
+
+struct srb_xmit {
+ unsigned char command;
+ unsigned char cmd_corr;
+ unsigned char ret_code;
+ unsigned char reserved1;
+ unsigned short station_id;
+};
+
+#define DIR_INTERRUPT 0x00
+struct srb_interrupt {
+ unsigned char command;
+ unsigned char cmd_corr;
+ unsigned char ret_code;
+};
+
+#define DIR_READ_LOG 0x08
+struct srb_read_log {
+ unsigned char command;
+ unsigned char reserved1;
+ unsigned char ret_code;
+ unsigned char reserved2;
+ unsigned char line_errors;
+ unsigned char internal_errors;
+ unsigned char burst_errors;
+ unsigned char A_C_errors;
+ unsigned char abort_delimiters;
+ unsigned char reserved3;
+ unsigned char lost_frames;
+ unsigned char recv_congest_count;
+ unsigned char frame_copied_errors;
+ unsigned char frequency_errors;
+ unsigned char token_errors;
+};
+
+struct asb_xmit_resp {
+ unsigned char command;
+ unsigned char cmd_corr;
+ unsigned char ret_code;
+ unsigned char reserved;
+ unsigned short station_id;
+ unsigned short frame_length;
+ unsigned char hdr_length;
+ unsigned char rsap_value;
+};
+
+#define XMIT_DATA_REQ 0x82
+struct arb_xmit_req {
+ unsigned char command;
+ unsigned char cmd_corr;
+ unsigned char reserved1[2];
+ unsigned short station_id;
+ unsigned short dhb_address;
+};
+
+#define REC_DATA 0x81
+struct arb_rec_req {
+ unsigned char command;
+ unsigned char reserved1[3];
+ unsigned short station_id;
+ unsigned short rec_buf_addr;
+ unsigned char lan_hdr_len;
+ unsigned char dlc_hdr_len;
+ unsigned short frame_len;
+ unsigned char msg_type;
+};
+
+#define DATA_LOST 0x20
+struct asb_rec {
+ unsigned char command;
+ unsigned char reserved1;
+ unsigned char ret_code;
+ unsigned char reserved2;
+ unsigned short station_id;
+ unsigned short rec_buf_addr;
+};
+
+struct rec_buf {
+ unsigned char reserved1[2];
+ unsigned short buf_ptr;
+ unsigned char reserved2;
+ unsigned short buf_len;
+ unsigned char data[0];
+};
+
+#define DLC_STATUS 0x83
+struct arb_dlc_status {
+ unsigned char command;
+ unsigned char reserved1[3];
+ unsigned short station_id;
+ unsigned short status;
+ unsigned char frmr_data[5];
+ unsigned char access_prio;
+ unsigned char rem_addr[TR_ALEN];
+ unsigned char rsap_value;
+};
+
+#define RING_STAT_CHANGE 0x84
+struct arb_ring_stat_change {
+ unsigned char command;
+ unsigned char reserved1[5];
+ unsigned short ring_status;
+};
+
+#define DIR_CLOSE_ADAPTER 0x04
+struct srb_close_adapter {
+ unsigned char command;
+ unsigned char reserved1;
+ unsigned char ret_code;
+};
+
+#define DIR_MOD_OPEN_PARAMS 0x01
+#define DIR_SET_GRP_ADDR 0x06
+#define DIR_SET_FUNC_ADDR 0x07
+#define DLC_CLOSE_SAP 0x16
+
+
+#define SIGNAL_LOSS 0x8000
+#define HARD_ERROR 0x4000
+#define XMIT_BEACON 0x1000
+#define LOBE_FAULT 0x0800
+#define AUTO_REMOVAL 0x0400
+#define REMOVE_RECV 0x0100
+#define LOG_OVERFLOW 0x0080
+#define RING_RECOVER 0x0020
+
diff --git a/drivers/net/lance.c b/drivers/net/lance.c
index d366d5f10..a58efcd4c 100644
--- a/drivers/net/lance.c
+++ b/drivers/net/lance.c
@@ -1,6 +1,6 @@
/* lance.c: An AMD LANCE ethernet driver for linux. */
/*
- Written 1993-94 by Donald Becker.
+ Written 1993,1994,1995 by Donald Becker.
Copyright 1993 United States Government as represented by the
Director, National Security Agency.
@@ -15,7 +15,7 @@
Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
*/
-static char *version = "lance.c:v1.05 9/23/94 becker@cesdis.gsfc.nasa.gov\n";
+static char *version = "lance.c:v1.08 4/10/95 dplatt@3do.com\n";
#include <linux/config.h>
#include <linux/kernel.h>
@@ -26,6 +26,8 @@ static char *version = "lance.c:v1.05 9/23/94 becker@cesdis.gsfc.nasa.gov\n";
#include <linux/ioport.h>
#include <linux/malloc.h>
#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>
@@ -37,7 +39,7 @@ static char *version = "lance.c:v1.05 9/23/94 becker@cesdis.gsfc.nasa.gov\n";
struct device *init_etherdev(struct device *dev, int sizeof_private,
unsigned long *mem_startp);
static unsigned int lance_portlist[] = {0x300, 0x320, 0x340, 0x360, 0};
-unsigned long lance_probe1(short ioaddr, unsigned long mem_start);
+unsigned long lance_probe1(int ioaddr, unsigned long mem_start);
#ifdef HAVE_DEVLIST
struct netdev_entry lance_drv =
@@ -72,7 +74,7 @@ have on-board buffer memory needed to support the slower shared memory mode.)
Most ISA boards have jumpered settings for the I/O base, IRQ line, and DMA
channel. This driver probes the likely base addresses:
{0x300, 0x320, 0x340, 0x360}.
-After the board is found it generates an DMA-timeout interrupt and uses
+After the board is found it generates a DMA-timeout interrupt and uses
autoIRQ to find the IRQ line. The DMA channel can be set with the low bits
of the otherwise-unused dev->mem_start value (aka PARAM1). If unset it is
probed for by enabling each free DMA channel in turn and checking if
@@ -102,7 +104,7 @@ statically allocates full-sized (slightly oversized -- PKT_BUF_SZ) buffers to
avoid the administrative overhead. For the Rx side this avoids dynamically
allocating full-sized buffers "just in case", at the expense of a
memory-to-memory data copy for each packet received. For most systems this
-is an good tradeoff: the Rx buffer will always be in low memory, the copy
+is a good tradeoff: the Rx buffer will always be in low memory, the copy
is inexpensive, and it primes the cache for later packet processing. For Tx
the buffers are only used when needed as low-memory bounce buffers.
@@ -186,7 +188,8 @@ struct lance_init_block {
};
struct lance_private {
- char devname[8];
+ char *name;
+ void *pad;
/* The Tx and Rx ring entries must aligned on 8-byte boundaries. */
struct lance_rx_head rx_ring[RX_RING_SIZE];
struct lance_tx_head tx_ring[TX_RING_SIZE];
@@ -200,12 +203,18 @@ struct lance_private {
int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
int dma;
struct enet_statistics stats;
- char chip_version; /* See lance_chip_type. */
+ unsigned char chip_version; /* See lance_chip_type. */
char tx_full;
char lock;
int pad0, pad1; /* Used for 8-byte alignment */
};
+#define LANCE_MUST_PAD 0x00000001
+#define LANCE_ENABLE_AUTOSELECT 0x00000002
+#define LANCE_MUST_REINIT_RING 0x00000004
+#define LANCE_MUST_UNRESET 0x00000008
+#define LANCE_HAS_MISSED_FRAME 0x00000010
+
/* A mapping from the chip ID number to the part number and features.
These are from the datasheets -- in real life the '970 version
reportedly has the same ID as the '965. */
@@ -214,21 +223,37 @@ static struct lance_chip_type {
char *name;
int flags;
} chip_table[] = {
- {0x0000, "LANCE 7990", 0}, /* Ancient lance chip. */
- {0x0003, "PCnet/ISA 79C960", 0}, /* 79C960 PCnet/ISA. */
- {0x2260, "PCnet/ISA+ 79C961", 0}, /* 79C961 PCnet/ISA+, Plug-n-Play. */
- {0x2420, "PCnet/PCI 79C970", 0}, /* 79C970 or 79C974 PCnet-SCSI, PCI. */
- {0x2430, "PCnet/VLB 79C965", 0}, /* 79C965 PCnet for VL bus. */
- {0x0, "PCnet (unknown)", 0},
+ {0x0000, "LANCE 7990", /* Ancient lance chip. */
+ LANCE_MUST_PAD + LANCE_MUST_UNRESET},
+ {0x0003, "PCnet/ISA 79C960", /* 79C960 PCnet/ISA. */
+ LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+ LANCE_HAS_MISSED_FRAME},
+ {0x2260, "PCnet/ISA+ 79C961", /* 79C961 PCnet/ISA+, Plug-n-Play. */
+ LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+ LANCE_HAS_MISSED_FRAME},
+ {0x2420, "PCnet/PCI 79C970", /* 79C970 or 79C974 PCnet-SCSI, PCI. */
+ LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+ LANCE_HAS_MISSED_FRAME},
+ /* Bug: the PCnet/PCI actually uses the PCnet/VLB ID number, so just call
+ it the PCnet32. */
+ {0x2430, "PCnet32", /* 79C965 PCnet for VL bus. */
+ LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+ LANCE_HAS_MISSED_FRAME},
+ {0x0, "PCnet (unknown)",
+ LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+ LANCE_HAS_MISSED_FRAME},
};
enum {OLD_LANCE = 0, PCNET_ISA=1, PCNET_ISAP=2, PCNET_PCI=3, PCNET_VLB=4, LANCE_UNKNOWN=5};
+/* Non-zero only if the current card is a PCI with BIOS-set IRQ. */
+static unsigned char pci_irq_line = 0;
+
static int lance_open(struct device *dev);
static void lance_init_ring(struct device *dev);
static int lance_start_xmit(struct sk_buff *skb, struct device *dev);
static int lance_rx(struct device *dev);
-static void lance_interrupt(int reg_ptr);
+static void lance_interrupt(int irq, struct pt_regs *regs);
static int lance_close(struct device *dev);
static struct enet_statistics *lance_get_stats(struct device *dev);
#ifdef HAVE_MULTICAST
@@ -239,12 +264,53 @@ static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
/* This lance probe is unlike the other board probes in 1.0.*. The LANCE may
have to allocate a contiguous low-memory region for bounce buffers.
- This requirement is satisfied by having the lance initialization occur before the
- memory management system is started, and thus well before the other probes. */
+ This requirement is satisfied by having the lance initialization occur
+ before the memory management system is started, and thus well before the
+ other probes. */
+
unsigned long lance_init(unsigned long mem_start, unsigned long mem_end)
{
int *port;
+#if defined(CONFIG_PCI)
+ if (pcibios_present()) {
+ int pci_index;
+ printk("lance.c: PCI bios is present, checking for devices...\n");
+ for (pci_index = 0; pci_index < 8; pci_index++) {
+ unsigned char pci_bus, pci_device_fn;
+ unsigned long pci_ioaddr;
+ unsigned short pci_command;
+
+ if (pcibios_find_device (PCI_VENDOR_ID_AMD,
+ PCI_DEVICE_ID_AMD_LANCE, pci_index,
+ &pci_bus, &pci_device_fn) != 0)
+ break;
+ pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &pci_irq_line);
+ pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &pci_ioaddr);
+ /* Remove I/O space marker in bit 0. */
+ pci_ioaddr &= ~3;
+ /* PCI Spec 2.1 states that it is either the driver or PCI card's
+ * responsibility to set the PCI Master Enable Bit if needed.
+ * (From Mark Stockton <marks@schooner.sys.hou.compaq.com>)
+ */
+ pcibios_read_config_word(pci_bus, pci_device_fn,
+ PCI_COMMAND, &pci_command);
+ if ( ! (pci_command & PCI_COMMAND_MASTER)) {
+ printk("PCI Master Bit has not been set. Setting...\n");
+ pci_command |= PCI_COMMAND_MASTER;
+ pcibios_write_config_word(pci_bus, pci_device_fn,
+ PCI_COMMAND, pci_command);
+ }
+ printk("Found PCnet/PCI at %#lx, irq %d (mem_start is %#lx).\n",
+ pci_ioaddr, pci_irq_line, mem_start);
+ mem_start = lance_probe1(pci_ioaddr, mem_start);
+ pci_irq_line = 0;
+ }
+ }
+#endif /* defined(CONFIG_PCI) */
+
for (port = lance_portlist; *port; port++) {
int ioaddr = *port;
@@ -258,12 +324,13 @@ unsigned long lance_init(unsigned long mem_start, unsigned long mem_end)
return mem_start;
}
-unsigned long lance_probe1(short ioaddr, unsigned long mem_start)
+unsigned long lance_probe1(int ioaddr, unsigned long mem_start)
{
struct device *dev;
struct lance_private *lp;
short dma_channels; /* Mark spuriously-busy DMA channels */
int i, reset_val, lance_version;
+ char *chipname;
/* Flags for specific chips or boards. */
unsigned char hpJ2405A = 0; /* HP ISA adaptor */
int hp_builtin = 0; /* HP on-board ethernet. */
@@ -321,7 +388,8 @@ unsigned long lance_probe1(short ioaddr, unsigned long mem_start)
+ PKT_BUF_SZ*(RX_RING_SIZE + TX_RING_SIZE),
&mem_start);
- printk("%s: %s at %#3x,", dev->name, chip_table[lance_version].name, ioaddr);
+ chipname = chip_table[lance_version].name;
+ printk("%s: %s at %#3x,", dev->name, chipname, ioaddr);
/* There is a 16 byte station address PROM at the base address.
The first six bytes are the station address. */
@@ -329,11 +397,12 @@ unsigned long lance_probe1(short ioaddr, unsigned long mem_start)
printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i));
dev->base_addr = ioaddr;
- snarf_region(ioaddr, LANCE_TOTAL_SIZE);
+ request_region(ioaddr, LANCE_TOTAL_SIZE, chip_table[lance_version].name);
/* Make certain the data structures used by the LANCE are aligned. */
dev->priv = (void *)(((int)dev->priv + 7) & ~7);
lp = (struct lance_private *)dev->priv;
+ lp->name = chipname;
lp->rx_buffs = (long)dev->priv + sizeof(struct lance_private);
lp->tx_bounce_buffs = (char (*)[PKT_BUF_SZ])
(lp->rx_buffs + PKT_BUF_SZ*RX_RING_SIZE);
@@ -365,7 +434,10 @@ unsigned long lance_probe1(short ioaddr, unsigned long mem_start)
outw(0x0000, ioaddr+LANCE_ADDR);
inw(ioaddr+LANCE_ADDR);
- if (hp_builtin) {
+ if (pci_irq_line) {
+ dev->dma = 4; /* Native bus-master, no DMA channel needed. */
+ dev->irq = pci_irq_line;
+ } else if (hp_builtin) {
char dma_tbl[4] = {3, 5, 6, 0};
char irq_tbl[8] = {3, 4, 5, 9};
unsigned char port_val = inb(hp_builtin);
@@ -425,7 +497,7 @@ unsigned long lance_probe1(short ioaddr, unsigned long mem_start)
if (dev->dma == 4) {
printk(", no DMA needed.\n");
} else if (dev->dma) {
- if (request_dma(dev->dma, "lance")) {
+ if (request_dma(dev->dma, chipname)) {
printk("DMA %d allocation failed.\n", dev->dma);
return mem_start;
} else
@@ -441,7 +513,7 @@ unsigned long lance_probe1(short ioaddr, unsigned long mem_start)
if (test_bit(dma, &dma_channels))
continue;
outw(0x7f04, ioaddr+LANCE_DATA); /* Clear the memory error bits. */
- if (request_dma(dma, "lance"))
+ if (request_dma(dma, chipname))
continue;
set_dma_mode(dma, DMA_MODE_CASCADE);
enable_dma(dma);
@@ -466,7 +538,7 @@ unsigned long lance_probe1(short ioaddr, unsigned long mem_start)
}
}
- if (lp->chip_version != OLD_LANCE) {
+ if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) {
/* Turn on auto-select of media (10baseT or BNC) so that the user
can watch the LEDs even if the board isn't opened. */
outw(0x0002, ioaddr+LANCE_ADDR);
@@ -494,7 +566,8 @@ lance_open(struct device *dev)
int ioaddr = dev->base_addr;
int i;
- if (request_irq(dev->irq, &lance_interrupt, 0, "lance")) {
+ if (dev->irq == 0 ||
+ request_irq(dev->irq, &lance_interrupt, 0, lp->name)) {
return -EAGAIN;
}
@@ -513,10 +586,10 @@ lance_open(struct device *dev)
}
/* Un-Reset the LANCE, needed only for the NE2100. */
- if (lp->chip_version == OLD_LANCE)
+ if (chip_table[lp->chip_version].flags & LANCE_MUST_UNRESET)
outw(0, ioaddr+LANCE_RESET);
- if (lp->chip_version != OLD_LANCE) {
+ if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) {
/* This is 79C960-specific: Turn on auto-select of media (AUI, BNC). */
outw(0x0002, ioaddr+LANCE_ADDR);
outw(0x0002, ioaddr+LANCE_BUS_IF);
@@ -535,7 +608,7 @@ lance_open(struct device *dev)
outw(((int)&lp->init_block) >> 16, ioaddr+LANCE_DATA);
outw(0x0004, ioaddr+LANCE_ADDR);
- outw(0x0d15, ioaddr+LANCE_DATA);
+ outw(0x0915, ioaddr+LANCE_DATA);
outw(0x0000, ioaddr+LANCE_ADDR);
outw(0x0001, ioaddr+LANCE_DATA);
@@ -547,7 +620,11 @@ lance_open(struct device *dev)
while (i++ < 100)
if (inw(ioaddr+LANCE_DATA) & 0x0100)
break;
- outw(0x0142, ioaddr+LANCE_DATA);
+ /*
+ * We used to clear the InitDone bit, 0x0100, here but Mark Stockton
+ * reports that doing so triggers a bug in the '974.
+ */
+ outw(0x0042, ioaddr+LANCE_DATA);
if (lance_debug > 2)
printk("%s: LANCE open after %d ticks, init block %#x csr0 %4.4x.\n",
@@ -556,6 +633,33 @@ lance_open(struct device *dev)
return 0; /* Always succeed */
}
+/* The LANCE has been halted for one reason or another (busmaster memory
+ arbitration error, Tx FIFO underflow, driver stopped it to reconfigure,
+ etc.). Modern LANCE variants always reload their ring-buffer
+ configuration when restarted, so we must reinitialize our ring
+ context before restarting. As part of this reinitialization,
+ find all packets still on the Tx ring and pretend that they had been
+ sent (in effect, drop the packets on the floor) - the higher-level
+ protocols will time out and retransmit. It'd be better to shuffle
+ these skbs to a temp list and then actually re-Tx them after
+ restarting the chip, but I'm too lazy to do so right now. dplatt@3do.com
+*/
+
+static void
+lance_purge_tx_ring(struct device *dev)
+{
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+ int i;
+
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ if (lp->tx_skbuff[i]) {
+ dev_kfree_skb(lp->tx_skbuff[i],FREE_WRITE);
+ lp->tx_skbuff[i] = NULL;
+ }
+ }
+}
+
+
/* Initialize the LANCE Rx and Tx rings. */
static void
lance_init_ring(struct device *dev)
@@ -586,12 +690,27 @@ lance_init_ring(struct device *dev)
lp->init_block.tx_ring = (int)lp->tx_ring | TX_RING_LEN_BITS;
}
+static void
+lance_restart(struct device *dev, unsigned int csr0_bits, int must_reinit)
+{
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+
+ if (must_reinit ||
+ (chip_table[lp->chip_version].flags & LANCE_MUST_REINIT_RING)) {
+ lance_purge_tx_ring(dev);
+ lance_init_ring(dev);
+ }
+ outw(0x0000, dev->base_addr + LANCE_ADDR);
+ outw(csr0_bits, dev->base_addr + LANCE_DATA);
+}
+
static int
lance_start_xmit(struct sk_buff *skb, struct device *dev)
{
struct lance_private *lp = (struct lance_private *)dev->priv;
int ioaddr = dev->base_addr;
int entry;
+ unsigned long flags;
/* Transmitter timeout, serious problems. */
if (dev->tbusy) {
@@ -620,8 +739,7 @@ lance_start_xmit(struct sk_buff *skb, struct device *dev)
printk("\n");
}
#endif
- lance_init_ring(dev);
- outw(0x0043, ioaddr+LANCE_DATA);
+ lance_restart(dev, 0x0043, 1);
dev->tbusy=0;
dev->trans_start = jiffies;
@@ -667,7 +785,7 @@ lance_start_xmit(struct sk_buff *skb, struct device *dev)
with the "ownership" bits last. */
/* The old LANCE chips doesn't automatically pad buffers to min. size. */
- if (lp->chip_version == OLD_LANCE) {
+ if (chip_table[lp->chip_version].flags & LANCE_MUST_PAD) {
lp->tx_ring[entry].length =
-(ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN);
} else
@@ -697,25 +815,26 @@ lance_start_xmit(struct sk_buff *skb, struct device *dev)
dev->trans_start = jiffies;
+ save_flags(flags);
cli();
lp->lock = 0;
if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0)
dev->tbusy=0;
else
lp->tx_full = 1;
- sti();
+ restore_flags(flags);
return 0;
}
/* The LANCE interrupt handler. */
static void
-lance_interrupt(int reg_ptr)
+lance_interrupt(int irq, struct pt_regs * regs)
{
- int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
struct device *dev = (struct device *)(irq2dev_map[irq]);
struct lance_private *lp;
int csr0, ioaddr, boguscnt=10;
+ int must_restart;
if (dev == NULL) {
printk ("lance_interrupt(): irq %d for unknown device.\n", irq);
@@ -735,6 +854,8 @@ lance_interrupt(int reg_ptr)
/* Acknowledge all of the current interrupt sources ASAP. */
outw(csr0 & ~0x004f, dev->base_addr + LANCE_DATA);
+ must_restart = 0;
+
if (lance_debug > 5)
printk("%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n",
dev->name, csr0, inw(dev->base_addr + LANCE_DATA));
@@ -768,7 +889,7 @@ lance_interrupt(int reg_ptr)
printk("%s: Tx FIFO error! Status %4.4x.\n",
dev->name, csr0);
/* Restart the chip. */
- outw(0x0002, dev->base_addr + LANCE_DATA);
+ must_restart = 1;
}
} else {
if (status & 0x18000000)
@@ -811,7 +932,14 @@ lance_interrupt(int reg_ptr)
printk("%s: Bus master arbitration failure, status %4.4x.\n",
dev->name, csr0);
/* Restart the chip. */
- outw(0x0002, dev->base_addr + LANCE_DATA);
+ must_restart = 1;
+ }
+
+ if (must_restart) {
+ /* stop the chip to clear the error condition, then restart */
+ outw(0x0000, dev->base_addr + LANCE_ADDR);
+ outw(0x0004, dev->base_addr + LANCE_DATA);
+ lance_restart(dev, 0x0002, 0);
}
}
@@ -840,7 +968,7 @@ lance_rx(struct device *dev)
int status = lp->rx_ring[entry].base >> 24;
if (status != 0x03) { /* There was an error. */
- /* There is an tricky error noted by John Murphy,
+ /* There is a tricky error noted by John Murphy,
<murf@perftech.com> to Russ Nelson: Even with full-sized
buffers it's possible for a jabber packet to use two
buffers, with only the last correctly noting the error. */
@@ -853,7 +981,7 @@ lance_rx(struct device *dev)
lp->rx_ring[entry].base &= 0x03ffffff;
} else {
/* Malloc up new buffer, compatible with net-2e. */
- short pkt_len = lp->rx_ring[entry].msg_length;
+ short pkt_len = (lp->rx_ring[entry].msg_length & 0xfff)-4;
struct sk_buff *skb;
skb = alloc_skb(pkt_len, GFP_ATOMIC);
@@ -875,6 +1003,7 @@ lance_rx(struct device *dev)
memcpy(skb->data,
(unsigned char *)(lp->rx_ring[entry].base & 0x00ffffff),
pkt_len);
+ skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
lp->stats.rx_packets++;
}
@@ -901,7 +1030,7 @@ lance_close(struct device *dev)
dev->start = 0;
dev->tbusy = 1;
- if (lp->chip_version != OLD_LANCE) {
+ if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) {
outw(112, ioaddr+LANCE_ADDR);
lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
}
@@ -931,14 +1060,16 @@ lance_get_stats(struct device *dev)
struct lance_private *lp = (struct lance_private *)dev->priv;
short ioaddr = dev->base_addr;
short saved_addr;
+ unsigned long flags;
- if (lp->chip_version != OLD_LANCE) {
+ if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) {
+ save_flags(flags);
cli();
saved_addr = inw(ioaddr+LANCE_ADDR);
outw(112, ioaddr+LANCE_ADDR);
lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
outw(saved_addr, ioaddr+LANCE_ADDR);
- sti();
+ restore_flags(flags);
}
return &lp->stats;
@@ -955,11 +1086,9 @@ set_multicast_list(struct device *dev, int num_addrs, void *addrs)
{
short ioaddr = dev->base_addr;
- /* We take the simple way out and always enable promiscuous mode. */
outw(0, ioaddr+LANCE_ADDR);
outw(0x0004, ioaddr+LANCE_DATA); /* Temporarily stop the lance. */
- outw(15, ioaddr+LANCE_ADDR);
if (num_addrs >= 0) {
short multicast_table[4];
int i;
@@ -969,13 +1098,17 @@ set_multicast_list(struct device *dev, int num_addrs, void *addrs)
outw(8 + i, ioaddr+LANCE_ADDR);
outw(multicast_table[i], ioaddr+LANCE_DATA);
}
+ outw(15, ioaddr+LANCE_ADDR);
outw(0x0000, ioaddr+LANCE_DATA); /* Unset promiscuous mode */
} else {
+ /* Log any net taps. */
+ printk("%s: Promiscuous mode enabled.\n", dev->name);
+ outw(15, ioaddr+LANCE_ADDR);
outw(0x8000, ioaddr+LANCE_DATA); /* Set promiscuous mode */
}
- outw(0, ioaddr+LANCE_ADDR);
- outw(0x0142, ioaddr+LANCE_DATA); /* Resume normal operation. */
+ lance_restart(dev, 0x0142, 0); /* Resume normal operation */
+
}
diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
index b1ec43b23..ab3bc6583 100644
--- a/drivers/net/loopback.c
+++ b/drivers/net/loopback.c
@@ -12,6 +12,8 @@
* Donald Becker, <becker@cesdis.gsfc.nasa.gov>
*
* Alan Cox : Fixed oddments for NET3.014
+ * Alan Cox : Rejig for NET3.029 snap #3
+ * Alan Cox : Fixed NET3.029 bugs and sped up
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -39,68 +41,75 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
+#include <net/sock.h>
-static int
-loopback_xmit(struct sk_buff *skb, struct device *dev)
+static int loopback_xmit(struct sk_buff *skb, struct device *dev)
{
- struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
- int done;
-
- if (skb == NULL || dev == NULL) return(0);
-
- cli();
- if (dev->tbusy != 0) {
- sti();
- stats->tx_errors++;
- return(1);
- }
- dev->tbusy = 1;
- sti();
+ struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
+ unsigned long flags;
+ int unlock=1;
- /* FIXME: Optimise so buffers with skb->free=1 are not copied but
- instead are lobbed from tx queue to rx queue */
-
- done = dev_rint(skb->data, skb->len, 0, dev);
- dev_kfree_skb(skb, FREE_WRITE);
-
- while (done != 1) {
- done = dev_rint(NULL, 0, 0, dev);
- }
- stats->tx_packets++;
-
- dev->tbusy = 0;
+ if (skb == NULL || dev == NULL)
+ return(0);
+
+ save_flags(flags);
+ cli();
+ if (dev->tbusy != 0)
+ {
+ restore_flags(flags);
+ stats->tx_errors++;
+ return(1);
+ }
+ dev->tbusy = 1;
+ restore_flags(flags);
+
+ /*
+ * Optimise so buffers with skb->free=1 are not copied but
+ * instead are lobbed from tx queue to rx queue
+ */
+
+ if(skb->free==0)
+ {
+ struct sk_buff *skb2=skb;
+ skb=skb_clone(skb, GFP_ATOMIC); /* Clone the buffer */
+ if(skb==NULL)
+ return 1;
+ dev_kfree_skb(skb2, FREE_READ);
+ unlock=0;
+ }
+ else if(skb->sk)
+ {
+ /*
+ * Packet sent but looped back around. Cease to charge
+ * the socket for the frame.
+ */
+ save_flags(flags);
+ cli();
+ skb->sk->wmem_alloc-=skb->mem_len;
+ skb->sk->write_space(skb->sk);
+ restore_flags(flags);
+ }
+
+ skb->protocol=eth_type_trans(skb,dev);
+ skb->dev=dev;
+ save_flags(flags);
+ cli();
+ netif_rx(skb);
+ if(unlock)
+ skb_device_unlock(skb);
+ restore_flags(flags);
+
+ stats->tx_packets++;
-#if 1
-#if defined (__i386__)
- __asm__("cmpl $0,_intr_count\n\t"
- "jne 1f\n\t"
- "movl _bh_active,%%eax\n\t"
- "testl _bh_mask,%%eax\n\t"
- "je 1f\n\t"
- "incl _intr_count\n\t"
- "call _do_bottom_half\n\t"
- "decl _intr_count\n"
- "1:"
- :
- :
- : "ax", "dx", "cx");
-#elif defined (__mips__)
- if(intr_count == 0 && (bh_active & bh_mask) != 0) {
- intr_count++;
- do_bottom_half();
- intr_count--;
- }
-#endif
-#endif
+ dev->tbusy = 0;
- return(0);
+ return(0);
}
-static struct enet_statistics *
-get_stats(struct device *dev)
+static struct enet_statistics *get_stats(struct device *dev)
{
- return (struct enet_statistics *)dev->priv;
+ return (struct enet_statistics *)dev->priv;
}
static int loopback_open(struct device *dev)
@@ -110,48 +119,38 @@ static int loopback_open(struct device *dev)
}
/* Initialize the rest of the LOOPBACK device. */
-int
-loopback_init(struct device *dev)
+int loopback_init(struct device *dev)
{
- int i;
-
- dev->mtu = 2000; /* MTU */
- dev->tbusy = 0;
- dev->hard_start_xmit = loopback_xmit;
- dev->open = NULL;
-#if 1
- dev->hard_header = eth_header;
- dev->hard_header_len = ETH_HLEN; /* 14 */
- dev->addr_len = ETH_ALEN; /* 6 */
- dev->type = ARPHRD_ETHER; /* 0x0001 */
- dev->type_trans = eth_type_trans;
- dev->rebuild_header = eth_rebuild_header;
- dev->open = loopback_open;
-#else
- dev->hard_header_length = 0;
- dev->addr_len = 0;
- dev->type = 0; /* loopback_type (0) */
- dev->hard_header = NULL;
- dev->type_trans = NULL;
- dev->rebuild_header = NULL;
-#endif
-
- /* New-style flags. */
- dev->flags = IFF_LOOPBACK|IFF_BROADCAST;
- dev->family = AF_INET;
+ int i;
+
+ dev->mtu = 2000; /* MTU */
+ dev->tbusy = 0;
+ dev->hard_start_xmit = loopback_xmit;
+ dev->open = NULL;
+ dev->hard_header = eth_header;
+ dev->hard_header_len = ETH_HLEN; /* 14 */
+ dev->addr_len = ETH_ALEN; /* 6 */
+ dev->type = ARPHRD_ETHER; /* 0x0001 */
+ dev->rebuild_header = eth_rebuild_header;
+ dev->open = loopback_open;
+ dev->flags = IFF_LOOPBACK|IFF_BROADCAST;
+ dev->family = AF_INET;
#ifdef CONFIG_INET
- dev->pa_addr = in_aton("127.0.0.1");
- dev->pa_brdaddr = in_aton("127.255.255.255");
- dev->pa_mask = in_aton("255.0.0.0");
- dev->pa_alen = sizeof(unsigned long);
+ dev->pa_addr = in_aton("127.0.0.1");
+ dev->pa_brdaddr = in_aton("127.255.255.255");
+ dev->pa_mask = in_aton("255.0.0.0");
+ dev->pa_alen = sizeof(unsigned long);
#endif
- dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
- memset(dev->priv, 0, sizeof(struct enet_statistics));
- dev->get_stats = get_stats;
-
- /* Fill in the generic fields of the device structure. */
- for (i = 0; i < DEV_NUMBUFFS; i++)
- skb_queue_head_init(&dev->buffs[i]);
+ dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
+ memset(dev->priv, 0, sizeof(struct enet_statistics));
+ dev->get_stats = get_stats;
+
+ /*
+ * Fill in the generic fields of the device structure.
+ */
+
+ for (i = 0; i < DEV_NUMBUFFS; i++)
+ skb_queue_head_init(&dev->buffs[i]);
- return(0);
+ return(0);
};
diff --git a/drivers/net/ne.c b/drivers/net/ne.c
index c2c97ff28..2d787895e 100644
--- a/drivers/net/ne.c
+++ b/drivers/net/ne.c
@@ -1,4 +1,3 @@
-#define rw_bugfix
/* ne.c: A general non-shared-memory NS8390 ethernet driver for linux. */
/*
Written 1992-94 by Donald Becker.
@@ -14,8 +13,16 @@
Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
This driver should work with many programmed-I/O 8390-based ethernet
- boards. Currently it support the NE1000, NE2000, many clones,
+ boards. Currently it supports the NE1000, NE2000, many clones,
and some Cabletron products.
+
+ 13/04/95 -- Change in philosophy. We now monitor ENISR_RDC for
+ handshaking the Tx PIO xfers. If we don't get a RDC within a
+ reasonable period of time, we know the 8390 has gone south, and we
+ kick the board before it locks the system. Also use set_bit() to
+ create atomic locks on the PIO xfers, and added some defines
+ that the end user can play with to save memory. -- Paul Gortmaker
+
*/
/* Routines for the NatSemi-based designs (NE[12]000). */
@@ -23,7 +30,6 @@
static char *version =
"ne.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
@@ -32,6 +38,20 @@ static char *version =
#include <linux/netdevice.h>
#include "8390.h"
+
+/* Some defines that people can play with if so inclined. */
+
+/* Do we support clones that don't adhere to 14,15 of the SAprom ? */
+#define CONFIG_NE_BAD_CLONES
+
+/* Do we perform extra sanity checks on stuff ? */
+/* #define CONFIG_NE_SANITY */
+
+/* Do we implement the read before write bugfix ? */
+/* #define CONFIG_NE_RW_BUGFIX */
+
+/* ---- No user-serviceable parts below ---- */
+
extern struct device *init_etherdev(struct device *dev, int sizeof_private,
unsigned long *mem_startp);
@@ -40,14 +60,20 @@ extern struct device *init_etherdev(struct device *dev, int sizeof_private,
static unsigned int netcard_portlist[] =
{ 0x300, 0x280, 0x320, 0x340, 0x360, 0};
+#ifdef CONFIG_NE_BAD_CLONES
/* A list of bad clones that we none-the-less recognize. */
static struct { char *name8, *name16; unsigned char SAprefix[4];}
bad_clone_list[] = {
{"DE100", "DE200", {0x00, 0xDE, 0x01,}},
{"DE120", "DE220", {0x00, 0x80, 0xc8,}},
{"DFI1000", "DFI2000", {'D', 'F', 'I',}}, /* Original, eh? */
+ {"EtherNext UTP8", "EtherNext UTP16", {0x00, 0x00, 0x79}},
+ {"NE1000","NE2000-invalid", {0x00, 0x00, 0xd8}}, /* Ancient real NE1000. */
+ {"NN1000", "NN2000", {0x08, 0x03, 0x08}}, /* Outlaw no-name clone. */
+ {"4-DIM8","4-DIM16", {0x00,0x00,0x4d,}}, /* Outlaw 4-Dimension cards. */
{0,}
};
+#endif
#define NE_BASE (dev->base_addr)
#define NE_CMD 0x00
@@ -60,6 +86,8 @@ bad_clone_list[] = {
#define NESM_START_PG 0x40 /* First page of TX buffer */
#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
+#define NE_RDC_TIMEOUT 0x02 /* Max wait in jiffies for Tx RDC */
+
int ne_probe(struct device *dev);
static int ne_probe1(struct device *dev, int ioaddr);
@@ -126,9 +154,9 @@ static int ne_probe1(struct device *dev, int ioaddr)
char *name = NULL;
int start_page, stop_page;
int neX000, ctron;
- int reg0 = inb(ioaddr);
+ int reg0 = inb_p(ioaddr);
- if ( reg0 == 0xFF)
+ if (reg0 == 0xFF)
return ENODEV;
/* Do a preliminary verification that we have a 8390. */
@@ -140,7 +168,7 @@ static int ne_probe1(struct device *dev, int ioaddr)
inb_p(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
if (inb_p(ioaddr + EN0_COUNTER0) != 0) {
outb_p(reg0, ioaddr);
- outb(regd, ioaddr + 0x0d); /* Restore the old values. */
+ outb_p(regd, ioaddr + 0x0d); /* Restore the old values. */
return ENODEV;
}
}
@@ -169,6 +197,7 @@ static int ne_probe1(struct device *dev, int ioaddr)
};
for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++)
outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
+
}
for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) {
SA_prom[i] = inb(ioaddr + NE_DATAPORT);
@@ -208,6 +237,7 @@ static int ne_probe1(struct device *dev, int ioaddr)
start_page = 0x01;
stop_page = (wordlength == 2) ? 0x40 : 0x20;
} else {
+#ifdef CONFIG_NE_BAD_CLONES
/* Ack! Well, there might be a *bad* NE*000 clone there.
Check for total bogus addresses. */
for (i = 0; bad_clone_list[i].name8; i++) {
@@ -227,6 +257,11 @@ static int ne_probe1(struct device *dev, int ioaddr)
SA_prom[14], SA_prom[15]);
return ENXIO;
}
+#else
+ printk(" not found.\n");
+ return ENXIO;
+#endif
+
}
@@ -242,7 +277,7 @@ static int ne_probe1(struct device *dev, int ioaddr)
outb_p(0x00, ioaddr + EN0_IMR); /* Mask it again. */
dev->irq = autoirq_report(0);
if (ei_debug > 2)
- printk(" autoirq is %d", dev->irq);
+ printk(" autoirq is %d\n", dev->irq);
} else if (dev->irq == 2)
/* Fixup for users that don't know that IRQ 2 is really IRQ 9,
or don't know which one to set. */
@@ -251,7 +286,7 @@ static int ne_probe1(struct device *dev, int ioaddr)
/* Snarf the interrupt now. There's no point in waiting since we cannot
share and the board will usually be enabled. */
{
- int irqval = request_irq (dev->irq, ei_interrupt, 0, "ne");
+ int irqval = request_irq (dev->irq, ei_interrupt, 0, wordlength==2 ? "ne2000":"ne1000");
if (irqval) {
printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
return EAGAIN;
@@ -260,7 +295,7 @@ static int ne_probe1(struct device *dev, int ioaddr)
dev->base_addr = ioaddr;
- snarf_region(ioaddr, NE_IO_EXTENT);
+ request_region(ioaddr, NE_IO_EXTENT, wordlength==2 ? "ne2000":"ne1000");
for(i = 0; i < ETHER_ADDR_LEN; i++)
dev->dev_addr[i] = SA_prom[i];
@@ -318,10 +353,13 @@ ne_reset_8390(struct device *dev)
static int
ne_block_input(struct device *dev, int count, char *buf, int ring_offset)
{
+#ifdef CONFIG_NE_SANITY
int xfer_count = count;
+#endif
int nic_base = dev->base_addr;
- if (ei_status.dmaing) {
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (set_bit(0,(void*)&ei_status.dmaing)) {
if (ei_debug > 0)
printk("%s: DMAing conflict in ne_block_input "
"[DMAstat:%d][irqlock:%d][intr:%d].\n",
@@ -329,7 +367,7 @@ ne_block_input(struct device *dev, int count, char *buf, int ring_offset)
dev->interrupt);
return 0;
}
- ei_status.dmaing |= 0x01;
+ ei_status.dmaing |= 0x02;
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
outb_p(count >> 8, nic_base + EN0_RCNTHI);
@@ -338,21 +376,26 @@ ne_block_input(struct device *dev, int count, char *buf, int ring_offset)
outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
if (ei_status.word16) {
insw(NE_BASE + NE_DATAPORT,buf,count>>1);
- if (count & 0x01)
- buf[count-1] = inb(NE_BASE + NE_DATAPORT), xfer_count++;
+ if (count & 0x01) {
+ buf[count-1] = inb(NE_BASE + NE_DATAPORT);
+#ifdef CONFIG_NE_SANITY
+ xfer_count++;
+#endif
+ }
} else {
insb(NE_BASE + NE_DATAPORT, buf, count);
}
/* This was for the ALPHA version only, but enough people have
- encountering problems that it is still here. If you see
+ been encountering problems so it is still here. If you see
this message you either 1) have a slightly incompatible clone
or 2) have noise/speed problems with your bus. */
+#ifdef CONFIG_NE_SANITY
if (ei_debug > 1) { /* DMA termination address check... */
int addr, tries = 20;
do {
/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
- -- it's broken! Check the "DMA" address instead. */
+ -- it's broken for Rx on some cards! */
int high = inb_p(nic_base + EN0_RSARHI);
int low = inb_p(nic_base + EN0_RSARLO);
addr = (high << 8) + low;
@@ -364,7 +407,9 @@ ne_block_input(struct device *dev, int count, char *buf, int ring_offset)
"%#4.4x (expected) vs. %#4.4x (actual).\n",
dev->name, ring_offset + xfer_count, addr);
}
- ei_status.dmaing &= ~0x01;
+#endif
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x03;
return ring_offset + count;
}
@@ -372,15 +417,20 @@ static void
ne_block_output(struct device *dev, int count,
const unsigned char *buf, const int start_page)
{
+#ifdef CONFIG_NE_SANITY
int retries = 0;
+#endif
int nic_base = NE_BASE;
+ unsigned long dma_start;
/* Round the count up for word writes. Do we need to do this?
What effect will an odd byte count have on the 8390?
I should check someday. */
if (ei_status.word16 && (count & 0x01))
count++;
- if (ei_status.dmaing) {
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (set_bit(0,(void*)&ei_status.dmaing)) {
if (ei_debug > 0)
printk("%s: DMAing conflict in ne_block_output."
"[DMAstat:%d][irqlock:%d][intr:%d]\n",
@@ -388,12 +438,15 @@ ne_block_output(struct device *dev, int count,
dev->interrupt);
return;
}
- ei_status.dmaing |= 0x02;
+ ei_status.dmaing |= 0x04;
/* We should already be in page 0, but to be safe... */
outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+#ifdef CONFIG_NE_SANITY
retry:
-#if defined(rw_bugfix)
+#endif
+
+#ifdef CONFIG_NE_RW_BUGFIX
/* Handle the read-before-write bug the same way as the
Crynwr packet driver -- the NatSemi method doesn't work.
Actually this doesn't always work either, but if you have
@@ -409,7 +462,9 @@ ne_block_output(struct device *dev, int count,
SLOW_DOWN_IO;
#endif /* rw_bugfix */
- /* Now the normal output. */
+ outb_p(ENISR_RDC, nic_base + EN0_ISR);
+
+ /* Now the normal output. */
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
outb_p(count >> 8, nic_base + EN0_RCNTHI);
outb_p(0x00, nic_base + EN0_RSARLO);
@@ -422,13 +477,14 @@ ne_block_output(struct device *dev, int count,
outsb(NE_BASE + NE_DATAPORT, buf, count);
}
+ dma_start = jiffies;
+
+#ifdef CONFIG_NE_SANITY
/* This was for the ALPHA version only, but enough people have
- encountering problems that it is still here. */
+ been encountering problems so it is still here. */
if (ei_debug > 1) { /* DMA termination address check... */
int addr, tries = 20;
do {
- /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
- -- it's broken! Check the "DMA" address instead. */
int high = inb_p(nic_base + EN0_RSARHI);
int low = inb_p(nic_base + EN0_RSARLO);
addr = (high << 8) + low;
@@ -443,7 +499,18 @@ ne_block_output(struct device *dev, int count,
goto retry;
}
}
- ei_status.dmaing &= ~0x02;
+#endif
+
+ while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0)
+ if (jiffies - dma_start > NE_RDC_TIMEOUT) {
+ printk("%s: timeout waiting for Tx RDC.\n", dev->name);
+ ne_reset_8390(dev);
+ NS8390_init(dev,1);
+ break;
+ }
+
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x05;
return;
}
diff --git a/drivers/net/net_init.c b/drivers/net/net_init.c
index a78dbf3f1..983271b66 100644
--- a/drivers/net/net_init.c
+++ b/drivers/net/net_init.c
@@ -1,6 +1,6 @@
/* netdrv_init.c: Initialization for network devices. */
/*
- Written 1993,1994 by Donald Becker.
+ Written 1993,1994,1995 by Donald Becker.
The author may be reached as becker@cesdis.gsfc.nasa.gov or
C/O Center of Excellence in Space Data and Information Sciences
@@ -27,6 +27,7 @@
#include <linux/string.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#include <linux/trdevice.h>
/* The network devices currently exist only in the socket namespace, so these
entries are unused. The only ones that make sense are
@@ -40,8 +41,9 @@
Given that almost all of these functions are handled in the current
socket-based scheme, putting ethercard devices in /dev/ seems pointless.
- [Removed all support for /dev network devices. When someone adds streams then
- by magic we get them, but otherwise they are un-needed and a space waste]
+ [Removed all support for /dev network devices. When someone adds
+ streams then by magic we get them, but otherwise they are un-needed
+ and a space waste]
*/
/* The list of used and available "eth" slots (for "eth0", "eth1", etc.) */
@@ -51,7 +53,7 @@ static struct device *ethdev_index[MAX_ETH_CARDS];
unsigned long lance_init(unsigned long mem_start, unsigned long mem_end);
unsigned long pi_init(unsigned long mem_start, unsigned long mem_end);
unsigned long apricot_init(unsigned long mem_start, unsigned long mem_end);
-
+unsigned long dec21040_init(unsigned long mem_start, unsigned long mem_end);
/*
net_dev_init() is our network device initialization routine.
@@ -62,14 +64,17 @@ unsigned long apricot_init(unsigned long mem_start, unsigned long mem_end);
unsigned long net_dev_init (unsigned long mem_start, unsigned long mem_end)
{
-#if defined(CONFIG_LANCE) /* Note this is _not_ CONFIG_AT1500. */
+ /* Network device initialization for devices that must allocate
+ low-memory or contiguous DMA buffers.
+ */
+#if defined(CONFIG_LANCE)
mem_start = lance_init(mem_start, mem_end);
#endif
#if defined(CONFIG_PI)
mem_start = pi_init(mem_start, mem_end);
#endif
-#if defined(CONFIG_APRICOT)
- mem_start = apricot_init(mem_start, mem_end);
+#if defined(CONFIG_DEC_ELCP)
+ mem_start = dec21040_init(mem_start, mem_end);
#endif
return mem_start;
}
@@ -85,14 +90,35 @@ unsigned long net_dev_init (unsigned long mem_start, unsigned long mem_end)
*/
struct device *
-init_etherdev(struct device *dev, int sizeof_private, unsigned long *mem_startp)
+init_etherdev(struct device *dev, int sizeof_priv, unsigned long *mem_startp)
{
int new_device = 0;
int i;
+ /* Use an existing correctly named device in Space.c:dev_base. */
if (dev == NULL) {
int alloc_size = sizeof(struct device) + sizeof("eth%d ")
- + sizeof_private + 3;
+ + sizeof_priv + 3;
+ struct device *cur_dev;
+ char pname[8]; /* Putative name for the device. */
+
+ for (i = 0; i < MAX_ETH_CARDS; ++i)
+ if (ethdev_index[i] == NULL) {
+ sprintf(pname, "eth%d", i);
+ for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next)
+ if (strcmp(pname, cur_dev->name) == 0) {
+ dev = cur_dev;
+ dev->init = NULL;
+ sizeof_priv = (sizeof_priv + 3) & ~3;
+ if (mem_startp && *mem_startp ) {
+ dev->priv = (void*) *mem_startp;
+ *mem_startp += sizeof_priv;
+ } else
+ dev->priv = kmalloc(sizeof_priv, GFP_KERNEL);
+ memset(dev->priv, 0, sizeof_priv);
+ goto found;
+ }
+ }
alloc_size &= ~3; /* Round to dword boundary. */
@@ -102,12 +128,14 @@ init_etherdev(struct device *dev, int sizeof_private, unsigned long *mem_startp)
} else
dev = (struct device *)kmalloc(alloc_size, GFP_KERNEL);
memset(dev, 0, alloc_size);
- if (sizeof_private)
+ if (sizeof_priv)
dev->priv = (void *) (dev + 1);
- dev->name = sizeof_private + (char *)(dev + 1);
+ dev->name = sizeof_priv + (char *)(dev + 1);
new_device = 1;
}
+ found: /* From the double loop above. */
+
if (dev->name &&
((dev->name[0] == '\0') || (dev->name[0] == ' '))) {
for (i = 0; i < MAX_ETH_CARDS; ++i)
@@ -118,7 +146,7 @@ init_etherdev(struct device *dev, int sizeof_private, unsigned long *mem_startp)
}
}
- ether_setup(dev); /* should this be called here? */
+ ether_setup(dev); /* Hmmm, should this be called here? */
if (new_device) {
/* Append the device to the device queue. */
@@ -154,7 +182,6 @@ void ether_setup(struct device *dev)
dev->hard_header = eth_header;
dev->rebuild_header = eth_rebuild_header;
- dev->type_trans = eth_type_trans;
dev->type = ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
@@ -165,6 +192,36 @@ void ether_setup(struct device *dev)
}
/* New-style flags. */
+ dev->flags = IFF_BROADCAST|IFF_MULTICAST;
+ dev->family = AF_INET;
+ dev->pa_addr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ dev->pa_alen = sizeof(unsigned long);
+}
+
+#ifdef CONFIG_TR
+
+void tr_setup(struct device *dev)
+{
+ int i;
+ /* Fill in the fields of the device structure with ethernet-generic values.
+ This should be in a common file instead of per-driver. */
+ for (i = 0; i < DEV_NUMBUFFS; i++)
+ skb_queue_head_init(&dev->buffs[i]);
+
+ dev->hard_header = tr_header;
+ dev->rebuild_header = tr_rebuild_header;
+
+ dev->type = ARPHRD_IEEE802;
+ dev->hard_header_len = TR_HLEN;
+ dev->mtu = 2000; /* bug in fragmenter...*/
+ dev->addr_len = TR_ALEN;
+ for (i = 0; i < TR_ALEN; i++) {
+ dev->broadcast[i]=0xff;
+ }
+
+ /* New-style flags. */
dev->flags = IFF_BROADCAST;
dev->family = AF_INET;
dev->pa_addr = 0;
@@ -173,6 +230,8 @@ void ether_setup(struct device *dev)
dev->pa_alen = sizeof(unsigned long);
}
+#endif
+
int ether_config(struct device *dev, struct ifmap *map)
{
if (map->mem_start != (u_long)(-1))
diff --git a/drivers/net/ni52.c b/drivers/net/ni52.c
index 12ad62caf..9dabb5f32 100644
--- a/drivers/net/ni52.c
+++ b/drivers/net/ni52.c
@@ -4,14 +4,21 @@
* This is an extension to the Linux operating system, and is covered by the
* same Gnu Public License that covers that work.
*
- * Alphacode 0.51 (94/08/19) for Linux 1.1.47 (or later)
- * Copyrights (c) 1994 by Michael Hipp (mhipp@student.uni-tuebingen.de)
+ * Alphacode 0.62 (95/01/19) for Linux 1.1.82 (or later)
+ * Copyrights (c) 1994,1995 by M.Hipp (Michael.Hipp@student.uni-tuebingen.de)
* [feel free to mail ....]
*
* CAN YOU PLEASE REPORT ME YOUR PERFORMANCE EXPERIENCES !!.
+ *
+ * If you find a bug, please report me:
+ * The kernel panic output and any kmsg from the ni52 driver
+ * the ni5210-driver-version and the linux-kernel version
+ * how many shared memory (memsize) on the netcard,
+ * bootprom: yes/no, base_addr, mem_start
+ * maybe the ni5210-card revision and the i82586 version
*
* autoprobe for: base_addr: 0x300,0x280,0x360,0x320,0x340
- * mem_start: 0xd0000,0xd4000,0xd8000 (8K and 16K)
+ * mem_start: 0xc8000,0xd0000,0xd4000,0xd8000 (8K and 16K)
*
* sources:
* skeleton.c from Donald Becker
@@ -19,16 +26,40 @@
* I have also done a look in the following sources: (mail me if you need them)
* crynwr-packet-driver by Russ Nelson
* Garret A. Wollman's (fourth) i82586-driver for BSD
- * (before getting an i82596 manual, the existing drivers helped
+ * (before getting an i82596 (yes 596 not 586) manual, the existing drivers helped
* me a lot to understand this tricky chip.)
*
- * Known Bugs:
+ * Known Problems:
* The internal sysbus seems to be slow. So we often lose packets because of
* overruns while receiving from a fast remote host.
* This can slow down TCP connections. Maybe the newer ni5210 cards are better.
+ *
+ * IMPORTANT NOTE:
+ * On fast networks, it's a (very) good idea to have 16K shared memory. With
+ * 8K, we can store only 4 receive frames, so it can (easily) happen that a remote
+ * machine 'overruns' our system.
+ *
+ * Known i82586 bugs (I'm sure, there are many more!):
+ * Running the NOP-mode, the i82586 sometimes seems to forget to report
+ * every xmit-interrupt until we restart the CU.
+ * Another MAJOR bug is, that the RU sometimes seems to ignore the EL-Bit
+ * in the RBD-Struct which indicates an end of the RBD queue.
+ * Instead, the RU fetches another (randomly selected and
+ * usually used) RBD and begins to fill it. (Maybe, this happens only if
+ * the last buffer from the previous RFD fits exact into the queue and
+ * the next RFD can't fetch an initial RBD. Anyone knows more? )
*/
/*
+ * 19.Jan.95: verified (MH)
+ *
+ * 19.Sep.94: Added Multicast support (not tested yet) (MH)
+ *
+ * 18.Sep.94: Workaround for 'EL-Bug'. Removed flexible RBD-handling.
+ * Now, every RFD has exact one RBD. (MH)
+ *
+ * 14.Sep.94: added promiscuous mode, a few cleanups (MH)
+ *
* 19.Aug.94: changed request_irq() parameter (MH)
*
* 20.July.94: removed cleanup bugs, removed a 16K-mem-probe-bug (MH)
@@ -42,13 +73,13 @@
* 26.March.94: patches for Linux 1.0 and iomem-auto-probe (MH)
*
* 30.Sep.93: Added nop-chain .. driver now runs with only one Xmit-Buff, too (MH)
+ *
+ * < 30.Sep.93: first versions
*/
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
-#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/malloc.h>
@@ -63,15 +94,8 @@
#include "ni52.h"
-#define DEBUG /* debug on */
-
-/*
-#define DEBUG1
-#define DEBUG2
-#define DEBUG3
-*/
-
-#define SYSBUSVAL 1
+#define DEBUG /* debug on */
+#define SYSBUSVAL 1 /* 8 Bit */
#define ni_attn586() {outb(0,dev->base_addr+NI52_ATTENTION);}
#define ni_reset586() {outb(0,dev->base_addr+NI52_RESET);}
@@ -80,12 +104,11 @@
#define make24(ptr32) ((char *) (ptr32) - p->base)
#define make16(ptr32) ((unsigned short) ((unsigned long) (ptr32) - (unsigned long) p->memtop ))
-/******************* how to calc the buffers *****************************
-
-IMPORTANT NOTE: if you configure only one NUM_XMIT_BUFFS, do also a
---------------- #define ONLY_ONE_XMIT_BUF
- btw: it seems, that only the ONLY_ONE_XMIT_BUF Mode is stable
+/******************* how to calculate the buffers *****************************
+ * IMPORTANT NOTE: if you configure only one NUM_XMIT_BUFFS, the driver works
+ * --------------- in a different (more stable?) mode. Only in this mode it's
+ * possible to configure the driver with 'NO_NOPCOMMANDS'
sizeof(scp)=12; sizeof(scb)=16; sizeof(iscp)=8;
sizeof(scp)+sizeof(iscp)+sizeof(scb) = 36 = INIT
@@ -93,81 +116,16 @@ sizeof(rfd) = 24; sizeof(rbd) = 12;
sizeof(tbd) = 8; sizeof(transmit_cmd) = 16;
sizeof(nop_cmd) = 8;
-examples:
----------
-
-->cfg1: NUM_RECV_FRAMES=16, NUM_RECV_BUFFS=48, RECV_BUFF_SIZE=256,
- NUM_XMIT_BUFFS=2 ,XMIT_BUFF_SIZE=1514
-
-NUM_RECV_FRAMES * sizeof(rfd) = 384;
-NUM_RECV_BUFFS * ( sizeof(rbd) + RECV_BUFF_SIZE) = 12864
-NUM_XMIT_BUFFS * ( sizeof(tbd+transmit_cmd+nop_cmd) + XMIT_BUFF_SIZE) = 3092
-INIT = 36
---------------------
-16358 (36 bytes left!)
-
-************************
-
-->cfg2: NUM_RECV_FRAMES=9, NUM_RECV_BUFFS=18, RECV_BUFF_SIZE=256,
- NUM_XMIT_BUFFS=2 ,XMIT_BUFF_SIZE=1514
-
-NUM_RECV_FRAMES * sizeof(rfd) = 216
-NUM_RECV_BUFFS * ( sizeof(rbd) + RECV_BUFF_SIZE) = 4824
-NUM_XMIT_BUFFS * ( sizeof(tbd+transmit_cmd+nop_cmd) + XMIT_BUFF_SIZE) = 3092
-INIT = 36
-------------------
-8180 (24 bytes left!)
-
-->cfg3: NUM_RECV_FRAMES=7, NUM_RECV_BUFFS=24, RECV_BUFF_SIZE=256,
- NUM_XMIT_BUFFS=1, XMIT_BUFF_SIZE=1514
- 168 + 6432 + 1538 + 36 + 16 = 8190
-
-***************************************************************************/
-
-#if 0
-/* config-1 for 16Kram card */
-# define NUM_RECV_FRAMES 16 /* number of frames to allow for receive */
-# define NUM_RECV_BUFFS 48 /* number of buffers to allocate */
-# define RECV_BUFF_SIZE 256 /* size of each buffer, POWER OF 2 & EVEN*/
-# define XMIT_BUFF_SIZE 1514 /* length of transmit buffer (EVEN) */
-# define NUM_XMIT_BUFFS 2 /* number of Xmit-Buffs */
-#elif 0
-/* config-2 for 8Kram card */
-# define NUM_RECV_FRAMES 9
-# define NUM_RECV_BUFFS 18
-# define RECV_BUFF_SIZE 256
-# define XMIT_BUFF_SIZE 1514
-# define NUM_XMIT_BUFFS 2
-#elif 1
-/*
- * config-3 for 8Kram card ___use_this_config____ seems to be stable
- */
-# define NUM_RECV_FRAMES 7
-# define NUM_RECV_BUFFS 24
-# define RECV_BUFF_SIZE 256
-# define XMIT_BUFF_SIZE 1514
-# define NUM_XMIT_BUFFS 1
-# define ONLY_ONE_XMIT_BUF
-# define NO_NOPCOMMANDS
-#elif 0
-/*
- * cfg-4 for 16K, ONLY_ONE_XMIT_BUF
- */
-# define NUM_RECV_FRAMES 20
-# define NUM_RECV_BUFFS 27
-# define RECV_BUFF_SIZE 512
-# define XMIT_BUFF_SIZE 1514
-# define NUM_XMIT_BUFFS 1
-# define ONLY_ONE_XMIT_BUF
-#else
-# define NUM_RECV_FRAMES 4
-# define NUM_RECV_BUFFS 4
-# define RECV_BUFF_SIZE 1536
-# define XMIT_BUFF_SIZE 1536
-# define NUM_XMIT_BUFFS 1
-# define ONLY_ONE_XMIT_BUF
-# define NO_NOPCOMMANDS
-#endif
+ * if you don't know the driver, better do not change this values: */
+
+#define RECV_BUFF_SIZE 1524 /* slightly oversized */
+#define XMIT_BUFF_SIZE 1524 /* slightly oversized */
+#define NUM_XMIT_BUFFS 1 /* config for both, 8K and 16K shmem */
+#define NUM_RECV_BUFFS_8 4 /* config for 8K shared mem */
+#define NUM_RECV_BUFFS_16 9 /* config for 16K shared mem */
+#define NO_NOPCOMMANDS /* only possible with NUM_XMIT_BUFFS=1 */
+
+/**************************************************************************/
#define DELAY(x) {int i=jiffies; \
if(loops_per_sec == 1) \
@@ -176,30 +134,42 @@ INIT = 36
__delay((loops_per_sec>>5)*x); \
}
+/* a much shorter delay: */
+#define DELAY_16(); { __delay( (loops_per_sec>>16)+1 ); }
+
+/* wait for command with timeout: */
+#define WAIT_4_SCB_CMD() { int i; \
+ for(i=0;i<1024;i++) { \
+ if(!p->scb->cmd) break; \
+ DELAY_16(); \
+ if(i == 1023) { \
+ printk("%s: scb_cmd timed out .. resetting i82586\n",dev->name); \
+ ni_reset586(); } } }
+
extern void autoirq_setup(int waittime);
-extern int autoirq_report(int waittime);
+extern int autoirq_report(int waittime);
extern void *irq2dev_map[16];
-#ifndef HAVE_PORTRESERVE
-#define check_region(ioaddr, size) 0
-#define snarf_region(ioaddr, size); do ; while (0)
-#endif
-
#define NI52_TOTAL_SIZE 16
#define NI52_ADDR0 0x02
#define NI52_ADDR1 0x07
#define NI52_ADDR2 0x01
+#ifndef HAVE_PORTRESERVE
+#define check_region(ioaddr, size) 0
+#define request_region(ioaddr, size,name) do ; while (0)
+#endif
+
static int ni52_probe1(struct device *dev,int ioaddr);
-static void ni52_interrupt(int reg_ptr);
+static void ni52_interrupt(int irq,struct pt_regs *reg_ptr);
static int ni52_open(struct device *dev);
static int ni52_close(struct device *dev);
static int ni52_send_packet(struct sk_buff *,struct device *);
-static struct enet_statistics *ni52_get_stats(struct device *dev);
-static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+static struct enet_statistics *ni52_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
/* helper-functions */
-static int init586(struct device *dev);
+static int init586(struct device *dev,int num_addrs,void *addrs);
static int check586(struct device *dev,char *where,unsigned size);
static void alloc586(struct device *dev);
static void startrecv586(struct device *dev);
@@ -212,20 +182,19 @@ struct priv
{
struct enet_statistics stats;
unsigned long base;
- char *memtop,*max_cbuff32,*min_cbuff32,*max_cbuff24;
- volatile struct rbd_struct *rbd_last;
+ char *memtop;
volatile struct rfd_struct *rfd_last,*rfd_top,*rfd_first;
volatile struct scp_struct *scp; /* volatile is important */
volatile struct iscp_struct *iscp; /* volatile is important */
volatile struct scb_struct *scb; /* volatile is important */
volatile struct tbd_struct *xmit_buffs[NUM_XMIT_BUFFS];
volatile struct transmit_cmd_struct *xmit_cmds[NUM_XMIT_BUFFS];
-#ifdef ONLY_ONE_XMIT_BUF
+#if (NUM_XMIT_BUFFS == 1)
volatile struct nop_cmd_struct *nop_cmds[2];
#else
volatile struct nop_cmd_struct *nop_cmds[NUM_XMIT_BUFFS];
#endif
- volatile int nop_point;
+ volatile int nop_point,num_recv_buffs;
volatile char *xmit_cbuffs[NUM_XMIT_BUFFS];
volatile int xmit_count,xmit_last;
};
@@ -255,7 +224,7 @@ static int ni52_close(struct device *dev)
static int ni52_open(struct device *dev)
{
alloc586(dev);
- init586(dev);
+ init586(dev,0,NULL);
startrecv586(dev);
if(request_irq(dev->irq, &ni52_interrupt,0,"ni52"))
@@ -286,7 +255,7 @@ static int check586(struct device *dev,char *where,unsigned size)
p->memtop = where + size;
p->scp = (struct scp_struct *)(p->base + SCP_DEFAULT_ADDRESS);
memset((char *)p->scp,0, sizeof(struct scp_struct));
- p->scp->sysbus = SYSBUSVAL; /* 1 = 8Bit-Bus */
+ p->scp->sysbus = SYSBUSVAL; /* 1 = 8Bit-Bus, 0 = 16 Bit */
iscp_addrs[0] = where;
iscp_addrs[1]= (char *) p->scp - sizeof(struct iscp_struct);
@@ -303,7 +272,7 @@ static int check586(struct device *dev,char *where,unsigned size)
ni_attn586();
DELAY(2); /* wait a while... */
- if(p->iscp->busy)
+ if(p->iscp->busy) /* i82586 clears 'busy' after successful init */
return 0;
}
return 1;
@@ -317,6 +286,9 @@ void alloc586(struct device *dev)
{
struct priv *p = (struct priv *) dev->priv;
+ ni_reset586();
+ DELAY(2);
+
p->scp = (struct scp_struct *) (p->base + SCP_DEFAULT_ADDRESS);
p->scb = (struct scb_struct *) (dev->mem_start);
p->iscp = (struct iscp_struct *) ((char *)p->scp - sizeof(struct iscp_struct));
@@ -332,12 +304,10 @@ void alloc586(struct device *dev)
ni_reset586();
ni_attn586();
-#ifdef DEBUG
DELAY(2);
if(p->iscp->busy)
printk("%s: Init-Problems (alloc).\n",dev->name);
-#endif
memset((char *)p->scb,0,sizeof(struct scb_struct));
}
@@ -377,7 +347,7 @@ int ni52_probe(struct device *dev)
static int ni52_probe1(struct device *dev,int ioaddr)
{
- long memaddrs[] = { 0xd0000,0xd2000,0xd4000,0xd6000,0xd8000, 0 };
+ long memaddrs[] = { 0xd0000,0xd2000,0xc8000,0xca000,0xd4000,0xd6000,0xd8000, 0 };
int i,size;
for(i=0;i<ETH_ALEN;i++)
@@ -389,7 +359,7 @@ static int ni52_probe1(struct device *dev,int ioaddr)
printk("%s: Ni52 found at %#3x, ",dev->name,dev->base_addr);
- snarf_region(ioaddr,NI52_TOTAL_SIZE);
+ request_region(ioaddr,NI52_TOTAL_SIZE,"ni52");
dev->priv = (void *) kmalloc(sizeof(struct priv),GFP_KERNEL);
/* warning: we don't free it on errors */
@@ -400,9 +370,9 @@ static int ni52_probe1(struct device *dev,int ioaddr)
*/
if(dev->mem_start != 0) /* no auto-mem-probe */
{
- size = 0x4000;
+ size = 0x4000; /* check for 16K mem */
if(!check586(dev,(char *) dev->mem_start,size)) {
- size = 0x2000;
+ size = 0x2000; /* check for 8K mem */
if(!check586(dev,(char *) dev->mem_start,size)) {
printk("?memprobe, Can't find memory at 0x%lx!\n",dev->mem_start);
return ENODEV;
@@ -418,18 +388,25 @@ static int ni52_probe1(struct device *dev,int ioaddr)
return ENODEV;
}
dev->mem_start = memaddrs[i];
- size = 0x2000;
+ size = 0x2000; /* check for 8K mem */
if(check586(dev,(char *)dev->mem_start,size)) /* 8K-check */
break;
- size = 0x4000;
+ size = 0x4000; /* check for 16K mem */
if(check586(dev,(char *)dev->mem_start,size)) /* 16K-check */
break;
}
}
-
+ dev->mem_end = dev->mem_start + size; /* set mem_end showed by 'ifconfig' */
+
((struct priv *) (dev->priv))->base = dev->mem_start + size - 0x01000000;
alloc586(dev);
+ /* set number of receive-buffs according to memsize */
+ if(size == 0x2000)
+ ((struct priv *) dev->priv)->num_recv_buffs = NUM_RECV_BUFFS_8;
+ else
+ ((struct priv *) dev->priv)->num_recv_buffs = NUM_RECV_BUFFS_16;
+
printk("Memaddr: 0x%lx, Memsize: %d, ",dev->mem_start,size);
if(dev->irq < 2)
@@ -470,7 +447,7 @@ static int ni52_probe1(struct device *dev,int ioaddr)
* needs a correct 'allocated' memory
*/
-static int init586(struct device *dev)
+static int init586(struct device *dev,int num_addrs,void *addrs)
{
void *ptr;
unsigned long s;
@@ -479,19 +456,26 @@ static int init586(struct device *dev)
volatile struct configure_cmd_struct *cfg_cmd;
volatile struct iasetup_cmd_struct *ias_cmd;
volatile struct tdr_cmd_struct *tdr_cmd;
+ volatile struct mcsetup_cmd_struct *mc_cmd;
ptr = (void *) ((char *)p->scb + sizeof(struct scb_struct));
cfg_cmd = (struct configure_cmd_struct *)ptr; /* configure-command */
-
- cfg_cmd->byte_cnt = 0x04; /* number of cfg bytes */
- cfg_cmd->fifo = 0xc8; /* fifo-limit (8=tx:32/rx:64) | monitor */
- cfg_cmd->sav_bf = 0x40; /* hold or discard bad recv frames (bit 7) */
- cfg_cmd->adr_len = 0x2e; /* addr_len |!src_insert |pre-len |loopback */
- cfg_cmd->cmd_status = 0;
- cfg_cmd->cmd_cmd = CMD_CONFIGURE | CMD_LAST;
- cfg_cmd->cmd_link = 0xffff;
+ cfg_cmd->cmd_status = 0;
+ cfg_cmd->cmd_cmd = CMD_CONFIGURE | CMD_LAST;
+ cfg_cmd->cmd_link = 0xffff;
+ cfg_cmd->byte_cnt = 0x0a; /* number of cfg bytes */
+ cfg_cmd->fifo = 0x08; /* fifo-limit (8=tx:32/rx:64) */
+ cfg_cmd->sav_bf = 0x40; /* hold or discard bad recv frames (bit 7) */
+ cfg_cmd->adr_len = 0x2e; /* addr_len |!src_insert |pre-len |loopback */
+ cfg_cmd->priority = 0x00;
+ cfg_cmd->ifs = 0x60;
+ cfg_cmd->time_low = 0x00;
+ cfg_cmd->time_high = 0xf2;
+ cfg_cmd->promisc = (num_addrs < 0) ? 1 : 0; /* promisc on/off */
+ cfg_cmd->carr_coll = 0x00;
+
p->scb->cbl_offset = make16(cfg_cmd);
p->scb->cmd = CUC_START; /* cmd.-unit start */
@@ -585,41 +569,78 @@ static int init586(struct device *dev)
/*
* alloc nop/xmit-cmds
*/
-#ifdef ONLY_ONE_XMIT_BUF
+#if (NUM_XMIT_BUFFS == 1)
for(i=0;i<2;i++)
{
p->nop_cmds[i] = (struct nop_cmd_struct *)ptr;
- p->nop_cmds[i]->cmd_cmd = 0;
+ p->nop_cmds[i]->cmd_cmd = CMD_NOP;
p->nop_cmds[i]->cmd_status = 0;
p->nop_cmds[i]->cmd_link = make16((p->nop_cmds[i]));
- ptr += sizeof(struct nop_cmd_struct);
+ ptr = (char *) ptr + sizeof(struct nop_cmd_struct);
}
p->xmit_cmds[0] = (struct transmit_cmd_struct *)ptr; /* transmit cmd/buff 0 */
- ptr += sizeof(struct transmit_cmd_struct);
+ ptr = (char *) ptr + sizeof(struct transmit_cmd_struct);
#else
for(i=0;i<NUM_XMIT_BUFFS;i++)
{
p->nop_cmds[i] = (struct nop_cmd_struct *)ptr;
- p->nop_cmds[i]->cmd_cmd = 0;
+ p->nop_cmds[i]->cmd_cmd = CMD_NOP;
p->nop_cmds[i]->cmd_status = 0;
p->nop_cmds[i]->cmd_link = make16((p->nop_cmds[i]));
- ptr += sizeof(struct nop_cmd_struct);
- p->xmit_cmds[i] = (struct transmit_cmd_struct *)ptr; /* transmit cmd/buff 0 */
- ptr += sizeof(struct transmit_cmd_struct);
+ ptr = (char *) ptr + sizeof(struct nop_cmd_struct);
+ p->xmit_cmds[i] = (struct transmit_cmd_struct *)ptr; /*transmit cmd/buff 0*/
+ ptr = (char *) ptr + sizeof(struct transmit_cmd_struct);
}
#endif
ptr = alloc_rfa(dev,(void *)ptr); /* init receive-frame-area */
- /*
- * alloc xmit-buffs
- */
+ /*
+ * Multicast setup
+ */
+
+ if(num_addrs > 0)
+ { /* I don't understand this: do we really need memory after the init? */
+ int len = ((char *) p->iscp - (char *) ptr - 8) / 6;
+ if(len <= 0)
+ {
+ printk("%s: Ooooops, no memory for MC-Setup!\n",dev->name);
+ }
+ else
+ {
+ if(len < num_addrs)
+ {
+ num_addrs = len;
+ printk("%s: Sorry, can only apply %d MC-Address(es).\n",dev->name,num_addrs);
+ }
+ mc_cmd = (struct mcsetup_cmd_struct *) ptr;
+ mc_cmd->cmd_status = 0;
+ mc_cmd->cmd_cmd = CMD_MCSETUP | CMD_LAST;
+ mc_cmd->cmd_link = 0xffff;
+ mc_cmd->mc_cnt = num_addrs * 6;
+ for(i=0;i<num_addrs;i++)
+ memcpy((char *) mc_cmd->mc_list[i],((char (*)[6]) addrs)[i],6);
+ p->scb->cbl_offset = make16(mc_cmd);
+ p->scb->cmd = CUC_START;
+ ni_attn586();
+ s = jiffies;
+ while(!(mc_cmd->cmd_status & STAT_COMPL))
+ if(jiffies - s > 30)
+ break;
+ if(!(mc_cmd->cmd_status & STAT_COMPL))
+ printk("%s: Can't apply multicast-address-list.\n",dev->name);
+ }
+ }
+
+ /*
+ * alloc xmit-buffs / init xmit_cmds
+ */
for(i=0;i<NUM_XMIT_BUFFS;i++)
{
p->xmit_cbuffs[i] = (char *)ptr; /* char-buffs */
- ptr += XMIT_BUFF_SIZE;
+ ptr = (char *) ptr + XMIT_BUFF_SIZE;
p->xmit_buffs[i] = (struct tbd_struct *)ptr; /* TBD */
- ptr += sizeof(struct tbd_struct);
+ ptr = (char *) ptr + sizeof(struct tbd_struct);
if((void *)ptr > (void *)p->iscp)
{
printk("%s: not enough shared-mem for your configuration!\n",dev->name);
@@ -628,11 +649,12 @@ static int init586(struct device *dev)
memset((char *)(p->xmit_cmds[i]) ,0, sizeof(struct transmit_cmd_struct));
memset((char *)(p->xmit_buffs[i]),0, sizeof(struct tbd_struct));
p->xmit_cmds[i]->cmd_status = STAT_COMPL;
+ p->xmit_cmds[i]->cmd_cmd = CMD_XMIT | CMD_INT;
p->xmit_cmds[i]->tbd_offset = make16((p->xmit_buffs[i]));
p->xmit_buffs[i]->next = 0xffff;
p->xmit_buffs[i]->buffer = make24((p->xmit_cbuffs[i]));
}
-
+
p->xmit_count = 0;
p->xmit_last = 0;
#ifndef NO_NOPCOMMANDS
@@ -646,12 +668,8 @@ static int init586(struct device *dev)
p->scb->cbl_offset = make16(p->nop_cmds[0]);
p->scb->cmd = CUC_START;
ni_attn586();
- while(p->scb->cmd);
+ WAIT_4_SCB_CMD();
#else
-/*
- p->nop_cmds[0]->cmd_link = make16(p->nop_cmds[1]);
- p->nop_cmds[1]->cmd_link = make16(p->xmit_cmds[0]);
-*/
p->xmit_cmds[0]->cmd_link = 0xffff;
p->xmit_cmds[0]->cmd_cmd = CMD_XMIT | CMD_LAST | CMD_INT;
#endif
@@ -660,7 +678,7 @@ static int init586(struct device *dev)
}
/******************************************************
- * This is a helper routine for ni52_nr_int() and init586().
+ * This is a helper routine for ni52_rnr_int() and init586().
* It sets up the Receive Frame Area (RFA).
*/
@@ -671,38 +689,32 @@ static void *alloc_rfa(struct device *dev,void *ptr)
int i;
struct priv *p = (struct priv *) dev->priv;
- memset((char *) rfd,0,sizeof(struct rfd_struct)*NUM_RECV_FRAMES);
+ memset((char *) rfd,0,sizeof(struct rfd_struct)*p->num_recv_buffs);
p->rfd_first = rfd;
- for(i = 0; i < NUM_RECV_FRAMES; i++)
- rfd[i].next = make16(rfd + (i+1) % NUM_RECV_FRAMES);
- rfd[NUM_RECV_FRAMES-1].last = RFD_LAST; /* set EOL (no RU suspend) */
+ for(i = 0; i < p->num_recv_buffs; i++)
+ rfd[i].next = make16(rfd + (i+1) % p->num_recv_buffs);
+ rfd[p->num_recv_buffs-1].last = RFD_SUSP; /* RU suspend */
- ptr = (char *) (rfd + NUM_RECV_FRAMES);
+ ptr = (void *) (rfd + p->num_recv_buffs);
rbd = (struct rbd_struct *) ptr;
- ptr += sizeof(struct rbd_struct)*NUM_RECV_BUFFS;
+ ptr = (void *) (rbd + p->num_recv_buffs);
/* clr descriptors */
- memset((char *) rbd,0,sizeof(struct rbd_struct)*NUM_RECV_BUFFS);
+ memset((char *) rbd,0,sizeof(struct rbd_struct)*p->num_recv_buffs);
- p->min_cbuff32 = ptr;
- for(i=0;i<NUM_RECV_BUFFS;i++)
+ for(i=0;i<p->num_recv_buffs;i++)
{
- rbd[i].next = make16((rbd + (i+1) % NUM_RECV_BUFFS));
+ rbd[i].next = make16((rbd + (i+1) % p->num_recv_buffs));
rbd[i].size = RECV_BUFF_SIZE;
rbd[i].buffer = make24(ptr);
- ptr += RECV_BUFF_SIZE;
+ ptr = (char *) ptr + RECV_BUFF_SIZE;
}
- rbd[NUM_RECV_BUFFS-1].size |= RBD_LAST; /* set eol */
- p->max_cbuff32 = ptr;
- p->max_cbuff24 = make24(p->max_cbuff32);
-
+
p->rfd_top = p->rfd_first;
- p->rfd_last = p->rfd_first + NUM_RECV_FRAMES - 1;
+ p->rfd_last = p->rfd_first + p->num_recv_buffs - 1;
- p->rbd_last = rbd + NUM_RECV_BUFFS - 1;
-
p->scb->rfa_offset = make16(p->rfd_first);
p->rfd_first->rbd_offset = make16(rbd);
@@ -714,30 +726,18 @@ static void *alloc_rfa(struct device *dev,void *ptr)
* Interrupt Handler ...
*/
-static void ni52_interrupt(int reg_ptr)
+static void ni52_interrupt(int irq,struct pt_regs *reg_ptr)
{
- int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
- struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct device *dev = (struct device *) irq2dev_map[irq];
unsigned short stat;
- int pd = 0;
struct priv *p;
-#ifdef DEBUG2
- printk("(1)");
-#endif
-
if (dev == NULL) {
- printk ("ni52-interrupt: irq %d for unknown device.\n", irq);
+ printk ("ni52-interrupt: irq %d for unknown device.\n",(int) -(((struct pt_regs *)reg_ptr)->orig_eax+2));
return;
}
p = (struct priv *) dev->priv;
- if(dev->interrupt)
- {
- printk("(ni52-I)");
- return;
- }
-
dev->interrupt = 1;
while((stat=p->scb->status & STAT_MASK))
@@ -745,57 +745,38 @@ static void ni52_interrupt(int reg_ptr)
p->scb->cmd = stat;
ni_attn586(); /* ack inter. */
- if(pd)
- printk("ni52-%04x/%04x-",(int) stat,(int) p->scb->status); /* debug */
+ if(stat & STAT_CX) /* command with I-bit set complete */
+ ni52_xmt_int(dev);
- if(stat & (STAT_FR | STAT_RNR))
+ if(stat & STAT_FR) /* received a frame */
ni52_rcv_int(dev);
- if(stat & STAT_CX)
- ni52_xmt_int(dev);
-
#ifndef NO_NOPCOMMANDS
- if(stat & STAT_CNA)
-#else
- if( (stat & STAT_CNA) && !(stat & STAT_CX) )
-#endif
- printk("%s: oops! CU has left active state. stat: %04x/%04x.\n",dev->name,(int) stat,(int) p->scb->status);
-
- if(stat & STAT_RNR)
+ if(stat & STAT_CNA) /* CU went 'not ready' */
{
- printk("%s: rnr: %04x/%04x.\n",dev->name,(int) stat,(int) p->scb->status);
- ni52_rnr_int(dev);
- pd = 1; /* local debug on */
+ if(dev->start)
+ printk("%s: oops! CU has left active state. stat: %04x/%04x.\n",dev->name,(int) stat,(int) p->scb->status);
}
-
-#ifdef DEBUG2
- pd++;
#endif
- while(p->scb->cmd)
- {
- int i; /* wait for ack. (ni52_xmt_int can be faster than ack!!) */
- for(i=0;i<200;i++);
- }
- }
-
-#ifdef DEBUG
- {
- static int old_ovr=0;
- int l;
- if((l = p->scb->ovrn_errs - old_ovr))
+ if(stat & STAT_RNR) /* RU went 'not ready' */
{
- if(l > 0)
- p->stats.rx_over_errors += l;
+ if(p->scb->status & RU_SUSPEND) /* special case: RU_SUSPEND */
+ {
+ WAIT_4_SCB_CMD();
+ p->scb->cmd = RUC_RESUME;
+ ni_attn586();
+ }
else
- old_ovr=0;
+ {
+ printk("%s: Receiver-Unit went 'NOT READY': %04x/%04x.\n",dev->name,(int) stat,(int) p->scb->status);
+ ni52_rnr_int(dev);
+ }
}
+ WAIT_4_SCB_CMD(); /* wait for ack. (ni52_xmt_int can be faster than ack!!) */
+ if(p->scb->cmd) /* timed out? */
+ break;
}
-#endif
-
-#ifdef DEBUG2
- printk("(2)");
-#endif
dev->interrupt = 0;
}
@@ -807,92 +788,38 @@ static void ni52_interrupt(int reg_ptr)
static void ni52_rcv_int(struct device *dev)
{
int status;
- unsigned short totlen,pnt;
+ unsigned short totlen;
struct sk_buff *skb;
- struct rbd_struct *rbd,*rbd_first;
+ struct rbd_struct *rbd;
struct priv *p = (struct priv *) dev->priv;
for(;(status = p->rfd_top->status) & STAT_COMPL;)
{
- rbd = rbd_first = (struct rbd_struct *) make32(p->rfd_top->rbd_offset);
+ rbd = (struct rbd_struct *) make32(p->rfd_top->rbd_offset);
-#ifdef DEBUG1
- {
- struct rbd_struct *rbd1 = rbd;
- if(rbd1==p->rbd_last)
- printk("L");
- printk("S:%04x/%x/%02x >",(int) rbd1->status,(int) rbd1->size>>12,(int)((unsigned long) rbd1 & 0xff));
- rbd1 = (struct rbd_struct *) make32(rbd1->next);
- for(;rbd1 != rbd_first;rbd1 = (struct rbd_struct *) make32(rbd1->next))
- {
- if(rbd1 == p->rbd_last)
- printk("L:");
- printk("%04x/%x-",(int) rbd1->status>>12,(int) rbd1->size>>12);
- }
- printk("< ");
- }
+ if(status & STAT_OK) /* frame received without error? */
{
- struct rfd_struct *rfd1 = p->rfd_top;
- if(rfd1==p->rfd_last)
- printk("L");
- printk("S:%04x/%x/%02x >",(int) rfd1->status,(int) rfd1->last>>12,(int)((unsigned long) rfd1 & 0xff));
- rfd1 = (struct rfd_struct *) make32(rfd1->next);
- for(;rfd1 != p->rfd_top;rfd1 = (struct rfd_struct *) make32(rfd1->next))
+ if( (totlen = rbd->status) & RBD_LAST) /* the first and the last buffer? */
{
- if(rfd1 == p->rfd_last)
- printk("L:");
- printk("%x/%x-",(int) rfd1->status>>12,(int) rfd1->last>>12);
- }
- printk("<\n");
- }
-#endif
-
- p->rfd_top->status = 0;
- p->rfd_top->last = RFD_LAST;
- p->rfd_last->last = 0; /* delete RFD_LAST, no RU suspend */
- p->rfd_last = p->rfd_top;
- p->rfd_top = (struct rfd_struct *) make32(p->rfd_top->next);
-
- if(status & RFD_ERRMASK)
- printk("%s: RFD-Error ... status: %04x.\n",dev->name,status);
-
- if(status & STAT_OK)
- {
- for(totlen=0; !(rbd->status & RBD_LAST); rbd=(struct rbd_struct *) make32(rbd->next)) {
- totlen += RECV_BUFF_SIZE;
+ totlen &= RBD_MASK; /* length of this frame */
rbd->status = 0;
- }
- totlen += rbd->status & RBD_MASK;
- rbd->status = 0;
-
- skb = (struct sk_buff *) alloc_skb(totlen, GFP_ATOMIC);
-
- if (skb != NULL) /* copy header */
- {
- skb->len = totlen;
- skb->dev = dev;
-
- if(rbd->buffer < rbd_first->buffer)
+ skb = (struct sk_buff *) alloc_skb(totlen, GFP_ATOMIC);
+ if(skb != NULL)
{
- pnt = p->max_cbuff24 - rbd_first->buffer;
- memcpy( (char *) skb->data,p->max_cbuff32-pnt,pnt);
- memcpy( (char *) skb->data+pnt,p->min_cbuff32,totlen-pnt);
+ skb->len = totlen;
+ skb->dev = dev;
+ memcpy( (char *) skb->data,(char *) p->base+(unsigned long) rbd->buffer, totlen);
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ p->stats.rx_packets++;
}
else
- memcpy( (char *) skb->data,(char *) p->base+(unsigned long) rbd_first->buffer, totlen);
-
- rbd->size |= RBD_LAST;
- p->rbd_last->size &= ~RBD_LAST;
- p->rbd_last = rbd;
-
- netif_rx(skb);
- p->stats.rx_packets++;
+ p->stats.rx_dropped++;
}
else
{
- rbd->size |= RBD_LAST;
- p->rbd_last->size &= ~RBD_LAST;
- p->rbd_last = rbd;
+ printk("%s: received oversized frame.\n",dev->name);
+ p->stats.rx_dropped++;
}
}
else /* frame !(ok), only with 'save-bad-frames' */
@@ -900,13 +827,16 @@ static void ni52_rcv_int(struct device *dev)
printk("%s: oops! rfd-error-status: %04x\n",dev->name,status);
p->stats.rx_errors++;
}
+ p->rfd_top->status = 0;
+ p->rfd_top->last = RFD_SUSP;
+ p->rfd_last->last = 0; /* delete RU_SUSP */
+ p->rfd_last = p->rfd_top;
+ p->rfd_top = (struct rfd_struct *) make32(p->rfd_top->next); /* step to next RFD */
}
}
/**********************************************************
- * I never got this error , (which should occur if someone
- * wants to blast your machine) so I couldn't debug it for now.
- * but we _try_ to fix the receiver not ready int.
+ * handle 'Receiver went not ready'.
*/
static void ni52_rnr_int(struct device *dev)
@@ -915,13 +845,16 @@ static void ni52_rnr_int(struct device *dev)
p->stats.rx_errors++;
- while(p->scb->cmd); /* wait for the last cmd */
- p->scb->cmd = RUC_ABORT;
+ WAIT_4_SCB_CMD(); /* wait for the last cmd */
+ p->scb->cmd = RUC_ABORT; /* usually the RU is in the 'no resource'-state .. abort it now. */
ni_attn586();
- while(p->scb->cmd); /* wait for accept cmd. */
+ WAIT_4_SCB_CMD(); /* wait for accept cmd. */
alloc_rfa(dev,(char *)p->rfd_first);
- startrecv586(dev); /* restart */
+ startrecv586(dev); /* restart RU */
+
+ printk("%s: Receive-Unit restarted. Status: %04x\n",dev->name,p->scb->status);
+
}
/**********************************************************
@@ -933,17 +866,14 @@ static void ni52_xmt_int(struct device *dev)
int status;
struct priv *p = (struct priv *) dev->priv;
-/*
- if(!(p->xmit_cmds[0]->cmd_status & STAT_COMPL))
- return;
-*/
+ status = p->xmit_cmds[p->xmit_last]->cmd_status;
+ if(!(status & STAT_COMPL))
+ printk("%s: strange .. xmit-int without a 'COMPLETE'\n",dev->name);
- if( (status=p->xmit_cmds[p->xmit_last]->cmd_status) & STAT_OK)
+ if(status & STAT_OK)
{
p->stats.tx_packets++;
p->stats.collisions += (status & TCMD_MAXCOLLMASK);
- dev->tbusy = 0;
- mark_bh(NET_BH);
}
else
{
@@ -959,6 +889,7 @@ static void ni52_xmt_int(struct device *dev)
else if(status & TCMD_LOSTCTS)
printk("%s: loss of CTS detected.\n",dev->name);
else if(status & TCMD_UNDERRUN) {
+ p->stats.tx_fifo_errors++;
printk("%s: DMA underrun detected.\n",dev->name);
}
else if(status & TCMD_MAXCOLL) {
@@ -967,11 +898,13 @@ static void ni52_xmt_int(struct device *dev)
}
}
-#ifndef ONLY_ONE_XMIT_BUF
+#if (NUM_XMIT_BUFFS != 1)
if( (++p->xmit_last) == NUM_XMIT_BUFFS)
p->xmit_last = 0;
#endif
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
}
/***********************************************************
@@ -984,13 +917,8 @@ static void startrecv586(struct device *dev)
p->scb->rfa_offset = make16(p->rfd_first);
p->scb->cmd = RUC_START;
- ni_attn586(); /* start cmd. */
- while(p->scb->cmd); /* wait for accept cmd. (no timeout!!) */
-
- DELAY(2); /* isn't necess. */
-
- p->scb->cmd = p->scb->status & STAT_MASK;
- ni_attn586(); /* ack interr */
+ ni_attn586(); /* start cmd. */
+ WAIT_4_SCB_CMD(); /* wait for accept cmd. (no timeout!!) */
}
/******************************************************
@@ -999,7 +927,7 @@ static void startrecv586(struct device *dev)
static int ni52_send_packet(struct sk_buff *skb, struct device *dev)
{
- int len;
+ int len,i;
#ifndef NO_NOPCOMMANDS
int next_nop;
#endif
@@ -1008,18 +936,37 @@ static int ni52_send_packet(struct sk_buff *skb, struct device *dev)
if(dev->tbusy)
{
int tickssofar = jiffies - dev->trans_start;
-
- if (tickssofar < 30)
+ if (tickssofar < 5)
return 1;
+ if(p->scb->status & CU_ACTIVE) /* COMMAND-UNIT active? */
+ {
+ dev->tbusy = 0;
#ifdef DEBUG
- printk("%s: xmitter timed out, try to restart! stat: %04x\n",dev->name,p->scb->status);
- printk("%s: command-stats: %04x %04x\n",dev->name,p->xmit_cmds[0]->cmd_status,p->xmit_cmds[1]->cmd_status);
+ printk("%s: strange ... timeout with CU active?!?\n",dev->name);
+ printk("%s: X0: %04x N0: %04x N1: %04x %d\n",dev->name,(int)p->xmit_cmds[0]->cmd_status,(int)p->nop_cmds[0]->cmd_status,(int)p->nop_cmds[1]->cmd_status,(int)p->nop_point);
#endif
-
- ni52_close(dev);
- ni52_open(dev);
+ p->scb->cmd = CUC_ABORT;
+ ni_attn586();
+ WAIT_4_SCB_CMD();
+ p->scb->cbl_offset = make16(p->nop_cmds[p->nop_point]);
+ p->scb->cmd = CUC_START;
+ ni_attn586();
+ WAIT_4_SCB_CMD();
+ dev->trans_start = jiffies;
+ return 0;
+ }
+ else
+ {
+#ifdef DEBUG
+ printk("%s: xmitter timed out, try to restart! stat: %04x\n",dev->name,p->scb->status);
+ printk("%s: command-stats: %04x %04x\n",dev->name,p->xmit_cmds[0]->cmd_status,p->xmit_cmds[1]->cmd_status);
+#endif
+ ni52_close(dev);
+ ni52_open(dev);
+ }
dev->trans_start = jiffies;
+ return 0;
}
if(skb == NULL)
@@ -1030,6 +977,11 @@ static int ni52_send_packet(struct sk_buff *skb, struct device *dev)
if (skb->len <= 0)
return 0;
+ if(skb->len > XMIT_BUFF_SIZE)
+ {
+ printk("%s: Sorry, max. framelength is %d bytes. The length of your frame is %ld bytes.\n",dev->name,XMIT_BUFF_SIZE,skb->len);
+ return 0;
+ }
if (set_bit(0, (void*)&dev->tbusy) != 0)
printk("%s: Transmitter access conflict.\n", dev->name);
@@ -1038,39 +990,45 @@ static int ni52_send_packet(struct sk_buff *skb, struct device *dev)
memcpy((char *)p->xmit_cbuffs[p->xmit_count],(char *)(skb->data),skb->len);
len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
-#ifdef ONLY_ONE_XMIT_BUF
+#if (NUM_XMIT_BUFFS == 1)
# ifdef NO_NOPCOMMANDS
p->xmit_buffs[0]->size = TBD_LAST | len;
- p->xmit_cmds[0]->cmd_status = 0;
- p->scb->cbl_offset = make16(p->xmit_cmds[0]);
- p->scb->cmd = CUC_START;
-
- dev->trans_start = jiffies;
- ni_attn586();
- while(p->scb->cmd)
- for(len=0;len<256;len++);
-
- /* DELAY(1); */ /* TEST;TEST;TEST */
+ for(i=0;i<16;i++)
+ {
+ p->scb->cbl_offset = make16(p->xmit_cmds[0]);
+ p->scb->cmd = CUC_START;
+ p->xmit_cmds[0]->cmd_status = 0;
+
+ ni_attn586();
+ dev->trans_start = jiffies;
+ if(!i)
+ dev_kfree_skb(skb,FREE_WRITE);
+ WAIT_4_SCB_CMD();
+ if( (p->scb->status & CU_ACTIVE)) /* test it, because CU sometimes doesn't start immediately */
+ break;
+ if(p->xmit_cmds[0]->cmd_status)
+ break;
+ if(i==15)
+ printk("%s: Can't start transmit-command.\n",dev->name);
+ }
# else
next_nop = (p->nop_point + 1) & 0x1;
p->xmit_buffs[0]->size = TBD_LAST | len;
- p->xmit_cmds[0]->cmd_cmd = CMD_XMIT | CMD_INT;
- p->xmit_cmds[0]->cmd_status = 0;
p->xmit_cmds[0]->cmd_link = p->nop_cmds[next_nop]->cmd_link
= make16((p->nop_cmds[next_nop]));
- p->nop_cmds[next_nop]->cmd_status = 0;
+ p->xmit_cmds[0]->cmd_status = p->nop_cmds[next_nop]->cmd_status = 0;
p->nop_cmds[p->nop_point]->cmd_link = make16((p->xmit_cmds[0]));
dev->trans_start = jiffies;
p->nop_point = next_nop;
+ dev_kfree_skb(skb,FREE_WRITE);
# endif
#else
p->xmit_buffs[p->xmit_count]->size = TBD_LAST | len;
if( (next_nop = p->xmit_count + 1) == NUM_XMIT_BUFFS )
next_nop = 0;
- p->xmit_cmds[p->xmit_count]->cmd_cmd = CMD_XMIT | CMD_INT;
p->xmit_cmds[p->xmit_count]->cmd_status = 0;
p->xmit_cmds[p->xmit_count]->cmd_link = p->nop_cmds[next_nop]->cmd_link
= make16((p->nop_cmds[next_nop]));
@@ -1084,71 +1042,57 @@ static int ni52_send_packet(struct sk_buff *skb, struct device *dev)
if(p->xmit_count != p->xmit_last)
dev->tbusy = 0;
sti();
+ dev_kfree_skb(skb,FREE_WRITE);
#endif
}
-
- dev_kfree_skb(skb,FREE_WRITE);
-
return 0;
}
+/*******************************************
+ * Someone wanna have the statistics
+ */
+
static struct enet_statistics *ni52_get_stats(struct device *dev)
{
struct priv *p = (struct priv *) dev->priv;
-#ifdef DEBUG3
- printk("ni52: errs, crc %d, align %d, resource %d, ovrn %d.\n",(int) p->scb->crc_errs,(int) p->scb->aln_errs,(int) p->scb->rsc_errs,(int) p->scb->ovrn_errs);
-#endif
+ unsigned short crc,aln,rsc,ovrn;
+
+ crc = p->scb->crc_errs; /* get error-statistic from the ni82586 */
+ p->scb->crc_errs -= crc;
+ aln = p->scb->aln_errs;
+ p->scb->aln_errs -= aln;
+ rsc = p->scb->rsc_errs;
+ p->scb->rsc_errs -= rsc;
+ ovrn = p->scb->ovrn_errs;
+ p->scb->ovrn_errs -= ovrn;
+
+ p->stats.rx_crc_errors += crc;
+ p->stats.rx_fifo_errors += ovrn;
+ p->stats.rx_frame_errors += aln;
+ p->stats.rx_dropped += rsc;
+
return &p->stats;
}
+/********************************************************
+ * Set MC list ..
+ */
+
static void set_multicast_list(struct device *dev, int num_addrs, void *addrs)
{
-/*
- struct priv *p = (struct priv *) dev->priv;
- volatile struct configure_cmd_struct *cfg_cmd;
-*/
-
- if(!num_addrs)
- printk("%s: Currently, the Ni52 driver doesn't support promiscuous or multicast mode.\n",dev->name);
-
-#if 0
- p->scb->cmd = CUC_SUSPEND;
- ni_attn586();
- while(p->scb->cmd);
- p->scb->cmd = RUC_SUSPEND;
- ni_attn586();
- while(p->scb->cmd);
-
- cfg_cmd = (struct configure_cmd_struct *) p->xmit_cbuffs[0]; /* we're using a transmitcommand */
-
- cfg_cmd->cmd_status = 0;
- cfg_cmd->cmd_cmd = CMD_CONFIGURE | CMD_LAST;
- cfg_cmd->cmd_link = 0xffff;
-
- cfg_cmd->byte_cnt = 0x0a; /* number of cfg bytes */
- cfg_cmd->fifo = 0x08; /* fifo-limit (8=tx:32/rx:64) */
- cfg_cmd->sav_bf = 0x40; /* hold or discard bad recv frames (bit 7) */
- cfg_cmd->adr_len = 0x2e; /* addr_len |!src_insert |pre-len |loopback */
- cfg_cmd->priority = 0x00;
- cfg_cmd->ifd = 0x60;
- cfg_cmd->time_low = 0x00;
- cfg_cmd->time_high = 0xf2;
- cfg_cmd->promisc = 0x01; /* promisc on */
- cfg_cmd->carr_coll = 0x00;
-
- p->scb->cbl_offset = make16(cfg_cmd);
-
- p->scb->cmd = CUC_START; /* cmd.-unit start */
- ni_attn586();
- while(p->scb->cmd);
-
- p->scb->cbl_offset = p->nop_cmds[0]->cmd_link = make16(p->nop_cmds[0]);
- p->scb->cmd = CUC_START;
- ni_atthn586();
- while(p->scb->cmd);
- p->scb->cmd = RUC_RESUME;
- ni_atthn586();
- while(p->scb->cmd);
-#endif
+ if(!dev->start && !num_addrs)
+ {
+ printk("%s: Can't apply promiscuous/multicastmode to a not running interface.\n",dev->name);
+ return;
+ }
+ dev->start = 0;
+ alloc586(dev);
+ init586(dev,num_addrs,addrs);
+ startrecv586(dev);
+ dev->start = 1;
}
+
+/*
+ * END: linux/drivers/net/ni52.c
+ */
diff --git a/drivers/net/ni52.h b/drivers/net/ni52.h
index 652da6eac..23b0a0e89 100644
--- a/drivers/net/ni52.h
+++ b/drivers/net/ni52.h
@@ -227,7 +227,7 @@ struct mcsetup_cmd_struct
unsigned short cmd_cmd;
unsigned short cmd_link;
unsigned short mc_cnt; /* number of bytes in the MC-List */
- unsigned char mc_list[16][6]; /* the list for 16 entries */
+ unsigned char mc_list[0][6]; /* pointer to 6 bytes entries */
};
/*
diff --git a/drivers/net/ni65.c b/drivers/net/ni65.c
index e17f71908..b2a51b4f0 100644
--- a/drivers/net/ni65.c
+++ b/drivers/net/ni65.c
@@ -30,7 +30,6 @@
* known BUGS: 16MB limit
*/
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
@@ -55,7 +54,7 @@
#ifndef HAVE_PORTRESERVE
#define check_region(ioaddr, size) 0
-#define snarf_region(ioaddr, size); do ; while (0)
+#define request_region(ioaddr, size,name) do ; while (0)
#endif
#ifndef NET_DEBUG
@@ -113,7 +112,7 @@ static unsigned int net_debug = NET_DEBUG;
#define writedatareg(val) {outw(val,PORT+L_DATAREG);inw(PORT+L_DATAREG);}
static int ni65_probe1(struct device *dev,int);
-static void ni65_interrupt(int reg_ptr);
+static void ni65_interrupt(int irq, struct pt_regs *regs);
static void recv_intr(struct device *dev);
static void xmit_intr(struct device *dev);
static int ni65_open(struct device *dev);
@@ -244,7 +243,7 @@ static int ni65_probe1(struct device *dev,int ioaddr)
irq2dev_map[dev->irq] = dev;
/* Grab the region so we can find another board if autoIRQ fails. */
- snarf_region(ioaddr,NI65_TOTAL_SIZE);
+ request_region(ioaddr,NI65_TOTAL_SIZE,"ni65");
p = dev->priv = (void *) kmalloc(sizeof(struct priv),GFP_KERNEL);
memset((char *) dev->priv,0,sizeof(struct priv));
@@ -398,9 +397,8 @@ static int am7990_reinit(struct device *dev)
* interrupt handler
*/
-static void ni65_interrupt(int reg_ptr)
+static void ni65_interrupt(int irq, struct pt_regs * regs)
{
- int irq = pt_regs2irq(reg_ptr);
int csr0;
struct device *dev = (struct device *) irq2dev_map[irq];
@@ -545,6 +543,7 @@ static void recv_intr(struct device *dev)
skb1->len = len;
skb1->dev = dev;
p->stats.rx_packets++;
+ skb1->protocol=eth_type_trans(skb1,dev);
netif_rx(skb1);
}
else
diff --git a/drivers/net/pi2.c b/drivers/net/pi2.c
new file mode 100644
index 000000000..384b36c25
--- /dev/null
+++ b/drivers/net/pi2.c
@@ -0,0 +1,1693 @@
+/*
+ pi2.c: Driver for the Ottawa Amateur Radio Club PI and PI2 interface.
+ Copyright (c) 1994 David Perry
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2, as
+ published by the Free Software Foundation.
+
+ 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.
+
+ The file skeleton.c by Donald Becker was used as a starting point
+ for this driver.
+
+ Revision History
+
+ April 6, 1994 (dp) Created
+ version 0.0 ALPHA
+ April 10, 1994 (dp) Included cleanup, suggestions from J. P. Morrison.
+ version 0.1 ALPHA
+ April 13, 1994 (dp) Included address probing from JPM, autoirq
+ version 0.2 ALPHA
+ April 14, 1994 (ac) Sketched in the NET3 changes.
+ April 17, 1994 (dp) Finished the NET3 changes. Used init_etherdev()
+ instead of kmalloc() to ensure that DMA buffers will
+ reside under the 16 meg line.
+ version 0.4 ALPHA
+ April 18, 1994 (dp) Now using the kernel provided sk_buff handling functions.
+ Fixed a nasty problem with DMA.
+ version 0.5 ALPHA
+ June 6, 1994 (ac) Fixed to match the buffer locking changes. Added a hack to
+ fix a funny I see (search for HACK) and fixed the calls in
+ init() so it doesn't migrate module based ethernet cards up
+ to eth2 Took out the old module ideas as they are no longer
+ relevant to the PI driver.
+ July 16, 1994 (dp) Fixed the B channel rx overrun problem ac referred to
+ above. Also added a bit of a hack to improve the maximum
+ baud rate on the B channel (Search for STUFF2). Included
+ ioctl stuff from John Paul Morrison. version 0.6 ALPHA
+ Feb 9, 1995 (dp) Updated for 1.1.90 kernel
+ version 0.7 ALPHA
+ Apr 6, 1995 (ac) Tweaks for NET3 pre snapshot 002 AX.25
+ April 23, 1995 (dp) Fixed ioctl so it works properly with piconfig program
+ when changing the baud rate or clock mode.
+ version 0.8 ALPHA
+
+*/
+
+/* The following #define invokes a hack that will improve performance (baud)
+ for the B port. The up side is it makes 9600 baud work ok on the B port.
+ It may do 38400, depending on the host. The down side is it burns up
+ CPU cycles with ints locked for up to 1 character time, at the beginning
+ of each transmitted packet. If this causes you to lose sleep, #undefine it.
+*/
+
+#define STUFF2 1
+
+/* The default configuration */
+#define PI_DMA 3
+
+#define DEF_A_SPEED 0 /* 0 means external clock */
+#define DEF_A_TXDELAY 15 /* 15 mS transmit delay */
+#define DEF_A_PERSIST 128 /* 50% persistence */
+#define DEF_A_SLOTIME 15 /* 15 mS slot time */
+#define DEF_A_SQUELDELAY 1 /* 1 mS squelch delay - allows fcs and flag */
+#define DEF_A_CLOCKMODE 0 /* clock mode - 0 is normal */
+
+#define DEF_B_SPEED 1200 /* 1200 baud */
+#define DEF_B_TXDELAY 40 /* 400 mS */
+#define DEF_B_PERSIST 128 /* 50% */
+#define DEF_B_SLOTIME 30 /* 300 mS */
+#define DEF_B_SQUELDELAY 3 /* 30 mS */
+#define DEF_B_CLOCKMODE 0 /* Normal clock mode */
+
+struct device *init_etherdev(struct device *dev, int sizeof_private,
+ unsigned long *mem_startp);
+
+static char *version =
+"PI: V0.8 ALPHA April 23 1995 David Perry (dp@hydra.carleton.ca)\n";
+
+/* The following #define is only really required for the PI card, not
+ the PI2 - but it's safer to leave it in. */
+#define REALLY_SLOW_IO 1
+
+#define PI2_MODULE 0
+
+#if PI2_MODULE > 0
+#include <linux/modules.h>
+#endif
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/segment.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/timer.h>
+#include "pi2.h"
+#include "z8530.h"
+#include "ax25.h"
+
+
+struct mbuf {
+ struct mbuf *next;
+ int cnt;
+ char data[0];
+};
+
+/*
+ * The actual devices we will use
+ */
+
+/*
+ * PI device declarations.
+ */
+
+static int pi0_preprobe(struct device *dev){return 0;} /* Dummy probe function */
+static struct device pi0a = { "pi0a", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pi0_preprobe };
+static struct device pi0b = { "pi0b", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pi0_preprobe };
+
+
+/* The number of low I/O ports used by the card. */
+#define PI_TOTAL_SIZE 8
+
+
+/* Index to functions, as function prototypes. */
+
+static int pi_probe(struct device *dev, int card_type);
+static int pi_open(struct device *dev);
+static int pi_send_packet(struct sk_buff *skb, struct device *dev);
+static void pi_interrupt(int reg_ptr, struct pt_regs *regs);
+static int pi_close(struct device *dev);
+static int pi_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
+static struct enet_statistics *pi_get_stats(struct device *dev);
+static void rts(struct pi_local *lp, int x);
+static void b_rxint(struct device *dev, struct pi_local *lp);
+static void b_txint(struct pi_local *lp);
+static void b_exint(struct pi_local *lp);
+static void a_rxint(struct device *dev, struct pi_local *lp);
+static void a_txint(struct pi_local *lp);
+static void a_exint(struct pi_local *lp);
+static char *get_dma_buffer(unsigned long *mem_ptr);
+static int valid_dma_page(unsigned long addr, unsigned long dev_buffsize);
+
+static char ax25_bcast[7] =
+{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static char ax25_test[7] =
+{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+static int ext2_secrm_seed = 152; /* Random generator base */
+
+static inline unsigned char random(void)
+{
+ return (unsigned char) (ext2_secrm_seed = ext2_secrm_seed
+ * 69069l + 1);
+}
+
+static inline void wrtscc(int cbase, int ctl, int sccreg, int val)
+{
+ /* assume caller disables interrupts! */
+ outb_p(0, cbase + DMAEN); /* Disable DMA while we touch the scc */
+ outb_p(sccreg, ctl); /* Select register */
+ outb_p(val, ctl); /* Output value */
+ outb_p(1, cbase + DMAEN); /* Enable DMA */
+}
+
+static inline int rdscc(int cbase, int ctl, int sccreg)
+{
+ int retval;
+
+ /* assume caller disables interrupts! */
+ outb_p(0, cbase + DMAEN); /* Disable DMA while we touch the scc */
+ outb_p(sccreg, ctl); /* Select register */
+ retval = inb_p(ctl);
+ outb_p(1, cbase + DMAEN); /* Enable DMA */
+ return retval;
+}
+
+static void switchbuffers(struct pi_local *lp)
+{
+ if (lp->rcvbuf == lp->rxdmabuf1)
+ lp->rcvbuf = lp->rxdmabuf2;
+ else
+ lp->rcvbuf = lp->rxdmabuf1;
+}
+
+static void hardware_send_packet(struct pi_local *lp, struct sk_buff *skb)
+{
+ char kickflag;
+ unsigned long flags;
+
+ lp->stats.tx_packets++;
+
+ save_flags(flags);
+ cli();
+ kickflag = (skb_peek(&lp->sndq) == NULL) && (lp->sndbuf == NULL);
+ restore_flags(flags);
+
+ skb_queue_tail(&lp->sndq, skb);
+ if (kickflag) {
+ /* simulate interrupt to xmit */
+ switch (lp->base & 2) {
+ case 2:
+ a_txint(lp); /* process interrupt */
+ break;
+ case 0:
+ save_flags(flags);
+ cli();
+ if (lp->tstate == IDLE)
+ b_txint(lp);
+ restore_flags(flags);
+ break;
+ }
+ }
+}
+
+static void setup_rx_dma(struct pi_local *lp)
+{
+ unsigned long flags;
+ int cmd;
+ unsigned long dma_abs;
+ unsigned dmachan;
+
+ save_flags(flags);
+ cli();
+
+ dma_abs = (unsigned long) (lp->rcvbuf->data);
+ dmachan = lp->dmachan;
+ cmd = lp->base + CTL;
+
+ if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf)))
+ panic("PI: RX buffer violates DMA boundary!");
+
+ /* Get ready for RX DMA */
+ wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB);
+
+ disable_dma(dmachan);
+ clear_dma_ff(dmachan);
+
+ /* Set DMA mode register to single transfers, incrementing address,
+ * auto init, writes
+ */
+ set_dma_mode(dmachan, DMA_MODE_READ | 0x10);
+ set_dma_addr(dmachan, dma_abs);
+ set_dma_count(dmachan, lp->bufsiz);
+ enable_dma(dmachan);
+
+ /* If a packet is already coming in, this line is supposed to
+ avoid receiving a partial packet.
+ */
+ wrtscc(lp->cardbase, cmd, R0, RES_Rx_CRC);
+
+ /* Enable RX dma */
+ wrtscc(lp->cardbase, cmd, R1,
+ WT_RDY_ENAB | WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB);
+
+ restore_flags(flags);
+}
+
+static void setup_tx_dma(struct pi_local *lp, int length)
+{
+ unsigned long dma_abs;
+ unsigned long flags;
+ unsigned long dmachan;
+
+ save_flags(flags);
+ cli();
+
+ dmachan = lp->dmachan;
+ dma_abs = (unsigned long) (lp->txdmabuf);
+
+ if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf)))
+ panic("PI: TX buffer violates DMA boundary!");
+
+ disable_dma(dmachan);
+ /* Set DMA mode register to single transfers, incrementing address,
+ * no auto init, reads
+ */
+ set_dma_mode(dmachan, DMA_MODE_WRITE);
+ clear_dma_ff(dmachan);
+ set_dma_addr(dmachan, dma_abs);
+ /* output byte count */
+ set_dma_count(dmachan, length);
+
+ restore_flags(flags);
+}
+
+static void tdelay(struct pi_local *lp, int time)
+{
+ int port;
+ unsigned int t1;
+ unsigned char sc;
+
+ if (lp->base & 2) { /* If A channel */
+ sc = SC1;
+ t1 = time;
+ port = lp->cardbase + TMR1;
+ } else {
+ sc = SC2;
+ t1 = 10 * time; /* 10s of milliseconds for the B channel */
+ port = lp->cardbase + TMR2;
+ wrtscc(lp->cardbase, lp->base + CTL, R1, INT_ALL_Rx | EXT_INT_ENAB);
+ }
+
+ /* Setup timer sc */
+ outb_p(sc | LSB_MSB | MODE0, lp->cardbase + TMRCMD);
+
+ /* times 2 to make millisecs */
+ outb_p((t1 << 1) & 0xFF, port);
+ outb_p((t1 >> 7) & 0xFF, port);
+
+ /* Enable correct int for timeout */
+ wrtscc(lp->cardbase, lp->base + CTL, R15, CTSIE);
+ wrtscc(lp->cardbase, lp->base + CTL, R0, RES_EXT_INT);
+}
+
+static void free_p(struct sk_buff *skb)
+{
+ dev_kfree_skb(skb, FREE_WRITE);
+}
+
+static void a_txint(struct pi_local *lp)
+{
+ int cmd;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ cmd = CTL + lp->base;
+
+ switch (lp->tstate) {
+ case IDLE:
+ /* Transmitter idle. Find a frame for transmission */
+ if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) {
+ rts(lp, OFF);
+ restore_flags(flags);
+ return;
+ }
+ /* If a buffer to send, we drop thru here */
+ case DEFER:
+ /* we may have deferred prev xmit attempt */
+ /* Check DCD - debounce it
+ * See Intel Microcommunications Handbook, p2-308
+ */
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) {
+ lp->tstate = DEFER;
+ tdelay(lp, 100);
+ /* defer until DCD transition or timeout */
+ wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE);
+ restore_flags(flags);
+ return;
+ }
+ if (random() > lp->persist) {
+ lp->tstate = DEFER;
+ tdelay(lp, lp->slotime);
+ restore_flags(flags);
+ return;
+ }
+ /* Assert RTS early minimize collision window */
+ wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8);
+ rts(lp, ON); /* Transmitter on */
+ lp->tstate = ST_TXDELAY;
+ tdelay(lp, lp->txdelay);
+ restore_flags(flags);
+ return;
+ default:
+ break;
+ } /* end switch(lp->state) */
+
+ restore_flags(flags);
+} /*a_txint */
+
+static void a_exint(struct pi_local *lp)
+{
+ unsigned long flags;
+ int cmd;
+ char st;
+ int length;
+
+ save_flags(flags);
+ cli(); /* disable interrupts */
+
+ st = rdscc(lp->cardbase, lp->base + CTL, R0); /* Fetch status */
+
+ /* reset external status latch */
+ wrtscc(lp->cardbase, CTL + lp->base, R0, RES_EXT_INT);
+ cmd = lp->base + CTL;
+
+ if ((lp->rstate >= ACTIVE) && (st & BRK_ABRT)) {
+ setup_rx_dma(lp);
+ lp->rstate = ACTIVE;
+ }
+ switch (lp->tstate) {
+ case ACTIVE:
+ free_p(lp->sndbuf);
+ lp->sndbuf = NULL;
+ lp->tstate = FLAGOUT;
+ tdelay(lp, lp->squeldelay);
+ break;
+ case FLAGOUT:
+ if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) {
+ /* Nothing to send - return to receive mode */
+ lp->tstate = IDLE;
+ rts(lp, OFF);
+ restore_flags(flags);
+ return;
+ }
+ /* NOTE - fall through if more to send */
+ case ST_TXDELAY:
+ /* Disable DMA chan */
+ disable_dma(lp->dmachan);
+
+ /* Set up for TX dma */
+ wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB);
+
+
+ /* Get all chars */
+ /* Strip KISS control byte */
+ length = lp->sndbuf->len - 1;
+ memcpy(lp->txdmabuf, &lp->sndbuf->data[1], length);
+
+
+ /* Setup DMA controller for tx */
+ setup_tx_dma(lp, length);
+
+ /* select transmit interrupts to enable */
+ /* Allow DMA on chan */
+ enable_dma(lp->dmachan);
+
+ /* reset CRC, Txint pend*/
+ wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC | RES_Tx_P);
+
+ /* allow Underrun int only */
+ wrtscc(lp->cardbase, cmd, R15, TxUIE);
+
+ /* Enable TX DMA */
+ wrtscc(lp->cardbase, cmd, R1, WT_RDY_ENAB | WT_FN_RDYFN | EXT_INT_ENAB);
+
+ /* Send CRC on underrun */
+ wrtscc(lp->cardbase, cmd, R0, RES_EOM_L);
+
+
+ /* packet going out now */
+ lp->tstate = ACTIVE;
+ break;
+ case DEFER:
+ /* we have deferred prev xmit attempt
+ * See Intel Microcommunications Handbook, p2-308
+ */
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) {
+ lp->tstate = DEFER;
+ tdelay(lp, 100);
+ /* Defer until dcd transition or 100mS timeout */
+ wrtscc(lp->cardbase, CTL + lp->base, R15, CTSIE | DCDIE);
+ restore_flags(flags);
+ return;
+ }
+ if (random() > lp->persist) {
+ lp->tstate = DEFER;
+ tdelay(lp, lp->slotime);
+ restore_flags(flags);
+ return;
+ }
+ /* Assert RTS early minimize collision window */
+ wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8);
+ rts(lp, ON); /* Transmitter on */
+ lp->tstate = ST_TXDELAY;
+ tdelay(lp, lp->txdelay);
+ restore_flags(flags);
+ return;
+ } /* switch(lp->tstate) */
+
+ restore_flags(flags);
+} /* a_exint() */
+
+/* Receive interrupt handler for the A channel
+ */
+static void a_rxint(struct device *dev, struct pi_local *lp)
+{
+ unsigned long flags;
+ int cmd;
+ int bytecount;
+ char rse;
+ struct sk_buff *skb;
+ int sksize, pkt_len;
+ struct mbuf *cur_buf;
+
+ save_flags(flags);
+ cli(); /* disable interrupts */
+ cmd = lp->base + CTL;
+
+ rse = rdscc(lp->cardbase, cmd, R1); /* Get special condition bits from R1 */
+ if (rse & Rx_OVR)
+ lp->rstate = RXERROR;
+
+ if (rse & END_FR) {
+ /* If end of frame */
+ /* figure length of frame from 8237 */
+ clear_dma_ff(lp->dmachan);
+ bytecount = lp->bufsiz - get_dma_residue(lp->dmachan);
+
+ if ((rse & CRC_ERR) || (lp->rstate > ACTIVE) || (bytecount < 10)) {
+ if ((bytecount >= 10) && (rse & CRC_ERR)) {
+ lp->stats.rx_crc_errors++;
+ }
+ if (lp->rstate == RXERROR) {
+ lp->stats.rx_errors++;
+ lp->stats.rx_over_errors++;
+ }
+ /* Reset buffer pointers */
+ lp->rstate = ACTIVE;
+ setup_rx_dma(lp);
+ } else {
+ /* Here we have a valid frame */
+ /* Toss 2 crc bytes , add one for KISS */
+ pkt_len = lp->rcvbuf->cnt = bytecount - 2 + 1;
+
+ /* Get buffer for next frame */
+ cur_buf = lp->rcvbuf;
+ switchbuffers(lp);
+ setup_rx_dma(lp);
+
+
+ /* Malloc up new buffer. */
+ sksize = pkt_len;
+
+ skb = alloc_skb(sksize, GFP_ATOMIC);
+ if (skb == NULL) {
+ printk("PI: %s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ restore_flags(flags);
+ return;
+ }
+ skb->len = (unsigned long) pkt_len;
+ skb->dev = dev;
+
+ /* KISS kludge - prefix with a 0 byte */
+ skb->data[0] = 0;
+ /* 'skb->data' points to the start of sk_buff data area. */
+ memcpy(&skb->data[1], (char *) cur_buf->data,
+ pkt_len - 1);
+ skb->protocol=ntohs(ETH_P_AX25);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ } /* end good frame */
+ } /* end EOF check */
+ wrtscc(lp->cardbase, lp->base + CTL, R0, ERR_RES); /* error reset */
+ restore_flags(flags);
+}
+
+static void b_rxint(struct device *dev, struct pi_local *lp)
+{
+ unsigned long flags;
+ int cmd;
+ char rse;
+ struct sk_buff *skb;
+ int sksize;
+ int pkt_len;
+
+ save_flags(flags);
+ cli(); /* disable interrupts */
+ cmd = CTL + lp->base;
+
+ rse = rdscc(lp->cardbase, cmd, R1); /* get status byte from R1 */
+
+ if ((rdscc(lp->cardbase, cmd, R0)) & Rx_CH_AV) {
+ /* there is a char to be stored
+ * read special condition bits before reading the data char
+ */
+ if (rse & Rx_OVR) {
+ /* Rx overrun - toss buffer */
+ /* reset buffer pointers */
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0;
+
+ lp->rstate = RXERROR; /* set error flag */
+ lp->stats.rx_errors++;
+ lp->stats.rx_over_errors++;
+ } else if (lp->rcvbuf->cnt >= lp->bufsiz) {
+ /* Too large -- toss buffer */
+ /* reset buffer pointers */
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0;
+ lp->rstate = TOOBIG;/* when set, chars are not stored */
+ }
+ /* ok, we can store the received character now */
+ if (lp->rstate == ACTIVE) { /* If no errors... */
+ *lp->rcp++ = rdscc(lp->cardbase, cmd, R8); /* char to rcv buff */
+ lp->rcvbuf->cnt++; /* bump count */
+ } else {
+ /* got to empty FIFO */
+ (void) rdscc(lp->cardbase, cmd, R8);
+ wrtscc(lp->cardbase, cmd, R0, ERR_RES); /* reset err latch */
+ lp->rstate = ACTIVE;
+ }
+ }
+ if (rse & END_FR) {
+ /* END OF FRAME -- Make sure Rx was active */
+ if (lp->rcvbuf->cnt > 0) {
+ if ((rse & CRC_ERR) || (lp->rstate > ACTIVE) || (lp->rcvbuf->cnt < 10)) {
+ if ((lp->rcvbuf->cnt >= 10) && (rse & CRC_ERR)) {
+ lp->stats.rx_crc_errors++;
+ }
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0;
+ } else {
+ /* Here we have a valid frame */
+ pkt_len = lp->rcvbuf->cnt -= 2; /* Toss 2 crc bytes */
+ pkt_len += 1; /* Make room for KISS control byte */
+
+ /* Malloc up new buffer. */
+ sksize = pkt_len;
+ skb = alloc_skb(sksize, GFP_ATOMIC);
+ if (skb == NULL) {
+ printk("PI: %s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ restore_flags(flags);
+ return;
+ }
+ skb->len = pkt_len;
+ skb->dev = dev;
+
+ /* KISS kludge - prefix with a 0 byte */
+ skb->data[0] = 0;
+ /* 'skb->data' points to the start of sk_buff data area. */
+ memcpy(&skb->data[1], lp->rcvbuf->data, pkt_len - 1);
+ skb->protocol=ntohs(ETH_P_AX25);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ /* packet queued - initialize buffer for next frame */
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0;
+
+ } /* end good frame queued */
+ } /* end check for active receive upon EOF */
+ lp->rstate = ACTIVE; /* and clear error status */
+ } /* end EOF check */
+ restore_flags(flags);
+}
+
+
+static void b_txint(struct pi_local *lp)
+{
+ unsigned long flags;
+ int cmd;
+ unsigned char c;
+
+ save_flags(flags);
+ cli();
+ cmd = CTL + lp->base;
+
+ switch (lp->tstate) {
+ case CRCOUT:
+ lp->tstate = FLAGOUT;
+ tdelay(lp, lp->squeldelay);
+ restore_flags(flags);
+ return;
+ case IDLE:
+ /* Transmitter idle. Find a frame for transmission */
+ if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) {
+ /* Nothing to send - return to receive mode
+ * Tx OFF now - flag should have gone
+ */
+ rts(lp, OFF);
+
+ restore_flags(flags);
+ return;
+ }
+ lp->txptr = lp->sndbuf->data;
+ lp->txptr++; /* Ignore KISS control byte */
+ lp->txcnt = (int) lp->sndbuf->len - 1;
+ /* If a buffer to send, we drop thru here */
+ case DEFER: /* we may have deferred prev xmit attempt */
+ /* Check DCD - debounce it */
+ /* See Intel Microcommunications Handbook, p2-308 */
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) {
+ lp->tstate = DEFER;
+ tdelay(lp, 100);
+ /* defer until DCD transition or timeout */
+ wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE);
+ restore_flags(flags);
+ return;
+ }
+ if (random() > lp->persist) {
+ lp->tstate = DEFER;
+ tdelay(lp, lp->slotime);
+ restore_flags(flags);
+ return;
+ }
+ rts(lp, ON); /* Transmitter on */
+ lp->tstate = ST_TXDELAY;
+ tdelay(lp, lp->txdelay);
+ restore_flags(flags);
+ return;
+
+ case ACTIVE:
+ /* Here we are actively sending a frame */
+ if (lp->txcnt--) {
+ c = *lp->txptr++;
+ /* next char is gone */
+ wrtscc(lp->cardbase, cmd, R8, c);
+ /* stuffing a char satisfies Interrupt condition */
+ } else {
+ /* No more to send */
+ free_p(lp->sndbuf);
+ lp->sndbuf = NULL;
+ if ((rdscc(lp->cardbase, cmd, R0) & 0x40)) {
+ /* Did we underrun? */
+ /* unexpected underrun */
+ lp->stats.tx_errors++;
+ lp->stats.tx_fifo_errors++;
+ wrtscc(lp->cardbase, cmd, R0, SEND_ABORT);
+ lp->tstate = FLAGOUT;
+ tdelay(lp, lp->squeldelay);
+ restore_flags(flags);
+ return;
+ }
+ lp->tstate = UNDERRUN; /* Now we expect to underrun */
+ /* Send flags on underrun */
+ if (lp->speed) { /* If internally clocked */
+ wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI);
+ } else {
+ wrtscc(lp->cardbase, cmd, R10, CRCPS);
+ }
+ wrtscc(lp->cardbase, cmd, R0, RES_Tx_P); /* reset Tx Int Pend */
+ }
+ restore_flags(flags);
+ return; /* back to wait for interrupt */
+ } /* end switch */
+ restore_flags(flags);
+}
+
+/* Pi SIO External/Status interrupts (for the B channel)
+ * This can be caused by a receiver abort, or a Tx UNDERRUN/EOM.
+ * Receiver automatically goes to Hunt on an abort.
+ *
+ * If the Tx Underrun interrupt hits, change state and
+ * issue a reset command for it, and return.
+ */
+static void b_exint(struct pi_local *lp)
+{
+ unsigned long flags;
+ char st;
+ int cmd;
+ char c;
+
+ cmd = CTL + lp->base;
+ save_flags(flags);
+ cli(); /* disable interrupts */
+ st = rdscc(lp->cardbase, cmd, R0); /* Fetch status */
+ /* reset external status latch */
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+
+
+ switch (lp->tstate) {
+ case ACTIVE: /* Unexpected underrun */
+ free_p(lp->sndbuf);
+ lp->sndbuf = NULL;
+ wrtscc(lp->cardbase, cmd, R0, SEND_ABORT);
+ lp->tstate = FLAGOUT;
+ lp->stats.tx_errors++;
+ lp->stats.tx_fifo_errors++;
+ tdelay(lp, lp->squeldelay);
+ restore_flags(flags);
+ return;
+ case UNDERRUN:
+ lp->tstate = CRCOUT;
+ restore_flags(flags);
+ return;
+ case FLAGOUT:
+ /* Find a frame for transmission */
+ if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) {
+ /* Nothing to send - return to receive mode
+ * Tx OFF now - flag should have gone
+ */
+ rts(lp, OFF);
+ lp->tstate = IDLE;
+ restore_flags(flags);
+ return;
+ }
+ lp->txptr = lp->sndbuf->data;
+ lp->txptr++; /* Ignore KISS control byte */
+ lp->txcnt = (int) lp->sndbuf->len - 1;
+ /* Get first char to send */
+ lp->txcnt--;
+ c = *lp->txptr++;
+ wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC); /* reset for next frame */
+
+ /* Send abort on underrun */
+ if (lp->speed) { /* If internally clocked */
+ wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER);
+ } else {
+ wrtscc(lp->cardbase, cmd, R10, CRCPS | ABUNDER);
+ }
+
+ wrtscc(lp->cardbase, cmd, R8, c); /* First char out now */
+ wrtscc(lp->cardbase, cmd, R0, RES_EOM_L); /* Reset end of message latch */
+
+#ifdef STUFF2
+ /* stuff an extra one if we can */
+ if (lp->txcnt) {
+ lp->txcnt--;
+ c = *lp->txptr++;
+ /* Wait for tx buffer empty */
+ while((rdscc(lp->cardbase, cmd, R0) & 0x04) == 0)
+ ;
+ wrtscc(lp->cardbase, cmd, R8, c);
+ }
+#endif
+
+ /* select transmit interrupts to enable */
+
+ wrtscc(lp->cardbase, cmd, R15, TxUIE); /* allow Underrun int only */
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB); /* Tx/Ext ints */
+
+ lp->tstate = ACTIVE; /* char going out now */
+ restore_flags(flags);
+ return;
+
+ case DEFER:
+ /* Check DCD - debounce it
+ * See Intel Microcommunications Handbook, p2-308
+ */
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) {
+ lp->tstate = DEFER;
+ tdelay(lp, 100);
+ /* defer until DCD transition or timeout */
+ wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE);
+ restore_flags(flags);
+ return;
+ }
+ if (random() > lp->persist) {
+ lp->tstate = DEFER;
+ tdelay(lp, lp->slotime);
+ restore_flags(flags);
+ return;
+ }
+ rts(lp, ON); /* Transmitter on */
+ lp->tstate = ST_TXDELAY;
+ tdelay(lp, lp->txdelay);
+ restore_flags(flags);
+ return;
+
+ case ST_TXDELAY:
+
+ /* Get first char to send */
+ lp->txcnt--;
+ c = *lp->txptr++;
+ wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC); /* reset for next frame */
+
+ /* Send abort on underrun */
+ if (lp->speed) { /* If internally clocked */
+ wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER);
+ } else {
+ wrtscc(lp->cardbase, cmd, R10, CRCPS | ABUNDER);
+ }
+
+ wrtscc(lp->cardbase, cmd, R8, c); /* First char out now */
+ wrtscc(lp->cardbase, cmd, R0, RES_EOM_L); /* Reset end of message latch */
+
+#ifdef STUFF2
+ /* stuff an extra one if we can */
+ if (lp->txcnt) {
+ lp->txcnt--;
+ c = *lp->txptr++;
+ /* Wait for tx buffer empty */
+ while((rdscc(lp->cardbase, cmd, R0) & 0x04) == 0)
+ ;
+ wrtscc(lp->cardbase, cmd, R8, c);
+ }
+#endif
+
+ /* select transmit interrupts to enable */
+
+ wrtscc(lp->cardbase, cmd, R15, TxUIE); /* allow Underrun int only */
+ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+ /* Tx/Extern ints on */
+ wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB);
+
+ lp->tstate = ACTIVE; /* char going out now */
+ restore_flags(flags);
+ return;
+ }
+
+ /* Receive Mode only
+ * This triggers when hunt mode is entered, & since an ABORT
+ * automatically enters hunt mode, we use that to clean up
+ * any waiting garbage
+ */
+ if ((lp->rstate == ACTIVE) && (st & BRK_ABRT)) {
+ (void) rdscc(lp->cardbase, cmd, R8);
+ (void) rdscc(lp->cardbase, cmd, R8);
+ (void) rdscc(lp->cardbase, cmd, R8);
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0; /* rewind on DCD transition */
+ }
+ restore_flags(flags);
+}
+
+/* Probe for a PI card. */
+/* This routine also initializes the timer chip */
+
+static int hw_probe(int ioaddr)
+{
+ int time = 1000; /* Number of milliseconds for test */
+ unsigned long start_time, end_time;
+
+ int base, tmr0, tmr1, tmrcmd;
+ int a = 1;
+ int b = 1;
+
+ base = ioaddr & 0x3f0;
+ tmr0 = TMR0 + base;
+ tmr1 = TMR1 + base;
+ tmrcmd = TMRCMD + base;
+
+ /* Set up counter chip timer 0 for 500 uS period square wave */
+ /* assuming a 3.68 mhz clock for now */
+ outb_p(SC0 | LSB_MSB | MODE3, tmrcmd);
+ outb_p(922 & 0xFF, tmr0);
+ outb_p(922 >> 8, tmr0);
+
+ /* Setup timer control word for timer 1*/
+ outb_p(SC1 | LSB_MSB | MODE0, tmrcmd);
+ outb_p((time << 1) & 0xFF, tmr1);
+ outb_p((time >> 7) & 0XFF, tmr1);
+
+ /* wait until counter reg is loaded */
+ do {
+ /* Latch count for reading */
+ outb_p(SC1, tmrcmd);
+ a = inb_p(tmr1);
+ b = inb_p(tmr1);
+ } while (b == 0);
+ start_time = jiffies;
+ while (b != 0) {
+ /* Latch count for reading */
+ outb_p(SC1, tmrcmd);
+ a = inb_p(tmr1);
+ b = inb_p(tmr1);
+ end_time = jiffies;
+ /* Don't wait forever - there may be no card here */
+ if ((end_time - start_time) > 200)
+ return 0; /* No card found */
+ }
+ end_time = jiffies;
+ /* 87 jiffies, for a 3.68 mhz clock, half that for a double speed clock */
+ if ((end_time - start_time) > 65) {
+ return (1); /* PI card found */
+ } else {
+ /* Faster crystal - tmr0 needs adjusting */
+ /* Set up counter chip */
+ /* 500 uS square wave */
+ outb_p(SC0 | LSB_MSB | MODE3, tmrcmd);
+ outb_p(1844 & 0xFF, tmr0);
+ outb_p(1844 >> 8, tmr0);
+ return (2); /* PI2 card found */
+ }
+}
+
+static void rts(struct pi_local *lp, int x)
+{
+ int tc;
+ long br;
+ int cmd;
+ int dummy;
+
+ /* assumes interrupts are off */
+ cmd = CTL + lp->base;
+
+ /* Reprogram BRG and turn on transmitter to send flags */
+ if (x == ON) { /* Turn Tx ON and Receive OFF */
+ /* Exints off first to avoid abort int */
+ wrtscc(lp->cardbase, cmd, R15, 0);
+ wrtscc(lp->cardbase, cmd, R3, Rx8); /* Rx off */
+ lp->rstate = IDLE;
+ if (cmd & 2) { /* if channel a */
+ /* Set up for TX dma */
+ wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB);
+ } else {
+ wrtscc(lp->cardbase, cmd, R1, 0); /* No interrupts */
+ }
+
+ if (!lp->clockmode) {
+ if (lp->speed) { /* if internally clocked */
+ br = lp->speed; /* get desired speed */
+ tc = (lp->xtal / br) - 2; /* calc 1X BRG divisor */
+ wrtscc(lp->cardbase, cmd, R12, tc & 0xFF); /* lower byte */
+ wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xFF); /* upper byte */
+ }
+ }
+ wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8 | DTR);
+ /* Transmitter now on */
+ } else { /* Tx OFF and Rx ON */
+ lp->tstate = IDLE;
+ wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR); /* TX off */
+
+ if (!lp->clockmode) {
+ if (lp->speed) { /* if internally clocked */
+ /* Reprogram BRG for 32x clock for receive DPLL */
+ /* BRG off, keep Pclk source */
+ wrtscc(lp->cardbase, cmd, R14, BRSRC);
+ br = lp->speed; /* get desired speed */
+ /* calc 32X BRG divisor */
+ tc = ((lp->xtal / 32) / br) - 2;
+ wrtscc(lp->cardbase, cmd, R12, tc & 0xFF); /* lower byte */
+ wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xFF); /* upper byte */
+ /* SEARCH mode, BRG source */
+ wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH);
+ /* Enable the BRG */
+ wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL);
+ }
+ }
+ /* Flush rx fifo */
+ wrtscc(lp->cardbase, cmd, R3, Rx8); /* Make sure rx is off */
+ wrtscc(lp->cardbase, cmd, R0, ERR_RES); /* reset err latch */
+ dummy = rdscc(lp->cardbase, cmd, R1); /* get status byte from R1 */
+ (void) rdscc(lp->cardbase, cmd, R8);
+ (void) rdscc(lp->cardbase, cmd, R8);
+
+ (void) rdscc(lp->cardbase, cmd, R8);
+
+ /* Now, turn on the receiver and hunt for a flag */
+ wrtscc(lp->cardbase, cmd, R3, RxENABLE | Rx8);
+ lp->rstate = ACTIVE; /* Normal state */
+
+ if (cmd & 2) { /* if channel a */
+ setup_rx_dma(lp);
+ } else {
+ /* reset buffer pointers */
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0;
+ wrtscc(lp->cardbase, cmd, R1, (INT_ALL_Rx | EXT_INT_ENAB));
+ }
+ wrtscc(lp->cardbase, cmd, R15, BRKIE); /* allow ABORT int */
+ }
+}
+
+/* Fill in the MAC-level header. */
+static int pi_header(unsigned char *buff, struct device *dev, unsigned short type,
+ void *daddr, void *saddr, unsigned len, struct sk_buff *skb)
+{
+ return ax25_encapsulate(buff, dev, type, daddr, saddr, len, skb);
+}
+
+/* Rebuild the MAC-level header. */
+static int pi_rebuild_header(void *buff, struct device *dev, unsigned long raddr,
+ struct sk_buff *skb)
+{
+ return ax25_rebuild_header(buff, dev, raddr, skb);
+}
+
+static void scc_init(struct device *dev)
+{
+ unsigned long flags;
+ struct pi_local *lp = (struct pi_local *) dev->priv;
+
+ int tc;
+ long br;
+ register int cmd;
+
+ /* Initialize 8530 channel for SDLC operation */
+
+ cmd = CTL + lp->base;
+ save_flags(flags);
+ cli();
+
+ switch (cmd & CHANA) {
+ case CHANA:
+ wrtscc(lp->cardbase, cmd, R9, CHRA); /* Reset channel A */
+ wrtscc(lp->cardbase, cmd, R2, 0xff); /* Initialize interrupt vector */
+ break;
+ default:
+ wrtscc(lp->cardbase, cmd, R9, CHRB); /* Reset channel B */
+ break;
+ }
+
+ /* Deselect all Rx and Tx interrupts */
+ wrtscc(lp->cardbase, cmd, R1, 0);
+
+ /* Turn off external interrupts (like CTS/CD) */
+ wrtscc(lp->cardbase, cmd, R15, 0);
+
+ /* X1 clock, SDLC mode */
+ wrtscc(lp->cardbase, cmd, R4, SDLC | X1CLK);
+
+ /* Tx/Rx parameters */
+ if (lp->speed) { /* Use internal clocking */
+ wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI);
+ if (!lp->clockmode)
+ /* Tx Clk from BRG. Rcv Clk from DPLL, TRxC pin outputs DPLL */
+ wrtscc(lp->cardbase, cmd, R11, TCBR | RCDPLL | TRxCDP | TRxCOI);
+ else
+ /* Tx Clk from DPLL, Rcv Clk from DPLL, TRxC Outputs BRG */
+ wrtscc(lp->cardbase, cmd, R11, TCDPLL | RCDPLL | TRxCBR | TRxCOI);
+ } else { /* Use external clocking */
+ wrtscc(lp->cardbase, cmd, R10, CRCPS);
+ /* Tx Clk from Trxcl. Rcv Clk from Rtxcl, TRxC pin is input */
+ wrtscc(lp->cardbase, cmd, R11, TCTRxCP);
+ }
+
+ /* Null out SDLC start address */
+ wrtscc(lp->cardbase, cmd, R6, 0);
+
+ /* SDLC flag */
+ wrtscc(lp->cardbase, cmd, R7, FLAG);
+
+ /* Set up the Transmitter but don't enable it
+ * DTR, 8 bit TX chars only
+ */
+ wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR);
+
+ /* Receiver initial setup */
+ wrtscc(lp->cardbase, cmd, R3, Rx8); /* 8 bits/char */
+
+ /* Setting up BRG now - turn it off first */
+ wrtscc(lp->cardbase, cmd, R14, BRSRC); /* BRG off, keep Pclk source */
+
+ /* set the 32x time constant for the BRG in Receive mode */
+
+ if (lp->speed) {
+ br = lp->speed; /* get desired speed */
+ tc = ((lp->xtal / 32) / br) - 2; /* calc 32X BRG divisor */
+ } else {
+ tc = 14;
+ }
+
+ wrtscc(lp->cardbase, cmd, R12, tc & 0xFF); /* lower byte */
+ wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xFF); /* upper byte */
+
+ /* Following subroutine sets up and ENABLES the receiver */
+ rts(lp, OFF); /* TX OFF and RX ON */
+
+ if (lp->speed) {
+ /* DPLL frm BRG, BRG src PCLK */
+ wrtscc(lp->cardbase, cmd, R14, BRSRC | SSBR);
+ } else {
+ /* DPLL frm rtxc,BRG src PCLK */
+ wrtscc(lp->cardbase, cmd, R14, BRSRC | SSRTxC);
+ }
+ wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH); /* SEARCH mode, keep BRG src */
+ wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL); /* Enable the BRG */
+
+ if (!(cmd & 2)) /* if channel b */
+ wrtscc(lp->cardbase, cmd, R1, (INT_ALL_Rx | EXT_INT_ENAB));
+
+ wrtscc(lp->cardbase, cmd, R15, BRKIE); /* ABORT int */
+
+ /* Now, turn on the receiver and hunt for a flag */
+ wrtscc(lp->cardbase, cmd, R3, RxENABLE | RxCRC_ENAB | Rx8);
+
+ restore_flags(flags);
+}
+
+static void chipset_init(struct device *dev)
+{
+ int cardbase;
+ unsigned long flags;
+
+ cardbase = dev->base_addr & 0x3f0;
+
+ save_flags(flags);
+ cli();
+ wrtscc(cardbase, dev->base_addr + CTL, R9, FHWRES); /* Hardware reset */
+ /* Disable interrupts with master interrupt ctrl reg */
+ wrtscc(cardbase, dev->base_addr + CTL, R9, 0);
+ restore_flags(flags);
+
+}
+
+
+unsigned long pi_init(unsigned long mem_start, unsigned long mem_end)
+{
+ int *port;
+ int ioaddr = 0;
+ int card_type = 0;
+ int ports[] =
+ {0x380, 0x300, 0x320, 0x340, 0x360, 0x3a0, 0};
+
+ printk(version);
+
+ /* Only one card supported for now */
+ for (port = &ports[0]; *port && !card_type; port++) {
+ ioaddr = *port;
+
+ if (check_region(ioaddr, PI_TOTAL_SIZE) == 0) {
+ printk("PI: Probing for card at address %#3x\n",ioaddr);
+ card_type = hw_probe(ioaddr);
+ }
+ }
+
+ switch (card_type) {
+ case 1:
+ printk("PI: Found a PI card at address %#3x\n", ioaddr);
+ break;
+ case 2:
+ printk("PI: Found a PI2 card at address %#3x\n", ioaddr);
+ break;
+ default:
+ printk("PI: ERROR: No card found\n");
+ return mem_start;
+ }
+
+ /* Link a couple of device structures into the chain */
+ /* For the A port */
+ /* Allocate space for 4 buffers even though we only need 3,
+ because one of them may cross a DMA page boundary and
+ be rejected by get_dma_buffer().
+ */
+ register_netdev(&pi0a);
+
+ pi0a.priv=(void *)mem_start;
+ mem_start+=sizeof(struct pi_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4
+ + 8; /* for alignment */
+
+ pi0a.dma = PI_DMA;
+ pi0a.base_addr = ioaddr + 2;
+ pi0a.irq = 0;
+
+ /* And the B port */
+ register_netdev(&pi0b);
+ pi0b.base_addr = ioaddr;
+ pi0b.irq = 0;
+ pi0b.priv=(void *)mem_start;
+ mem_start+=sizeof(struct pi_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4
+ + 8; /* for alignment */
+
+ /* Now initialize them */
+ pi_probe(&pi0a, card_type);
+ pi_probe(&pi0b, card_type);
+
+ pi0b.irq = pi0a.irq; /* IRQ is shared */
+
+ return mem_start;
+}
+
+static int valid_dma_page(unsigned long addr, unsigned long dev_buffsize)
+{
+ if (((addr & 0xffff) + dev_buffsize) <= 0x10000)
+ return 1;
+ else
+ return 0;
+}
+
+static int pi_set_mac_address(struct device *dev, void *addr)
+{
+ memcpy(dev->dev_addr, addr, 7); /* addr is an AX.25 shifted ASCII */
+ return 0; /* mac address */
+}
+
+/* Allocate a buffer which does not cross a DMA page boundary */
+static char *
+get_dma_buffer(unsigned long *mem_ptr)
+{
+ char *ret;
+
+ ret = (char *)*mem_ptr;
+
+ if(!valid_dma_page(*mem_ptr, DMA_BUFF_SIZE + sizeof(struct mbuf))){
+ *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf));
+ ret = (char *)*mem_ptr;
+ }
+ *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf));
+ return (ret);
+}
+
+static int pi_probe(struct device *dev, int card_type)
+{
+ short ioaddr;
+ struct pi_local *lp;
+ int i;
+ unsigned long flags;
+ unsigned long mem_ptr;
+
+ ioaddr = dev->base_addr;
+
+ /* Initialize the device structure. */
+ /* Must be done before chipset_init */
+ /* Make certain the data structures used by the PI2 are aligned. */
+ dev->priv = (void *) (((int) dev->priv + 7) & ~7);
+ lp = (struct pi_local *) dev->priv;
+
+ memset(dev->priv, 0, sizeof(struct pi_local));
+
+ /* Allocate some buffers which do not cross DMA page boundaries */
+ mem_ptr = (unsigned long) dev->priv + sizeof(struct pi_local);
+ lp->txdmabuf = get_dma_buffer(&mem_ptr);
+ lp->rxdmabuf1 = (struct mbuf *) get_dma_buffer(&mem_ptr);
+ lp->rxdmabuf2 = (struct mbuf *) get_dma_buffer(&mem_ptr);
+
+ /* Initialize rx buffer */
+ lp->rcvbuf = lp->rxdmabuf1;
+ lp->rcp = lp->rcvbuf->data;
+ lp->rcvbuf->cnt = 0;
+
+ /* Initialize the transmit queue head structure */
+ skb_queue_head_init(&lp->sndq);
+
+ /* These need to be initialized before scc_init is called. */
+ if (card_type == 1)
+ lp->xtal = (unsigned long) SINGLE / 2;
+ else
+ lp->xtal = (unsigned long) DOUBLE / 2;
+ lp->base = dev->base_addr;
+ lp->cardbase = dev->base_addr & 0x3f0;
+ if (dev->base_addr & CHANA) {
+ lp->speed = DEF_A_SPEED;
+ /* default channel access Params */
+ lp->txdelay = DEF_A_TXDELAY;
+ lp->persist = DEF_A_PERSIST;
+ lp->slotime = DEF_A_SLOTIME;
+ lp->squeldelay = DEF_A_SQUELDELAY;
+ lp->clockmode = DEF_A_CLOCKMODE;
+
+ } else {
+ lp->speed = DEF_B_SPEED;
+ /* default channel access Params */
+ lp->txdelay = DEF_B_TXDELAY;
+ lp->persist = DEF_B_PERSIST;
+ lp->slotime = DEF_B_SLOTIME;
+ lp->squeldelay = DEF_B_SQUELDELAY;
+ lp->clockmode = DEF_B_CLOCKMODE;
+ }
+ lp->bufsiz = DMA_BUFF_SIZE;
+ lp->tstate = IDLE;
+
+ chipset_init(dev);
+
+ if (dev->base_addr & CHANA) { /* Do these things only for the A port */
+ /* Note that a single IRQ services 2 devices (A and B channels) */
+
+ lp->dmachan = dev->dma;
+ if (lp->dmachan < 1 || lp->dmachan > 3)
+ printk("PI: DMA channel %d out of range\n", lp->dmachan);
+
+ /* chipset_init() was already called */
+
+ if (dev->irq < 2) {
+ autoirq_setup(0);
+ save_flags(flags);
+ cli();
+ wrtscc(lp->cardbase, CTL + lp->base, R1, EXT_INT_ENAB);
+ /* enable PI card interrupts */
+ wrtscc(lp->cardbase, CTL + lp->base, R9, MIE | NV);
+ restore_flags(flags);
+ /* request a timer interrupt for 1 mS hence */
+ tdelay(lp, 1);
+ /* 20 "jiffies" should be plenty of time... */
+ dev->irq = autoirq_report(20);
+ if (!dev->irq) {
+ printk(". Failed to detect IRQ line.\n");
+ }
+ save_flags(flags);
+ cli();
+ wrtscc(lp->cardbase, dev->base_addr + CTL, R9, FHWRES); /* Hardware reset */
+ /* Disable interrupts with master interrupt ctrl reg */
+ wrtscc(lp->cardbase, dev->base_addr + CTL, R9, 0);
+ restore_flags(flags);
+ }
+
+ printk("PI: Autodetected IRQ %d, assuming DMA %d.\n",
+ dev->irq, dev->dma);
+
+ /* This board has jumpered interrupts. Snarf the interrupt vector
+ now. There is no point in waiting since no other device can use
+ the interrupt, and this marks the 'irqaction' as busy. */
+ {
+ int irqval = request_irq(dev->irq, &pi_interrupt,0, "pi2");
+ if (irqval) {
+ printk("PI: unable to get IRQ %d (irqval=%d).\n",
+ dev->irq, irqval);
+ return EAGAIN;
+ }
+ }
+
+ /* Grab the region */
+ snarf_region(ioaddr & 0x3f0, PI_TOTAL_SIZE);
+
+
+ } /* Only for A port */
+ dev->open = pi_open;
+ dev->stop = pi_close;
+ dev->do_ioctl = pi_ioctl;
+ dev->hard_start_xmit = pi_send_packet;
+ dev->get_stats = pi_get_stats;
+
+ /* Fill in the fields of the device structure */
+ for (i = 0; i < DEV_NUMBUFFS; i++)
+ skb_queue_head_init(&dev->buffs[i]);
+
+
+ dev->hard_header = pi_header;
+ dev->rebuild_header = pi_rebuild_header;
+ dev->set_mac_address = pi_set_mac_address;
+
+ dev->type = AF_AX25; /* AF_AX25 device */
+ dev->hard_header_len = 17; /* We don't do digipeaters */
+ dev->mtu = 1500; /* eth_mtu is the default */
+ dev->addr_len = 7; /* sizeof an ax.25 address */
+ for (i = 0; i < ETH_ALEN; i++) {
+ dev->broadcast[i] = 0xff;
+ }
+ memcpy(dev->broadcast, ax25_bcast, 7);
+ memcpy(dev->dev_addr, ax25_test, 7);
+
+ /* New-style flags. */
+ dev->flags = 0;
+ dev->family = AF_INET;
+ dev->pa_addr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ dev->pa_alen = sizeof(unsigned long);
+
+ return 0;
+}
+
+/* Open/initialize the board. This is called (in the current kernel)
+ sometime after booting when the 'ifconfig' program is run.
+
+ This routine should set everything up anew at each open, even
+ registers that "should" only need to be set once at boot, so that
+ there is non-reboot way to recover if something goes wrong.
+ */
+static int pi_open(struct device *dev)
+{
+ unsigned long flags;
+ static first_time = 1;
+
+ struct pi_local *lp = (struct pi_local *) dev->priv;
+
+ if (dev->base_addr & 2) { /* if A channel */
+ if (first_time) {
+ if (request_dma(dev->dma,"pi2")) {
+ free_irq(dev->irq);
+ return -EAGAIN;
+ }
+ irq2dev_map[dev->irq] = dev;
+ }
+ /* Reset the hardware here. */
+ chipset_init(dev);
+ }
+ lp->tstate = IDLE;
+
+ if (dev->base_addr & 2) { /* if A channel */
+ scc_init(dev); /* Called once for each channel */
+ scc_init(dev->next);
+ }
+ /* master interrupt enable */
+ save_flags(flags);
+ cli();
+ wrtscc(lp->cardbase, CTL + lp->base, R9, MIE | NV);
+ restore_flags(flags);
+
+ lp->open_time = jiffies;
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+ first_time = 0;
+ return 0;
+}
+
+static int pi_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct pi_local *lp = (struct pi_local *) dev->priv;
+
+ /* If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+ hardware_send_packet(lp, skb);
+ dev->trans_start = jiffies;
+
+ return 0;
+}
+
+/* The typical workload of the driver:
+ Handle the network interface interrupts. */
+static void pi_interrupt(int reg_ptr, struct pt_regs *regs)
+{
+/* int irq = -(((struct pt_regs *) reg_ptr)->orig_eax + 2);*/
+ struct pi_local *lp;
+ int st;
+ unsigned long flags;
+
+/* dev_b = dev_a->next; Relies on the order defined in Space.c */
+
+#if 0
+ if (dev_a == NULL) {
+ printk("PI: pi_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+#endif
+ /* Read interrupt status register (only valid from channel A)
+ * Process all pending interrupts in while loop
+ */
+ lp = (struct pi_local *) pi0a.priv; /* Assume channel A */
+ while ((st = rdscc(lp->cardbase, pi0a.base_addr | CHANA | CTL, R3)) != 0) {
+ if (st & CHBTxIP) {
+ /* Channel B Transmit Int Pending */
+ lp = (struct pi_local *) pi0b.priv;
+ b_txint(lp);
+ } else if (st & CHARxIP) {
+ /* Channel A Rcv Interrupt Pending */
+ lp = (struct pi_local *) pi0a.priv;
+ a_rxint(&pi0a, lp);
+ } else if (st & CHATxIP) {
+ /* Channel A Transmit Int Pending */
+ lp = (struct pi_local *) pi0a.priv;
+ a_txint(lp);
+ } else if (st & CHAEXT) {
+ /* Channel A External Status Int */
+ lp = (struct pi_local *) pi0a.priv;
+ a_exint(lp);
+ } else if (st & CHBRxIP) {
+ /* Channel B Rcv Interrupt Pending */
+ lp = (struct pi_local *) pi0b.priv;
+ b_rxint(&pi0b, lp);
+ } else if (st & CHBEXT) {
+ /* Channel B External Status Int */
+ lp = (struct pi_local *) pi0b.priv;
+ b_exint(lp);
+ }
+ /* Reset highest interrupt under service */
+ save_flags(flags);
+ cli();
+ wrtscc(lp->cardbase, lp->base + CTL, R0, RES_H_IUS);
+ restore_flags(flags);
+ } /* End of while loop on int processing */
+ return;
+}
+
+/* The inverse routine to pi_open(). */
+static int pi_close(struct device *dev)
+{
+ unsigned long flags;
+ struct pi_local *lp;
+ struct sk_buff *ptr;
+
+ save_flags(flags);
+ cli();
+
+ lp = (struct pi_local *) dev->priv;
+ ptr = NULL;
+
+ chipset_init(dev); /* reset the scc */
+ disable_dma(lp->dmachan);
+
+ lp->open_time = 0;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* Free any buffers left in the hardware transmit queue */
+ while ((ptr = skb_dequeue(&lp->sndq)) != NULL)
+ free_p(ptr);
+
+ restore_flags(flags);
+ return 0;
+}
+
+static int pi_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+ unsigned long flags;
+ struct pi_req rq;
+ struct pi_local *lp = (struct pi_local *) dev->priv;
+
+ int ret = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct pi_req));
+ if (ret)
+ return ret;
+
+ if(cmd!=SIOCDEVPRIVATE)
+ return -EINVAL;
+
+ memcpy_fromfs(&rq, ifr->ifr_data, sizeof(struct pi_req));
+
+ switch (rq.cmd) {
+ case SIOCSPIPARAM:
+
+ if (!suser())
+ return -EPERM;
+ save_flags(flags);
+ cli();
+ lp->txdelay = rq.txdelay;
+ lp->persist = rq.persist;
+ lp->slotime = rq.slotime;
+ lp->squeldelay = rq.squeldelay;
+ lp->clockmode = rq.clockmode;
+ lp->speed = rq.speed;
+ pi_open(&pi0a); /* both channels get reset %%% */
+ restore_flags(flags);
+ ret = 0;
+ break;
+
+ case SIOCSPIDMA:
+
+ if (!suser())
+ return -EPERM;
+ ret = 0;
+ if (dev->base_addr & 2) { /* if A channel */
+ if (rq.dmachan < 1 || rq.dmachan > 3)
+ return -EINVAL;
+ save_flags(flags);
+ cli();
+ pi_close(dev);
+ free_dma(lp->dmachan);
+ dev->dma = lp->dmachan = rq.dmachan;
+ if (request_dma(lp->dmachan,"pi2"))
+ ret = -EAGAIN;
+ pi_open(dev);
+ restore_flags(flags);
+ }
+ break;
+
+ case SIOCSPIIRQ:
+ ret = -EINVAL; /* add this later */
+ break;
+
+ case SIOCGPIPARAM:
+ case SIOCGPIDMA:
+ case SIOCGPIIRQ:
+
+ rq.speed = lp->speed;
+ rq.txdelay = lp->txdelay;
+ rq.persist = lp->persist;
+ rq.slotime = lp->slotime;
+ rq.squeldelay = lp->squeldelay;
+ rq.clockmode = lp->clockmode;
+ rq.dmachan = lp->dmachan;
+ rq.irq = dev->irq;
+ memcpy_tofs(ifr->ifr_data, &rq, sizeof(struct pi_req));
+ ret = 0;
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+/* Get the current statistics. This may be called with the card open or
+ closed. */
+static struct netstats *
+ pi_get_stats(struct device *dev)
+{
+ struct pi_local *lp = (struct pi_local *) dev->priv;
+
+ return &lp->stats;
+}
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c skeleton.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * End:
+ */
diff --git a/drivers/net/pi2.h b/drivers/net/pi2.h
new file mode 100644
index 000000000..8f45ea420
--- /dev/null
+++ b/drivers/net/pi2.h
@@ -0,0 +1,133 @@
+
+#define DMA_BUFF_SIZE 2200
+
+/* Network statistics, with the same names as 'struct enet_statistics'. */
+#define netstats enet_statistics
+
+#define ON 1
+#define OFF 0
+
+
+/* Register offset info, specific to the PI
+ * E.g., to read the data port on channel A, use
+ * inportb(pichan[dev].base + CHANA + DATA)
+ */
+#define CHANB 0 /* Base of channel B regs */
+#define CHANA 2 /* Base of channel A regs */
+
+/* 8530 ports on each channel */
+#define CTL 0
+#define DATA 1
+
+#define DMAEN 0x4 /* Offset off DMA Enable register */
+
+/* Timer chip offsets */
+#define TMR0 0x8 /* Offset of timer 0 register */
+#define TMR1 0x9 /* Offset of timer 1 register */
+#define TMR2 0xA /* Offset of timer 2 register */
+#define TMRCMD 0xB /* Offset of timer command register */
+
+/* Timer chip equates */
+#define SC0 0x00 /* Select counter 0 */
+#define SC1 0x40 /* Select counter 1 */
+#define SC2 0x80 /* Select counter 2 */
+#define CLATCH 0x00 /* Counter latching operation */
+#define MSB 0x20 /* Read/load MSB only */
+#define LSB 0x10 /* Read/load LSB only */
+#define LSB_MSB 0x30 /* Read/load LSB, then MSB */
+#define MODE0 0x00 /* Interrupt on terminal count */
+#define MODE1 0x02 /* Programmable one shot */
+#define MODE2 0x04 /* Rate generator */
+#define MODE3 0x06 /* Square wave rate generator */
+#define MODE4 0x08 /* Software triggered strobe */
+#define MODE5 0x0a /* Hardware triggered strobe */
+#define BCD 0x01 /* BCD counter */
+
+/* DMA controller registers */
+#define DMA_STAT 8 /* DMA controller status register */
+#define DMA_CMD 8 /* DMA controller command register */
+#define DMA_MASK 10 /* DMA controller mask register */
+#define DMA_MODE 11 /* DMA controller mode register */
+#define DMA_RESETFF 12 /* DMA controller first/last flip flop */
+/* DMA data */
+#define DMA_DISABLE (0x04) /* Disable channel n */
+#define DMA_ENABLE (0x00) /* Enable channel n */
+/* Single transfers, incr. address, auto init, writes, ch. n */
+#define DMA_RX_MODE (0x54)
+/* Single transfers, incr. address, no auto init, reads, ch. n */
+#define DMA_TX_MODE (0x48)
+
+#define SINGLE 3686400
+#define DOUBLE 7372800
+
+#define SIOCGPIPARAM 0x5000 /* get PI parameters */
+#define SIOCSPIPARAM 0x5001 /* set */
+#define SIOCGPIBAUD 0x5002 /* get only baud rate */
+#define SIOCSPIBAUD 0x5003
+#define SIOCGPIDMA 0x5004 /* get only DMA */
+#define SIOCSPIDMA 0x5005
+#define SIOCGPIIRQ 0x5006 /* get only IRQ */
+#define SIOCSPIIRQ 0x5007
+
+struct pi_req {
+ int cmd;
+ int speed;
+ int clockmode;
+ int txdelay;
+ unsigned char persist;
+ int slotime;
+ int squeldelay;
+ int dmachan;
+ int irq;
+};
+
+#ifdef __KERNEL__
+
+/* Information that needs to be kept for each channel. */
+struct pi_local {
+ struct netstats stats; /* %%%dp*/
+ long open_time; /* Useless example local info. */
+ unsigned long xtal;
+
+ struct mbuf *rcvbuf;/* Buffer for current rx packet */
+ struct mbuf *rxdmabuf1; /* DMA rx buffer */
+ struct mbuf *rxdmabuf2; /* DMA rx buffer */
+
+ int bufsiz; /* Size of rcvbuf */
+ char *rcp; /* Pointer into rcvbuf */
+
+ struct sk_buff_head sndq; /* Packets awaiting transmission */
+ int sndcnt; /* Number of packets on sndq */
+ struct sk_buff *sndbuf; /* Current buffer being transmitted */
+ char *txdmabuf; /* Transmit DMA buffer */
+ char *txptr; /* Used by B port tx */
+ int txcnt;
+ char tstate; /* Transmitter state */
+#define IDLE 0 /* Transmitter off, no data pending */
+#define ACTIVE 1 /* Transmitter on, sending data */
+#define UNDERRUN 2 /* Transmitter on, flushing CRC */
+#define FLAGOUT 3 /* CRC sent - attempt to start next frame */
+#define DEFER 4 /* Receive Active - DEFER Transmit */
+#define ST_TXDELAY 5 /* Sending leading flags */
+#define CRCOUT 6
+ char rstate; /* Set when !DCD goes to 0 (TRUE) */
+/* Normal state is ACTIVE if Receive enabled */
+#define RXERROR 2 /* Error -- Aborting current Frame */
+#define RXABORT 3 /* ABORT sequence detected */
+#define TOOBIG 4 /* too large a frame to store */
+ int dev; /* Device number */
+ int base; /* Base of I/O registers */
+ int cardbase; /* Base address of card */
+ int stata; /* address of Channel A status regs */
+ int statb; /* address of Channel B status regs */
+ int speed; /* Line speed, bps */
+ int clockmode; /* tapr 9600 modem clocking option */
+ int txdelay; /* Transmit Delay 10 ms/cnt */
+ unsigned char persist; /* Persistence (0-255) as a % */
+ int slotime; /* Delay to wait on persistence hit */
+ int squeldelay; /* Delay after XMTR OFF for squelch tail */
+ struct iface *iface; /* Associated interface */
+ int dmachan; /* DMA channel for this port */
+};
+
+#endif
diff --git a/drivers/net/plip.c b/drivers/net/plip.c
index e0c6a10b1..cc68fe356 100644
--- a/drivers/net/plip.c
+++ b/drivers/net/plip.c
@@ -1,4 +1,5 @@
-/* plip.c: A parallel port "network" driver for linux. */
+/* $Id: plip.c,v 1.12 1995/02/11 10:26:05 gniibe Exp $ */
+/* PLIP: A parallel port "network" driver for Linux. */
/* This driver is for parallel port with 5-bit cable (LapLink (R) cable). */
/*
* Authors: Donald Becker, <becker@super.org>
@@ -8,12 +9,8 @@
* Peter Bauer, <100136.3530@compuserve.com>
* Niibe Yutaka, <gniibe@mri.co.jp>
*
- * This is the all improved state based PLIP that Niibe Yutaka has contributed.
- *
- * Modularization by Alan Cox. I also added the plipconfig program to tune the timeouts
- * and ifmap support for funny serial port settings or setting odd values using the
- * modular plip. I also took the panic() calls out. I don't like panic - especially when
- * it can be avoided.
+ * Modularization and ifreq/ifmap support by Alan Cox.
+ * Rewritten by Niibe Yutaka.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,16 +21,18 @@
/*
* Original version and the name 'PLIP' from Donald Becker <becker@super.org>
* inspired by Russ Nelson's parallel port packet driver.
+ *
+ * NOTE:
+ * Tanabe Hiroyasu had changed the protocol, and it was in Linux v1.0.
+ * Because of the necessity to communicate to DOS machines with the
+ * Crynwr packet driver, Peter Bauer changed the protocol again
+ * back to original protocol.
+ *
+ * This version follows original PLIP protocol.
+ * So, this PLIP can't communicate the PLIP of Linux v1.0.
*/
-static char *version =
- "NET3 "
-#ifdef MODULE
- "MODULAR "
-#endif
- "PLIP.014 gniibe@mri.co.jp\n";
-
-#include <linux/config.h>
+static char *version = "NET3 PLIP version 2.0 gniibe@mri.co.jp\n";
/*
Sources:
@@ -41,17 +40,19 @@ static char *version =
"parallel.asm" parallel port packet driver.
The "Crynwr" parallel port standard specifies the following protocol:
- send header nibble '8'
- count-low octet
- count-high octet
- ... data octets
- checksum octet
+ Trigger by sending '0x08' (this cause interrupt on other end)
+ count-low octet
+ count-high octet
+ ... data octets
+ checksum octet
Each octet is sent as <wait for rx. '0x1?'> <send 0x10+(octet&0x0F)>
<wait for rx. '0x0?'> <send 0x00+((octet>>4)&0x0F)>
-The cable used is a de facto standard parallel null cable -- sold as
-a "LapLink" cable by various places. You'll need a 10-conductor cable to
-make one yourself. The wiring is:
+ The packet is encapsulated as if it were ethernet.
+
+ The cable used is a de facto standard parallel null cable -- sold as
+ a "LapLink" cable by various places. You'll need a 12-conductor cable to
+ make one yourself. The wiring is:
SLCTIN 17 - 17
GROUND 25 - 25
D0->ERROR 2 - 15 15 - 2
@@ -65,6 +66,14 @@ make one yourself. The wiring is:
extra grounds are 18,19,20,21,22,23,24
*/
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
@@ -89,15 +98,11 @@ make one yourself. The wiring is:
#include <linux/ioport.h>
#include <asm/bitops.h>
#include <asm/irq.h>
+#include <asm/byteorder.h>
-#ifdef MODULE
-#include <linux/module.h>
-#include "../../tools/version.h"
-#endif
-
-/* use 0 for production, 1 for verification, >2 for debug */
+/* Use 0 for production, 1 for verification, >2 for debug */
#ifndef NET_DEBUG
-#define NET_DEBUG 3
+#define NET_DEBUG 1
#endif
static unsigned int net_debug = NET_DEBUG;
@@ -108,798 +113,923 @@ static unsigned int net_debug = NET_DEBUG;
#define PLIP_TRIGGER_WAIT 500
/* Nibble time out = PLIP_NIBBLE_WAIT * PLIP_DELAY_UNIT usec */
-#define PLIP_NIBBLE_WAIT 5000
+#define PLIP_NIBBLE_WAIT 3000
-#define PAR_DATA(dev) (dev->base_addr+0)
-#define PAR_STATUS(dev) (dev->base_addr+1)
-#define PAR_CONTROL(dev) (dev->base_addr+2)
+#define PAR_INTR_ON (LP_PINITP|LP_PSELECP|LP_PINTEN)
+#define PAR_INTR_OFF (LP_PINITP|LP_PSELECP)
+#define PAR_DATA(dev) ((dev)->base_addr+0)
+#define PAR_STATUS(dev) ((dev)->base_addr+1)
+#define PAR_CONTROL(dev) ((dev)->base_addr+2)
-/* Index to functions, as function prototypes. */
+/* Bottom halfs */
+static void plip_kick_bh(struct device *dev);
+static void plip_bh(struct device *dev);
+
+/* Interrupt handler */
+static void plip_interrupt(int irq, struct pt_regs *regs);
+
+/* Functions for DEV methods */
+static int plip_rebuild_header(void *buff, struct device *dev,
+ unsigned long raddr, struct sk_buff *skb);
static int plip_tx_packet(struct sk_buff *skb, struct device *dev);
static int plip_open(struct device *dev);
static int plip_close(struct device *dev);
static struct enet_statistics *plip_get_stats(struct device *dev);
-static int plip_rebuild_header(void *buff, struct device *dev,
- unsigned long raddr, struct sk_buff *skb);
-static void plip_kick_bh(struct device *dev);
-static void plip_bh(struct device *dev);
+static int plip_config(struct device *dev, struct ifmap *map);
+static int plip_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
enum plip_connection_state {
- PLIP_CN_NONE=0,
- PLIP_CN_RECEIVE,
- PLIP_CN_SEND,
- PLIP_CN_CLOSING,
- PLIP_CN_ERROR
+ PLIP_CN_NONE=0,
+ PLIP_CN_RECEIVE,
+ PLIP_CN_SEND,
+ PLIP_CN_CLOSING,
+ PLIP_CN_ERROR
};
enum plip_packet_state {
- PLIP_PK_DONE=0,
- PLIP_PK_TRIGGER,
- PLIP_PK_LENGTH_LSB,
- PLIP_PK_LENGTH_MSB,
- PLIP_PK_DATA,
- PLIP_PK_CHECKSUM
+ PLIP_PK_DONE=0,
+ PLIP_PK_TRIGGER,
+ PLIP_PK_LENGTH_LSB,
+ PLIP_PK_LENGTH_MSB,
+ PLIP_PK_DATA,
+ PLIP_PK_CHECKSUM
};
enum plip_nibble_state {
- PLIP_NB_BEGIN,
- PLIP_NB_1,
- PLIP_NB_2,
+ PLIP_NB_BEGIN,
+ PLIP_NB_1,
+ PLIP_NB_2,
};
-#define PLIP_STATE_STRING(x) \
- (((x) == PLIP_PK_DONE)?"0":\
- ((x) == PLIP_PK_TRIGGER)?"t":\
- ((x) == PLIP_PK_LENGTH_LSB)?"l":\
- ((x) == PLIP_PK_LENGTH_MSB)?"m":\
- ((x) == PLIP_PK_DATA)?"d":\
- ((x) == PLIP_PK_CHECKSUM)?"s":"B")
-
struct plip_local {
- enum plip_packet_state state;
- enum plip_nibble_state nibble;
- unsigned short length;
- unsigned short byte;
- unsigned char checksum;
- unsigned char data;
- struct sk_buff *skb;
+ enum plip_packet_state state;
+ enum plip_nibble_state nibble;
+ union {
+ struct {
+#if defined(LITTLE_ENDIAN)
+ unsigned char lsb;
+ unsigned char msb;
+#elif defined(BIG_ENDIAN)
+ unsigned char msb;
+ unsigned char lsb;
+#else
+#error "Please fix the endianness defines in <asm/byteorder.h>"
+#endif
+ } b;
+ unsigned short h;
+ } length;
+ unsigned short byte;
+ unsigned char checksum;
+ unsigned char data;
+ struct sk_buff *skb;
};
struct net_local {
- struct enet_statistics e;
- struct tq_struct immediate;
- struct tq_struct deferred;
- struct plip_local snd_data;
- struct plip_local rcv_data;
- unsigned long trigger_us;
- unsigned long nibble_us;
- unsigned long unit_us;
- enum plip_connection_state connection;
- unsigned short timeout_count;
+ struct enet_statistics enet_stats;
+ struct tq_struct immediate;
+ struct tq_struct deferred;
+ struct plip_local snd_data;
+ struct plip_local rcv_data;
+ unsigned long trigger;
+ unsigned long nibble;
+ enum plip_connection_state connection;
+ unsigned short timeout_count;
+ char is_deferred;
+ int (*orig_rebuild_header)(void *eth, struct device *dev,
+ unsigned long raddr, struct sk_buff *skb);
};
-
-/* Routines used internally. */
-static void plip_device_clear(struct device *dev);
-static void plip_interrupt(int reg_ptr);
-
-static int plip_error(struct device *dev);
-static int plip_receive_packet(struct device *dev);
-static int plip_send_packet(struct device *dev);
-static int plip_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
-static int plip_config(struct device *dev, struct ifmap *map);
+/* Entry point of PLIP driver.
+ Probe the hardware, and register/initialize the driver. */
int
plip_init(struct device *dev)
{
- struct net_local *nl;
+ struct net_local *nl;
- /* Check that there is something at base_addr. */
- outb(LP_PINITP, PAR_CONTROL(dev));
- outb(0x00, PAR_DATA(dev));
- if (inb(PAR_DATA(dev)) != 0x00)
- return -ENODEV;
+ /* Check region before the probe */
+ if (check_region(PAR_DATA(dev), 3) < 0)
+ return -ENODEV;
- /* Alpha testers must have the version number to report bugs. */
- if (net_debug)
- printk(version);
+ /* Check that there is something at base_addr. */
+ outb(0, PAR_DATA(dev));
+ udelay(1000);
+ if (inb(PAR_DATA(dev)) != 0)
+ return -ENODEV;
- if (dev->irq) {
- printk("%s: configured for parallel port at %#3x, IRQ %d.\n",
- dev->name, dev->base_addr, dev->irq);
- } else {
- printk("%s: configured for parallel port at %#3x",
- dev->name, dev->base_addr);
+ printk(version);
+ printk("%s: Parallel port at %#3lx, ", dev->name, dev->base_addr);
+ if (dev->irq) {
+ printk("using assigned IRQ %d.\n", dev->irq);
+ } else {
+ int irq = 0;
#ifdef MODULE
- /* autoirq doesn't work :(, but we can set it by ifconfig */
+ /* dev->irq==0 means autoprobe, but we don't try to do so
+ with module. We can change it by ifconfig */
#else
- autoirq_setup(0);
- outb(LP_PINITP|LP_PSELECP, PAR_CONTROL(dev));
- outb(LP_PINITP|LP_PSELECP|LP_PINTEN, PAR_CONTROL(dev));
- outb(LP_PINITP|LP_PSELECP, PAR_CONTROL(dev));
- dev->irq = autoirq_report(1);
+ unsigned int irqs = probe_irq_on();
+
+ outb(0x00, PAR_CONTROL(dev));
+ udelay(1000);
+ outb(PAR_INTR_OFF, PAR_CONTROL(dev));
+ outb(PAR_INTR_ON, PAR_CONTROL(dev));
+ outb(PAR_INTR_OFF, PAR_CONTROL(dev));
+ udelay(1000);
+ irq = probe_irq_off(irqs);
#endif
- if (dev->irq)
- printk(", probed IRQ %d.\n", dev->irq);
- else {
- printk(", failed to detect IRQ line.\n");
- return -ENODEV;
+ if (irq > 0) {
+ dev->irq = irq;
+ printk("using probed IRQ %d.\n", dev->irq);
+ } else
+ printk("failed to detect IRQ(%d) --"
+ " Please set IRQ by ifconfig.\n", irq);
}
- }
-
- /* Fill in the generic fields of the device structure. */
- ether_setup(dev);
-
- /* And, override parts of it */
- dev->rebuild_header = plip_rebuild_header;
- dev->hard_start_xmit = plip_tx_packet;
- dev->open = plip_open;
- dev->stop = plip_close;
- dev->get_stats = plip_get_stats;
- dev->set_config = plip_config;
- dev->do_ioctl = plip_ioctl;
- dev->flags = IFF_POINTOPOINT;
-
- /* Set private structure */
- dev->priv = kmalloc(sizeof (struct net_local), GFP_KERNEL);
- memset(dev->priv, 0, sizeof(struct net_local));
- nl = (struct net_local *) dev->priv;
-
- /* initialize constants */
- nl->trigger_us = PLIP_TRIGGER_WAIT;
- nl->nibble_us = PLIP_NIBBLE_WAIT;
- nl->unit_us = PLIP_DELAY_UNIT;
-
- /* initialize task queue structures */
- nl->immediate.next = &tq_last;
- nl->immediate.sync = 0;
- nl->immediate.routine = (void *)(void *)plip_bh;
- nl->immediate.data = dev;
-
- nl->deferred.next = &tq_last;
- nl->deferred.sync = 0;
- nl->deferred.routine = (void *)(void *)plip_kick_bh;
- nl->deferred.data = dev;
-
- return 0;
+
+ request_region(PAR_DATA(dev), 3, dev->name);
+
+ /* Fill in the generic fields of the device structure. */
+ ether_setup(dev);
+
+ /* Then, override parts of it */
+ dev->hard_start_xmit = plip_tx_packet;
+ dev->open = plip_open;
+ dev->stop = plip_close;
+ dev->get_stats = plip_get_stats;
+ dev->set_config = plip_config;
+ dev->do_ioctl = plip_ioctl;
+ dev->flags = IFF_POINTOPOINT|IFF_NOARP;
+
+ /* Set the private structure */
+ dev->priv = kmalloc(sizeof (struct net_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return EAGAIN;
+ memset(dev->priv, 0, sizeof(struct net_local));
+ nl = (struct net_local *) dev->priv;
+
+ nl->orig_rebuild_header = dev->rebuild_header;
+ dev->rebuild_header = plip_rebuild_header;
+
+ /* Initialize constants */
+ nl->trigger = PLIP_TRIGGER_WAIT;
+ nl->nibble = PLIP_NIBBLE_WAIT;
+
+ /* Initialize task queue structures */
+ nl->immediate.next = &tq_last;
+ nl->immediate.sync = 0;
+ nl->immediate.routine = (void *)(void *)plip_bh;
+ nl->immediate.data = dev;
+
+ nl->deferred.next = &tq_last;
+ nl->deferred.sync = 0;
+ nl->deferred.routine = (void *)(void *)plip_kick_bh;
+ nl->deferred.data = dev;
+
+ return 0;
}
+/* Bottom half handler for the delayed request.
+ This routine is kicked by do_timer().
+ Request `plip_bh' to be invoked. */
static void
plip_kick_bh(struct device *dev)
{
- struct net_local *nl = (struct net_local *)dev->priv;
+ struct net_local *nl = (struct net_local *)dev->priv;
- if (nl->connection == PLIP_CN_NONE)
- return;
- queue_task(&nl->immediate, &tq_immediate);
- mark_bh(IMMEDIATE_BH);
- return;
+ if (nl->is_deferred) {
+ queue_task(&nl->immediate, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ }
}
+/* Forward declarations of internal routines */
+static int plip_none(struct device *, struct net_local *,
+ struct plip_local *, struct plip_local *);
+static int plip_receive_packet(struct device *, struct net_local *,
+ struct plip_local *, struct plip_local *);
+static int plip_send_packet(struct device *, struct net_local *,
+ struct plip_local *, struct plip_local *);
+static int plip_connection_close(struct device *, struct net_local *,
+ struct plip_local *, struct plip_local *);
+static int plip_error(struct device *, struct net_local *,
+ struct plip_local *, struct plip_local *);
+static int plip_bh_timeout_error(struct device *dev, struct net_local *nl,
+ struct plip_local *snd,
+ struct plip_local *rcv,
+ int error);
+
+#define OK 0
+#define TIMEOUT 1
+#define ERROR 2
+
+typedef int (*plip_func)(struct device *dev, struct net_local *nl,
+ struct plip_local *snd, struct plip_local *rcv);
+
+static plip_func connection_state_table[] =
+{
+ plip_none,
+ plip_receive_packet,
+ plip_send_packet,
+ plip_connection_close,
+ plip_error
+};
+
+/* Bottom half handler of PLIP. */
static void
plip_bh(struct device *dev)
{
- struct net_local *nl = (struct net_local *)dev->priv;
- struct enet_statistics *stats = (struct enet_statistics *) dev->priv;
- struct plip_local *rcv = &nl->rcv_data;
- struct plip_local *snd = &nl->snd_data;
- int result, timeout=0;
- unsigned char *s;
- unsigned char c0;
- struct sk_buff *skb;
-
- while (!timeout) {
- cli();
- switch (nl->connection) {
- case PLIP_CN_NONE:
- sti();
- return;
-
- case PLIP_CN_RECEIVE:
- sti();
- disable_irq(dev->irq);
- dev->interrupt = 0;
- result = plip_receive_packet(dev);
- if (result == 0) { /* success */
- outb (0x00, PAR_DATA(dev));
- skb = rcv->skb;
- rcv->skb = NULL;
- stats->rx_packets++;
- netif_rx(skb);
- if (snd->state != PLIP_PK_DONE) {
- nl->connection = PLIP_CN_SEND;
- enable_irq(dev->irq);
- } else {
- nl->connection = PLIP_CN_NONE;
- enable_irq(dev->irq);
- return;
- }
- } else if (result == -1) { /* failure */
- outb(0x00, PAR_DATA(dev));
- if (rcv->skb)
- dev_kfree_skb(rcv->skb, FREE_WRITE);
- rcv->state = PLIP_PK_DONE;
- rcv->skb = NULL;
- if (snd->skb)
- dev_kfree_skb(snd->skb, FREE_WRITE);
- snd->state = PLIP_PK_DONE;
- snd->skb = NULL;
- dev->tbusy = 1;
- nl->connection = PLIP_CN_ERROR;
- } else
- timeout = 1;
- break;
-
- case PLIP_CN_SEND:
- sti();
- result = plip_send_packet(dev);
- if (result == -1) /* interrupted */
- break;
- if (result == 0) { /* success */
- outb (0x00, PAR_DATA(dev));
- snd->state = PLIP_PK_DONE;
- snd->skb = NULL;
- nl->connection = PLIP_CN_CLOSING;
+ struct net_local *nl = (struct net_local *)dev->priv;
+ struct plip_local *snd = &nl->snd_data;
+ struct plip_local *rcv = &nl->rcv_data;
+ plip_func f;
+ int r;
+
+ nl->is_deferred = 0;
+ f = connection_state_table[nl->connection];
+ if ((r = (*f)(dev, nl, snd, rcv)) != OK
+ && (r = plip_bh_timeout_error(dev, nl, snd, rcv, r)) != OK) {
+ nl->is_deferred = 1;
queue_task(&nl->deferred, &tq_timer);
- enable_irq(dev->irq);
- return;
- } else
- timeout = 1;
- break;
-
- case PLIP_CN_CLOSING:
- sti();
- nl->connection = PLIP_CN_NONE;
- mark_bh(NET_BH);
- dev->tbusy = 0;
- return;
-
- case PLIP_CN_ERROR:
- sti();
- result = plip_error(dev);
- if (result == 0) {
- nl->connection = PLIP_CN_NONE;
- dev->tbusy = 0;
- enable_irq(dev->irq);
- return;
- } else {
- queue_task(&nl->deferred, &tq_timer);
- return;
- }
- break;
}
- }
+}
- /* timeout */
- if (++nl->timeout_count > 3) { /* cable problem? */
- c0 = inb(PAR_STATUS(dev));
+static int
+plip_bh_timeout_error(struct device *dev, struct net_local *nl,
+ struct plip_local *snd, struct plip_local *rcv,
+ int error)
+{
+ unsigned char c0;
+ cli();
if (nl->connection == PLIP_CN_SEND) {
- stats->tx_errors++;
- stats->tx_aborted_errors++;
- s = PLIP_STATE_STRING(snd->state);
- if (net_debug > 1)
- printk("%s: transmit timeout(%s,%02x)... reset interface.\n",
- dev->name, s, (unsigned int)c0);
- if (snd->skb)
- dev_kfree_skb(snd->skb, FREE_WRITE);
+
+ if (error != ERROR) { /* Timeout */
+ nl->timeout_count++;
+ if ((snd->state == PLIP_PK_TRIGGER
+ && nl->timeout_count <= 10)
+ || nl->timeout_count <= 3) {
+ sti();
+ /* Try again later */
+ return TIMEOUT;
+ }
+ c0 = inb(PAR_STATUS(dev));
+ printk("%s: transmit timeout(%d,%02x)\n",
+ dev->name, snd->state, c0);
+ }
+ nl->enet_stats.tx_errors++;
+ nl->enet_stats.tx_aborted_errors++;
} else if (nl->connection == PLIP_CN_RECEIVE) {
- stats->rx_dropped++;
- s = PLIP_STATE_STRING(rcv->state);
- if (net_debug > 1)
- printk("%s: receive timeout(%s,%02x)... reset interface.\n",
- dev->name, s, (unsigned int)c0);
- if (rcv->skb)
- dev_kfree_skb(rcv->skb, FREE_WRITE);
+ if (rcv->state == PLIP_PK_TRIGGER) {
+ /* Transmission was interrupted. */
+ sti();
+ return OK;
+ }
+ if (error != ERROR) { /* Timeout */
+ if (++nl->timeout_count <= 3) {
+ sti();
+ /* Try again later */
+ return TIMEOUT;
+ }
+ c0 = inb(PAR_STATUS(dev));
+ printk("%s: receive timeout(%d,%02x)\n",
+ dev->name, rcv->state, c0);
+ }
+ nl->enet_stats.rx_dropped++;
+ }
+ rcv->state = PLIP_PK_DONE;
+ if (rcv->skb) {
+ rcv->skb->free = 1;
+ kfree_skb(rcv->skb, FREE_READ);
+ rcv->skb = NULL;
+ }
+ snd->state = PLIP_PK_DONE;
+ if (snd->skb) {
+ dev_kfree_skb(snd->skb, FREE_WRITE);
+ snd->skb = NULL;
}
disable_irq(dev->irq);
+ outb(PAR_INTR_OFF, PAR_CONTROL(dev));
dev->tbusy = 1;
nl->connection = PLIP_CN_ERROR;
outb(0x00, PAR_DATA(dev));
- }
+ sti();
- queue_task(&nl->deferred, &tq_timer);
- return;
+ return TIMEOUT;
}
static int
-plip_tx_packet(struct sk_buff *skb, struct device *dev)
+plip_none(struct device *dev, struct net_local *nl,
+ struct plip_local *snd, struct plip_local *rcv)
{
- struct net_local *nl = (struct net_local *)dev->priv;
- struct plip_local *snd = &nl->snd_data;
-
- if (dev->tbusy)
- return 1;
-
- /* If some higher layer thinks we've missed an tx-done interrupt
- we are passed NULL. Caution: dev_tint() handles the cli()/sti()
- itself. */
- if (skb == NULL) {
- dev_tint(dev);
- return 0;
- }
+ return OK;
+}
- if (set_bit(0, (void*)&dev->tbusy) != 0) {
- printk("%s: Transmitter access conflict.\n", dev->name);
- return 1;
- }
+/* PLIP_RECEIVE --- receive a byte(two nibbles)
+ Returns OK on success, TIMEOUT on timeout */
+inline static int
+plip_receive(unsigned short nibble_timeout, unsigned short status_addr,
+ enum plip_nibble_state *ns_p, unsigned char *data_p)
+{
+ unsigned char c0, c1;
+ unsigned int cx;
+
+ switch (*ns_p) {
+ case PLIP_NB_BEGIN:
+ cx = nibble_timeout;
+ while (1) {
+ c0 = inb(status_addr);
+ udelay(PLIP_DELAY_UNIT);
+ if ((c0 & 0x80) == 0) {
+ c1 = inb(status_addr);
+ if (c0 == c1)
+ break;
+ }
+ if (--cx == 0)
+ return TIMEOUT;
+ }
+ *data_p = (c0 >> 3) & 0x0f;
+ outb(0x10, --status_addr); /* send ACK */
+ status_addr++;
+ *ns_p = PLIP_NB_1;
+
+ case PLIP_NB_1:
+ cx = nibble_timeout;
+ while (1) {
+ c0 = inb(status_addr);
+ udelay(PLIP_DELAY_UNIT);
+ if (c0 & 0x80) {
+ c1 = inb(status_addr);
+ if (c0 == c1)
+ break;
+ }
+ if (--cx == 0)
+ return TIMEOUT;
+ }
+ *data_p |= (c0 << 1) & 0xf0;
+ outb(0x00, --status_addr); /* send ACK */
+ status_addr++;
+ *ns_p = PLIP_NB_BEGIN;
+ return OK;
- if (skb->len > dev->mtu) {
- printk("%s: packet too big, %d.\n", dev->name, (int)skb->len);
- dev->tbusy = 0;
- return 0;
- }
+ case PLIP_NB_2:
+ }
+}
- snd->state = PLIP_PK_TRIGGER;
- dev->trans_start = jiffies;
+/* PLIP_RECEIVE_PACKET --- receive a packet */
+static int
+plip_receive_packet(struct device *dev, struct net_local *nl,
+ struct plip_local *snd, struct plip_local *rcv)
+{
+ unsigned short status_addr = PAR_STATUS(dev);
+ unsigned short nibble_timeout = nl->nibble;
+ unsigned char *lbuf;
- snd->skb = skb;
- snd->length = skb->len;
+ switch (rcv->state) {
+ case PLIP_PK_TRIGGER:
+ disable_irq(dev->irq);
+ outb(PAR_INTR_OFF, PAR_CONTROL(dev));
+ dev->interrupt = 0;
+ outb(0x01, PAR_DATA(dev)); /* send ACK */
+ if (net_debug > 2)
+ printk("%s: receive start\n", dev->name);
+ rcv->state = PLIP_PK_LENGTH_LSB;
+ rcv->nibble = PLIP_NB_BEGIN;
+
+ case PLIP_PK_LENGTH_LSB:
+ if (snd->state != PLIP_PK_DONE) {
+ if (plip_receive(nl->trigger, status_addr,
+ &rcv->nibble, &rcv->length.b.lsb)) {
+ /* collision, here dev->tbusy == 1 */
+ rcv->state = PLIP_PK_DONE;
+ nl->is_deferred = 1;
+ nl->connection = PLIP_CN_SEND;
+ queue_task(&nl->deferred, &tq_timer);
+ outb(PAR_INTR_ON, PAR_CONTROL(dev));
+ enable_irq(dev->irq);
+ return OK;
+ }
+ } else {
+ if (plip_receive(nibble_timeout, status_addr,
+ &rcv->nibble, &rcv->length.b.lsb))
+ return TIMEOUT;
+ }
+ rcv->state = PLIP_PK_LENGTH_MSB;
+
+ case PLIP_PK_LENGTH_MSB:
+ if (plip_receive(nibble_timeout, status_addr,
+ &rcv->nibble, &rcv->length.b.msb))
+ return TIMEOUT;
+ if (rcv->length.h > dev->mtu || rcv->length.h < 8) {
+ printk("%s: bogus packet size %d.\n", dev->name, rcv->length.h);
+ return ERROR;
+ }
+ /* Malloc up new buffer. */
+ rcv->skb = alloc_skb(rcv->length.h, GFP_ATOMIC);
+ if (rcv->skb == NULL) {
+ printk("%s: Memory squeeze.\n", dev->name);
+ return ERROR;
+ }
+ rcv->skb->len = rcv->length.h;
+ rcv->skb->dev = dev;
+ rcv->state = PLIP_PK_DATA;
+ rcv->byte = 0;
+ rcv->checksum = 0;
+
+ case PLIP_PK_DATA:
+ lbuf = rcv->skb->data;
+ do
+ if (plip_receive(nibble_timeout, status_addr,
+ &rcv->nibble, &lbuf[rcv->byte]))
+ return TIMEOUT;
+ while (++rcv->byte < rcv->length.h);
+ do
+ rcv->checksum += lbuf[--rcv->byte];
+ while (rcv->byte);
+ rcv->state = PLIP_PK_CHECKSUM;
+
+ case PLIP_PK_CHECKSUM:
+ if (plip_receive(nibble_timeout, status_addr,
+ &rcv->nibble, &rcv->data))
+ return TIMEOUT;
+ if (rcv->data != rcv->checksum) {
+ nl->enet_stats.rx_crc_errors++;
+ if (net_debug)
+ printk("%s: checksum error\n", dev->name);
+ return ERROR;
+ }
+ rcv->state = PLIP_PK_DONE;
- cli();
- if (nl->connection == PLIP_CN_NONE) {
- nl->connection = PLIP_CN_SEND;
- nl->timeout_count = 0;
- }
- sti();
- queue_task(&nl->immediate, &tq_immediate);
- mark_bh(IMMEDIATE_BH);
+ case PLIP_PK_DONE:
+ /* Inform the upper layer for the arrival of a packet. */
+ rcv->skb->protocol=eth_type_trans(rcv->skb, dev);
+ netif_rx(rcv->skb);
+ nl->enet_stats.rx_packets++;
+ rcv->skb = NULL;
+ if (net_debug > 2)
+ printk("%s: receive end\n", dev->name);
- return 0;
+ /* Close the connection. */
+ outb (0x00, PAR_DATA(dev));
+ cli();
+ if (snd->state != PLIP_PK_DONE) {
+ nl->connection = PLIP_CN_SEND;
+ sti();
+ queue_task(&nl->immediate, &tq_immediate);
+ outb(PAR_INTR_ON, PAR_CONTROL(dev));
+ enable_irq(dev->irq);
+ return OK;
+ } else {
+ nl->connection = PLIP_CN_NONE;
+ sti();
+ outb(PAR_INTR_ON, PAR_CONTROL(dev));
+ enable_irq(dev->irq);
+ return OK;
+ }
+ }
+ return OK;
}
-/* Open/initialize the board. This is called (in the current kernel)
- sometime after booting when the 'ifconfig' program is run.
-
- This routine gets exclusive access to the parallel port by allocating
- its IRQ line.
- */
-static int
-plip_open(struct device *dev)
+/* PLIP_SEND --- send a byte (two nibbles)
+ Returns OK on success, TIMEOUT when timeout */
+inline static int
+plip_send(unsigned short nibble_timeout, unsigned short data_addr,
+ enum plip_nibble_state *ns_p, unsigned char data)
{
- int i;
-
- cli();
- if (request_irq(dev->irq , plip_interrupt, 0, "plip") != 0) {
- sti();
- printk("%s: couldn't get IRQ %d.\n", dev->name, dev->irq);
- return -EAGAIN;
- }
- irq2dev_map[dev->irq] = dev;
- sti();
- /* enable rx interrupt. */
- outb(LP_PINITP|LP_PSELECP|LP_PINTEN, PAR_CONTROL(dev));
- plip_device_clear(dev);
-
- /* Fill in the MAC-level header. */
- for (i=0; i < ETH_ALEN - sizeof(unsigned long); i++)
- dev->dev_addr[i] = 0xfc;
- memcpy(&(dev->dev_addr[i]), &dev->pa_addr, sizeof(unsigned long));
-
- dev->start = 1;
-#ifdef MODULE
- MOD_INC_USE_COUNT;
-#endif
- return 0;
+ unsigned char c0;
+ unsigned int cx;
+
+ switch (*ns_p) {
+ case PLIP_NB_BEGIN:
+ outb((data & 0x0f), data_addr);
+ *ns_p = PLIP_NB_1;
+
+ case PLIP_NB_1:
+ outb(0x10 | (data & 0x0f), data_addr);
+ cx = nibble_timeout;
+ data_addr++;
+ while (1) {
+ c0 = inb(data_addr);
+ if ((c0 & 0x80) == 0)
+ break;
+ if (--cx == 0)
+ return TIMEOUT;
+ udelay(PLIP_DELAY_UNIT);
+ }
+ outb(0x10 | (data >> 4), --data_addr);
+ *ns_p = PLIP_NB_2;
+
+ case PLIP_NB_2:
+ outb((data >> 4), data_addr);
+ data_addr++;
+ cx = nibble_timeout;
+ while (1) {
+ c0 = inb(data_addr);
+ if (c0 & 0x80)
+ break;
+ if (--cx == 0)
+ return TIMEOUT;
+ udelay(PLIP_DELAY_UNIT);
+ }
+ data_addr--;
+ *ns_p = PLIP_NB_BEGIN;
+ return OK;
+ }
}
-/* The inverse routine to plip_open (). */
+/* PLIP_SEND_PACKET --- send a packet */
static int
-plip_close(struct device *dev)
+plip_send_packet(struct device *dev, struct net_local *nl,
+ struct plip_local *snd, struct plip_local *rcv)
{
- dev->tbusy = 1;
- dev->start = 0;
- cli();
- free_irq(dev->irq);
- irq2dev_map[dev->irq] = NULL;
- sti();
- outb(0x00, PAR_DATA(dev));
- /* release the interrupt. */
- outb(LP_PINITP|LP_PSELECP, PAR_CONTROL(dev));
-#ifdef MODULE
- MOD_DEC_USE_COUNT;
-#endif
- return 0;
+ unsigned short data_addr = PAR_DATA(dev);
+ unsigned short nibble_timeout = nl->nibble;
+ unsigned char *lbuf;
+ unsigned char c0;
+ unsigned int cx;
+
+ if (snd->skb == NULL || (lbuf = snd->skb->data) == NULL) {
+ printk("%s: send skb lost\n", dev->name);
+ snd->state = PLIP_PK_DONE;
+ snd->skb = NULL;
+ return ERROR;
+ }
+
+ switch (snd->state) {
+ case PLIP_PK_TRIGGER:
+ /* Trigger remote rx interrupt. */
+ outb(0x08, data_addr);
+ cx = nl->trigger;
+ while (1) {
+ udelay(PLIP_DELAY_UNIT);
+ cli();
+ if (nl->connection == PLIP_CN_RECEIVE) {
+ sti();
+ /* interrupted */
+ nl->enet_stats.collisions++;
+ if (net_debug > 1)
+ printk("%s: collision.\n", dev->name);
+ return OK;
+ }
+ c0 = inb(PAR_STATUS(dev));
+ if (c0 & 0x08) {
+ disable_irq(dev->irq);
+ outb(PAR_INTR_OFF, PAR_CONTROL(dev));
+ if (net_debug > 2)
+ printk("%s: send start\n", dev->name);
+ snd->state = PLIP_PK_LENGTH_LSB;
+ snd->nibble = PLIP_NB_BEGIN;
+ nl->timeout_count = 0;
+ sti();
+ break;
+ }
+ sti();
+ if (--cx == 0) {
+ outb(0x00, data_addr);
+ return TIMEOUT;
+ }
+ }
+
+ case PLIP_PK_LENGTH_LSB:
+ if (plip_send(nibble_timeout, data_addr,
+ &snd->nibble, snd->length.b.lsb))
+ return TIMEOUT;
+ snd->state = PLIP_PK_LENGTH_MSB;
+
+ case PLIP_PK_LENGTH_MSB:
+ if (plip_send(nibble_timeout, data_addr,
+ &snd->nibble, snd->length.b.msb))
+ return TIMEOUT;
+ snd->state = PLIP_PK_DATA;
+ snd->byte = 0;
+ snd->checksum = 0;
+
+ case PLIP_PK_DATA:
+ do
+ if (plip_send(nibble_timeout, data_addr,
+ &snd->nibble, lbuf[snd->byte]))
+ return TIMEOUT;
+ while (++snd->byte < snd->length.h);
+ do
+ snd->checksum += lbuf[--snd->byte];
+ while (snd->byte);
+ snd->state = PLIP_PK_CHECKSUM;
+
+ case PLIP_PK_CHECKSUM:
+ if (plip_send(nibble_timeout, data_addr,
+ &snd->nibble, snd->checksum))
+ return TIMEOUT;
+
+ dev_kfree_skb(snd->skb, FREE_WRITE);
+ nl->enet_stats.tx_packets++;
+ snd->state = PLIP_PK_DONE;
+
+ case PLIP_PK_DONE:
+ /* Close the connection */
+ outb (0x00, data_addr);
+ snd->skb = NULL;
+ if (net_debug > 2)
+ printk("%s: send end\n", dev->name);
+ nl->connection = PLIP_CN_CLOSING;
+ nl->is_deferred = 1;
+ queue_task(&nl->deferred, &tq_timer);
+ outb(PAR_INTR_ON, PAR_CONTROL(dev));
+ enable_irq(dev->irq);
+ return OK;
+ }
+ return OK;
}
-static struct enet_statistics *
-plip_get_stats(struct device *dev)
+static int
+plip_connection_close(struct device *dev, struct net_local *nl,
+ struct plip_local *snd, struct plip_local *rcv)
{
- struct enet_statistics *localstats = (struct enet_statistics*)dev->priv;
- return localstats;
+ cli();
+ if (nl->connection == PLIP_CN_CLOSING) {
+ nl->connection = PLIP_CN_NONE;
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ sti();
+ return OK;
}
-/* We don't need to send arp, for plip is point-to-point. */
+/* PLIP_ERROR --- wait till other end settled */
static int
-plip_rebuild_header(void *buff, struct device *dev, unsigned long dst,
- struct sk_buff *skb)
+plip_error(struct device *dev, struct net_local *nl,
+ struct plip_local *snd, struct plip_local *rcv)
{
- struct ethhdr *eth = (struct ethhdr *)buff;
- int i;
+ unsigned char status;
- if (eth->h_proto != htons(ETH_P_IP)) {
- printk("plip_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto);
- memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
- return 0;
- }
+ status = inb(PAR_STATUS(dev));
+ if ((status & 0xf8) == 0x80) {
+ if (net_debug > 2)
+ printk("%s: reset interface.\n", dev->name);
+ nl->connection = PLIP_CN_NONE;
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ outb(PAR_INTR_ON, PAR_CONTROL(dev));
+ enable_irq(dev->irq);
+ mark_bh(NET_BH);
+ } else {
+ nl->is_deferred = 1;
+ queue_task(&nl->deferred, &tq_timer);
+ }
- for (i=0; i < ETH_ALEN - sizeof(unsigned long); i++)
- eth->h_dest[i] = 0xfc;
- memcpy(&(eth->h_dest[i]), &dst, sizeof(unsigned long));
- return 0;
+ return OK;
}
+/* Handle the parallel port interrupts. */
static void
-plip_device_clear(struct device *dev)
+plip_interrupt(int irq, struct pt_regs * regs)
{
- struct net_local *nl = (struct net_local *)dev->priv;
-
- outb (0x00, PAR_DATA(dev));
- nl->rcv_data.state = PLIP_PK_DONE;
- nl->snd_data.state = PLIP_PK_DONE;
- nl->rcv_data.skb = NULL;
- nl->snd_data.skb = NULL;
- nl->connection = PLIP_CN_NONE;
- cli();
- dev->tbusy = 0;
- sti();
- enable_irq(dev->irq);
-}
+ struct device *dev = (struct device *) irq2dev_map[irq];
+ struct net_local *nl = (struct net_local *)dev->priv;
+ struct plip_local *rcv = &nl->rcv_data;
+ unsigned char c0;
-/* PLIP_ERROR --- wait til other end settled */
-static int
-plip_error(struct device *dev)
-{
- unsigned char status;
+ if (dev == NULL) {
+ printk ("plip_interrupt: irq %d for unknown device.\n", irq);
+ return;
+ }
- status = inb(PAR_STATUS(dev));
- if ((status & 0xf8) == 0x80)
- return 0;
- return 1;
+ if (dev->interrupt)
+ return;
+
+ c0 = inb(PAR_STATUS(dev));
+ if ((c0 & 0xf8) != 0xc0) {
+ if (net_debug > 1)
+ printk("%s: spurious interrupt\n", dev->name);
+ return;
+ }
+ dev->interrupt = 1;
+ if (net_debug > 3)
+ printk("%s: interrupt.\n", dev->name);
+
+ cli();
+ switch (nl->connection) {
+ case PLIP_CN_CLOSING:
+ dev->tbusy = 0;
+ case PLIP_CN_NONE:
+ case PLIP_CN_SEND:
+ dev->last_rx = jiffies;
+ rcv->state = PLIP_PK_TRIGGER;
+ nl->connection = PLIP_CN_RECEIVE;
+ nl->timeout_count = 0;
+ queue_task(&nl->immediate, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ sti();
+ break;
+
+ case PLIP_CN_RECEIVE:
+ sti();
+ printk("%s: receive interrupt when receiving packet\n", dev->name);
+ break;
+
+ case PLIP_CN_ERROR:
+ sti();
+ printk("%s: receive interrupt in error state\n", dev->name);
+ break;
+ }
}
-/* PLIP_RECEIVE --- receive a byte(two nibbles)
- Returns 0 on success, 1 on failure */
-inline static int
-plip_receive(unsigned short nibble_timeout, unsigned short unit_us,
- unsigned short status_addr, unsigned short data_addr,
- enum plip_nibble_state *ns_p, unsigned char *data_p)
+/* We don't need to send arp, for plip is point-to-point. */
+static int
+plip_rebuild_header(void *buff, struct device *dev, unsigned long dst,
+ struct sk_buff *skb)
{
- unsigned char c0, c1;
- unsigned int cx;
-
- switch (*ns_p) {
- case PLIP_NB_BEGIN:
- cx = nibble_timeout;
- while (1) {
- c0 = inb(status_addr);
- udelay(unit_us);
- if ((c0 & 0x80) == 0) {
- c1 = inb(status_addr);
- if (c0 == c1)
- break;
- }
- if (--cx == 0)
- return 1;
- }
- *data_p = (c0 >> 3) & 0x0f;
- outb(0x10, data_addr); /* send ACK */
- *ns_p = PLIP_NB_1;
-
- case PLIP_NB_1:
- cx = nibble_timeout;
- while (1) {
- c0 = inb(status_addr);
- udelay(unit_us);
- if (c0 & 0x80) {
- c1 = inb(status_addr);
- if (c0 == c1)
- break;
- }
- if (--cx == 0)
- return 1;
+ struct net_local *nl = (struct net_local *)dev->priv;
+ struct ethhdr *eth = (struct ethhdr *)buff;
+ int i;
+
+ if ((dev->flags & IFF_NOARP)==0)
+ return nl->orig_rebuild_header(buff, dev, dst, skb);
+
+ if (eth->h_proto != htons(ETH_P_IP)) {
+ printk("plip_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto);
+ memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+ return 0;
}
- *data_p |= (c0 << 1) & 0xf0;
- outb(0x00, data_addr); /* send ACK */
- *ns_p = PLIP_NB_BEGIN;
- return 0;
- case PLIP_NB_2:
- }
+ for (i=0; i < ETH_ALEN - sizeof(unsigned long); i++)
+ eth->h_dest[i] = 0xfc;
+ memcpy(&(eth->h_dest[i]), &dst, sizeof(unsigned long));
+ return 0;
}
-/* PLIP_RECEIVE_PACKET --- receive a packet
- Returns 0 on success, 1 when timeout, -1 on failure */
static int
-plip_receive_packet(struct device *dev)
+plip_tx_packet(struct sk_buff *skb, struct device *dev)
{
- unsigned short data_addr = PAR_DATA(dev), status_addr = PAR_STATUS(dev);
- struct net_local *nl = (struct net_local *)dev->priv;
- unsigned short nibble_timeout = nl->nibble_us, unit_us = nl->unit_us;
- struct plip_local *rcv = &nl->rcv_data;
- unsigned char *lbuf;
- struct enet_statistics *stats = (struct enet_statistics *) dev->priv;
-
- switch (rcv->state) {
- case PLIP_PK_TRIGGER:
- rcv->state = PLIP_PK_LENGTH_LSB;
- rcv->nibble = PLIP_NB_BEGIN;
-
- case PLIP_PK_LENGTH_LSB:
- if (plip_receive(nibble_timeout, unit_us, status_addr, data_addr,
- &rcv->nibble, (unsigned char *)&rcv->length))
- return 1;
- rcv->state = PLIP_PK_LENGTH_MSB;
-
- case PLIP_PK_LENGTH_MSB:
- if (plip_receive(nibble_timeout, unit_us, status_addr, data_addr,
- &rcv->nibble, (unsigned char *)&rcv->length+1))
- return 1;
- if (rcv->length > dev->mtu || rcv->length < 8) {
- printk("%s: bogus packet size %d.\n", dev->name, rcv->length);
- return -1;
- }
- /* Malloc up new buffer. */
- rcv->skb = alloc_skb(rcv->length, GFP_ATOMIC);
- if (rcv->skb == NULL) {
- printk("%s: Memory squeeze.\n", dev->name);
- return -1;
+ struct net_local *nl = (struct net_local *)dev->priv;
+ struct plip_local *snd = &nl->snd_data;
+
+ if (dev->tbusy)
+ return 1;
+
+ /* If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
}
- rcv->skb->len = rcv->length;
- rcv->skb->dev = dev;
- rcv->state = PLIP_PK_DATA;
- rcv->byte = 0;
- rcv->checksum = 0;
-
- case PLIP_PK_DATA:
- lbuf = rcv->skb->data;
- do {
- if (plip_receive(nibble_timeout, unit_us, status_addr, data_addr,
- &rcv->nibble, &lbuf[rcv->byte]))
+
+ if (set_bit(0, (void*)&dev->tbusy) != 0) {
+ printk("%s: Transmitter access conflict.\n", dev->name);
return 1;
- rcv->checksum += lbuf[rcv->byte];
- } while (++rcv->byte < rcv->length);
- rcv->state = PLIP_PK_CHECKSUM;
-
- case PLIP_PK_CHECKSUM:
- if (plip_receive(nibble_timeout, unit_us, status_addr, data_addr,
- &rcv->nibble, &rcv->data))
- return 1;
- if (rcv->data != rcv->checksum) {
- stats->rx_crc_errors++;
- if (net_debug)
- printk("%s: checksum error\n", dev->name);
- return -1;
}
- rcv->state = PLIP_PK_DONE;
- case PLIP_PK_DONE:
- }
- return 0;
-}
+ if (skb->len > dev->mtu) {
+ printk("%s: packet too big, %d.\n", dev->name, (int)skb->len);
+ dev->tbusy = 0;
+ return 0;
+ }
-/* Handle the parallel port interrupts. */
-static void
-plip_interrupt(int reg_ptr)
-{
- int irq = pt_regs2irq(reg_ptr);
- struct device *dev = (struct device *) irq2dev_map[irq];
- struct net_local *nl = (struct net_local *)dev->priv;
- struct plip_local *rcv = &nl->rcv_data;
- unsigned char c0;
-
- if (dev == NULL) {
- if (net_debug)
- printk ("plip_interrupt: irq %d for unknown device.\n", irq);
- return;
- }
-
- if (dev->interrupt)
- return;
-
- c0 = inb(PAR_STATUS(dev));
- if ((c0 & 0xf8) != 0xc0) {
- if (net_debug > 3)
- printk("plip: spurious interrupt\n");
- return;
- }
- outb(0x01, PAR_DATA(dev)); /* send ACK */
- dev->interrupt = 1;
- if (net_debug > 3)
- printk("%s: interrupt.\n", dev->name);
-
- cli();
- switch (nl->connection) {
- case PLIP_CN_CLOSING:
- dev->tbusy = 0;
- case PLIP_CN_NONE:
- case PLIP_CN_SEND:
- sti();
- dev->last_rx = jiffies;
- rcv->state = PLIP_PK_TRIGGER;
- nl->connection = PLIP_CN_RECEIVE;
- nl->timeout_count = 0;
+ if (net_debug > 2)
+ printk("%s: send request\n", dev->name);
+
+ cli();
+ dev->trans_start = jiffies;
+ snd->skb = skb;
+ snd->length.h = skb->len;
+ snd->state = PLIP_PK_TRIGGER;
+ if (nl->connection == PLIP_CN_NONE) {
+ nl->connection = PLIP_CN_SEND;
+ nl->timeout_count = 0;
+ }
queue_task(&nl->immediate, &tq_immediate);
mark_bh(IMMEDIATE_BH);
- break;
-
- case PLIP_CN_RECEIVE:
sti();
- printk("%s: receive interrupt when receiving packet\n", dev->name);
- break;
- case PLIP_CN_ERROR:
- sti();
- printk("%s: receive interrupt in error state\n", dev->name);
- break;
- }
+ return 0;
}
-
-/* PLIP_SEND --- send a byte (two nibbles)
- Returns 0 on success, 1 on failure */
-inline static int
-plip_send(unsigned short nibble_timeout, unsigned short unit_us,
- unsigned short status_addr, unsigned short data_addr,
- enum plip_nibble_state *ns_p, unsigned char data)
+
+/* Open/initialize the board. This is called (in the current kernel)
+ sometime after booting when the 'ifconfig' program is run.
+
+ This routine gets exclusive access to the parallel port by allocating
+ its IRQ line.
+ */
+static int
+plip_open(struct device *dev)
{
- unsigned char c0;
- unsigned int cx;
-
- switch (*ns_p) {
- case PLIP_NB_BEGIN:
- outb((data & 0x0f), data_addr);
- *ns_p = PLIP_NB_1;
-
- case PLIP_NB_1:
- outb(0x10 | (data & 0x0f), data_addr);
- cx = nibble_timeout;
- while (1) {
- c0 = inb(status_addr);
- if ((c0 & 0x80) == 0)
- break;
- if (--cx == 0) /* time out */
- return 1;
- udelay(unit_us);
+ struct net_local *nl = (struct net_local *)dev->priv;
+ int i;
+
+ if (dev->irq == 0) {
+ printk("%s: IRQ is not set. Please set it by ifconfig.\n", dev->name);
+ return -EAGAIN;
}
- outb(0x10 | (data >> 4), data_addr);
- *ns_p = PLIP_NB_2;
-
- case PLIP_NB_2:
- outb((data >> 4), data_addr);
- cx = nibble_timeout;
- while (1) {
- c0 = inb(status_addr);
- if (c0 & 0x80)
- break;
- if (--cx == 0) /* time out */
- return 1;
- udelay(unit_us);
+ cli();
+ if (request_irq(dev->irq , plip_interrupt, 0, dev->name) != 0) {
+ sti();
+ printk("%s: couldn't get IRQ %d.\n", dev->name, dev->irq);
+ return -EAGAIN;
}
- *ns_p = PLIP_NB_BEGIN;
+ irq2dev_map[dev->irq] = dev;
+ sti();
+
+ /* Clear the data port. */
+ outb (0x00, PAR_DATA(dev));
+
+ /* Enable rx interrupt. */
+ outb(PAR_INTR_ON, PAR_CONTROL(dev));
+
+ /* Initialize the state machine. */
+ nl->rcv_data.state = nl->snd_data.state = PLIP_PK_DONE;
+ nl->rcv_data.skb = nl->snd_data.skb = NULL;
+ nl->connection = PLIP_CN_NONE;
+ nl->is_deferred = 0;
+
+ /* Fill in the MAC-level header. */
+ for (i=0; i < ETH_ALEN - sizeof(unsigned long); i++)
+ dev->dev_addr[i] = 0xfc;
+ memcpy(&(dev->dev_addr[i]), &dev->pa_addr, sizeof(unsigned long));
+
+ dev->interrupt = 0;
+ dev->start = 1;
+ dev->tbusy = 0;
+ MOD_INC_USE_COUNT;
return 0;
- }
}
-/* PLIP_SEND_PACKET --- send a packet
- Returns 0 on success, 1 when timeout, -1 when interrupted */
+/* The inverse routine to plip_open (). */
static int
-plip_send_packet(struct device *dev)
+plip_close(struct device *dev)
{
- unsigned short data_addr = PAR_DATA(dev), status_addr = PAR_STATUS(dev);
- struct net_local *nl = (struct net_local *)dev->priv;
- unsigned short nibble_timeout = nl->nibble_us, unit_us = nl->unit_us;
- struct plip_local *snd = &nl->snd_data;
- unsigned char *lbuf = snd->skb->data;
- unsigned char c0;
- unsigned int cx;
- struct enet_statistics *stats = (struct enet_statistics *) dev->priv;
-
- switch (snd->state) {
- case PLIP_PK_TRIGGER:
- /* Trigger remote rx interrupt. */
- outb(0x08, PAR_DATA(dev));
- cx = nl->trigger_us;
- while (1) {
- if (nl->connection == PLIP_CN_RECEIVE) { /* interrupted */
- stats->collisions++;
- if (net_debug > 3)
- printk("%s: collision.\n", dev->name);
- return -1;
- }
- cli();
- c0 = inb(PAR_STATUS(dev));
- if (c0 & 0x08) {
- disable_irq(dev->irq);
- if (net_debug > 3)
- printk("+");
- /* OK, connection established! */
- snd->state = PLIP_PK_LENGTH_LSB;
- snd->nibble = PLIP_NB_BEGIN;
- nl->timeout_count = 0;
- sti();
- break;
- }
- sti();
- udelay(nl->unit_us);
- if (--cx == 0) {
- outb(0x00, PAR_DATA(dev));
- return 1;
- }
- }
+ struct net_local *nl = (struct net_local *)dev->priv;
+ struct plip_local *snd = &nl->snd_data;
+ struct plip_local *rcv = &nl->rcv_data;
- case PLIP_PK_LENGTH_LSB:
- if (plip_send(nibble_timeout, unit_us, status_addr, data_addr,
- &snd->nibble, snd->length & 0xff)) /* timeout */
- return 1;
- snd->state = PLIP_PK_LENGTH_MSB;
-
- case PLIP_PK_LENGTH_MSB:
- if (plip_send(nibble_timeout, unit_us, status_addr, data_addr,
- &snd->nibble, snd->length >> 8)) /* timeout */
- return 1;
- snd->state = PLIP_PK_DATA;
- snd->byte = 0;
- snd->checksum = 0;
-
- case PLIP_PK_DATA:
- do {
- if (plip_send(nibble_timeout, unit_us, status_addr, data_addr,
- &snd->nibble, lbuf[snd->byte])) /* timeout */
- return 1;
- snd->checksum += lbuf[snd->byte];
- } while (++snd->byte < snd->length);
- snd->state = PLIP_PK_CHECKSUM;
+ dev->tbusy = 1;
+ dev->start = 0;
+ cli();
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = NULL;
+ nl->is_deferred = 0;
+ nl->connection = PLIP_CN_NONE;
+ sti();
+ outb(0x00, PAR_DATA(dev));
+
+ snd->state = PLIP_PK_DONE;
+ if (snd->skb) {
+ dev_kfree_skb(snd->skb, FREE_WRITE);
+ snd->skb = NULL;
+ }
+ rcv->state = PLIP_PK_DONE;
+ if (rcv->skb) {
+ rcv->skb->free = 1;
+ kfree_skb(rcv->skb, FREE_READ);
+ rcv->skb = NULL;
+ }
- case PLIP_PK_CHECKSUM:
- if (plip_send(nibble_timeout, unit_us, status_addr, data_addr,
- &snd->nibble, snd->checksum)) /* timeout */
- return 1;
+ /* Reset. */
+ outb(0x00, PAR_CONTROL(dev));
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
- dev_kfree_skb(snd->skb, FREE_WRITE);
- stats->tx_packets++;
+static struct enet_statistics *
+plip_get_stats(struct device *dev)
+{
+ struct net_local *nl = (struct net_local *)dev->priv;
+ struct enet_statistics *r = &nl->enet_stats;
- case PLIP_PK_DONE:
- }
- return 0;
+ return r;
}
-
-static int plip_config(struct device *dev, struct ifmap *map)
+
+static int
+plip_config(struct device *dev, struct ifmap *map)
{
- if(dev->flags&IFF_UP)
+ if (dev->flags & IFF_UP)
return -EBUSY;
-/*
- * We could probe this for verification, but since they told us
- * to do it then they can suffer.
- */
- if(map->base_addr!= (unsigned short)-1)
- dev->base_addr=map->base_addr;
- if(map->irq!= (unsigned char)-1)
- dev->irq= map->irq;
+
+ if (map->base_addr != (unsigned long)-1
+ && map->base_addr != dev->base_addr)
+ printk("%s: You cannot change base_addr of this interface (ignored).\n", dev->name);
+
+ if (map->irq != (unsigned char)-1)
+ dev->irq = map->irq;
return 0;
}
-static int plip_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+static int
+plip_ioctl(struct device *dev, struct ifreq *rq, int cmd)
{
struct net_local *nl = (struct net_local *) dev->priv;
struct plipconf *pc = (struct plipconf *) &rq->ifr_data;
- switch(pc->pcmd)
- {
- case PLIP_GET_TIMEOUT:
- pc->trigger=nl->trigger_us;
- pc->nibble=nl->nibble_us;
- pc->unit=nl->unit_us;
- break;
- case PLIP_SET_TIMEOUT:
- nl->trigger_us=pc->trigger;
- nl->nibble_us=pc->nibble;
- nl->unit_us=pc->unit;
- break;
- default:
- return -EOPNOTSUPP;
+ switch(pc->pcmd) {
+ case PLIP_GET_TIMEOUT:
+ pc->trigger = nl->trigger;
+ pc->nibble = nl->nibble;
+ break;
+ case PLIP_SET_TIMEOUT:
+ nl->trigger = pc->trigger;
+ nl->nibble = pc->nibble;
+ break;
+ default:
+ return -EOPNOTSUPP;
}
return 0;
}
-
#ifdef MODULE
char kernel_version[] = UTS_RELEASE;
@@ -931,53 +1061,45 @@ static struct device dev_plip2 =
int
init_module(void)
{
- int devices=0;
-
- if (register_netdev(&dev_plip0) != 0)
- devices++;
- if (register_netdev(&dev_plip1) != 0)
- devices++;
- if (register_netdev(&dev_plip2) != 0)
- devices++;
- if (devices == 0)
- return -EIO;
- return 0;
+ int devices=0;
+
+ if (register_netdev(&dev_plip0) != 0)
+ devices++;
+ if (register_netdev(&dev_plip1) != 0)
+ devices++;
+ if (register_netdev(&dev_plip2) != 0)
+ devices++;
+ if (devices == 0)
+ return -EIO;
+ return 0;
}
void
cleanup_module(void)
{
- if (MOD_IN_USE)
- printk("plip: device busy, remove delayed\n");
- else {
if (dev_plip0.priv) {
- unregister_netdev(&dev_plip0);
- kfree_s(dev_plip0.priv, sizeof(struct net_local));
- dev_plip0.priv = NULL;
+ unregister_netdev(&dev_plip0);
+ release_region(PAR_DATA(&dev_plip0), 3);
+ kfree_s(dev_plip0.priv, sizeof(struct net_local));
+ dev_plip0.priv = NULL;
}
if (dev_plip1.priv) {
- unregister_netdev(&dev_plip1);
- kfree_s(dev_plip1.priv, sizeof(struct net_local));
- dev_plip1.priv = NULL;
+ unregister_netdev(&dev_plip1);
+ release_region(PAR_DATA(&dev_plip1), 3);
+ kfree_s(dev_plip1.priv, sizeof(struct net_local));
+ dev_plip1.priv = NULL;
}
if (dev_plip2.priv) {
- unregister_netdev(&dev_plip2);
- kfree_s(dev_plip2.priv, sizeof(struct net_local));
- dev_plip2.priv = NULL;
+ unregister_netdev(&dev_plip2);
+ release_region(PAR_DATA(&dev_plip2), 3);
+ kfree_s(dev_plip2.priv, sizeof(struct net_local));
+ dev_plip2.priv = NULL;
}
- }
}
#endif /* MODULE */
/*
* Local variables:
- * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -m486 -I../../net/inet -c plip.c"
- * c-indent-level: 4
- * c-continued-statement-offset: 4
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * version-control: t
- * kept-new-versions: 10
+ * compile-command: "gcc -DMODULE -DCONFIG_MODVERSIONS -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -g -fomit-frame-pointer -pipe -m486 -c plip.c"
* End:
*/
diff --git a/drivers/net/ppp.c b/drivers/net/ppp.c
index a93be1e18..8e0ca9263 100644
--- a/drivers/net/ppp.c
+++ b/drivers/net/ppp.c
@@ -31,6 +31,12 @@
/* #define NET02D -* */
#define NEW_TTY_DRIVERS /* */
#define OPTIMIZE_FLAG_TIME ((HZ * 3)/2) /* */
+#define CHECK_CHARACTERS
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
#include <linux/kernel.h>
#include <linux/sched.h>
@@ -61,10 +67,10 @@
#include <linux/inet.h>
#endif
-#include <linux/ppp.h>
+#include <linux/if_ppp.h>
-#include <ip.h>
-#include <tcp.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
#include "slhc.h"
@@ -270,7 +276,6 @@ ppp_init(struct device *dev)
dev->stop = ppp_dev_close;
dev->get_stats = ppp_get_stats;
dev->hard_header = ppp_header;
- dev->type_trans = ppp_type_trans;
dev->rebuild_header = ppp_rebuild_header;
dev->hard_header_len = 0;
dev->addr_len = 0;
@@ -464,8 +469,8 @@ ppp_release(struct ppp *ppp)
#endif
if (ppp->dev) {
- ppp->dev->flags &= ~IFF_UP; /* down the device */
- ppp->dev->flags |= IFF_POINTOPOINT;
+ dev_close (ppp->dev);
+ ppp->dev->flags = 0;
}
kfree (ppp->xbuff);
@@ -563,6 +568,10 @@ ppp_open(struct tty_struct *tty)
PRINTKN (2,(KERN_INFO "ppp: channel %s open\n", ppp->dev->name));
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+
return (ppp->line);
}
@@ -603,6 +612,9 @@ ppp_dev_close(struct device *dev)
PRINTKN (2,(KERN_INFO "ppp: channel %s going down for IP packets!\n",
dev->name));
CHECK_PPP(-ENXIO);
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
return 0;
}
@@ -941,14 +953,14 @@ static void ppp_receive_buf(struct tty_struct *tty, unsigned char *cp,
#ifdef CHECK_CHARACTERS
if (c & 0x80)
- sc->sc_flags |= SC_RCV_B7_1;
+ ppp->flags |= SC_RCV_B7_1;
else
- sc->sc_flags |= SC_RCV_B7_0;
+ ppp->flags |= SC_RCV_B7_0;
if (paritytab[c >> 5] & (1 << (c & 0x1F)))
- sc->sc_flags |= SC_RCV_ODDP;
+ ppp->flags |= SC_RCV_ODDP;
else
- sc->sc_flags |= SC_RCV_EVNP;
+ ppp->flags |= SC_RCV_EVNP;
#endif
switch (c) {
@@ -993,6 +1005,7 @@ ppp_doframe(struct ppp *ppp)
if (ppp->toss) {
PRINTKN (1, (KERN_WARNING "ppp_toss: tossing frame, reason = %d\n",
ppp->toss));
+ slhc_toss (ppp->slcomp);
ppp->stats.rerrors++;
return;
}
@@ -1006,6 +1019,7 @@ ppp_doframe(struct ppp *ppp)
if (count < 4) {
PRINTKN (1,(KERN_WARNING "ppp: got runt ppp frame, %d chars\n", count));
+ slhc_toss (ppp->slcomp);
ppp->stats.runts++;
return;
}
@@ -1013,6 +1027,7 @@ ppp_doframe(struct ppp *ppp)
/* check PPP error detection field */
if (!ppp_check_fcs(ppp)) {
PRINTKN (1,(KERN_WARNING "ppp: frame with bad fcs\n"));
+ slhc_toss (ppp->slcomp);
ppp->stats.rerrors++;
return;
}
@@ -1055,6 +1070,7 @@ ppp_doframe(struct ppp *ppp)
/* couldn't cope. */
PRINTKN (1,(KERN_WARNING
"ppp: dropping packet on the floor: nobody could take it.\n"));
+ slhc_toss (ppp->slcomp);
ppp->stats.tossed++;
}
@@ -1067,6 +1083,7 @@ ppp_do_ip (struct ppp *ppp, unsigned short proto, unsigned char *c,
int count)
{
int flags, done;
+ struct sk_buff *skb;
PRINTKN (4,(KERN_DEBUG "ppp_do_ip: proto %x len %d first byte %x\n",
(int) proto, count, c[0]));
@@ -1097,6 +1114,7 @@ ppp_do_ip (struct ppp *ppp, unsigned short proto, unsigned char *c,
PRINTKN (1,(KERN_NOTICE
"ppp: no space to decompress VJ compressed TCP header.\n"));
ppp->stats.roverrun++;
+ slhc_toss (ppp->slcomp);
return 1;
}
@@ -1104,6 +1122,7 @@ ppp_do_ip (struct ppp *ppp, unsigned short proto, unsigned char *c,
if (count <= 0) {
ppp->stats.rerrors++;
PRINTKN (1,(KERN_NOTICE "ppp: error in VJ decompression\n"));
+ slhc_toss (ppp->slcomp);
return 1;
}
ppp->stats.rcomp++;
@@ -1114,6 +1133,7 @@ ppp_do_ip (struct ppp *ppp, unsigned short proto, unsigned char *c,
if (slhc_remember(ppp->slcomp, c, count) <= 0) {
ppp->stats.rerrors++;
PRINTKN (1,(KERN_NOTICE "ppp: error in VJ memorizing\n"));
+ slhc_toss (ppp->slcomp);
return 1;
}
ppp->stats.runcomp++;
@@ -1131,7 +1151,16 @@ ppp_do_ip (struct ppp *ppp, unsigned short proto, unsigned char *c,
}
/* receive the frame through the network software */
- (void) dev_rint (c, count, 0, ppp->dev);
+
+ skb=alloc_skb(count, GFP_ATOMIC);
+ if(skb)
+ {
+ memcpy(skb->data, c,count);
+ skb->protocol=htons(ETH_P_IP);
+ skb->dev=ppp->dev;
+ skb->len=count;
+ netif_rx(skb);
+ }
return 1;
}
@@ -1409,8 +1438,8 @@ ppp_ioctl(struct tty_struct *tty, struct file *file, unsigned int i,
case PPPIOCSMRU:
error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
if (error == 0) {
- PRINTKN (3,(KERN_INFO "ppp_ioctl: set mru to %x\n", temp_i));
temp_i = (int) get_fs_long (l);
+ PRINTKN (3,(KERN_INFO "ppp_ioctl: set mru to %d\n", temp_i));
if (ppp->mru != temp_i)
ppp_changedmtu (ppp, ppp->dev->mtu, temp_i);
}
@@ -1451,7 +1480,6 @@ ppp_ioctl(struct tty_struct *tty, struct file *file, unsigned int i,
case PPPIOCSASYNCMAP:
error = verify_area (VERIFY_READ, (void *) l, sizeof (temp_i));
if (error == 0) {
- memset (ppp->xmit_async_map, 0, sizeof (ppp->xmit_async_map));
ppp->xmit_async_map[0] = get_fs_long (l);
bset (ppp->xmit_async_map, PPP_FLAG);
bset (ppp->xmit_async_map, PPP_ESC);
@@ -1473,7 +1501,7 @@ ppp_ioctl(struct tty_struct *tty, struct file *file, unsigned int i,
error = verify_area (VERIFY_WRITE, (void *) l, sizeof (temp_i));
if (error == 0) {
put_fs_long (ppp->dev->base_addr, l);
- PRINTKN (3,(KERN_INFO "ppp_ioctl: get unit: %d", ppp->dev->base_addr));
+ PRINTKN (3,(KERN_INFO "ppp_ioctl: get unit: %ld", ppp->dev->base_addr));
}
break;
@@ -1698,7 +1726,12 @@ ppp_xmit(struct sk_buff *skb, struct device *dev)
PRINTKN(4,(KERN_DEBUG "ppp_xmit [%s]: skb %lX busy %d\n", dev->name,
(unsigned long int) skb, ppp->sending));
- CHECK_PPP(0);
+ /* avoid race conditions when the link fails */
+ if (!ppp->inuse) {
+ dev_kfree_skb(skb, FREE_WRITE);
+ dev_close (dev);
+ return 0;
+ }
if (tty == NULL) {
PRINTKN(1,(KERN_ERR "ppp_xmit: %s not connected to a TTY!\n", dev->name));
@@ -1712,12 +1745,14 @@ ppp_xmit(struct sk_buff *skb, struct device *dev)
goto done;
}
+#ifdef CURED_AGES_AGO
/* get length from IP header as per Alan Cox bugfix for slip.c */
if (len < sizeof(struct iphdr)) {
PRINTKN(0,(KERN_ERR "ppp_xmit: given runt packet, ignoring\n"));
- return 1;
+ goto done;
}
len = ntohs( ((struct iphdr *)(skb->data)) -> tot_len );
+#endif
/* If doing demand dial then divert the first frame to pppd. */
if (ppp->flags & SC_IP_DOWN) {
@@ -1739,8 +1774,8 @@ ppp_xmit(struct sk_buff *skb, struct device *dev)
/* try to compress, if VJ compression mode is on */
if (ppp->flags & SC_COMP_TCP) {
- /* NOTE: last 0 argument says never to compress connection ID */
- len = slhc_compress(ppp->slcomp, p, len, ppp->cbuff, &p, 0);
+ len = slhc_compress(ppp->slcomp, p, len, ppp->cbuff, &p,
+ !(ppp->flags & SC_NO_TCP_CCID));
if (p[0] & SL_TYPE_COMPRESSED_TCP)
proto = PROTO_VJCOMP;
else {
@@ -1809,12 +1844,6 @@ ppp_xmit(struct sk_buff *skb, struct device *dev)
return 0;
}
-static unsigned short
-ppp_type_trans (struct sk_buff *skb, struct device *dev)
-{
- return(htons(ETH_P_IP));
-}
-
#ifdef NET02D
static int
ppp_header(unsigned char *buff, struct device *dev, unsigned short type,
@@ -2034,3 +2063,69 @@ static void ppp_print_buffer(const char *name, char *buf, int count, int seg)
set_fs (old_fs);
}
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+
+static struct device dev_ppp[PPP_NRUNIT] = {
+ {
+ "ppp0", /* ppp */
+ 0, 0, 0, 0, /* memory */
+ 0, 0, /* base, irq */
+ 0, 0, 0, NULL, ppp_init,
+ }
+ , { "ppp1" , 0, 0, 0, 0, 1, 0, 0, 0, 0, NULL, ppp_init }
+ , { "ppp2" , 0, 0, 0, 0, 2, 0, 0, 0, 0, NULL, ppp_init }
+ , { "ppp3" , 0, 0, 0, 0, 3, 0, 0, 0, 0, NULL, ppp_init }
+
+#ifdef PPP_PPP_LOTS
+ , { "ppp4" , 0, 0, 0, 0, 4, 0, 0, 0, 0, NULL, ppp_init }
+ , { "ppp5" , 0, 0, 0, 0, 5, 0, 0, 0, 0, NULL, ppp_init }
+ , { "ppp6" , 0, 0, 0, 0, 6, 0, 0, 0, 0, NULL, ppp_init }
+ , { "ppp7" , 0, 0, 0, 0, 7, 0, 0, 0, 0, NULL, ppp_init }
+ , { "ppp8" , 0, 0, 0, 0, 8, 0, 0, 0, 0, NULL, ppp_init }
+ , { "ppp9" , 0, 0, 0, 0, 9, 0, 0, 0, 0, NULL, ppp_init }
+ , { "ppp10" , 0, 0, 0, 0, 10, 0, 0, 0, 0, NULL, ppp_init }
+ , { "ppp11" , 0, 0, 0, 0, 11, 0, 0, 0, 0, NULL, ppp_init }
+ , { "ppp12" , 0, 0, 0, 0, 12, 0, 0, 0, 0, NULL, ppp_init }
+ , { "ppp13" , 0, 0, 0, 0, 13, 0, 0, 0, 0, NULL, ppp_init }
+ , { "ppp14" , 0, 0, 0, 0, 14, 0, 0, 0, 0, NULL, ppp_init }
+ , { "ppp15" , 0, 0, 0, 0, 15, 0, 0, 0, 0, NULL, ppp_init }
+#endif
+};
+
+int
+init_module(void)
+{
+ int err;
+ int i;
+
+ for (i = 0; i < PPP_NRUNIT; i++) {
+ if ((err = register_netdev(&dev_ppp[i]))) {
+ if (err == -EEXIST) {
+ printk("PPP: devices already present. Module not loaded.\n");
+ }
+ return err;
+ }
+ }
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ int i;
+
+ if (MOD_IN_USE) {
+ printk("PPP: device busy, remove delayed\n");
+ return;
+ }
+ for (i = 0; i < PPP_NRUNIT; i++) {
+ unregister_netdev(&dev_ppp[i]);
+ }
+ if ((i = tty_register_ldisc(N_PPP, NULL))) {
+ printk("PPP: can't unregister line discipline (err = %d)\n", i);
+ }
+}
+
+#endif
diff --git a/drivers/net/sk_g16.c b/drivers/net/sk_g16.c
index acdce289f..dd3c5a4e8 100644
--- a/drivers/net/sk_g16.c
+++ b/drivers/net/sk_g16.c
@@ -56,7 +56,6 @@ static char *rcsid = "$Id: sk_g16.c,v 1.1 1994/06/30 16:25:15 root Exp $";
* - (Try to make it slightly faster.)
*/
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/ptrace.h>
@@ -330,7 +329,7 @@ static char *rcsid = "$Id: sk_g16.c,v 1.1 1994/06/30 16:25:15 root Exp $";
#ifndef HAVE_PORTRESERVE
#define check_region(ioaddr, size) 0
-#define snarf_region(ioaddr, size); do ; while (0)
+#define request_region(ioaddr, size,name) do ; while (0)
#endif
@@ -488,7 +487,7 @@ static int SK_probe(struct device *dev, short ioaddr);
static int SK_open(struct device *dev);
static int SK_send_packet(struct sk_buff *skb, struct device *dev);
-static void SK_interrupt(int reg_ptr);
+static void SK_interrupt(int irq, struct pt_regs * regs);
static void SK_rxintr(struct device *dev);
static void SK_txintr(struct device *dev);
static int SK_close(struct device *dev);
@@ -722,7 +721,7 @@ int SK_probe(struct device *dev, short ioaddr)
outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off */
- /* We found a Boot_ROM and its gone. Set RAM address on
+ /* We found a Boot_ROM and it's gone. Set RAM address on
* Boot_ROM address.
*/
@@ -783,7 +782,7 @@ int SK_probe(struct device *dev, short ioaddr)
dev->dev_addr[5]);
/* Grab the I/O Port region */
- snarf_region(ioaddr, ETHERCARD_TOTAL_SIZE);
+ request_region(ioaddr, ETHERCARD_TOTAL_SIZE,"sk_g16");
/* Initialize device structure */
@@ -1301,7 +1300,7 @@ static int SK_send_packet(struct sk_buff *skb, struct device *dev)
* Description : SK_G16 interrupt handler which checks for LANCE
* Errors, handles transmit and receive interrupts
*
- * Parameters : I : int reg_ptr -
+ * Parameters : I : int irq, struct pt_regs * regs -
* Return Value : None
* Errors : None
* Globals : None
@@ -1310,9 +1309,8 @@ static int SK_send_packet(struct sk_buff *skb, struct device *dev)
* YY/MM/DD uid Description
-*/
-static void SK_interrupt(int reg_ptr)
+static void SK_interrupt(int irq, struct pt_regs * regs)
{
- int irq = pt_regs2irq(reg_ptr);
int csr0;
struct device *dev = (struct device *) irq2dev_map[irq];
struct priv *p = (struct priv *) dev->priv;
@@ -1613,6 +1611,7 @@ static void SK_rxintr(struct device *dev)
* netif_rx() always succeeds. see /net/inet/dev.c for more.
*/
+ skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb); /* queue packet and mark it for processing */
/*
diff --git a/drivers/net/skeleton.c b/drivers/net/skeleton.c
index f98250c85..2d56c4e69 100644
--- a/drivers/net/skeleton.c
+++ b/drivers/net/skeleton.c
@@ -97,7 +97,7 @@ extern int netcard_probe(struct device *dev);
static int netcard_probe1(struct device *dev, int ioaddr);
static int net_open(struct device *dev);
static int net_send_packet(struct sk_buff *skb, struct device *dev);
-static void net_interrupt(int reg_ptr);
+static void net_interrupt(int irq, struct pt_regs *regs);
static void net_rx(struct device *dev);
static int net_close(struct device *dev);
static struct enet_statistics *net_get_stats(struct device *dev);
@@ -247,7 +247,7 @@ static int netcard_probe1(struct device *dev, int ioaddr)
#endif /* jumpered DMA */
/* Grab the region so we can find another board if autoIRQ fails. */
- snarf_region(ioaddr, NETCARD_IO_EXTENT);
+ request_region(ioaddr, NETCARD_IO_EXTENT,"skeleton");
/* Initialize the device structure. */
if (dev->priv == NULL)
@@ -355,9 +355,8 @@ net_send_packet(struct sk_buff *skb, struct device *dev)
/* The typical workload of the driver:
Handle the network interface interrupts. */
static void
-net_interrupt(int reg_ptr)
+net_interrupt(int irq, struct pt_regs * regs)
{
- int irq = pt_regs2irq(reg_ptr);
struct device *dev = (struct device *)(irq2dev_map[irq]);
struct net_local *lp;
int ioaddr, status, boguscount = 0;
diff --git a/drivers/net/slhc.c b/drivers/net/slhc.c
index 9063a97df..ad59454cc 100644
--- a/drivers/net/slhc.c
+++ b/drivers/net/slhc.c
@@ -38,15 +38,24 @@
* status display
* - Jul 1994 Dmitry Gorodchanin
* Fixes for memory leaks.
+ * - Oct 1994 Dmitry Gorodchanin
+ * Modularization.
+ * - Jan 1995 Bjorn Ekwall
+ * Use ip_fast_csum from ip.h
*
*
- * This module is a difficult issue. Its clearly inet code but its also clearly
+ * This module is a difficult issue. It's clearly inet code but it's also clearly
* driver code belonging close to PPP and SLIP
*/
#include <linux/config.h>
#ifdef CONFIG_INET
/* Entire module is for IP only */
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/mm.h>
@@ -58,17 +67,18 @@
#include <linux/fcntl.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
-#include "ip.h"
-#include "protocol.h"
-#include "icmp.h"
-#include "tcp.h"
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/icmp.h>
+#include <net/tcp.h>
#include <linux/skbuff.h>
-#include "sock.h"
+#include <net/sock.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <asm/system.h>
#include <asm/segment.h>
#include <linux/mm.h>
+#include <net/checksum.h>
#include "slhc.h"
int last_retran;
@@ -78,9 +88,6 @@ static long decode(unsigned char **cpp);
static unsigned char * put16(unsigned char *cp, unsigned short x);
static unsigned short pull16(unsigned char **cpp);
-extern int ip_csum(struct iphdr *iph);
-
-
/* Initialize compression data structure
* slots must be in range 0 to 255 (zero meaning no compression)
*/
@@ -91,7 +98,7 @@ slhc_init(int rslots, int tslots)
register struct cstate *ts;
struct slcompress *comp;
- comp = (struct slcompress *)kmalloc(sizeof(struct slcompress),
+ comp = (struct slcompress *)kmalloc(sizeof(struct slcompress),
GFP_KERNEL);
if (! comp)
return NULL;
@@ -112,7 +119,7 @@ slhc_init(int rslots, int tslots)
}
if ( tslots > 0 && tslots < 256 ) {
- comp->tstate =
+ comp->tstate =
(struct cstate *)kmalloc(tslots * sizeof(struct cstate),
GFP_KERNEL);
if (! comp->tstate)
@@ -145,6 +152,9 @@ slhc_init(int rslots, int tslots)
ts[0].next = &(ts[comp->tslot_limit]);
ts[0].cs_this = 0;
}
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
return comp;
}
@@ -162,6 +172,9 @@ slhc_free(struct slcompress *comp)
if ( comp->tstate != NULLSLSTATE )
kfree( comp->tstate );
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
kfree( comp );
}
@@ -216,7 +229,7 @@ decode(unsigned char **cpp)
}
}
-/*
+/*
* icp and isize are the original packet.
* ocp is a place to put a copy if necessary.
* cpp is initially a pointer to icp. If the copy is used,
@@ -241,7 +254,7 @@ slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
ip = (struct iphdr *) icp;
/* Bail if this packet isn't TCP, or is an IP fragment */
- if(ip->protocol != IPPROTO_TCP || (ntohs(ip->frag_off) & 0x1fff) ||
+ if(ip->protocol != IPPROTO_TCP || (ntohs(ip->frag_off) & 0x1fff) ||
(ip->frag_off & 32)){
/* Send as regular IP */
if(ip->protocol != IPPROTO_TCP)
@@ -310,7 +323,7 @@ found:
* Found it -- move to the front on the connection list.
*/
if(lcs == ocs) {
- /* found at most recently used */
+ /* found at most recently used */
} else if (cs == ocs) {
/* found at least recently used */
comp->xmit_oldest = lcs->cs_this;
@@ -345,7 +358,7 @@ found:
|| (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4 != 0))){
goto uncompressed;
}
-
+
/*
* Figure out which of the changing fields changed. The
* receiver expects changes in the order: urgent, window,
@@ -379,7 +392,7 @@ found:
cp = encode(cp,deltaS);
changes |= NEW_S;
}
-
+
switch(changes){
case 0: /* Nothing changed. If this packet contains data and the
* last one didn't, this is probably a data packet following
@@ -388,7 +401,7 @@ found:
* retransmitted ack or window probe. Send it uncompressed
* in case the other side missed the compressed version.
*/
- if(ip->tot_len != cs->cs_ip.tot_len &&
+ if(ip->tot_len != cs->cs_ip.tot_len &&
ntohs(cs->cs_ip.tot_len) == hlen)
break;
goto uncompressed;
@@ -522,7 +535,7 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)
thp->check = htons(x);
thp->psh = (changes & TCP_PUSH_BIT) ? 1 : 0;
-/*
+/*
* we can use the same number for the length of the saved header and
* the current one, because the packet wouldn't have been sent
* as compressed unless the options were the same as the previous one
@@ -557,7 +570,7 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)
if(changes & NEW_W){
if((x = decode(&cp)) == -1) {
goto bad;
- }
+ }
thp->window = htons( ntohs(thp->window) + x);
}
if(changes & NEW_A){
@@ -606,7 +619,7 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)
cp += ((ip->ihl) - 5) * 4;
}
- ((struct iphdr *)icp)->check = ip_csum((struct iphdr*)icp);
+ ((struct iphdr *)icp)->check = ip_fast_csum(icp, ((struct iphdr*)icp)->ihl);
memcpy(cp, thp, 20);
cp += 20;
@@ -649,7 +662,7 @@ slhc_remember(struct slcompress *comp, unsigned char *icp, int isize)
icp[9] = IPPROTO_TCP;
ip = (struct iphdr *) icp;
- if (ip_csum(ip)) {
+ if (ip_fast_csum(icp, ip->ihl)) {
/* Bad IP header checksum; discard */
comp->sls_i_badcheck++;
return slhc_toss( comp );
@@ -715,4 +728,21 @@ void slhc_o_status(struct slcompress *comp)
}
}
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+
+int init_module(void)
+{
+ printk("CSLIP: code copyright 1989 Regents of the University of California\n");
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ if (MOD_IN_USE) {
+ printk("CSLIP: module in use, remove delayed");
+ }
+ return;
+}
+#endif /* MODULE */
#endif /* CONFIG_INET */
diff --git a/drivers/net/slip.c b/drivers/net/slip.c
index 6d80df49a..8b1bc9476 100644
--- a/drivers/net/slip.c
+++ b/drivers/net/slip.c
@@ -3,7 +3,7 @@
* devices like TTY. It interfaces between a raw TTY, and the
* kernel's INET protocol layers (via DDI).
*
- * Version: @(#)slip.c 0.7.6 05/25/93
+ * Version: @(#)slip.c 0.8.3 12/24/94
*
* Authors: Laurence Culhane, <loz@holmes.demon.co.uk>
* Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
@@ -25,117 +25,92 @@
* Alan Cox : Default to 192.168.0.0 (RFC 1597)
* A.N.Kuznetsov : dev_tint() recursion fix.
* Dmitry Gorodchanin : SLIP memory leaks
- * Alan Cox : Oops - fix AX.25 buffer lengths
+ * Dmitry Gorodchanin : Code cleanup. Reduce tty driver
+ * buffering from 4096 to 256 bytes.
+ * Improving SLIP response time.
+ * CONFIG_SLIP_MODE_SLIP6.
+ * ifconfig sl? up & down now works correctly.
+ * Modularization.
+ * Alan Cox : Oops - fix AX.25 buffer lengths
+ * Dmitry Gorodchanin : Even more cleanups. Preserve CSLIP
+ * statistics. Include CSLIP code only
+ * if it really needed.
+ * Alan Cox : Free slhc buffers in the right place.
+ *
*
*
* FIXME: This driver still makes some IP'ish assumptions. It should build cleanly KISS TNC only without
* CONFIG_INET defined.
+ * I hope now it is fixed ;)
*/
-
-#include <asm/segment.h>
-#include <asm/system.h>
+#define SL_CHECK_TRANSMIT
#include <linux/config.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+/* Undef this, if you don't need 6bit encapsulation code in the driver */
+#define CONFIG_SLIP_MODE_SLIP6
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
#include <linux/string.h>
#include <linux/mm.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
#include <linux/interrupt.h>
+#include <linux/in.h>
#include <linux/tty.h>
#include <linux/errno.h>
-#include <linux/stat.h>
-#include <linux/in.h>
-#include <linux/inet.h>
#include <linux/netdevice.h>
#ifdef CONFIG_AX25
-#include "ax25.h"
+#include <linux/timer.h>
+#include <net/ax25.h>
#endif
#include <linux/etherdevice.h>
-#ifdef CONFIG_INET
-#include "ip.h"
-#include "route.h"
-#include "protocol.h"
-#include "tcp.h"
-#endif
#include <linux/skbuff.h>
#include <linux/if_arp.h>
-#include "sock.h"
#include "slip.h"
#ifdef CONFIG_INET
+#include <linux/ip.h>
+#include <linux/tcp.h>
#include "slhc.h"
#endif
-#define SLIP_VERSION "0.7.5-NET3.014-NEWTTY"
+#ifdef MODULE
+#define SLIP_VERSION "0.8.3-NET3.019-NEWTTY-MODULAR"
+#else
+#define SLIP_VERSION "0.8.3-NET3.019-NEWTTY"
+#endif
static struct slip sl_ctrl[SL_NRUNIT];
static struct tty_ldisc sl_ldisc;
static int already = 0;
-/* Initialize a SLIP control block for use. */
-static void
-sl_initialize(struct slip *sl, struct device *dev)
-{
- sl->magic = SLIP_MAGIC;
- sl->inuse = 0;
- sl->sending = 0;
- sl->escape = 0;
- sl->flags = 0;
-#ifdef SL_ADAPTIVE
- sl->mode = SL_MODE_ADAPTIVE; /* automatic CSLIP recognition */
-#else
-#ifdef SL_COMPRESSED
- sl->mode = SL_MODE_CSLIP | SL_MODE_ADAPTIVE; /* Default */
-#else
- sl->mode = SL_MODE_SLIP; /* Default for non compressors */
+static int slip_esc(unsigned char *p, unsigned char *d, int len);
+static void slip_unesc(struct slip *sl, unsigned char c);
+#ifdef CONFIG_SLIP_MODE_SLIP6
+static int slip_esc6(unsigned char *p, unsigned char *d, int len);
+static void slip_unesc6(struct slip *sl, unsigned char c);
#endif
-#endif
-
- sl->line = dev->base_addr;
- sl->tty = NULL;
- sl->dev = dev;
- sl->slcomp = NULL;
-
- /* Clear all pointers. */
- sl->rbuff = NULL;
- sl->xbuff = NULL;
- sl->cbuff = NULL;
-
- sl->rhead = NULL;
- sl->rend = NULL;
- dev->rmem_end = (unsigned long) NULL;
- dev->rmem_start = (unsigned long) NULL;
- dev->mem_end = (unsigned long) NULL;
- dev->mem_start = (unsigned long) NULL;
- dev->type = ARPHRD_SLIP + sl->mode;
- if(dev->type == 260) /* KISS */
- dev->type=ARPHRD_AX25;
-}
+
/* Find a free SLIP channel, and link in this `tty' line. */
static inline struct slip *
sl_alloc(void)
{
- unsigned long flags;
- struct slip *sl;
- int i;
-
- save_flags (flags);
- cli();
- for (i = 0; i < SL_NRUNIT; i++) {
- sl = &sl_ctrl[i];
- if (sl->inuse == 0) {
- sl->inuse = 1;
- sl->tty = NULL;
- restore_flags(flags);
- return(sl);
+ struct slip *sl;
+ int i;
+
+ for (i = 0; i < SL_NRUNIT; i++) {
+ sl = &sl_ctrl[i];
+ if (!set_bit(SLF_INUSE, &sl->flags)) {
+ return sl;
+ }
}
- }
- restore_flags(flags);
- return(NULL);
+ return NULL;
}
@@ -143,121 +118,138 @@ sl_alloc(void)
static inline void
sl_free(struct slip *sl)
{
- unsigned long flags;
+ /* Free all SLIP frame buffers. */
+ if (sl->rbuff) {
+ kfree(sl->rbuff);
+ }
+ sl->rbuff = NULL;
+ if (sl->xbuff) {
+ kfree(sl->xbuff);
+ }
+ sl->xbuff = NULL;
+#ifdef SL_INCLUDE_CSLIP
+ /* Save CSLIP statistics */
+ if (sl->slcomp) {
+ sl->rx_compressed += sl->slcomp->sls_i_compressed;
+ sl->rx_dropped += sl->slcomp->sls_i_tossed;
+ sl->tx_compressed += sl->slcomp->sls_o_compressed;
+ sl->tx_misses += sl->slcomp->sls_o_misses;
+ }
+ if (sl->cbuff) {
+ kfree(sl->cbuff);
+ }
+ sl->cbuff = NULL;
+ if(sl->slcomp)
+ slhc_free(sl->slcomp);
+ sl->slcomp = NULL;
+#endif
- if (sl->inuse) {
- save_flags(flags);
- cli();
- sl->inuse = 0;
- sl->tty = NULL;
- restore_flags(flags);
- }
+ if (!clear_bit(SLF_INUSE, &sl->flags)) {
+ printk("%s: sl_free for already free unit.\n", sl->dev->name);
+ }
}
/* MTU has been changed by the IP layer. Unfortunately we are not told about this, but
we spot it ourselves and fix things up. We could be in an upcall from the tty
driver, or in an ip packet queue. */
-
+
static void sl_changedmtu(struct slip *sl)
{
- struct device *dev=sl->dev;
- unsigned char *tb,*rb,*cb,*tf,*rf,*cf;
- int l;
- int omtu=sl->mtu;
+ struct device *dev = sl->dev;
+ unsigned char *xbuff, *rbuff, *oxbuff, *orbuff;
+#ifdef SL_INCLUDE_CSLIP
+ unsigned char *cbuff, *ocbuff;
+#endif
+ int len;
+ unsigned long flags;
-#ifdef CONFIG_AX25
- sl->mtu=dev->mtu+73;
-#else
- sl->mtu=dev->mtu;
-#endif
- l=(dev->mtu *2);
+ len = dev->mtu * 2;
/*
* allow for arrival of larger UDP packets, even if we say not to
* also fixes a bug in which SunOS sends 512-byte packets even with
* an MSS of 128
*/
- if (l < (576 * 2))
- l = 576 * 2;
-
- tb= (unsigned char *) kmalloc(l + 4, GFP_ATOMIC);
- rb= (unsigned char *) kmalloc(l + 4, GFP_ATOMIC);
- cb= (unsigned char *) kmalloc(l + 4, GFP_ATOMIC);
-
- if(tb==NULL || rb==NULL || cb==NULL)
- {
- printk("Unable to grow slip buffers. MTU change cancelled.\n");
- sl->mtu=omtu;
- dev->mtu=omtu;
- if(tb!=NULL)
- kfree(tb);
- if(rb!=NULL)
- kfree(rb);
- if(cb!=NULL)
- kfree(cb);
+ if (len < 576 * 2) {
+ len = 576 * 2;
+ }
+
+ xbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
+ rbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
+#ifdef SL_INCLUDE_CSLIP
+ cbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
+#endif
+
+#ifdef SL_INCLUDE_CSLIP
+ if (xbuff == NULL || rbuff == NULL || cbuff == NULL) {
+#else
+ if (xbuff == NULL || rbuff == NULL) {
+#endif
+ printk("%s: unable to grow slip buffers, MTU change cancelled.\n",
+ sl->dev->name);
+ dev->mtu = sl->mtu;
+ if (xbuff != NULL) {
+ kfree(xbuff);
+ }
+ if (rbuff != NULL) {
+ kfree(rbuff);
+ }
+#ifdef SL_INCLUDE_CSLIP
+ if (cbuff != NULL) {
+ kfree(cbuff);
+ }
+#endif
return;
}
-
- cli();
-
- tf=(unsigned char *)sl->dev->mem_start;
- sl->dev->mem_start=(unsigned long)tb;
- sl->dev->mem_end=(unsigned long) (sl->dev->mem_start + l);
- rf=(unsigned char *)sl->dev->rmem_start;
- sl->dev->rmem_start=(unsigned long)rb;
- sl->dev->rmem_end=(unsigned long) (sl->dev->rmem_start + l);
-
- sl->xbuff = (unsigned char *) sl->dev->mem_start;
- sl->rbuff = (unsigned char *) sl->dev->rmem_start;
- sl->rend = (unsigned char *) sl->dev->rmem_end;
- sl->rhead = sl->rbuff;
-
- cf=sl->cbuff;
- sl->cbuff=cb;
-
- sl->escape=0;
- sl->sending=0;
- sl->rcount=0;
- sti();
-
- if(rf!=NULL)
- kfree(rf);
- if(tf!=NULL)
- kfree(tf);
- if(cf!=NULL)
- kfree(cf);
-}
+ save_flags(flags); cli();
+ oxbuff = sl->xbuff;
+ sl->xbuff = xbuff;
+ orbuff = sl->rbuff;
+ sl->rbuff = rbuff;
+#ifdef SL_INCLUDE_CSLIP
+ ocbuff = sl->cbuff;
+ sl->cbuff = cbuff;
+#endif
+ if (sl->xleft) {
+ if (sl->xleft <= len) {
+ memcpy(sl->xbuff, sl->xhead, sl->xleft);
+ } else {
+ sl->xleft = 0;
+ sl->tx_dropped++;
+ }
+ }
+ sl->xhead = sl->xbuff;
+
+ if (sl->rcount) {
+ if (sl->rcount <= len) {
+ memcpy(sl->rbuff, orbuff, sl->rcount);
+ } else {
+ sl->rcount = 0;
+ sl->rx_over_errors++;
+ set_bit(SLF_ERROR, &sl->flags);
+ }
+ }
+#ifdef CONFIG_AX25
+ sl->mtu = dev->mtu + 73;
+#else
+ sl->mtu = dev->mtu;
+#endif
+ sl->buffsize = len;
-/* Stuff one byte into a SLIP receiver buffer. */
-static inline void
-sl_enqueue(struct slip *sl, unsigned char c)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
- if (sl->rhead < sl->rend) {
- *sl->rhead = c;
- sl->rhead++;
- sl->rcount++;
- } else sl->roverrun++;
- restore_flags(flags);
-}
+ restore_flags(flags);
-/* Release 'i' bytes from a SLIP receiver buffer. */
-static inline void
-sl_dequeue(struct slip *sl, int i)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
- if (sl->rhead > sl->rbuff) {
- sl->rhead -= i;
- sl->rcount -= i;
- }
- restore_flags(flags);
+ if (oxbuff != NULL) {
+ kfree(oxbuff);
+ }
+ if (orbuff != NULL) {
+ kfree(orbuff);
+ }
+#ifdef SL_INCLUDE_CSLIP
+ if (ocbuff != NULL) {
+ kfree(ocbuff);
+ }
+#endif
}
@@ -265,13 +257,9 @@ sl_dequeue(struct slip *sl, int i)
static inline void
sl_lock(struct slip *sl)
{
- unsigned long flags;
-
- save_flags(flags);
- cli();
- sl->sending = 1;
- sl->dev->tbusy = 1;
- restore_flags(flags);
+ if (set_bit(0, (void *) &sl->dev->tbusy)) {
+ printk("%s: trying to lock already locked device!\n", sl->dev->name);
+ }
}
@@ -279,123 +267,121 @@ sl_lock(struct slip *sl)
static inline void
sl_unlock(struct slip *sl)
{
- unsigned long flags;
-
- save_flags(flags);
- cli();
- sl->sending = 0;
- sl->dev->tbusy = 0;
- restore_flags(flags);
+ if (!clear_bit(0, (void *)&sl->dev->tbusy)) {
+ printk("%s: trying to unlock already unlocked device!\n", sl->dev->name);
+ }
}
-
/* Send one completely decapsulated IP datagram to the IP layer. */
static void
sl_bump(struct slip *sl)
{
- int done;
- unsigned char c;
- unsigned long flags;
- int count;
-
- count = sl->rcount;
-#ifdef CONFIG_INET
- if (sl->mode & (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) {
- if ((c = sl->rbuff[0]) & SL_TYPE_COMPRESSED_TCP) {
-#if 1
- /* ignore compressed packets when CSLIP is off */
- if (!(sl->mode & SL_MODE_CSLIP)) {
- printk("SLIP: compressed packet ignored\n");
- return;
- }
-#endif
- /* make sure we've reserved enough space for uncompress to use */
- save_flags(flags);
- cli();
- if ((sl->rhead + 80) < sl->rend) {
- sl->rhead += 80;
- sl->rcount += 80;
- done = 1;
- } else {
- sl->roverrun++;
- done = 0;
- }
- restore_flags(flags);
- if (! done) /* not enough space available */
- return;
-
- count = slhc_uncompress(sl->slcomp, sl->rbuff, count);
- if (count <= 0) {
- sl->errors++;
- return;
- }
- } else if (c >= SL_TYPE_UNCOMPRESSED_TCP) {
- if (!(sl->mode & SL_MODE_CSLIP)) {
- /* turn on header compression */
- sl->mode |= SL_MODE_CSLIP;
- printk("SLIP: header compression turned on\n");
- }
- sl->rbuff[0] &= 0x4f;
- if (slhc_remember(sl->slcomp, sl->rbuff, count) <= 0) {
- sl->errors++;
- return;
- }
- }
- }
+ struct sk_buff *skb;
+ int count;
+
+ count = sl->rcount;
+#ifdef SL_INCLUDE_CSLIP
+ if (sl->mode & (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) {
+ unsigned char c;
+ if ((c = sl->rbuff[0]) & SL_TYPE_COMPRESSED_TCP) {
+ /* ignore compressed packets when CSLIP is off */
+ if (!(sl->mode & SL_MODE_CSLIP)) {
+ printk("%s: compressed packet ignored\n", sl->dev->name);
+ return;
+ }
+ /* make sure we've reserved enough space for uncompress to use */
+ if (count + 80 > sl->buffsize) {
+ sl->rx_over_errors++;
+ return;
+ }
+ count = slhc_uncompress(sl->slcomp, sl->rbuff, count);
+ if (count <= 0) {
+ return;
+ }
+ } else if (c >= SL_TYPE_UNCOMPRESSED_TCP) {
+ if (!(sl->mode & SL_MODE_CSLIP)) {
+ /* turn on header compression */
+ sl->mode |= SL_MODE_CSLIP;
+ sl->mode &= ~SL_MODE_ADAPTIVE;
+ printk("%s: header compression turned on\n", sl->dev->name);
+ }
+ sl->rbuff[0] &= 0x4f;
+ if (slhc_remember(sl->slcomp, sl->rbuff, count) <= 0) {
+ return;
+ }
+ }
+ }
+#endif /* SL_INCLUDE_CSLIP */
-#endif
- /* Bump the datagram to the upper layers... */
- do {
- /* clh_dump(sl->rbuff, count); */
- done = dev_rint(sl->rbuff, count, 0, sl->dev);
- if (done == 0 || done == 1) break;
- } while(1);
-
- sl->rpacket++;
+ skb = alloc_skb(count, GFP_ATOMIC);
+ if (skb == NULL) {
+ printk("%s: memory squeeze, dropping packet.\n", sl->dev->name);
+ sl->rx_dropped++;
+ return;
+ }
+ skb->len = count;
+ skb->dev = sl->dev;
+ memcpy(skb->data, sl->rbuff, count);
+ if(sl->mode&(SL_MODE_AX25|SL_MODE_AX25VC))
+ skb->protocol=htons(ETH_P_AX25);
+ else
+ skb->protocol=htons(ETH_P_IP);
+ netif_rx(skb);
+ sl->rx_packets++;
}
/* Encapsulate one IP datagram and stuff into a TTY queue. */
static void
sl_encaps(struct slip *sl, unsigned char *icp, int len)
{
- unsigned char *p;
- int actual, count;
+ unsigned char *p;
+ int actual, count;
+
-
#ifdef CONFIG_AX25
- if(sl->mtu != sl->dev->mtu+73)/* Someone has been ifconfigging */
+ if (sl->mtu != sl->dev->mtu + 73) { /* Someone has been ifconfigging */
#else
- if(sl->mtu != sl->dev->mtu) /* Someone has been ifconfigging */
+ if (sl->mtu != sl->dev->mtu) { /* Someone has been ifconfigging */
+#endif
+ sl_changedmtu(sl);
+ }
+
+ if (len > sl->mtu) { /* Sigh, shouldn't occur BUT ... */
+ len = sl->mtu;
+ printk ("%s: truncating oversized transmit packet!\n", sl->dev->name);
+ sl->tx_dropped++;
+ sl_unlock(sl);
+ return;
+ }
+
+ p = icp;
+#ifdef SL_INCLUDE_CSLIP
+ if (sl->mode & SL_MODE_CSLIP) {
+ len = slhc_compress(sl->slcomp, p, len, sl->cbuff, &p, 1);
+ }
#endif
- sl_changedmtu(sl);
-
- if(len>sl->mtu) /* Sigh, shouldn't occur BUT ... */
- {
- len=sl->mtu;
- printk("slip: truncating oversized transmit packet!\n");
- }
-
- p = icp;
-#ifdef CONFIG_INET
- if(sl->mode & SL_MODE_CSLIP)
- len = slhc_compress(sl->slcomp, p, len, sl->cbuff, &p, 1);
+#ifdef CONFIG_SLIP_MODE_SLIP6
+ if(sl->mode & SL_MODE_SLIP6)
+ count = slip_esc6(p, (unsigned char *) sl->xbuff, len);
+ else
#endif
- if(sl->mode & SL_MODE_SLIP6)
- count=slip_esc6(p, (unsigned char *)sl->xbuff,len);
- else
- count=slip_esc(p, (unsigned char *)sl->xbuff,len);
- sl->spacket++;
-
- /* Tell TTY to send it on its way. */
- actual = sl->tty->driver.write(sl->tty, 0, sl->xbuff, count);
- if (actual == count) {
- sl_unlock(sl);
- mark_bh(NET_BH);
- } else {
- sl->xhead = sl->xbuff + count;
- sl->xtail = sl->xbuff + actual;
- sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
- }
+ count = slip_esc(p, (unsigned char *) sl->xbuff, len);
+
+ /* Order of next two lines is *very* important.
+ * When we are sending a little amount of data,
+ * the transfer may be completed inside driver.write()
+ * routine, because it's running with interrupts enabled.
+ * In this case we *never* got WRITE_WAKEUP event,
+ * if we did not request it before write operation.
+ * 14 Oct 1994 Dmitry Gorodchanin.
+ */
+ sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+ actual = sl->tty->driver.write(sl->tty, 0, sl->xbuff, count);
+#ifdef SL_CHECK_TRANSMIT
+ sl->dev->trans_start = jiffies;
+#endif
+ sl->xleft = count - actual;
+ sl->xhead = sl->xbuff + actual;
}
/*
@@ -404,103 +390,107 @@ sl_encaps(struct slip *sl, unsigned char *icp, int len)
*/
static void slip_write_wakeup(struct tty_struct *tty)
{
- register int count, answer;
+ int actual;
struct slip *sl = (struct slip *) tty->disc_data;
/* First make sure we're connected. */
- if (!sl || sl->magic != SLIP_MAGIC) {
+ if (!sl || sl->magic != SLIP_MAGIC || !sl->dev->start) {
return;
}
- if (!sl->xtail)
- return;
-
- cli();
- if (sl->flags & SLF_XMIT_BUSY) {
- sti();
- return;
- }
- sl->flags |= SLF_XMIT_BUSY;
- sti();
-
- count = sl->xhead - sl->xtail;
-
- answer = tty->driver.write(tty, 0, sl->xtail, count);
- if (answer == count) {
- sl->xtail = 0;
- tty->flags &= ~TTY_DO_WRITE_WAKEUP;
-
+ if (sl->xleft <= 0) {
+ /* Now serial buffer is almost free & we can start
+ * transmission of another packet */
+ sl->tx_packets++;
+ tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
sl_unlock(sl);
mark_bh(NET_BH);
- } else {
- sl->xtail += answer;
+ return;
}
- sl->flags &= ~SLF_XMIT_BUSY;
-}
-/*static void sl_hex_dump(unsigned char *x,int l)
-{
- int n=0;
- printk("sl_xmit: (%d bytes)\n",l);
- while(l)
- {
- printk("%2X ",(int)*x++);
- l--;
- n++;
- if(n%32==0)
- printk("\n");
- }
- if(n%32)
- printk("\n");
-}*/
+ actual = tty->driver.write(tty, 0, sl->xhead, sl->xleft);
+ sl->xleft -= actual;
+ sl->xhead += actual;
+}
/* Encapsulate an IP datagram and kick it into a TTY queue. */
static int
sl_xmit(struct sk_buff *skb, struct device *dev)
{
- struct tty_struct *tty;
- struct slip *sl;
- int size;
-
- /* Find the correct SLIP channel to use. */
- sl = &sl_ctrl[dev->base_addr];
- tty = sl->tty;
-
- /*
- * If we are busy already- too bad. We ought to be able
- * to queue things at this point, to allow for a little
- * frame buffer. Oh well...
- */
- if (sl->sending) {
- sl->sbusy++;
- return(1);
- }
-
- /* We were not, so we are now... :-) */
- if (skb != NULL) {
- sl_lock(sl);
-
- size=skb->len;
- sl_encaps(sl, skb->data, size);
- dev_kfree_skb(skb, FREE_WRITE);
- }
- return(0);
-}
+ struct slip *sl = &sl_ctrl[dev->base_addr];
+ if (!dev->start) {
+ printk("%s: xmit call when iface is down\n", dev->name);
+ return 1;
+ }
+ /*
+ * If we are busy already- too bad. We ought to be able
+ * to queue things at this point, to allow for a little
+ * frame buffer. Oh well...
+ * -----------------------------------------------------
+ * I hate queues in SLIP driver. May be it's efficient,
+ * but for me latency is more important. ;)
+ * So, no queues !
+ * 14 Oct 1994 Dmitry Gorodchanin.
+ */
+ if (dev->tbusy) {
+ /* May be we must check transmitter timeout here ?
+ * 14 Oct 1994 Dmitry Gorodchanin.
+ */
+#ifdef SL_CHECK_TRANSMIT
+ if (jiffies - dev->trans_start < 20 * HZ) {
+ /* 20 sec timeout not reached */
+ return 1;
+ }
+ printk("%s: transmit timed out, %s?\n", dev->name,
+ (sl->tty->driver.chars_in_buffer(sl->tty) || sl->xleft) ?
+ "bad line quality" : "driver error");
+ sl->xleft = 0;
+ sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ sl_unlock(sl);
+#else
+ return 1;
+#endif
+ }
-/* Return the frame type ID. This is normally IP but maybe be AX.25. */
-static unsigned short
-sl_type_trans (struct sk_buff *skb, struct device *dev)
-{
#ifdef CONFIG_AX25
- struct slip *sl=&sl_ctrl[dev->base_addr];
- if(sl->mode&SL_MODE_AX25)
- return htons(ETH_P_AX25);
+#ifdef CONFIG_INET
+ /*
+ * This code is not very intelligent. Fix me.
+ */
+ if (skb->data[15] == LAPB_UI && skb->data[16] == AX25_P_IP) {
+ struct sk_buff *skbn;
+ char mode;
+
+ mode = ax25_ip_mode_get((ax25_address *)(skb->data + 1), dev);
+
+ if (mode == 'V' || mode == 'v' || (mode == ' ' && sl->mode & SL_MODE_AX25VC)) {
+ if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+ sl->tx_errors++;
+ return 1;
+ }
+
+ ax25_send_frame(skbn, (ax25_address *)dev->dev_addr, (ax25_address *)(skbn->data + 1), dev);
+ dev_kfree_skb(skb, FREE_WRITE);
+ mark_bh(NET_BH);
+ return 0;
+ }
+ }
#endif
- return htons(ETH_P_IP);
+#endif
+
+ /* We were not busy, so we are now... :-) */
+ if (skb != NULL) {
+ sl_lock(sl);
+ sl_encaps(sl, skb->data, skb->len);
+ dev_kfree_skb(skb, FREE_WRITE);
+ }
+ return 0;
}
+/* Return the frame type ID. This is normally IP but maybe be AX.25. */
+
/* Fill in the MAC-level header. Not used by SLIP. */
static int
sl_header(unsigned char *buff, struct device *dev, unsigned short type,
@@ -508,30 +498,32 @@ sl_header(unsigned char *buff, struct device *dev, unsigned short type,
{
#ifdef CONFIG_AX25
#ifdef CONFIG_INET
- struct slip *sl=&sl_ctrl[dev->base_addr];
- if((sl->mode&SL_MODE_AX25) && type!=htons(ETH_P_AX25))
- return ax25_encapsulate(buff,dev,type,daddr,saddr,len,skb);
-#endif
-#endif
+ struct slip *sl = &sl_ctrl[dev->base_addr];
- return(0);
+ if (((sl->mode & SL_MODE_AX25) || (sl->mode & SL_MODE_AX25VC)) && type != htons(ETH_P_AX25)) {
+ return ax25_encapsulate(buff, dev, type, daddr, saddr, len, skb);
+ }
+#endif
+#endif
+ return 0;
}
/* Rebuild the MAC-level header. Not used by SLIP. */
static int
sl_rebuild_header(void *buff, struct device *dev, unsigned long raddr,
- struct sk_buff *skb)
+ struct sk_buff *skb)
{
#ifdef CONFIG_AX25
#ifdef CONFIG_INET
- struct slip *sl=&sl_ctrl[dev->base_addr];
-
- if(sl->mode&SL_MODE_AX25)
- return ax25_rebuild_header(buff,dev,raddr, skb);
-#endif
-#endif
- return(0);
+ struct slip *sl = &sl_ctrl[dev->base_addr];
+
+ if ((sl->mode & SL_MODE_AX25) || (sl->mode & SL_MODE_AX25VC)) {
+ return ax25_rebuild_header(buff, dev, raddr, skb);
+ }
+#endif
+#endif
+ return 0;
}
@@ -539,84 +531,83 @@ sl_rebuild_header(void *buff, struct device *dev, unsigned long raddr,
static int
sl_open(struct device *dev)
{
- struct slip *sl;
- unsigned char *p;
- unsigned long l;
-
- sl = &sl_ctrl[dev->base_addr];
- if (sl->tty == NULL) {
- return(-ENXIO);
- }
- sl->dev = dev;
-
- /*
- * Allocate the SLIP frame buffers:
- *
- * mem_end Top of frame buffers
- * mem_start Start of frame buffers
- * rmem_end Top of RECV frame buffer
- * rmem_start Start of RECV frame buffer
- */
- l = (dev->mtu * 2);
-/*
- * allow for arrival of larger UDP packets, even if we say not to
- * also fixes a bug in which SunOS sends 512-byte packets even with
- * an MSS of 128
- */
- if (l < (576 * 2))
- l = 576 * 2;
+ struct slip *sl = &sl_ctrl[dev->base_addr];
+ unsigned long len;
+
+ if (sl->tty == NULL) {
+ return -ENODEV;
+ }
- p = (unsigned char *) kmalloc(l + 4, GFP_KERNEL);
- if (p == NULL) {
- return(-ENOMEM);
- }
+ /*
+ * Allocate the SLIP frame buffers:
+ *
+ * rbuff Receive buffer.
+ * xbuff Transmit buffer.
+ * cbuff Temporary compression buffer.
+ */
+ len = dev->mtu * 2;
+ /*
+ * allow for arrival of larger UDP packets, even if we say not to
+ * also fixes a bug in which SunOS sends 512-byte packets even with
+ * an MSS of 128
+ */
+ if (len < 576 * 2) {
+ len = 576 * 2;
+ }
+ sl->rbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
+ if (sl->rbuff == NULL) {
+ goto norbuff;
+ }
+ sl->xbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
+ if (sl->xbuff == NULL) {
+ goto noxbuff;
+ }
+#ifdef SL_INCLUDE_CSLIP
+ sl->cbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
+ if (sl->cbuff == NULL) {
+ goto nocbuff;
+ }
+ sl->slcomp = slhc_init(16, 16);
+ if (sl->slcomp == NULL) {
+ goto noslcomp;
+ }
+#endif
#ifdef CONFIG_AX25
- sl->mtu = dev->mtu+73;
-#else
- sl->mtu = dev->mtu;
-#endif
- sl->dev->mem_start = (unsigned long) p;
- sl->dev->mem_end = (unsigned long) (sl->dev->mem_start + l);
-
- p = (unsigned char *) kmalloc(l + 4, GFP_KERNEL);
- if (p == NULL) {
- kfree((unsigned char *)sl->dev->mem_start);
- return(-ENOMEM);
- }
- sl->dev->rmem_start = (unsigned long) p;
- sl->dev->rmem_end = (unsigned long) (sl->dev->rmem_start + l);
-
- sl->xbuff = (unsigned char *) sl->dev->mem_start;
- sl->rbuff = (unsigned char *) sl->dev->rmem_start;
- sl->rend = (unsigned char *) sl->dev->rmem_end;
- sl->rhead = sl->rbuff;
-
- sl->escape = 0;
- sl->sending = 0;
- sl->rcount = 0;
-
- p = (unsigned char *) kmalloc(l + 4, GFP_KERNEL);
- if (p == NULL) {
- kfree((unsigned char *)sl->dev->mem_start);
- kfree((unsigned char *)sl->dev->rmem_start);
- return(-ENOMEM);
- }
- sl->cbuff = p;
-#ifdef CONFIG_INET
- sl->slcomp = slhc_init(16, 16);
- if (sl->slcomp == NULL) {
- kfree((unsigned char *)sl->dev->mem_start);
- kfree((unsigned char *)sl->dev->rmem_start);
- kfree((unsigned char *)sl->cbuff);
- return(-ENOMEM);
- }
+ sl->mtu = dev->mtu + 73;
+#else
+ sl->mtu = dev->mtu;
+#endif
+ sl->buffsize = len;
+ sl->rcount = 0;
+ sl->xleft = 0;
+#ifdef CONFIG_SLIP_MODE_SLIP6
+ sl->xdata = 0;
+ sl->xbits = 0;
+#endif
+ sl->flags &= (1 << SLF_INUSE); /* Clear ESCAPE & ERROR flags */
+
+ /* Needed because address '0' is special */
+ if (dev->pa_addr == 0) {
+ dev->pa_addr=ntohl(0xC0A80001);
+ }
+ dev->tbusy = 0;
+/* dev->flags |= IFF_UP; */
+ dev->start = 1;
+
+ return 0;
+
+ /* Cleanup */
+#ifdef SL_INCLUDE_CSLIP
+noslcomp:
+ kfree(sl->cbuff);
+nocbuff:
#endif
- dev->flags|=IFF_UP;
- /* Needed because address '0' is special */
- if(dev->pa_addr==0)
- dev->pa_addr=ntohl(0xC0A80001);
- return(0);
+ kfree(sl->xbuff);
+noxbuff:
+ kfree(sl->rbuff);
+norbuff:
+ return -ENOMEM;
}
@@ -624,28 +615,22 @@ sl_open(struct device *dev)
static int
sl_close(struct device *dev)
{
- struct slip *sl;
-
- sl = &sl_ctrl[dev->base_addr];
- if (sl->tty == NULL) {
- return(-EBUSY);
- }
- sl->tty->disc_data = 0;
- sl_free(sl);
-
- /* Free all SLIP frame buffers. */
- kfree(sl->rbuff);
- kfree(sl->xbuff);
- kfree(sl->cbuff);
-#ifdef CONFIG_INET
- slhc_free(sl->slcomp);
-#endif
- sl_initialize(sl, dev);
+ struct slip *sl = &sl_ctrl[dev->base_addr];
+
+ if (sl->tty == NULL) {
+ return -EBUSY;
+ }
+ sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ dev->tbusy = 1;
+ dev->start = 0;
+
+/* dev->flags &= ~IFF_UP; */
- return(0);
+ return 0;
}
-static int slip_receive_room(struct tty_struct *tty)
+static int
+slip_receive_room(struct tty_struct *tty)
{
return 65536; /* We can handle an infinite amount of data. :-) */
}
@@ -656,41 +641,46 @@ static int slip_receive_room(struct tty_struct *tty)
* a block of SLIP data has been received, which can now be decapsulated
* and sent on to some IP layer for further processing.
*/
-static void slip_receive_buf(struct tty_struct *tty, unsigned char *cp,
- char *fp, int count)
+static void
+slip_receive_buf(struct tty_struct *tty, unsigned char *cp, char *fp, int count)
{
struct slip *sl = (struct slip *) tty->disc_data;
-
- if (!sl || sl->magic != SLIP_MAGIC)
+
+ if (!sl || sl->magic != SLIP_MAGIC || !sl->dev->start)
return;
-
+
/*
* Argh! mtu change time! - costs us the packet part received
* at the change
*/
#ifdef CONFIG_AX25
- if(sl->mtu!=sl->dev->mtu+73)
-#else
- if(sl->mtu!=sl->dev->mtu)
-#endif
+ if (sl->mtu != sl->dev->mtu + 73) {
+#else
+ if (sl->mtu != sl->dev->mtu) {
+#endif
sl_changedmtu(sl);
-
+ }
+
/* Read the characters out of the buffer */
while (count--) {
if (fp && *fp++) {
- sl->flags |= SLF_ERROR;
+ if (!set_bit(SLF_ERROR, &sl->flags)) {
+ sl->rx_errors++;
+ }
cp++;
continue;
}
+#ifdef CONFIG_SLIP_MODE_SLIP6
if (sl->mode & SL_MODE_SLIP6)
- slip_unesc6(sl,*cp++);
+ slip_unesc6(sl, *cp++);
else
- slip_unesc(sl,*cp++);
+#endif
+ slip_unesc(sl, *cp++);
}
}
/*
- * Open the high-level part of the SLIP channel.
+ * Open the high-level part of the SLIP channel.
* This function is called by the TTY module when the
* SLIP line discipline is called for. Because we are
* sure the tty line exists, we only have to link it to
@@ -699,63 +689,50 @@ static void slip_receive_buf(struct tty_struct *tty, unsigned char *cp,
static int
slip_open(struct tty_struct *tty)
{
- struct slip *sl = (struct slip *) tty->disc_data;
-
- /* First make sure we're not already connected. */
- if (sl && sl->magic == SLIP_MAGIC) {
- return(-EEXIST);
- }
-
- /* OK. Find a free SLIP channel to use. */
- if ((sl = sl_alloc()) == NULL) {
- return(-ENFILE);
- }
- sl->tty = tty;
- tty->disc_data = sl;
- if (tty->driver.flush_buffer)
- tty->driver.flush_buffer(tty);
- if (tty->ldisc.flush_buffer)
- tty->ldisc.flush_buffer(tty);
-
- /* Perform the low-level SLIP initialization. */
- (void) sl_open(sl->dev);
-
- /* Done. We have linked the TTY line to a channel. */
- return(sl->line);
-}
+ struct slip *sl = (struct slip *) tty->disc_data;
+ int err;
-
-static struct enet_statistics *
-sl_get_stats(struct device *dev)
-{
- static struct enet_statistics stats;
- struct slip *sl;
- struct slcompress *comp;
-
- /* Find the correct SLIP channel to use. */
- sl = &sl_ctrl[dev->base_addr];
- if (! sl)
- return NULL;
-
- memset(&stats, 0, sizeof(struct enet_statistics));
-
- stats.rx_packets = sl->rpacket;
- stats.rx_over_errors = sl->roverrun;
- stats.tx_packets = sl->spacket;
- stats.tx_dropped = sl->sbusy;
- stats.rx_errors = sl->errors;
-#ifdef CONFIG_INET
- comp = sl->slcomp;
- if (comp) {
- stats.rx_fifo_errors = comp->sls_i_compressed;
- stats.rx_dropped = comp->sls_i_tossed;
- stats.tx_fifo_errors = comp->sls_o_compressed;
- stats.collisions = comp->sls_o_misses;
- }
+ /* First make sure we're not already connected. */
+ if (sl && sl->magic == SLIP_MAGIC) {
+ return -EEXIST;
+ }
+
+ /* OK. Find a free SLIP channel to use. */
+ if ((sl = sl_alloc()) == NULL) {
+ return -ENFILE;
+ }
+
+ sl->tty = tty;
+ tty->disc_data = sl;
+ if (tty->driver.flush_buffer) {
+ tty->driver.flush_buffer(tty);
+ }
+ if (tty->ldisc.flush_buffer) {
+ tty->ldisc.flush_buffer(tty);
+ }
+
+ /* Restore default settings */
+ sl->mode = SL_MODE_DEFAULT;
+ sl->dev->type = ARPHRD_SLIP + sl->mode;
+#ifdef CONFIG_AX25
+ if (sl->dev->type == 260 || sl->dev->type == 272) { /* KISS */
+ sl->dev->type = ARPHRD_AX25;
+ }
+#endif
+ /* Perform the low-level SLIP initialization. */
+ if ((err = sl_open(sl->dev))) {
+ return err;
+ }
+
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
#endif
- return (&stats);
+
+ /* Done. We have linked the TTY line to a channel. */
+ return sl->dev->base_addr;
}
+
/*
* Close down a SLIP channel.
* This means flushing out any pending queues, and then restoring the
@@ -765,334 +742,488 @@ sl_get_stats(struct device *dev)
static void
slip_close(struct tty_struct *tty)
{
- struct slip *sl = (struct slip *) tty->disc_data;
+ struct slip *sl = (struct slip *) tty->disc_data;
+
+ /* First make sure we're connected. */
+ if (!sl || sl->magic != SLIP_MAGIC) {
+ return;
+ }
+
+ (void) dev_close(sl->dev);
+
+ tty->disc_data = 0;
+ sl->tty = NULL;
+ sl_free(sl);
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+}
- /* First make sure we're connected. */
- if (!sl || sl->magic != SLIP_MAGIC) {
- return;
- }
- (void) dev_close(sl->dev);
+static struct enet_statistics *
+sl_get_stats(struct device *dev)
+{
+ static struct enet_statistics stats;
+ struct slip *sl = &sl_ctrl[dev->base_addr];
+#ifdef SL_INCLUDE_CSLIP
+ struct slcompress *comp;
+#endif
+
+ memset(&stats, 0, sizeof(struct enet_statistics));
+
+ stats.rx_packets = sl->rx_packets;
+ stats.tx_packets = sl->tx_packets;
+ stats.rx_dropped = sl->rx_dropped;
+ stats.tx_dropped = sl->tx_dropped;
+ stats.tx_errors = sl->tx_errors;
+ stats.rx_errors = sl->rx_errors;
+ stats.rx_over_errors = sl->rx_over_errors;
+#ifdef SL_INCLUDE_CSLIP
+ stats.rx_fifo_errors = sl->rx_compressed;
+ stats.tx_fifo_errors = sl->tx_compressed;
+ stats.collisions = sl->tx_misses;
+ comp = sl->slcomp;
+ if (comp) {
+ stats.rx_fifo_errors += comp->sls_i_compressed;
+ stats.rx_dropped += comp->sls_i_tossed;
+ stats.tx_fifo_errors += comp->sls_o_compressed;
+ stats.collisions += comp->sls_o_misses;
+ }
+#endif /* CONFIG_INET */
+ return (&stats);
}
/************************************************************************
- * STANDARD SLIP ENCAPSULATION *
- ************************************************************************
- *
- */
-
- int
- slip_esc(unsigned char *s, unsigned char *d, int len)
- {
- int count = 0;
-
- /*
- * Send an initial END character to flush out any
- * data that may have accumulated in the receiver
- * due to line noise.
- */
-
- d[count++] = END;
-
- /*
- * For each byte in the packet, send the appropriate
- * character sequence, according to the SLIP protocol.
- */
-
- while(len-- > 0) {
- switch(*s) {
- case END:
- d[count++] = ESC;
- d[count++] = ESC_END;
- break;
- case ESC:
- d[count++] = ESC;
- d[count++] = ESC_ESC;
- break;
- default:
- d[count++] = *s;
- }
- ++s;
- }
- d[count++] = END;
- return(count);
- }
-
- void
- slip_unesc(struct slip *sl, unsigned char s)
- {
- switch(s) {
- case ESC:
- sl->flags |= SLF_ESCAPE;
- break;
- case ESC_ESC:
- if (sl->flags & SLF_ESCAPE)
- sl_enqueue(sl, ESC);
- else
- sl_enqueue(sl, s);
- sl->flags &= ~SLF_ESCAPE;
- break;
- case ESC_END:
- if (sl->flags & SLF_ESCAPE)
- sl_enqueue(sl, END);
- else
- sl_enqueue(sl, s);
- sl->flags &= ~SLF_ESCAPE;
- break;
- case END:
- if (sl->rcount > 2)
- sl_bump(sl);
- sl_dequeue(sl, sl->rcount);
- sl->rcount = 0;
- sl->flags &= ~(SLF_ESCAPE | SLF_ERROR);
- break;
- default:
- sl_enqueue(sl, s);
- sl->flags &= ~SLF_ESCAPE;
- }
- }
-
-
- /************************************************************************
- * 6 BIT SLIP ENCAPSULATION *
- ************************************************************************
- *
- */
-
- int
- slip_esc6(unsigned char *s, unsigned char *d, int len)
- {
- int count = 0;
- int i;
- unsigned short v = 0;
- short bits = 0;
-
- /*
- * Send an initial END character to flush out any
- * data that may have accumulated in the receiver
- * due to line noise.
- */
-
- d[count++] = 0x70;
-
- /*
- * Encode the packet into printable ascii characters
- */
-
- for (i = 0; i < len; ++i) {
- v = (v << 8) | s[i];
- bits += 8;
- while (bits >= 6) {
- unsigned char c;
-
- bits -= 6;
- c = 0x30 + ((v >> bits) & 0x3F);
- d[count++] = c;
- }
- }
- if (bits) {
- unsigned char c;
-
- c = 0x30 + ((v << (6 - bits)) & 0x3F);
- d[count++] = c;
- }
- d[count++] = 0x70;
- return(count);
- }
-
- void
- slip_unesc6(struct slip *sl, unsigned char s)
- {
- unsigned char c;
-
- if (s == 0x70) {
- if (sl->rcount > 8) { /* XXX must be 2 for compressed slip */
- #ifdef NOTDEF
- printk("rbuff %02x %02x %02x %02x\n",
- sl->rbuff[0],
- sl->rbuff[1],
- sl->rbuff[2],
- sl->rbuff[3]
- );
- #endif
- sl_bump(sl);
- }
- sl_dequeue(sl, sl->rcount);
- sl->rcount = 0;
- sl->flags &= ~(SLF_ESCAPE | SLF_ERROR); /* SLF_ESCAPE not used */
- sl->xbits = 0;
+ * STANDARD SLIP ENCAPSULATION *
+ ************************************************************************/
+
+int
+slip_esc(unsigned char *s, unsigned char *d, int len)
+{
+ unsigned char *ptr = d;
+ unsigned char c;
+
+ /*
+ * Send an initial END character to flush out any
+ * data that may have accumulated in the receiver
+ * due to line noise.
+ */
+
+ *ptr++ = END;
+
+ /*
+ * For each byte in the packet, send the appropriate
+ * character sequence, according to the SLIP protocol.
+ */
+
+ while (len-- > 0) {
+ switch(c = *s++) {
+ case END:
+ *ptr++ = ESC;
+ *ptr++ = ESC_END;
+ break;
+ case ESC:
+ *ptr++ = ESC;
+ *ptr++ = ESC_ESC;
+ break;
+ default:
+ *ptr++ = c;
+ break;
+ }
+ }
+ *ptr++ = END;
+ return (ptr - d);
+}
+
+static void
+slip_unesc(struct slip *sl, unsigned char s)
+{
+
+ switch(s) {
+ case END:
+ if (!clear_bit(SLF_ERROR, &sl->flags) && (sl->rcount > 2)) {
+ sl_bump(sl);
+ }
+ clear_bit(SLF_ESCAPE, &sl->flags);
+ sl->rcount = 0;
+ return;
+
+ case ESC:
+ set_bit(SLF_ESCAPE, &sl->flags);
+ return;
+ case ESC_ESC:
+ if (clear_bit(SLF_ESCAPE, &sl->flags)) {
+ s = ESC;
+ }
+ break;
+ case ESC_END:
+ if (clear_bit(SLF_ESCAPE, &sl->flags)) {
+ s = END;
+ }
+ break;
+ }
+ if (!test_bit(SLF_ERROR, &sl->flags)) {
+ if (sl->rcount < sl->buffsize) {
+ sl->rbuff[sl->rcount++] = s;
+ return;
+ }
+ sl->rx_over_errors++;
+ set_bit(SLF_ERROR, &sl->flags);
+ }
+}
+
+
+#ifdef CONFIG_SLIP_MODE_SLIP6
+/************************************************************************
+ * 6 BIT SLIP ENCAPSULATION *
+ ************************************************************************/
+
+int
+slip_esc6(unsigned char *s, unsigned char *d, int len)
+{
+ unsigned char *ptr = d;
+ unsigned char c;
+ int i;
+ unsigned short v = 0;
+ short bits = 0;
+
+ /*
+ * Send an initial END character to flush out any
+ * data that may have accumulated in the receiver
+ * due to line noise.
+ */
+
+ *ptr++ = 0x70;
+
+ /*
+ * Encode the packet into printable ascii characters
+ */
+
+ for (i = 0; i < len; ++i) {
+ v = (v << 8) | s[i];
+ bits += 8;
+ while (bits >= 6) {
+ bits -= 6;
+ c = 0x30 + ((v >> bits) & 0x3F);
+ *ptr++ = c;
+ }
+ }
+ if (bits) {
+ c = 0x30 + ((v << (6 - bits)) & 0x3F);
+ *ptr++ = c;
+ }
+ *ptr++ = 0x70;
+ return ptr - d;
+}
+
+void
+slip_unesc6(struct slip *sl, unsigned char s)
+{
+ unsigned char c;
+
+ if (s == 0x70) {
+ if (!clear_bit(SLF_ERROR, &sl->flags) && (sl->rcount > 2)) {
+ sl_bump(sl);
+ }
+ sl->rcount = 0;
+ sl->xbits = 0;
+ sl->xdata = 0;
} else if (s >= 0x30 && s < 0x70) {
sl->xdata = (sl->xdata << 6) | ((s - 0x30) & 0x3F);
sl->xbits += 6;
if (sl->xbits >= 8) {
sl->xbits -= 8;
c = (unsigned char)(sl->xdata >> sl->xbits);
- sl_enqueue(sl, c);
+ if (!test_bit(SLF_ERROR, &sl->flags)) {
+ if (sl->rcount < sl->buffsize) {
+ sl->rbuff[sl->rcount++] = c;
+ return;
+ }
+ sl->rx_over_errors++;
+ set_bit(SLF_ERROR, &sl->flags);
+ }
}
}
- }
-
-
+}
+#endif /* CONFIG_SLIP_MODE_SLIP6 */
#ifdef CONFIG_AX25
-
-int sl_set_mac_address(struct device *dev, void *addr)
+int
+sl_set_mac_address(struct device *dev, void *addr)
{
- int err=verify_area(VERIFY_READ,addr,7);
- if(err)
+ int err;
+
+ err = verify_area(VERIFY_READ, addr, 7);
+ if (err) {
return err;
- memcpy_fromfs(dev->dev_addr,addr,7); /* addr is an AX.25 shifted ASCII mac address */
+ }
+
+ memcpy_fromfs(dev->dev_addr, addr, 7); /* addr is an AX.25 shifted ASCII mac address */
+
return 0;
}
-static int sl_set_dev_mac_address(struct device *dev, void *addr)
+static int
+sl_set_dev_mac_address(struct device *dev, void *addr)
{
- memcpy(dev->dev_addr,addr,7);
+ memcpy(dev->dev_addr, addr, 7);
return 0;
}
-#endif
+#endif /* CONFIG_AX25 */
/* Perform I/O control on an active SLIP channel. */
static int
slip_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
{
- struct slip *sl = (struct slip *) tty->disc_data;
- int err;
-
- /* First make sure we're connected. */
- if (!sl || sl->magic != SLIP_MAGIC) {
- return(-EINVAL);
- }
-
- switch(cmd) {
- case SIOCGIFNAME:
- err=verify_area(VERIFY_WRITE, arg, 16);
- if(err)
+ struct slip *sl = (struct slip *) tty->disc_data;
+ int err;
+ unsigned int tmp;
+
+ /* First make sure we're connected. */
+ if (!sl || sl->magic != SLIP_MAGIC) {
+ return -EINVAL;
+ }
+
+ switch(cmd) {
+ case SIOCGIFNAME:
+ err = verify_area(VERIFY_WRITE, arg, 16);
+ if (err) {
return -err;
+ }
memcpy_tofs(arg, sl->dev->name, strlen(sl->dev->name) + 1);
- return(0);
+ return 0;
+
case SIOCGIFENCAP:
- err=verify_area(VERIFY_WRITE,arg,sizeof(long));
- put_fs_long(sl->mode,(long *)arg);
- return(0);
+ err = verify_area(VERIFY_WRITE, arg, sizeof(long));
+ if (err) {
+ return -err;
+ }
+ put_fs_long(sl->mode, (long *)arg);
+ return 0;
+
case SIOCSIFENCAP:
- err=verify_area(VERIFY_READ,arg,sizeof(long));
- sl->mode=get_fs_long((long *)arg);
-#ifdef CONFIG_AX25
- if(sl->mode & SL_MODE_AX25)
- {
+ err = verify_area(VERIFY_READ, arg, sizeof(long));
+ if (err) {
+ return -err;
+ }
+ tmp = get_fs_long((long *)arg);
+#ifndef SL_INCLUDE_CSLIP
+ if (tmp & (SL_MODE_CSLIP|SL_MODE_ADAPTIVE)) {
+ return -EINVAL;
+ }
+#else
+ if ((tmp & (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) ==
+ (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) {
+ /* return -EINVAL; */
+ tmp &= ~SL_MODE_ADAPTIVE;
+ }
+#endif
+#ifndef CONFIG_SLIP_MODE_SLIP6
+ if (tmp & SL_MODE_SLIP6) {
+ return -EINVAL;
+ }
+#endif
+#ifndef CONFIG_AX25
+ if ((tmp & SL_MODE_AX25) || (tmp & SL_MODE_AX25VC)) {
+ return -EINVAL;
+ }
+#else
+ if ((tmp & SL_MODE_AX25) || (tmp & SL_MODE_AX25VC)) {
sl->dev->addr_len=7; /* sizeof an AX.25 addr */
sl->dev->hard_header_len=17; /* We don't do digipeaters */
- }
- else
- {
+ } else {
sl->dev->addr_len=0; /* No mac addr in slip mode */
sl->dev->hard_header_len=0;
}
+#endif
+ sl->mode = tmp;
+ sl->dev->type = ARPHRD_SLIP+sl->mode;
+#ifdef CONFIG_AX25
+ if (sl->dev->type == 260 || sl->dev->type == 272) {
+ sl->dev->type = ARPHRD_AX25;
+ }
#endif
- sl->dev->type=ARPHRD_SLIP+sl->mode;
- if(sl->dev->type==260)
- sl->dev->type=ARPHRD_AX25;
- return(0);
- case SIOCSIFHWADDR:
-#ifdef CONFIG_AX25
- return sl_set_mac_address(sl->dev,arg);
+ return 0;
+
+ case SIOCSIFHWADDR:
+#ifdef CONFIG_AX25
+ return sl_set_mac_address(sl->dev, arg);
+#else
+ return -EINVAL;
#endif
+
/* Allow stty to read, but not set, the serial port */
case TCGETS:
case TCGETA:
return n_tty_ioctl(tty, (struct file *) file, cmd, (unsigned long) arg);
-
+
default:
return -ENOIOCTLCMD;
- }
+ }
}
+static int sl_open_dev(struct device *dev)
+{
+ struct slip *sl = &sl_ctrl[dev->base_addr];
+ if(sl->tty==NULL)
+ return -ENODEV;
+ return 0;
+}
/* Initialize the SLIP driver. Called by DDI. */
int
slip_init(struct device *dev)
{
- struct slip *sl;
- int i;
-#ifdef CONFIG_AX25
- static char ax25_bcast[7]={'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1};
- static char ax25_test[7]={'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1};
+ struct slip *sl = &sl_ctrl[dev->base_addr];
+ int i;
+#ifdef CONFIG_AX25
+ static char ax25_bcast[7] =
+ {'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1};
+ static char ax25_test[7] =
+ {'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1};
#endif
- sl = &sl_ctrl[dev->base_addr];
-
- if (already++ == 0) {
- printk("SLIP: version %s (%d channels)\n",
- SLIP_VERSION, SL_NRUNIT);
-#ifdef SL_COMPRESSED
- printk("CSLIP: code copyright 1989 Regents of the University of California\n");
+ if (already++ == 0) {
+ printk("SLIP: version %s (%d channels) %s\n",
+ SLIP_VERSION, SL_NRUNIT,
+#ifdef CONFIG_SLIP_MODE_SLIP6
+ "(6 bit encapsulation enabled)"
+#else
+ ""
+#endif
+ );
+#if defined(SL_INCLUDE_CSLIP) && !defined(MODULE)
+ printk("CSLIP: code copyright 1989 Regents of the University of California\n");
#endif
#ifdef CONFIG_AX25
- printk("AX25: KISS encapsulation enabled\n");
+ printk("AX25: KISS encapsulation enabled\n");
#endif
- /* Fill in our LDISC request block. */
- memset(&sl_ldisc, 0, sizeof(sl_ldisc));
- sl_ldisc.magic = TTY_LDISC_MAGIC;
- sl_ldisc.flags = 0;
- sl_ldisc.open = slip_open;
- sl_ldisc.close = slip_close;
- sl_ldisc.read = NULL;
- sl_ldisc.write = NULL;
- sl_ldisc.ioctl = (int (*)(struct tty_struct *, struct file *,
- unsigned int, unsigned long)) slip_ioctl;
- sl_ldisc.select = NULL;
- sl_ldisc.receive_buf = slip_receive_buf;
- sl_ldisc.receive_room = slip_receive_room;
- sl_ldisc.write_wakeup = slip_write_wakeup;
- if ((i = tty_register_ldisc(N_SLIP, &sl_ldisc)) != 0)
- printk("ERROR: %d\n", i);
- }
-
- /* Set up the "SLIP Control Block". */
- sl_initialize(sl, dev);
-
- /* Clear all statistics. */
- sl->rcount = 0; /* SLIP receiver count */
- sl->rpacket = 0; /* #frames received */
- sl->roverrun = 0; /* "overrun" counter */
- sl->spacket = 0; /* #frames sent out */
- sl->sbusy = 0; /* "xmit busy" counter */
- sl->errors = 0; /* not used at present */
-
- /* Finish setting up the DEVICE info. */
- dev->mtu = SL_MTU;
- dev->hard_start_xmit = sl_xmit;
- dev->open = sl_open;
- dev->stop = sl_close;
- dev->hard_header = sl_header;
- dev->type_trans = sl_type_trans;
- dev->get_stats = sl_get_stats;
+ /* Fill in our LDISC request block. */
+ memset(&sl_ldisc, 0, sizeof(sl_ldisc));
+ sl_ldisc.magic = TTY_LDISC_MAGIC;
+ sl_ldisc.flags = 0;
+ sl_ldisc.open = slip_open;
+ sl_ldisc.close = slip_close;
+ sl_ldisc.read = NULL;
+ sl_ldisc.write = NULL;
+ sl_ldisc.ioctl = (int (*)(struct tty_struct *, struct file *,
+ unsigned int, unsigned long)) slip_ioctl;
+ sl_ldisc.select = NULL;
+ sl_ldisc.receive_buf = slip_receive_buf;
+ sl_ldisc.receive_room = slip_receive_room;
+ sl_ldisc.write_wakeup = slip_write_wakeup;
+ if ((i = tty_register_ldisc(N_SLIP, &sl_ldisc)) != 0) {
+ printk("SLIP: can't register line discipline (err = %d)\n", i);
+ }
+ }
+
+ /* Set up the "SLIP Control Block". (And clear statistics) */
+
+ memset(sl, 0, sizeof (struct slip));
+ sl->magic = SLIP_MAGIC;
+ sl->dev = dev;
+
+ /* Finish setting up the DEVICE info. */
+ dev->mtu = SL_MTU;
+ dev->hard_start_xmit = sl_xmit;
+ dev->open = sl_open_dev;
+ dev->stop = sl_close;
+ dev->hard_header = sl_header;
+ dev->get_stats = sl_get_stats;
#ifdef HAVE_SET_MAC_ADDR
#ifdef CONFIG_AX25
- dev->set_mac_address = sl_set_dev_mac_address;
+ dev->set_mac_address = sl_set_dev_mac_address;
+#endif
#endif
+ dev->hard_header_len = 0;
+ dev->addr_len = 0;
+ dev->type = ARPHRD_SLIP + SL_MODE_DEFAULT;
+#ifdef CONFIG_AX25
+ if (sl->dev->type == 260 || sl->dev->type == 272) {
+ sl->dev->type = ARPHRD_AX25;
+ }
+ memcpy(dev->broadcast, ax25_bcast, 7); /* Only activated in AX.25 mode */
+ memcpy(dev->dev_addr, ax25_test, 7); /* "" "" "" "" */
#endif
- dev->hard_header_len = 0;
- dev->addr_len = 0;
- dev->type = 0;
-#ifdef CONFIG_AX25
- memcpy(dev->broadcast,ax25_bcast,7); /* Only activated in AX.25 mode */
- memcpy(dev->dev_addr,ax25_test,7); /* "" "" "" "" */
-#endif
- dev->rebuild_header = sl_rebuild_header;
- for (i = 0; i < DEV_NUMBUFFS; i++)
- skb_queue_head_init(&dev->buffs[i]);
-
- /* New-style flags. */
- dev->flags = 0;
- dev->family = AF_INET;
- dev->pa_addr = 0;
- dev->pa_brdaddr = 0;
- dev->pa_mask = 0;
- dev->pa_alen = sizeof(unsigned long);
-
- return(0);
+ dev->rebuild_header = sl_rebuild_header;
+
+ for (i = 0; i < DEV_NUMBUFFS; i++) {
+ skb_queue_head_init(&dev->buffs[i]);
+ }
+
+ /* New-style flags. */
+ dev->flags = 0;
+ dev->family = AF_INET;
+ dev->pa_addr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ dev->pa_alen = sizeof(unsigned long);
+
+ return 0;
+}
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+
+static struct device dev_slip[SL_NRUNIT] = {
+ {
+ "sl0", /* slip */
+ 0, 0, 0, 0, /* memory */
+ 0, 0, /* base, irq */
+ 0, 0, 0, NULL, slip_init,
+ },
+ { "sl1" , 0, 0, 0, 0, 1, 0, 0, 0, 0, NULL, slip_init },
+ { "sl2" , 0, 0, 0, 0, 2, 0, 0, 0, 0, NULL, slip_init },
+ { "sl3" , 0, 0, 0, 0, 3, 0, 0, 0, 0, NULL, slip_init },
+#ifdef SL_SLIP_LOTS
+ { "sl4" , 0, 0, 0, 0, 4, 0, 0, 0, 0, NULL, slip_init },
+ { "sl5" , 0, 0, 0, 0, 5, 0, 0, 0, 0, NULL, slip_init },
+ { "sl6" , 0, 0, 0, 0, 6, 0, 0, 0, 0, NULL, slip_init },
+ { "sl7" , 0, 0, 0, 0, 7, 0, 0, 0, 0, NULL, slip_init },
+ { "sl8" , 0, 0, 0, 0, 8, 0, 0, 0, 0, NULL, slip_init },
+ { "sl9" , 0, 0, 0, 0, 9, 0, 0, 0, 0, NULL, slip_init },
+ { "sl10", 0, 0, 0, 0, 10, 0, 0, 0, 0, NULL, slip_init },
+ { "sl11", 0, 0, 0, 0, 11, 0, 0, 0, 0, NULL, slip_init },
+ { "sl12", 0, 0, 0, 0, 12, 0, 0, 0, 0, NULL, slip_init },
+ { "sl13", 0, 0, 0, 0, 13, 0, 0, 0, 0, NULL, slip_init },
+ { "sl14", 0, 0, 0, 0, 14, 0, 0, 0, 0, NULL, slip_init },
+ { "sl15", 0, 0, 0, 0, 15, 0, 0, 0, 0, NULL, slip_init },
+#endif /* SL_SLIP_LOTS */
+};
+
+int
+init_module(void)
+{
+ int err;
+ int i;
+
+ for (i = 0; i < SL_NRUNIT; i++) {
+ if ((err = register_netdev(&dev_slip[i]))) {
+ if (err == -EEXIST) {
+ printk("SLIP: devices already present. Module not loaded.\n");
+ }
+ return err;
+ }
+ }
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ int i;
+
+ if (MOD_IN_USE) {
+ printk("SLIP: device busy, remove delayed\n");
+ return;
+ }
+ for (i = 0; i < SL_NRUNIT; i++) {
+ unregister_netdev(&dev_slip[i]);
+ }
+ if ((i = tty_register_ldisc(N_SLIP, NULL))) {
+ printk("SLIP: can't unregister line discipline (err = %d)\n", i);
+ }
+ already = 0;
}
+#endif /* MODULE */
diff --git a/drivers/net/slip.h b/drivers/net/slip.h
index 8c2f3df96..efeff53d0 100644
--- a/drivers/net/slip.h
+++ b/drivers/net/slip.h
@@ -10,12 +10,26 @@
* Alan Cox : Added slip mtu field.
* Matt Dillon : Printable slip (borrowed from net2e)
* Alan Cox : Added SL_SLIP_LOTS
+ * Dmitry Gorodchanin : A lot of changes in the 'struct slip'
+ * Dmitry Gorodchanin : Added CSLIP statistics.
*
* Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
*/
#ifndef _LINUX_SLIP_H
#define _LINUX_SLIP_H
+#include <linux/config.h>
+
+#if defined(CONFIG_INET) && defined(CONFIG_SLIP_COMPRESSED)
+# define SL_INCLUDE_CSLIP
+#endif
+
+#ifdef SL_INCLUDE_CSLIP
+# define SL_MODE_DEFAULT SL_MODE_ADAPTIVE
+#else
+# define SL_MODE_DEFAULT SL_MODE_SLIP
+#endif
+
/* SLIP configuration. */
#ifndef SL_SLIP_LOTS
#define SL_NRUNIT 4 /* number of SLIP channels */
@@ -33,44 +47,49 @@
struct slip {
int magic;
- /* Bitmapped flag fields. */
- char inuse; /* are we allocated? */
- char sending; /* "channel busy" indicator */
- char escape; /* SLIP state machine */
- char unused; /* fillers */
/* Various fields. */
- int line; /* SLIP channel number */
struct tty_struct *tty; /* ptr to TTY structure */
struct device *dev; /* easy for intr handling */
+#ifdef SL_INCLUDE_CSLIP
struct slcompress *slcomp; /* for header compression */
+ unsigned char *cbuff; /* compression buffer */
+#endif
/* These are pointers to the malloc()ed frame buffers. */
unsigned char *rbuff; /* receiver buffer */
+ int rcount; /* received chars counter */
unsigned char *xbuff; /* transmitter buffer */
- unsigned char *cbuff; /* compression buffer */
-
- /* These are the various pointers into the buffers. */
- unsigned char *rhead; /* RECV buffer pointer (head) */
- unsigned char *rend; /* RECV buffer pointer (end) */
- int rcount; /* SLIP receive counter */
- unsigned char *xhead; /* XMIT buffer pointer (head) */
- unsigned char *xtail; /* XMIT buffer pointer (tail) */
+ unsigned char *xhead; /* pointer to next byte to XMIT */
+ int xleft; /* bytes left in XMIT queue */
/* SLIP interface statistics. */
- unsigned long rpacket; /* inbound frame counter */
- unsigned long roverrun; /* "buffer overrun" counter */
- unsigned long spacket; /* outbound frames counter */
- unsigned long sbusy; /* "transmitter busy" counter */
- unsigned long errors; /* error count */
-
+ unsigned long rx_packets; /* inbound frames counter */
+ unsigned long tx_packets; /* outbound frames counter */
+ unsigned long rx_errors; /* Parity, etc. errors */
+ unsigned long tx_errors; /* Planned stuff */
+ unsigned long rx_dropped; /* No memory for skb */
+ unsigned long tx_dropped; /* When MTU change */
+ unsigned long rx_over_errors; /* Frame bigger then SLIP buf. */
+#ifdef SL_INCLUDE_CSLIP
+ unsigned long tx_compressed;
+ unsigned long rx_compressed;
+ unsigned long tx_misses;
+#endif
+ /* Detailed SLIP statistics. */
+
int mtu; /* Our mtu (to spot changes!) */
+ int buffsize; /* Max buffers sizes */
+
+#ifdef CONFIG_SLIP_MODE_SLIP6
+ int xdata, xbits; /* 6 bit slip controls */
+#endif
+
unsigned char flags; /* Flag values/ mode etc */
-#define SLF_ESCAPE 2
-#define SLF_ERROR 4
-#define SLF_COMP 16
-#define SLF_EXPN 32
-#define SLF_XMIT_BUSY 64
+#define SLF_INUSE 0 /* Channel in use */
+#define SLF_ESCAPE 1 /* ESC received */
+#define SLF_ERROR 2 /* Parity, etc. error */
+
unsigned char mode; /* SLIP mode */
#define SL_MODE_SLIP 0
#define SL_MODE_CSLIP 1
@@ -78,15 +97,13 @@ struct slip {
#define SL_MODE_CSLIP6 (SL_MODE_SLIP6|SL_MODE_CSLIP)
#define SL_MODE_AX25 4
#define SL_MODE_ADAPTIVE 8
- int xdata,xbits; /* 6 bit slip controls */
+#define SL_MODE_AX25VC 16
};
+
+
#define SLIP_MAGIC 0x5302
-extern int slip_init(struct device *dev);
-extern int slip_esc(unsigned char *s, unsigned char *d, int len);
-extern int slip_esc6(unsigned char *s, unsigned char *d, int len);
-extern void slip_unesc(struct slip *sl, unsigned char s);
-extern void slip_unesc6(struct slip *sl, unsigned char s);
+extern int slip_init(struct device *dev);
#endif /* _LINUX_SLIP.H */
diff --git a/drivers/net/smc-ultra.c b/drivers/net/smc-ultra.c
index 45ff88eaf..6f0156887 100644
--- a/drivers/net/smc-ultra.c
+++ b/drivers/net/smc-ultra.c
@@ -1,6 +1,6 @@
/* smc-ultra.c: A SMC Ultra ethernet driver for linux. */
/*
- Written 1993-94 by Donald Becker.
+ Written 1993,1994,1995 by Donald Becker.
Copyright 1993 United States Government as represented by the
Director, National Security Agency.
@@ -12,14 +12,34 @@
Center of Excellence in Space Data and Information Sciences
Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
- This is a driver for the SMC Ultra ethercard.
+ This is a driver for the SMC Ultra and SMC EtherEZ ethercards.
+ This driver uses the cards in the 8390-compatible, shared memory mode.
+ Most of the run-time complexity is handled by the generic code in
+ 8390.c. The code in this file is responsible for
+
+ ultra_probe() Detecting and initializing the card.
+ ultra_probe1()
+
+ ultra_open() The card-specific details of starting, stopping
+ ultra_reset_8390() and resetting the 8390 NIC core.
+ ultra_close()
+
+ ultra_block_input() Routines for reading and writing blocks of
+ ultra_block_output() packet buffer memory.
+
+ This driver enables the shared memory only when doing the actual data
+ transfers to avoid a bug in early version of the card that corrupted
+ data transferred by a AHA1542.
+
+ This driver does not support the programmed-I/O data transfer mode of
+ the EtherEZ. That support (if available) is smc-ez.c. Nor does it
+ use the non-8390-compatible "Altego" mode. (No support currently planned.)
*/
static char *version =
- "smc-ultra.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+ "smc-ultra.c:v1.12 1/18/95 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
@@ -94,10 +114,13 @@ int ultra_probe1(struct device *dev, int ioaddr)
char *model_name;
unsigned char eeprom_irq = 0;
/* Values from various config regs. */
- unsigned char num_pages, irqreg, addr, reg4 = inb(ioaddr + 4) & 0x7f;
+ unsigned char num_pages, irqreg, addr;
+ unsigned char idreg = inb(ioaddr + 7);
+ unsigned char reg4 = inb(ioaddr + 4) & 0x7f;
/* Check the ID nibble. */
- if ((inb(ioaddr + 7) & 0xF0) != 0x20)
+ if ((idreg & 0xF0) != 0x20 /* SMC Ultra */
+ && (idreg & 0xF0) != 0x40) /* SMC EtherEZ */
return ENODEV;
/* Select the station address register set. */
@@ -111,7 +134,9 @@ int ultra_probe1(struct device *dev, int ioaddr)
if (dev == NULL)
dev = init_etherdev(0, sizeof(struct ei_device), 0);
- printk("%s: SMC Ultra at %#3x,", dev->name, ioaddr);
+ model_name = (idreg & 0xF0) == 0x20 ? "SMC Ultra" : "SMC EtherEZ";
+
+ printk("%s: %s at %#3x,", dev->name, model_name, ioaddr);
for (i = 0; i < 6; i++)
printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i));
@@ -129,8 +154,6 @@ int ultra_probe1(struct device *dev, int ioaddr)
can find the card after a warm boot. */
outb(reg4, ioaddr + 4);
- model_name = "SMC Ultra";
-
if (dev->irq < 2) {
unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15};
int irq;
@@ -147,8 +170,8 @@ int ultra_probe1(struct device *dev, int ioaddr)
}
- /* OK, were are certain this is going to work. Setup the device. */
- snarf_region(ioaddr, 32);
+ /* OK, we are certain this is going to work. Setup the device. */
+ request_region(ioaddr, 32, model_name);
/* The 8390 isn't at the base address, so fake the offset */
dev->base_addr = ioaddr+ULTRA_NIC_OFFSET;
@@ -193,7 +216,7 @@ ultra_open(struct device *dev)
{
int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
- if (request_irq(dev->irq, ei_interrupt, 0, "SMC Ultra"))
+ if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name))
return -EAGAIN;
outb(ULTRA_MEMENB, ioaddr); /* Enable memory, 16 bit mode. */
@@ -226,16 +249,21 @@ ultra_block_input(struct device *dev, int count, char *buf, int ring_offset)
void *xfer_start = (void *)(dev->mem_start + ring_offset
- (START_PG<<8));
+ /* Enable shared memory. */
+ outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);
+
if (xfer_start + count > (void*) dev->rmem_end) {
/* We must wrap the input move. */
int semi_count = (void*)dev->rmem_end - xfer_start;
memcpy(buf, xfer_start, semi_count);
count -= semi_count;
memcpy(buf + semi_count, (char *)dev->rmem_start, count);
+ outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */
return dev->rmem_start + count;
}
memcpy(buf, xfer_start, count);
+ outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */
return ring_offset + count;
}
@@ -246,8 +274,12 @@ ultra_block_output(struct device *dev, int count, const unsigned char *buf,
unsigned char *shmem
= (unsigned char *)dev->mem_start + ((start_page - START_PG)<<8);
+ /* Enable shared memory. */
+ outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);
+
memcpy(shmem, buf, count);
+ outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */
}
static int
diff --git a/drivers/net/sonic.c b/drivers/net/sonic.c
new file mode 100644
index 000000000..62796c2d0
--- /dev/null
+++ b/drivers/net/sonic.c
@@ -0,0 +1,1120 @@
+/*
+ * sonic.c
+ *
+ * (C) 1995 by Andreas Busse (andy@waldorf-gmbh.de)
+ *
+ * A driver for the onboard Sonic ethernet controller on Mips Jazz
+ * systems (Acer Pica-61, Mips Magnum 4000, Olivetti M700 and
+ * perhaps others, too)
+ *
+ * NOTE: THIS DRIVER IS NOT OPERATIONAL YET! DO NOT TRY TO REALLY USE IT!
+ */
+
+
+static char *version =
+ "sonic.c:v0.01 6/16/95 Andreas Busse (andy@waldorf-gmbh.de)\n";
+
+#include <linux/config.h>
+
+/*
+ * Sources: Olivetti M700-10 Risc Personal Computer hardware handbook,
+ * National Semiconductors data sheet for the DP83932B Sonic Ethernet
+ * controller, and the files "8390.c" and "skeleton.c" in this directory.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/jazz.h>
+#include <asm/jazzdma.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "sonic.h"
+
+extern struct device *init_etherdev(struct device *dev, int sizeof_private,
+ unsigned long *mem_startp);
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 3
+#endif
+static unsigned int sonic_debug = NET_DEBUG;
+
+/*
+ * set to 1 if you just want to debug the driver without
+ * having a real network connection.
+ */
+#define SONIC_LOOPBACK 0
+#define SONIC_DRIVER_TEST 1
+
+/* Information that need to be kept for each board. */
+struct sonic_local {
+ char *memptr; /* ptr to memory used for all buffers */
+ SONIC_CAM_DESCRIPTOR_AREA *cda_vaddr; /* virtual CPU address of CDA */
+ unsigned int cda_laddr; /* logical DMA address of CDA */
+ SONIC_TRANSMIT_DESCRIPTOR *tda_vaddr; /* virtual CPU address of TDA */
+ unsigned int tda_laddr; /* logical DMA address of TDA */
+ SONIC_RECEIVE_RESOURCE *rra_vaddr; /* virtual CPU address of RRA */
+ unsigned int rra_laddr; /* logical DMA address of RRA */
+ SONIC_RECEIVE_DESCRIPTOR *rda_vaddr; /* virtual CPU address of RDA */
+ unsigned int rda_laddr; /* logical DMA address of RDA */
+ unsigned int rd_next; /* the next RD where we expect data */
+ unsigned char *rba_vaddr; /* virtual CPU address of RBA */
+ unsigned int rba_laddr; /* logical DMA address of RBA */
+ unsigned char *pkt_vaddr; /* virtual CPU address of tx buffer */
+ unsigned int pkt_laddr; /* logical DMA address of tx buffer */
+ struct enet_statistics stats;
+};
+
+/*
+ * We cannot use station (ethernet) address prefixes to detect the
+ * sonic controller since these are board manufacturer depended.
+ * So we check for known Silicon Revision IDs instead.
+ */
+static unsigned short known_revisions[] =
+{
+ 0x04, /* Mips Magnum 4000 */
+ 0xffff /* end of list */
+};
+
+/* Index to functions, as function prototypes. */
+
+extern int sonic_probe(struct device *dev);
+
+static int sonic_open(struct device *dev);
+static int sonic_send_packet(struct sk_buff *skb, struct device *dev);
+static void sonic_interrupt(int irq, struct pt_regs *regs);
+static void sonic_rx(struct device *dev);
+static int sonic_close(struct device *dev);
+static struct enet_statistics *sonic_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+static int sonic_init(struct device *dev, int startp);
+static void sonic_init_tda(struct device *dev);
+static void sonic_init_rra(struct device *dev);
+static void sonic_init_rda(struct device *dev);
+static void sonic_init_cam(struct device *dev);
+static void sonic_load_cam(struct device *dev);
+static void sonic_dump_cam(void);
+static void sonic_test(struct device *dev);
+static void hardware_send_packet(struct device *dev, char *buf, int length);
+
+#define tx_done(dev) 1
+
+/*
+ * Probe for a SONIC ethernet controller on a Mips Jazz board.
+ * Either we find the Sonic controller at the usual location,
+ * or we don't deal with a Jazz board (or it's broken...)
+ */
+
+int sonic_probe(struct device *dev)
+{
+ static unsigned version_printed = 0;
+ unsigned int silicon_revision;
+ unsigned int val;
+ struct sonic_local *lp;
+ int i;
+
+ /*
+ * get the Silicon Revision ID. If this is one of the known
+ * one assume that we found a SONIC ethernet controller at
+ * the expected location.
+ */
+ silicon_revision = sonic_read_register(SONIC_SR);
+ if (sonic_debug > 1)
+ printk("SONIC Silicon Revision = 0x%04x\n",silicon_revision);
+
+ i = 0;
+ while ((known_revisions[i] != 0xffff) &&
+ (known_revisions[i] != silicon_revision))
+ i++;
+
+ if (known_revisions[i] == 0xffff) {
+ printk("SONIC ethernet controller not found (0x%4x)\n",
+ silicon_revision);
+ return ENODEV;
+ }
+
+ /* Allocate a new 'dev' if needed. */
+ if (dev == NULL)
+ dev = init_etherdev(0, sizeof(struct sonic_local), 0);
+
+ if (sonic_debug && version_printed++ == 0)
+ printk(version);
+
+ printk("%s: %s found at 0x%08x, ",
+ dev->name, "SONIC ethernet", SONIC_BASE_ADDR);
+
+ /* Fill in the 'dev' fields. */
+ dev->base_addr = SONIC_BASE_ADDR;
+
+
+ /*
+ * Put the sonic into software reset, then
+ * retrieve and print the ethernet address.
+ */
+
+ sonic_write_register(SONIC_CMD,SONIC_CR_RST);
+ sonic_write_register(SONIC_CEP,0);
+ for (i=0; i<3; i++) {
+ val = sonic_read_register(SONIC_CAP0-i);
+ dev->dev_addr[i*2] = val;
+ dev->dev_addr[i*2+1] = val >> 8;
+ }
+
+ printk("HW Address ");
+ for (i = 0; i < 6; i++) {
+ printk("%2.2x", dev->dev_addr[i]);
+ if (i<5)
+ printk(":");
+ }
+
+ /*
+ * We don't need to deal with auto-irq stuff since we
+ * hardwire the sonic interrupt to JAZZ_ETHERNET_IRQ
+ * (currently 15).
+ */
+
+ val = request_irq(JAZZ_ETHERNET_IRQ, &sonic_interrupt, 0, "sonic");
+ if (val) {
+ printk ("\n%s: unable to get IRQ %d (code %d).\n", dev->name,
+ JAZZ_ETHERNET_IRQ, val);
+ return EAGAIN;
+ }
+ dev->irq = JAZZ_ETHERNET_IRQ;
+ irq2dev_map[JAZZ_ETHERNET_IRQ] = dev;
+ if (sonic_debug > 1)
+ printk(" IRQ %d\n", JAZZ_ETHERNET_IRQ);
+
+ /* Initialize the device structure. */
+
+ if (dev->priv == NULL)
+ dev->priv = kmalloc(sizeof(struct sonic_local), GFP_KERNEL);
+ memset(dev->priv, 0, sizeof(struct sonic_local));
+
+ lp = (struct sonic_local *)dev->priv;
+ dev->open = sonic_open;
+ dev->stop = sonic_close;
+ dev->hard_start_xmit = sonic_send_packet;
+ dev->get_stats = sonic_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ /* Fill in the fields of the device structure with ethernet values. */
+
+ ether_setup(dev);
+ return 0;
+}
+
+
+/*
+ * Open/initialize the SONIC controller.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+
+static int sonic_open(struct device *dev)
+{
+ struct sonic_local *lp = (struct sonic_local *)dev->priv;
+ struct sk_buff *skb; /* only for initial checks */
+ int i;
+
+ if (sonic_debug)
+ printk("sonic_open: initializing sonic driver.\n");
+
+ /*
+ * Initialize the SONIC
+ */
+ sonic_init(dev, 1);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ /*
+ * This will be removed some day...
+ */
+#if SONIC_DRIVER_TEST
+ sonic_test(dev); /* go into test mode */
+#endif
+
+ if (sonic_debug)
+ printk("sonic_open: Initialization done.\n");
+
+ return 0;
+}
+
+static int sonic_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ /* struct sonic_local *lp = (struct sonic_local *)dev->priv; */
+
+ if (sonic_debug > 1)
+ printk("sonic_send_packet: skb=%p, dev=%p\n",skb,dev);
+
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+
+ /* If we get here, some higher level has decided we are broken.
+ There should really be a "kick me" function call instead. */
+
+ if (sonic_debug)
+ printk("sonic_send_packet: called with dev->tbusy = 1 !\n");
+
+ if (tickssofar < 5)
+ return 1;
+
+ printk("%s: transmit timed out, %s?\n", dev->name,
+ tx_done(dev) ? "IRQ conflict" : "network cable problem");
+
+ /* Try to restart the adaptor. */
+ sonic_init(dev, 1);
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ }
+
+ /* If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+
+ printk("sonic_send_packet: calling set_bit()\n");
+
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = skb->data;
+
+ if (sonic_debug)
+ printk("sonic_send_packet: calling hardware_send_packet(%p,%p,%d)\n",
+ dev,buf,length);
+
+ hardware_send_packet(dev, buf, length);
+ dev->trans_start = jiffies;
+ }
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ /* You might need to clean up and record Tx statistics here. */
+#if 0
+ if (inw(ioaddr) == /*RU*/81)
+ lp->stats.tx_aborted_errors++;
+#endif
+
+ return 0;
+}
+
+/*
+ * The typical workload of the driver:
+ * Handle the network interface interrupts.
+ */
+
+static void
+sonic_interrupt(int irq, struct pt_regs * regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct sonic_local *lp;
+ int status;
+
+ printk("in sonic_interrupt()\n");
+
+ if (dev == NULL) {
+ printk ("net_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+ dev->interrupt = 1;
+ lp = (struct sonic_local *)dev->priv;
+
+ status = sonic_read_register(SONIC_ISR);
+ sonic_write_register(SONIC_ISR,0x7fff); /* clear all bits */
+
+ if (sonic_debug > 2)
+ printk("sonic_interrupt: ISR=%x\n",status);
+
+ if (status & SONIC_INT_PKTRX) {
+ sonic_rx(dev); /* got packet(s) */
+ }
+ if (status & SONIC_INT_TXDN) {
+ lp->stats.tx_packets++; /* packet transmitted */
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
+ }
+ /*
+ * check error conditions
+ */
+ if (status & SONIC_INT_RFO)
+ lp->stats.rx_fifo_errors++;
+ if (status & (SONIC_INT_RDE | SONIC_INT_RBE | SONIC_INT_RBAE))
+ lp->stats.rx_over_errors++;
+
+ /*
+ * clear interrupt bits and return
+ */
+ sonic_write_register(SONIC_ISR,status);
+ dev->interrupt = 0;
+ return;
+}
+
+/*
+ * We have a good packet(s), get it/them out of the buffers.
+ */
+
+static void
+sonic_rx(struct device *dev)
+{
+ struct sonic_local *lp;
+ SONIC_RECEIVE_DESCRIPTOR *current;
+ SONIC_RECEIVE_DESCRIPTOR *next;
+ int status;
+
+ lp = (struct sonic_local *)dev->priv;
+ current = lp->rda_vaddr + lp->rd_next;
+ next = lp->rda_vaddr + ((lp->rd_next + 1) & ~(SONIC_MAX_RDS));
+
+ status = sonic_read_register(SONIC_RCR);
+ if (sonic_debug > 2)
+ printk("sonic_rx: RCR=%x, current=%p, next=%p, rx_status=%x, rx_pktlen=%x, rx_seqno=%x, link=%x, in_use=%x\n",
+ status,current,next,current->rx_status,current->rx_pktlen,
+ current->rx_seqno,current->link,current->in_use);
+#if 0
+ if ((status & SONIC_RCR_PRX) == 0) /* any error ? */
+ {
+ if (status & SONIC_RCR_FAER) lp->stats.rx_frame_errors++;
+ if (status & SONIC_RCR_CRCR) lp->stats.rx_crc_errors++;
+ }
+#endif
+ /*
+ * Due to a SONIC bug the packet might still be ok,
+ * so we need to check the rx_status field first.
+ */
+
+ while(current->in_use == 0)
+ {
+ struct sk_buff *skb;
+ int pkt_len;
+ char *pkt_ptr;
+ int i;
+
+ if ((current->rx_status & SONIC_RCR_PRX) == 0)
+ {
+ if (status & SONIC_RCR_FAER) lp->stats.rx_frame_errors++;
+ if (status & SONIC_RCR_CRCR) lp->stats.rx_crc_errors++;
+ }
+ else
+ {
+ pkt_len = current->rx_pktlen;
+ pkt_ptr = (char *)KSEG1ADDR(vdma_log2phys((current->rx_pktptr_h << 16) +
+ current->rx_pktptr_l));
+ printk("sonic_rx: packet at %p:\n",pkt_ptr);
+ for (i=0; i<32; i++)
+ printk("%02x ",(unsigned char)pkt_ptr[i]);
+ printk("\n");
+
+ /* Malloc up new buffer. */
+
+ printk("sonic_rx: allocating sk_buff...\n");
+ skb = alloc_skb(pkt_len, GFP_ATOMIC);
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ break;
+ }
+ skb->len = pkt_len;
+ skb->dev = dev;
+
+ /* 'skb->data' points to the start of sk_buff data area. */
+
+ printk("sonic_rx: copying packet from %p to %p\n",pkt_ptr,skb->data);
+ memcpy(skb->data, (void*)pkt_ptr, pkt_len);
+
+ printk("sonic_rx: passing packet to upper layer...\n");
+ netif_rx(skb); /* pass the packet to upper layers */
+ lp->stats.rx_packets++;
+
+ /*
+ * Mark that RD as available again, increment (and possibly
+ * wrap around) the RD index, and mark the current RD as
+ * the last in the circular buffer. Also unmark the following RD.
+ */
+
+ current->in_use = 1;
+ current->link |= SONIC_END_OF_LINKS;
+ next->link &= ~SONIC_END_OF_LINKS;
+ lp->rd_next = (lp->rd_next + 1) & (SONIC_MAX_RDS-1);
+ }
+ }
+
+#if 0
+ do {
+ int status = inw(ioaddr);
+ int pkt_len = inw(ioaddr);
+
+ if (pkt_len == 0) /* Read all the frames? */
+ break; /* Done for now */
+
+ if (status & 0x40) { /* There was an error. */
+ lp->stats.rx_errors++;
+ if (status & 0x20) lp->stats.rx_frame_errors++;
+ if (status & 0x10) lp->stats.rx_over_errors++;
+ if (status & 0x08) lp->stats.rx_crc_errors++;
+ if (status & 0x04) lp->stats.rx_fifo_errors++;
+ } else {
+ /* Malloc up new buffer. */
+ struct sk_buff *skb;
+
+ skb = alloc_skb(pkt_len, GFP_ATOMIC);
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ break;
+ }
+ skb->len = pkt_len;
+ skb->dev = dev;
+
+ /* 'skb->data' points to the start of sk_buff data area. */
+ memcpy(skb->data, (void*)dev->rmem_start,
+ pkt_len);
+ /* or */
+ insw(ioaddr, skb->data, (pkt_len + 1) >> 1);
+
+ netif_rx(skb); /* pass the packet to upper layers */
+ lp->stats.rx_packets++;
+ }
+ } while (--boguscount);
+#endif
+
+ /* If any worth-while packets have been received, dev_rint()
+ has done a mark_bh(NET_BH) for us and will work on them
+ when we get to the bottom-half routine. */
+ return;
+}
+
+
+/*
+ * Close the SONIC device
+ */
+static int
+sonic_close(struct device *dev)
+{
+ struct sonic_local *lp = (struct sonic_local *)dev->priv;
+
+/* lp->open_time = 0; */
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* Flush the Tx and disable Rx here. */
+
+#if 0
+ disable_dma(dev->dma);
+
+ /* If not IRQ or DMA jumpered, free up the line. */
+
+ outw(0x00, ioaddr+0); /* Release the physical interrupt line. */
+
+ free_irq(dev->irq); /* release the IRQ */
+ free_dma(dev->dma);
+ kfree(lp->memptr); /* free memory */
+
+ irq2dev_map[dev->irq] = 0;
+#endif
+
+#if 0
+ /* disable ethernet IRQ on JAZZ boards */
+
+ r4030_write_reg16(JAZZ_IO_IRQ_ENABLE,
+ r4030_read_reg16(JAZZ_IO_IRQ_ENABLE) &
+ ~JAZZ_IE_ETHERNET);
+#endif
+
+ /* Update the statistics here. */
+
+ return 0;
+
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the device open or closed.
+ */
+
+static struct enet_statistics *
+sonic_get_stats(struct device *dev)
+{
+ struct sonic_local *lp = (struct sonic_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+
+ cli();
+ /* Update the statistics from the device registers. */
+ lp->stats.rx_missed_errors = inw(ioaddr+1);
+ sti();
+
+ return &lp->stats;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1 Promiscuous mode, receive all packets
+ * num_addrs == 0 Normal mode, clear multicast list
+ * num_addrs > 0 Multicast mode, receive normal and MC packets, and do
+ * best-effort filtering.
+ */
+
+static void
+set_multicast_list(struct device *dev, int num_addrs, void *addrs)
+{
+ unsigned int rcr;
+
+ /* FIXME: don't know how to handle multicast */
+
+ rcr = sonic_read_register(SONIC_RCR);
+ rcr |= SONIC_RCR_BRD; /* accept broadcast packets */
+
+ if (sonic_debug > 2)
+ printk("set_multicast_list: RCR=%x\n",rcr);
+
+ if (num_addrs == -1)
+ rcr |= SONIC_RCR_PRO;
+ else
+ rcr &= ~SONIC_RCR_PRO;
+
+ if (sonic_debug > 2)
+ printk("set_multicast_list: setting RCR=%x\n",rcr);
+
+ sonic_write_register(SONIC_RCR,rcr);
+}
+
+
+/*
+ * Send a packet
+ */
+static void
+hardware_send_packet(struct device *dev, char *buf, int length)
+{
+ struct sonic_local *lp = (struct sonic_local *)dev->priv;
+ unsigned int laddr;
+ volatile SONIC_TRANSMIT_DESCRIPTOR *td = lp->tda_vaddr;
+
+ /*
+ * Map the packet data into the logical DMA address space
+ */
+ vdma_remap(lp->pkt_laddr,PHYSADDR(buf),length);
+ laddr = lp->pkt_laddr + VDMA_OFFSET(buf);
+
+#if 0
+ if (sonic_debug > 2)
+ printk("hardware_send_packet: buf=%p, laddr=%08x, length=%d\n",buf,laddr,length);
+#endif
+ /*
+ * Setup the transmit descriptor and issue the transmit command.
+ */
+
+ td->tx_status = 0; /* clear status */
+ td->tx_config = 0; /* no special configuration */
+ td->tx_pktsize = length; /* length of packet */
+ td->tx_frag_count = 1; /* single fragment */
+ td->tx_frag_ptr_l = laddr & 0xffff;
+ td->tx_frag_ptr_h = laddr >> 16;
+ td->tx_frag_size = length;
+ td->link = (lp->tda_laddr & 0xffff) | SONIC_END_OF_LINKS;
+
+ /* set the transmit descriptor pointers */
+
+ if (sonic_debug > 3)
+ printk("hardware_send_packet: setting TD pointers: laddr=%08x\n",lp->tda_laddr);
+
+ sonic_write_register(SONIC_UTDA, lp->tda_laddr >> 16);
+ sonic_write_register(SONIC_CTDA, lp->tda_laddr & 0xffff);
+
+ if (sonic_debug > 3)
+ {
+ printk("hardware_send_packet: td->tx_status = %x\n",
+ td->tx_status);
+ printk("hardware_send_packet: CMD=%x, UTDA=%x, CTDA=%x, TCR=%x, ISR=%x\n",
+ sonic_read_register(SONIC_CMD),
+ sonic_read_register(SONIC_UTDA),
+ sonic_read_register(SONIC_CTDA),
+ sonic_read_register(SONIC_TCR),
+ sonic_read_register(SONIC_ISR));
+ printk("hardware_send_packet: TPS=%x, TFC=%x, TSA0=%x, TSA1=%x, TFS=%x, TTDA=%x\n",
+ sonic_read_register(SONIC_TPS),
+ sonic_read_register(SONIC_TFC),
+ sonic_read_register(SONIC_TSA0),
+ sonic_read_register(SONIC_TSA1),
+ sonic_read_register(SONIC_TFS),
+ sonic_read_register(SONIC_TTDA));
+ }
+
+ if (sonic_debug > 2)
+ printk("hardware_send_packet: issueing Tx command\n");
+
+ dev->tbusy=1; /* XXXX needs to be removed later */
+ sonic_write_register(SONIC_CMD,SONIC_CR_TXP);
+
+ udelay(1000);
+
+ if (sonic_debug > 3)
+ {
+ printk("hardware_send_packet: td->tx_status = %x\n",
+ td->tx_status);
+ printk("hardware_send_packet: CMD=%x, UTDA=%x, CTDA=%x, TCR=%x, ISR=%x\n",
+ sonic_read_register(SONIC_CMD),
+ sonic_read_register(SONIC_UTDA),
+ sonic_read_register(SONIC_CTDA),
+ sonic_read_register(SONIC_TCR),
+ sonic_read_register(SONIC_ISR));
+ printk("hardware_send_packet: TPS=%x, TFC=%x, TSA0=%x, TSA1=%x, TFS=%x, TTDA=%x\n",
+ sonic_read_register(SONIC_TPS),
+ sonic_read_register(SONIC_TFC),
+ sonic_read_register(SONIC_TSA0),
+ sonic_read_register(SONIC_TSA1),
+ sonic_read_register(SONIC_TFS),
+ sonic_read_register(SONIC_TTDA));
+ }
+}
+
+/*
+ * Initialize the SONIC ethernet controller.
+ */
+static int
+sonic_init(struct device *dev, int startp)
+{
+ struct sonic_local *lp = (struct sonic_local *)dev->priv;
+ unsigned int cda_size,tda_size,rra_size,rda_size,rba_size;
+ unsigned int memneed;
+ char *memptr;
+ unsigned int laddr;
+ unsigned int cmd;
+
+ /*
+ * put the Sonic into software-reset mode and
+ * disable all interrupts
+ */
+
+ sonic_write_register(SONIC_ISR,0x7fff);
+ sonic_write_register(SONIC_IMR,0);
+ sonic_write_register(SONIC_CMD,SONIC_CR_RST);
+
+ /*
+ * allocate space for tx/rx control and buffers plus
+ * some pad bytes for alignment. Note that all buffers
+ * must be in the same 64k segment, so we alloc() and
+ * free() memory until we get such a memory block.
+ */
+ if (lp->memptr == NULL) {
+#if 1
+ cda_size = (sizeof(SONIC_CAM_DESCRIPTOR_AREA) + 15) & ~15;
+ tda_size = (sizeof(SONIC_TRANSMIT_DESCRIPTOR) + 15) & ~15;
+ rra_size = (sizeof(SONIC_RECEIVE_RESOURCE) + 15) & ~15;
+ rda_size = ((sizeof(SONIC_RECEIVE_DESCRIPTOR) * SONIC_MAX_RDS) + 15) & ~15;
+ rba_size = SONIC_RXBUFSIZE;
+#else
+ cda_size = (sizeof(SONIC_CAM_DESCRIPTOR_AREA) + 31) & ~31;
+ tda_size = (sizeof(SONIC_TRANSMIT_DESCRIPTOR) + 31) & ~31;
+ rra_size = (sizeof(SONIC_RECEIVE_RESOURCE) + 31) & ~31;
+ rda_size = ((sizeof(SONIC_RECEIVE_DESCRIPTOR) * SONIC_MAX_RDS) + 31) & ~31;
+ rba_size = SONIC_RXBUFSIZE;
+#endif
+ if (sonic_debug > 2)
+ printk("cda_size=%x, tda_size=%x, rra_size=%x, rda_size=%x, rba_size=%x\n",
+ cda_size,tda_size,rra_size,rda_size,rba_size);
+
+ memneed = cda_size+tda_size+rra_size+rda_size+rba_size;
+ lp->memptr = kmalloc(memneed+32,GFP_KERNEL);
+ if (lp->memptr == NULL) {
+ printk("sonic_open() failed (out of memory).\n");
+ return -EAGAIN;
+ }
+
+ /*
+ * If we don't get a memory block in a single 64kbyte area,
+ * allocate new memory and the free the old block.
+ */
+ while ((unsigned int)lp->memptr>>16 != ((unsigned int)lp->memptr+memneed+32)>>16) {
+ memptr = lp->memptr;
+ lp->memptr = kmalloc(memneed+32,GFP_KERNEL);
+ kfree(memptr);
+ }
+
+ /*
+ * setup the buffer pointers
+ */
+ lp->cda_vaddr = (SONIC_CAM_DESCRIPTOR_AREA *)((KSEG1ADDR(lp->memptr) + 15) & ~15);
+ lp->tda_vaddr = (SONIC_TRANSMIT_DESCRIPTOR *)(KSEG1ADDR(lp->cda_vaddr) + cda_size);
+ lp->rra_vaddr = (SONIC_RECEIVE_RESOURCE *)(KSEG1ADDR(lp->tda_vaddr) + tda_size);
+ lp->rda_vaddr = (SONIC_RECEIVE_DESCRIPTOR *)(KSEG1ADDR(lp->rra_vaddr) + rra_size);
+ lp->rba_vaddr = (char *)(KSEG1ADDR(lp->rda_vaddr) + rda_size);
+#if 0
+ if (sonic_debug > 2)
+ printk("cda_v=%p, tda_v=%p, rra_v=%p, rda_v=%p, rba_v=%p\n",
+ lp->cda_vaddr,lp->tda_vaddr,lp->rra_vaddr,lp->rda_vaddr,lp->rba_vaddr);
+#endif
+ /*
+ * allocate DMA pagetables
+ */
+ laddr = vdma_alloc(PHYSADDR(lp->cda_vaddr),memneed);
+ if (laddr == 0xffffffff) {
+ printk("sonic_open() failed (out of dma pagetables).\n");
+ return -EAGAIN;
+ }
+
+ lp->cda_laddr = laddr;
+ lp->tda_laddr = laddr + cda_size;
+ lp->rra_laddr = laddr + cda_size+tda_size;
+ lp->rda_laddr = laddr + cda_size+tda_size+rra_size;
+ lp->rba_laddr = laddr + cda_size+tda_size+rra_size+rda_size;
+#if 1
+ if (sonic_debug > 2)
+ printk("cda_l=%08x,tda_l=%08x,rra_l=%08x,rda_l=%08x,rba_l=%08x\n",
+ lp->cda_laddr,lp->tda_laddr,lp->rra_laddr,lp->rda_laddr,lp->rba_laddr);
+#endif
+ }
+
+ /*
+ * clear software reset flag, disable receiver, clear and
+ * enable interrupts, then completely initialize the SONIC
+ */
+ sonic_write_register(SONIC_CMD,0);
+ sonic_write_register(SONIC_CMD,SONIC_CR_RXDIS);
+#if 0
+ r4030_write_reg16(JAZZ_IO_IRQ_ENABLE,JAZZ_IE_ETHERNET); /*
+ r4030_read_reg16(JAZZ_IO_IRQ_ENABLE) |
+ JAZZ_IE_ETHERNET); */
+#endif
+ if (sonic_debug > 2)
+ printk("sonic_init: initializing descriptors\n");
+
+ sonic_init_rra(dev); /* initialize the receive resource area */
+ sonic_init_rda(dev); /* initialize the receive descriptor area */
+ sonic_init_cam(dev); /* load and init the CAM */
+ sonic_load_cam(dev);
+ sonic_init_tda(dev); /* initialize the transmit descriptor */
+
+ /*
+ * Now enable the receiver, disable loopback
+ * and enable all interrupts
+ */
+ sonic_write_register(SONIC_CMD,SONIC_CR_RXEN | SONIC_CR_STP);
+#if SONIC_LOOPBACK
+ sonic_write_register(SONIC_RCR,SONIC_RCR_DEFAULT | SONIC_RCR_LB_ENDEC);
+#else
+ sonic_write_register(SONIC_RCR,SONIC_RCR_DEFAULT);
+#endif
+ sonic_write_register(SONIC_TCR,SONIC_TCR_DEFAULT);
+ sonic_write_register(SONIC_ISR,0x7fff);
+ sonic_write_register(SONIC_IMR,SONIC_IMR_DEFAULT);
+
+ cmd = sonic_read_register(SONIC_CMD);
+ if ((cmd & SONIC_CR_RXEN) == 0 |
+ (cmd & SONIC_CR_STP) == 0)
+ printk("sonic_init: failed, status=%x\n",cmd);
+
+ if (sonic_debug > 2)
+ printk("sonic_init: new status=%x\n",sonic_read_register(SONIC_CMD));
+
+ return(0);
+}
+
+/*
+ * Initialize the SONIC CAM
+ */
+
+static void
+sonic_init_cam(struct device *dev)
+{
+ struct sonic_local *lp = (struct sonic_local *)dev->priv;
+ SONIC_CAM_DESCRIPTOR_AREA *cam_area = lp->cda_vaddr;
+ int i;
+
+ cam_area->cam_descriptors[0].cam_entry_pointer = 0;
+ cam_area->cam_descriptors[0].cam_frag2 = dev->dev_addr[1] << 8 | dev->dev_addr[0];
+ cam_area->cam_descriptors[0].cam_frag1 = dev->dev_addr[3] << 8 | dev->dev_addr[2];
+ cam_area->cam_descriptors[0].cam_frag0 = dev->dev_addr[5] << 8 | dev->dev_addr[4];
+
+ /* fill all other CAM ports by some test values */
+
+ for (i=1; i<CAM_DESCRIPTORS; i++) {
+ cam_area->cam_descriptors[i].cam_entry_pointer = i;
+ cam_area->cam_descriptors[i].cam_frag2 = i*4;
+ cam_area->cam_descriptors[i].cam_frag1 = i*4+1;
+ cam_area->cam_descriptors[i].cam_frag0 = i*4+2;
+ }
+ cam_area->cam_enable = 0x1; /* use only CAM entry 0 */
+}
+
+/*
+ * Load the SONIC CAM (Content Adressable Memory)
+ * Actually, this isn't needed as long we're dealing
+ * with the hardware address configured by the BIOS,
+ * but we might want to use multiple hardware addresses.
+ * Later...
+ */
+static void
+sonic_load_cam(struct device *dev)
+{
+ struct sonic_local *lp = (struct sonic_local *)dev->priv;
+
+ /*
+ * Clear software reset flag, disable receiver, then load the
+ * CAM with our hardware address(es).
+ * This assumes that the URRA (Upper Receive Resource Area)
+ * pointer has already been set.
+ */
+ sonic_write_register(SONIC_CMD,0);
+ sonic_write_register(SONIC_CMD,SONIC_CR_RXDIS);
+ sonic_write_register(SONIC_CE,(1<<CAM_DESCRIPTORS)-1);
+ sonic_write_register(SONIC_CDP, lp->cda_laddr & 0xffff);
+ sonic_write_register(SONIC_CDC,CAM_DESCRIPTORS);
+#if 0
+ if (sonic_debug > 2) {
+ printk("sonic_loadcam: CMD=%x, CME=%x, CDC=%x, CDP=%x, URRA=%x\n",
+ sonic_read_register(SONIC_CMD),
+ sonic_read_register(SONIC_CE),
+ sonic_read_register(SONIC_CDC),
+ sonic_read_register(SONIC_CDP),
+ sonic_read_register(SONIC_URRA));
+ printk("sonic_loadcam: ISR=%x\n",sonic_read_register(SONIC_ISR));
+ printk("sonic_loadcam: issuing LCAM command\n");
+ }
+#endif
+
+ /*
+ * Now load the CAM
+ */
+ sonic_write_register(SONIC_CMD,SONIC_CR_LCAM);
+ udelay(1000);
+ if ((sonic_read_register(SONIC_ISR) & SONIC_INT_LCD) == 0)
+ printk("sonic_load_cam: LCAM command failed!\n");
+
+ if (sonic_debug > 2) {
+ printk("sonic_loadcam: CMD=%x, ISR=%x\n",
+ sonic_read_register(SONIC_CMD),
+ sonic_read_register(SONIC_ISR));
+ if (sonic_debug > 3)
+ sonic_dump_cam();
+ }
+}
+
+/*
+ * initialize the transmit descriptor
+ */
+static void
+sonic_init_tda(struct device *dev)
+{
+ struct sonic_local *lp = (struct sonic_local *)dev->priv;
+ SONIC_TRANSMIT_DESCRIPTOR *td = lp->tda_vaddr;
+
+ td->tx_status = 0;
+ td->tx_config = 0;
+ td->tx_pktsize = 0;
+ td->tx_frag_count = 0;
+ td->link = SONIC_END_OF_LINKS;
+
+ sonic_write_register(SONIC_UTDA,lp->tda_laddr >> 16);
+ sonic_write_register(SONIC_CTDA,lp->tda_laddr & 0xffff);
+#if 0
+ if (sonic_debug > 2)
+ printk("sonic_init_tda: UTDA=%x, CTDA=%x\n",
+ sonic_read_register(SONIC_UTDA),
+ sonic_read_register(SONIC_CTDA));
+#endif
+ /*
+ * Allocate two DMA page for mapping transmit data. Two
+ * are needed since the tx data may cross a page boundary.
+ */
+ lp->pkt_vaddr = NULL; /* no data to send yet */
+ lp->pkt_laddr = vdma_alloc(0,VDMA_PAGESIZE*2);
+
+}
+
+/*
+ * initialize the receive resource area (RRA)
+ */
+static void
+sonic_init_rra(struct device *dev)
+{
+ struct sonic_local *lp = (struct sonic_local *)dev->priv;
+ unsigned int rra_start;
+ unsigned int rra_end;
+
+ rra_start = lp->rra_laddr & 0xffff;
+ rra_end = (rra_start + sizeof(SONIC_RECEIVE_RESOURCE)) & 0xffff;
+
+ lp->rra_vaddr->rx_bufadr_l = lp->rba_laddr & 0xffff;
+ lp->rra_vaddr->rx_bufadr_h = lp->rba_laddr >> 16;
+ lp->rra_vaddr->rx_bufsize_l = SONIC_RXBUFSIZE & 0xffff;
+ lp->rra_vaddr->rx_bufsize_h = SONIC_RXBUFSIZE >> 16;
+
+ /* initialize all registers */
+
+ sonic_write_register(SONIC_RSA,rra_start);
+ sonic_write_register(SONIC_REA,rra_end);
+ sonic_write_register(SONIC_RRP,rra_start);
+ sonic_write_register(SONIC_RWP,rra_end);
+ sonic_write_register(SONIC_URRA,lp->rra_laddr >> 16);
+
+ /* load the resource pointers */
+
+ if (sonic_debug > 2)
+ printk("sonic_init_rra: issueing RRRA command\n");
+
+ sonic_write_register(SONIC_CMD,SONIC_CR_RRRA);
+ udelay(1000);
+ while(sonic_read_register(SONIC_CMD) & SONIC_CR_RRRA) {
+ if (sonic_debug > 2)
+ printk(".");
+ }
+ if (sonic_debug > 2)
+ printk("sonic_init_rra: status=%x\n",sonic_read_register(SONIC_CMD));
+}
+
+
+/*
+ * initialize the receive descriptor area (RDA)
+ */
+static void
+sonic_init_rda(struct device *dev)
+{
+ struct sonic_local *lp = (struct sonic_local *)dev->priv;
+ SONIC_RECEIVE_DESCRIPTOR *rd;
+ unsigned int laddr = lp->rda_laddr;
+ int i;
+
+ /*
+ * Initialize the receive descriptors so that they
+ * become a circular linked list, ie. let the last
+ * descriptor point to the first again.
+ */
+ rd = lp->rda_vaddr;
+ for (i=0; i<SONIC_MAX_RDS; i++) {
+ laddr += sizeof(SONIC_RECEIVE_DESCRIPTOR);
+ rd[i].rx_status = 0;
+ rd[i].rx_pktlen = 0;
+ rd[i].rx_pktptr_l = 0;
+ rd[i].rx_pktptr_h = 0;
+ rd[i].rx_seqno = 0;
+ rd[i].link = laddr & 0xffff;
+ rd[i].in_use = 1; /* not in use */
+ }
+ rd[i-1].link = lp->rda_laddr | SONIC_END_OF_LINKS;
+ lp->rd_next = 0;
+
+ sonic_write_register(SONIC_URDA,lp->rda_laddr >> 16);
+ sonic_write_register(SONIC_CRDA,lp->rda_laddr & 0xffff);
+
+}
+
+static void sonic_dump_cam()
+{
+ int i,j;
+ int val;
+
+ printk("sonic_dump_cam: CME=%x, CDC=%x, CDP=%x, URRA=%x\n",
+ sonic_read_register(SONIC_CE),
+ sonic_read_register(SONIC_CDC),
+ sonic_read_register(SONIC_CDP),
+ sonic_read_register(SONIC_URRA));
+
+ sonic_write_register(SONIC_CMD,SONIC_CR_RST);
+ for (i=0; i<CAM_DESCRIPTORS; i++) {
+ sonic_write_register(SONIC_CEP,i);
+ printk("cam entry %d = ",i);
+ for (j=0; j<3; j++)
+ printk("%04x ",sonic_read_register(SONIC_CAP0-j));
+ printk("\n");
+ }
+
+ sonic_write_register(SONIC_CEP,0);
+ for (i=0; i<3; i++) {
+ val = sonic_read_register(SONIC_CAP0-i);
+ printk("%2.2x:%2.2x:",val&0xff,val>>8);
+ }
+ printk("\n");
+ sonic_write_register(SONIC_CMD,0); /* clear reset */
+}
+
+/*
+ * Let the driver check itself. We repeatedly send packets
+ * and expect them to be returned thru the Sonic loopback
+ * mode.
+ */
+#if SONIC_DRIVER_TEST
+static void sonic_test(struct device *dev)
+{
+ struct sk_buff *skb;
+ int i;
+
+ printk("SONIC TEST MODE STARTED\n");
+
+ /*
+ * switch the sonic to promiscous mode if we don't
+ * use the loopback mode. Otherwise we won't receive
+ * anything except occasional broadcast packets.
+ */
+#if !SONIC_LOOPBACK
+ set_multicast_list(dev,-1,0);
+#endif
+
+ /*
+ * allocate a single packet buffer and continously
+ * transmit this packet.
+ */
+ skb = (struct sk_buff *)KSEG1ADDR(kmalloc(sizeof(struct sk_buff)+128,GFP_KERNEL));
+ skb->len = 128;
+ for (i=0; i<6; i++) {
+ skb->data[i] = dev->dev_addr[i]; /* dst address */
+ skb->data[i+6] = dev->dev_addr[i]; /* src address */
+ }
+ skb->data[12] = 0;
+ skb->data[13] = 128;
+
+ for (i=0; i<32; i++)
+ skb->data[i+14] = i;
+
+ printk("sonic_test: Sending: skb=%p, data=%p\n",skb,skb->data);
+
+ while(1) {
+ hardware_send_packet(dev, skb->data, 128);
+ i = 1000;
+ while((dev->tbusy) && (--i))
+ udelay(2000);
+ if (!i)
+ printk("sonic_test: TRANSMIT FAILED!\n");
+ skb->data[14]++;
+ }
+}
+#endif
+
+
+
+/*
+ * Local variables:
+ * compile-command: "mips-linux-gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -mcpu=r4000 -c sonic.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * End:
+ */
diff --git a/drivers/net/sonic.h b/drivers/net/sonic.h
new file mode 100644
index 000000000..81487e2cd
--- /dev/null
+++ b/drivers/net/sonic.h
@@ -0,0 +1,359 @@
+/*
+ * Helpfile for sonic.c
+ *
+ * (C) Waldorf Electronics, Germany
+ * Written by Andreas Busse
+ *
+ * NOTE: most of the structure definitions here are endian dependent.
+ * If you want to use this driver on big endian machines, the data
+ * and pad structure members must be exchanged. Also, the structures
+ * need to be changed accordingly to the bus size.
+ *
+ */
+
+#ifndef SONIC_H
+#define SONIC_H
+
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+/*
+ * Macros to access SONIC registers
+ */
+
+#define sonic_read_register(reg) \
+ *((volatile unsigned int *)SONIC_BASE_ADDR+reg)
+
+#define sonic_write_register(reg,val) \
+ *((volatile unsigned int *)SONIC_BASE_ADDR+reg) = val
+
+/*
+ * Base address of the SONIC controller on JAZZ boards
+ */
+
+#define SONIC_BASE_ADDR 0xe0001000
+
+/*
+ * SONIC register offsets
+ */
+
+#define SONIC_CMD 0x00
+#define SONIC_DCR 0x01
+#define SONIC_RCR 0x02
+#define SONIC_TCR 0x03
+#define SONIC_IMR 0x04
+#define SONIC_ISR 0x05
+
+#define SONIC_UTDA 0x06
+#define SONIC_CTDA 0x07
+
+#define SONIC_URDA 0x0d
+#define SONIC_CRDA 0x0e
+#define SONIC_EOBC 0x13
+#define SONIC_URRA 0x14
+#define SONIC_RSA 0x15
+#define SONIC_REA 0x16
+#define SONIC_RRP 0x17
+#define SONIC_RWP 0x18
+#define SONIC_RSC 0x2b
+
+#define SONIC_CEP 0x21
+#define SONIC_CAP2 0x22
+#define SONIC_CAP1 0x23
+#define SONIC_CAP0 0x24
+#define SONIC_CE 0x25
+#define SONIC_CDP 0x26
+#define SONIC_CDC 0x27
+
+#define SONIC_WT0 0x29
+#define SONIC_WT1 0x2a
+
+#define SONIC_SR 0x28
+
+
+/* test-only registers */
+
+#define SONIC_TPS 0x08
+#define SONIC_TFC 0x09
+#define SONIC_TSA0 0x0a
+#define SONIC_TSA1 0x0b
+#define SONIC_TFS 0x0c
+
+#define SONIC_CRBA0 0x0f
+#define SONIC_CRBA1 0x10
+#define SONIC_RBWC0 0x11
+#define SONIC_RBWC1 0x12
+#define SONIC_TTDA 0x20
+#define SONIC_MDT 0x2f
+
+#define SONIC_TRBA0 0x19
+#define SONIC_TRBA1 0x1a
+#define SONIC_TBWC0 0x1b
+#define SONIC_TBWC1 0x1c
+#define SONIC_LLFA 0x1f
+
+#define SONIC_ADDR0 0x1d
+#define SONIC_ADDR1 0x1e
+
+/*
+ * Error counters
+ */
+#define SONIC_CRCT 0x2c
+#define SONIC_FAET 0x2d
+#define SONIC_MPT 0x2e
+
+
+/*
+ * SONIC command bits
+ */
+
+#define SONIC_CR_LCAM 0x0200
+#define SONIC_CR_RRRA 0x0100
+#define SONIC_CR_RST 0x0080
+#define SONIC_CR_ST 0x0020
+#define SONIC_CR_STP 0x0010
+#define SONIC_CR_RXEN 0x0008
+#define SONIC_CR_RXDIS 0x0004
+#define SONIC_CR_TXP 0x0002
+#define SONIC_CR_HTX 0x0001
+
+/*
+ * SONIC data configuration bits
+ */
+
+#define SONIC_DCR_EXBUS 0x8000
+#define SONIC_DCR_LBR 0x2000
+#define SONIC_DCR_PO1 0x1000
+#define SONIC_DCR_PO0 0x0800
+#define SONIC_DCR_SBUS 0x0400
+#define SONIC_DCR_USR1 0x0200
+#define SONIC_DCR_USR0 0x0100
+#define SONIC_DCR_WC1 0x0080
+#define SONIC_DCR_WC0 0x0040
+#define SONIC_DCR_DW 0x0020
+#define SONIC_DCR_BMS 0x0010
+#define SONIC_DCR_RFT1 0x0008
+#define SONIC_DCR_RFT0 0x0004
+#define SONIC_DCR_TFT1 0x0002
+#define SONIC_DCR_TFT0 0x0001
+
+/*
+ * Constants for the SONIC receive control register.
+ */
+
+#define SONIC_RCR_ERR 0x8000
+#define SONIC_RCR_RNT 0x4000
+#define SONIC_RCR_BRD 0x2000
+#define SONIC_RCR_PRO 0x1000
+#define SONIC_RCR_AMC 0x0800
+#define SONIC_RCR_LB1 0x0400
+#define SONIC_RCR_LB0 0x0200
+
+#define SONIC_RCR_MC 0x0100
+#define SONIC_RCR_BC 0x0080
+#define SONIC_RCR_LPKT 0x0040
+#define SONIC_RCR_CRS 0x0020
+#define SONIC_RCR_COL 0x0010
+#define SONIC_RCR_CRCR 0x0008
+#define SONIC_RCR_FAER 0x0004
+#define SONIC_RCR_LBK 0x0002
+#define SONIC_RCR_PRX 0x0001
+
+#define SONIC_RCR_LB_OFF 0
+#define SONIC_RCR_LB_MAC SONIC_RCR_LB0
+#define SONIC_RCR_LB_ENDEC SONIC_RCR_LB1
+#define SONIC_RCR_LB_TRANS (SONIC_RCR_LB0 | SONIC_RCR_LB1)
+
+/* default RCR setup */
+
+#define SONIC_RCR_DEFAULT (SONIC_RCR_ERR | SONIC_RCR_RNT)
+
+
+/*
+ * SONIC Transmit Control register bits
+ */
+
+#define SONIC_TCR_PINTR 0x8000
+#define SONIC_TCR_POWC 0x4000
+#define SONIC_TCR_CRCI 0x2000
+#define SONIC_TCR_EXDIS 0x1000
+#define SONIC_TCR_EXD 0x0400
+#define SONIC_TCR_DEF 0x0200
+#define SONIC_TCR_NCRS 0x0100
+#define SONIC_TCR_CRLS 0x0080
+#define SONIC_TCR_EXC 0x0040
+#define SONIC_TCR_PMB 0x0008
+#define SONIC_TCR_FU 0x0004
+#define SONIC_TCR_BCM 0x0002
+#define SONIC_TCR_PTX 0x0001
+
+#define SONIC_TCR_DEFAULT 0x0000
+
+/*
+ * Constants for the SONIC_INTERRUPT_MASK and
+ * SONIC_INTERRUPT_STATUS registers.
+ */
+
+#define SONIC_INT_BR 0x4000
+#define SONIC_INT_HBL 0x2000
+#define SONIC_INT_LCD 0x1000
+#define SONIC_INT_PINT 0x0800
+#define SONIC_INT_PKTRX 0x0400
+#define SONIC_INT_TXDN 0x0200
+#define SONIC_INT_TXER 0x0100
+#define SONIC_INT_TC 0x0080
+#define SONIC_INT_RDE 0x0040
+#define SONIC_INT_RBE 0x0020
+#define SONIC_INT_RBAE 0x0010
+#define SONIC_INT_CRC 0x0008
+#define SONIC_INT_FAE 0x0004
+#define SONIC_INT_MP 0x0002
+#define SONIC_INT_RFO 0x0001
+
+
+/*
+ * The interrupts we allow.
+ */
+
+#define SONIC_IMR_DEFAULT (SONIC_INT_BR | \
+ SONIC_INT_LCD | \
+ SONIC_INT_PINT | \
+ SONIC_INT_PKTRX | \
+ SONIC_INT_TXDN | \
+ SONIC_INT_TXER | \
+ SONIC_INT_RDE | \
+ SONIC_INT_RBE | \
+ SONIC_INT_RBAE | \
+ SONIC_INT_CRC | \
+ SONIC_INT_FAE | \
+ SONIC_INT_MP)
+
+
+#define SONIC_MAX_FRAGMENTS 4
+#define SONIC_MIN_FRAGMENT_SIZE 12
+
+#define SONIC_MIN_PACKET_SIZE 60
+
+#define SONIC_END_OF_LINKS 0x0001
+
+
+/*
+ * The number of receive descriptors and the
+ * buffer size we allocate by default
+ */
+#define SONIC_MAX_RDS 64
+#define SONIC_RXBUFSIZE (1024 * 16)
+
+
+/*
+ * structure definitions
+ */
+
+typedef struct SONIC_RECEIVE_RESOURCE {
+
+ unsigned int rx_bufadr_l; /* receive buffer ptr */
+ unsigned int rx_bufadr_h;
+
+ unsigned int rx_bufsize_l; /* no. of words in the receive buffer */
+ unsigned int rx_bufsize_h;
+
+} SONIC_RECEIVE_RESOURCE;
+
+/*
+ * Sonic receive descriptor. Receive descriptors are
+ * kept in a linked list of these structures.
+ */
+
+typedef struct SONIC_RECEIVE_DESCRIPTOR {
+
+ unsigned short rx_status; /* status after reception of a packet */
+ unsigned short pad0;
+ unsigned short rx_pktlen; /* length of the packet incl. CRC */
+ unsigned short pad1;
+
+ /*
+ * Pointers to the location in the receive buffer area (RBA)
+ * where the packet resides. A packet is always received into
+ * a contiguous piece of memory.
+ */
+
+ unsigned short rx_pktptr_l;
+ unsigned short pad2;
+ unsigned short rx_pktptr_h;
+ unsigned short pad3;
+
+ unsigned short rx_seqno; /* sequence no. */
+ unsigned short pad4;
+
+ unsigned short link; /* link to next RDD (end if EOL bit set) */
+ unsigned short pad5;
+
+ /*
+ * Owner of this descriptor, 0= driver, 1=sonic
+ */
+
+ unsigned short in_use;
+ unsigned short pad6;
+
+ caddr_t rda_next; /* pointer to next RD */
+
+} SONIC_RECEIVE_DESCRIPTOR;
+
+
+/*
+ * Describes a Transmit Descriptor
+ */
+typedef struct SONIC_TRANSMIT_DESCRIPTOR {
+
+ unsigned short tx_status; /* status after transmission of a packet */
+ unsigned short pad0;
+ unsigned short tx_config; /* transmit configuration for this packet */
+ unsigned short pad1;
+ unsigned short tx_pktsize; /* size of the packet to be transmitted */
+ unsigned short pad2;
+ unsigned short tx_frag_count; /* no. of fragments */
+ unsigned short pad3;
+
+ unsigned short tx_frag_ptr_l;
+ unsigned short pad4;
+ unsigned short tx_frag_ptr_h;
+ unsigned short pad5;
+ unsigned short tx_frag_size;
+ unsigned short pad6;
+
+ unsigned short link; /* ptr to next descriptor */
+ unsigned short pad7;
+
+} SONIC_TRANSMIT_DESCRIPTOR;
+
+
+/*
+ * Describes an entry in the CAM Descriptor Area.
+ */
+
+typedef struct SONIC_CAM_DESCRIPTOR
+{
+ unsigned short cam_entry_pointer;
+ unsigned short pad;
+ unsigned short cam_frag2;
+ unsigned short pad2;
+ unsigned short cam_frag1;
+ unsigned short pad1;
+ unsigned short cam_frag0;
+ unsigned short pad0;
+
+} SONIC_CAM_DESCRIPTOR;
+
+#define CAM_DESCRIPTORS 16
+
+
+typedef struct SONIC_CAM_DESCRIPTOR_AREA
+{
+ SONIC_CAM_DESCRIPTOR cam_descriptors[CAM_DESCRIPTORS];
+ unsigned short cam_enable;
+ unsigned short pad;
+
+} SONIC_CAM_DESCRIPTOR_AREA;
+
+
+#endif /* SONIC_H */
diff --git a/drivers/net/tulip.c b/drivers/net/tulip.c
new file mode 100644
index 000000000..4a4cde71e
--- /dev/null
+++ b/drivers/net/tulip.c
@@ -0,0 +1,737 @@
+/* tulip.c: A DEC 21040 ethernet driver for linux. */
+/*
+ NOTICE: this version works with kernels 1.1.82 and later only!
+ Written 1994,1995 by Donald Becker.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ This driver is for the SMC EtherPower PCI ethernet adapter.
+ It should work with most other DEC 21*40-based ethercards.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+*/
+
+static char *version = "tulip.c:v0.05 1/20/95 becker@cesdis.gsfc.nasa.gov\n";
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+/* This will be in linux/etherdevice.h someday. */
+struct device *init_etherdev(struct device *dev, int sizeof_private,
+ unsigned long *mem_startp);
+
+/* The total size is unusually large: The 21040 aligns each of its 16
+ longword-wide registers on a quadword boundary. */
+#define TULIP_TOTAL_SIZE 0x80
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry tulip_drv =
+{"Tulip", tulip_pci_probe, TULIP_TOTAL_SIZE, NULL};
+#endif
+
+#define TULIP_DEBUG 1
+#ifdef TULIP_DEBUG
+int tulip_debug = TULIP_DEBUG;
+#else
+int tulip_debug = 1;
+#endif
+
+/*
+ Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the DECchip 21040 "Tulip", Digital's
+single-chip ethernet controller for PCI, as used on the SMC EtherPower
+ethernet adapter.
+
+II. Board-specific settings
+
+PCI bus devices are configured by the system at boot time, so no jumpers
+need to be set on the board. The system BIOS should be set to assign the
+PCI INTA signal to an otherwise unused system IRQ line. While it's
+physically possible to shared PCI interrupt lines, the kernel doesn't
+support it.
+
+III. Driver operation
+
+IIIa. Ring buffers
+The Tulip can use either ring buffers or lists of Tx and Rx descriptors.
+The current driver uses a statically allocated Rx ring of descriptors and
+buffers, and a list of the Tx buffers.
+
+IIIC. Synchronization
+The driver runs as two independent, single-threaded flows of control. One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag. The other thread is the interrupt handler, which is single
+threaded by the hardware and other software.
+
+The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+the 'tp->tx_full' flag.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so
+we can't avoid the interrupt overhead by having the Tx routine reap the Tx
+stats.) After reaping the stats, it marks the queue entry as empty by setting
+the 'base' to zero. Iff the 'tp->tx_full' flag is set, it clears both the
+tx_full and tbusy flags.
+
+IV. Notes
+
+Thanks to Duke Kamstra of SMC for providing an EtherPower board.
+
+The DEC databook doesn't document which Rx filter settings accept broadcast
+packets. Nor does it document how to configure the part to configure the
+serial subsystem for normal (vs. loopback) operation or how to have it
+autoswitch between internal 10baseT, SIA and AUI transceivers.
+
+The databook claims that CSR13, CSR14, and CSR15 should each be the last
+register of the set CSR12-15 written. Hmmm, now how is that possible?
+*/
+
+#define DEC_VENDOR_ID 0x1011 /* Hex 'D' :-> */
+#define DEC_21040_ID 0x0002 /* Change for 21140. */
+
+/* Keep the ring sizes a power of two for efficiency. */
+#define TX_RING_SIZE 4
+#define RX_RING_SIZE 4
+#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
+
+/* Offsets to the Command and Status Registers, "CSRs". All accesses
+ must be longword instructions and quadword aligned. */
+enum tulip_offsets {
+ CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
+ CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
+ CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 };
+
+/* The Tulip Rx and Tx buffer descriptors. */
+struct tulip_rx_desc {
+ int status;
+ int length;
+ char *buffer1, *buffer2; /* We use only buffer 1. */
+};
+
+struct tulip_tx_desc {
+ int status;
+ int length;
+ char *buffer1, *buffer2; /* We use only buffer 1. */
+};
+
+struct tulip_private {
+ char devname[8]; /* Used only for kernel debugging. */
+ struct tulip_rx_desc rx_ring[RX_RING_SIZE];
+ struct tulip_tx_desc tx_ring[TX_RING_SIZE];
+ /* The saved address of a sent-in-place packet/buffer, for skfree(). */
+ struct sk_buff* tx_skbuff[TX_RING_SIZE];
+ long rx_buffs; /* Address of temporary Rx buffers. */
+ struct enet_statistics stats;
+ int setup_frame[48]; /* Pseudo-Tx frame to init address table. */
+ unsigned int cur_rx, cur_tx; /* The next free ring entry */
+ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
+ unsigned int tx_full:1;
+ int pad0, pad1; /* Used for 8-byte alignment */
+};
+
+static unsigned long tulip_probe1(unsigned long mem_start, int ioaddr,
+ int irq);
+static int tulip_open(struct device *dev);
+static void tulip_init_ring(struct device *dev);
+static int tulip_start_xmit(struct sk_buff *skb, struct device *dev);
+static int tulip_rx(struct device *dev);
+static void tulip_interrupt(int irq, struct pt_regs *regs);
+static int tulip_close(struct device *dev);
+static struct enet_statistics *tulip_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+static int set_mac_address(struct device *dev, void *addr);
+
+
+
+/* This 21040 probe is unlike most other board probes. We can use memory
+ efficiently by allocating a large contiguous region and dividing it
+ ourselves. This is done by having the initialization occur before
+ the 'kmalloc()' memory management system is started. */
+
+unsigned long dec21040_init(unsigned long mem_start, unsigned long mem_end)
+{
+
+ if (pcibios_present()) {
+ int pci_index;
+ for (pci_index = 0; pci_index < 8; pci_index++) {
+ unsigned char pci_bus, pci_device_fn, pci_irq_line;
+ unsigned long pci_ioaddr;
+
+ if (pcibios_find_device (DEC_VENDOR_ID, DEC_21040_ID, pci_index,
+ &pci_bus, &pci_device_fn) != 0)
+ break;
+ pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &pci_irq_line);
+ pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &pci_ioaddr);
+ /* Remove I/O space marker in bit 0. */
+ pci_ioaddr &= ~3;
+ if (tulip_debug > 2)
+ printk("Found DEC PCI Tulip at I/O %#lx, IRQ %d.\n",
+ pci_ioaddr, pci_irq_line);
+ mem_start = tulip_probe1(mem_start, pci_ioaddr, pci_irq_line);
+ }
+ }
+
+ return mem_start;
+}
+
+unsigned long tulip_probe1(unsigned long mem_start, int ioaddr, int irq)
+{
+ static int did_version = 0; /* Already printed version info. */
+ struct device *dev;
+ struct tulip_private *tp;
+ int i;
+
+ if (tulip_debug > 0 && did_version++ == 0)
+ printk(version);
+
+ dev = init_etherdev(0, sizeof(struct tulip_private)
+ + PKT_BUF_SZ*RX_RING_SIZE,
+ &mem_start);
+
+ printk("%s: DEC 21040 Tulip at %#3x,", dev->name, ioaddr);
+
+ /* Stop the chip's Tx and Rx processes. */
+ outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
+ /* Clear the missed-packet counter. */
+ inl(ioaddr + CSR8) & 0xffff;
+
+ /* The station address ROM is read byte serially. The register must
+ be polled, waiting for the value to be read bit serially from the
+ EEPROM.
+ */
+ outl(0, ioaddr + CSR9); /* Reset the pointer with a dummy write. */
+ for (i = 0; i < 6; i++) {
+ int value, boguscnt = 100000;
+ do
+ value = inl(ioaddr + CSR9);
+ while (value < 0 && --boguscnt > 0);
+ printk(" %2.2x", dev->dev_addr[i] = value);
+ }
+ printk(", IRQ %d\n", irq);
+
+ /* We do a request_region() only to register /proc/ioports info. */
+ request_region(ioaddr, TULIP_TOTAL_SIZE, "DEC Tulip Ethernet");
+
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+
+ /* Make certain the data structures are quadword aligned. */
+ dev->priv = (void *)(((int)dev->priv + 7) & ~7);
+ tp = (struct tulip_private *)dev->priv;
+ tp->rx_buffs = (long)dev->priv + sizeof(struct tulip_private);
+
+ /* The Tulip-specific entries in the device structure. */
+ dev->open = &tulip_open;
+ dev->hard_start_xmit = &tulip_start_xmit;
+ dev->stop = &tulip_close;
+ dev->get_stats = &tulip_get_stats;
+#ifdef HAVE_MULTICAST
+ dev->set_multicast_list = &set_multicast_list;
+#endif
+#ifdef HAVE_SET_MAC_ADDR
+ dev->set_mac_address = &set_mac_address;
+#endif
+
+ return mem_start;
+}
+
+
+static int
+tulip_open(struct device *dev)
+{
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ /* Reset the chip, holding bit 0 set at least 10 PCI cycles. */
+ outl(0xfff80001, ioaddr + CSR0);
+ SLOW_DOWN_IO;
+ /* Deassert reset. Set 8 longword cache alignment, 8 longword burst.
+ Cache alignment bits 15:14 Burst length 13:8
+ 0000 No alignment 0x00000000 unlimited 0800 8 longwords
+ 4000 8 longwords 0100 1 longword 1000 16 longwords
+ 8000 16 longwords 0200 2 longwords 2000 32 longwords
+ C000 32 longwords 0400 4 longwords
+ Wait the specified 50 PCI cycles after a reset by initializing
+ Tx and Rx queues and the address filter list. */
+ outl(0xfff84800, ioaddr + CSR0);
+
+ if (irq2dev_map[dev->irq] != NULL
+ || (irq2dev_map[dev->irq] = dev) == NULL
+ || dev->irq == 0
+ || request_irq(dev->irq, &tulip_interrupt, 0, "DEC 21040 Tulip")) {
+ return -EAGAIN;
+ }
+
+ if (tulip_debug > 1)
+ printk("%s: tulip_open() irq %d.\n", dev->name, dev->irq);
+
+ tulip_init_ring(dev);
+
+ /* Fill the whole address filter table with our physical address. */
+ {
+ unsigned short *eaddrs = (unsigned short *)dev->dev_addr;
+ int *setup_frm = tp->setup_frame, i;
+
+ /* You must add the broadcast address when doing perfect filtering! */
+ *setup_frm++ = 0xffff;
+ *setup_frm++ = 0xffff;
+ *setup_frm++ = 0xffff;
+ /* Fill the rest of the accept table with our physical address. */
+ for (i = 1; i < 16; i++) {
+ *setup_frm++ = eaddrs[0];
+ *setup_frm++ = eaddrs[1];
+ *setup_frm++ = eaddrs[2];
+ }
+ /* Put the setup frame on the Tx list. */
+ tp->tx_ring[0].length = 0x08000000 | 192;
+ tp->tx_ring[0].buffer1 = (char *)tp->setup_frame;
+ tp->tx_ring[0].buffer2 = 0;
+ tp->tx_ring[0].status = 0x80000000;
+
+ tp->cur_tx++, tp->dirty_tx++;
+ }
+
+ outl((int)tp->rx_ring, ioaddr + CSR3);
+ outl((int)tp->tx_ring, ioaddr + CSR4);
+
+ /* Turn on the xcvr interface. */
+ outl(0x00000000, ioaddr + CSR13);
+ outl(0x00000004, ioaddr + CSR13);
+
+ /* Start the chip's Tx and Rx processes. */
+ outl(0xfffe2002, ioaddr + CSR6);
+
+ /* Trigger an immediate transmit demand to process the setup frame. */
+ outl(0, ioaddr + CSR1);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ /* Enable interrupts by setting the interrupt mask. */
+ outl(0xFFFFFFFF, ioaddr + CSR7);
+
+ if (tulip_debug > 2) {
+ printk("%s: Done tulip_open(), CSR0 %8.8x, CSR13 %8.8x.\n",
+ dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR13));
+ }
+ return 0;
+}
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void
+tulip_init_ring(struct device *dev)
+{
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ int i;
+
+ tp->tx_full = 0;
+ tp->cur_rx = tp->cur_tx = 0;
+ tp->dirty_rx = tp->dirty_tx = 0;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ tp->rx_ring[i].status = 0x80000000; /* Owned by Tulip chip */
+ tp->rx_ring[i].length = PKT_BUF_SZ;
+ tp->rx_ring[i].buffer1 = (char *)(tp->rx_buffs + i*PKT_BUF_SZ);
+ tp->rx_ring[i].buffer2 = (char *)&tp->rx_ring[i+1];
+ }
+ /* Mark the last entry as wrapping the ring. */
+ tp->rx_ring[i-1].length = PKT_BUF_SZ | 0x02000000;
+ tp->rx_ring[i-1].buffer2 = (char *)&tp->rx_ring[0];
+
+ /* The Tx buffer descriptor is filled in as needed, but we
+ do need to clear the ownership bit. */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ tp->tx_ring[i].status = 0x00000000;
+ }
+}
+
+static int
+tulip_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int entry;
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ int i;
+ if (tickssofar < 20)
+ return 1;
+ printk("%s: transmit timed out, status %8.8x, SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
+ dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12),
+ inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15));
+ printk(" Rx ring %8.8x: ", (int)tp->rx_ring);
+ for (i = 0; i < RX_RING_SIZE; i++)
+ printk(" %8.8x", (unsigned int)tp->rx_ring[i].status);
+ printk("\n Tx ring %8.8x: ", (int)tp->tx_ring);
+ for (i = 0; i < TX_RING_SIZE; i++)
+ printk(" %8.8x", (unsigned int)tp->tx_ring[i].status);
+ printk("\n");
+
+ tp->stats.tx_errors++;
+ /* We should reinitialize the hardware here. */
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ return 0;
+ }
+
+ if (skb == NULL || skb->len <= 0) {
+ printk("%s: Obsolete driver layer request made: skbuff==NULL.\n",
+ dev->name);
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
+ If this ever occurs the queue layer is doing something evil! */
+ if (set_bit(0, (void*)&dev->tbusy) != 0) {
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ return 1;
+ }
+
+ /* Caution: the write order is important here, set the base address
+ with the "ownership" bits last. */
+
+ /* Calculate the next Tx descriptor entry. */
+ entry = tp->cur_tx % TX_RING_SIZE;
+
+ tp->tx_full = 1;
+ tp->tx_skbuff[entry] = skb;
+ tp->tx_ring[entry].length = skb->len |
+ (entry == TX_RING_SIZE-1 ? 0xe2000000 : 0xe0000000);
+ tp->tx_ring[entry].buffer1 = skb->data;
+ tp->tx_ring[entry].buffer2 = 0;
+ tp->tx_ring[entry].status = 0x80000000; /* Pass ownership to the chip. */
+
+ tp->cur_tx++;
+
+ /* Trigger an immediate transmit demand. */
+ outl(0, ioaddr + CSR1);
+
+ dev->trans_start = jiffies;
+
+ return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+ after the Tx thread. */
+static void tulip_interrupt(int irq, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct tulip_private *lp;
+ int csr5, ioaddr, boguscnt=10;
+
+ if (dev == NULL) {
+ printk ("tulip_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ ioaddr = dev->base_addr;
+ lp = (struct tulip_private *)dev->priv;
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+ dev->interrupt = 1;
+
+ do {
+ csr5 = inl(ioaddr + CSR5);
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ outl(csr5 & 0x0001ffff, ioaddr + CSR5);
+
+ if (tulip_debug > 4)
+ printk("%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n",
+ dev->name, csr5, inl(dev->base_addr + CSR5));
+
+ if ((csr5 & 0x00018000) == 0)
+ break;
+
+ if (csr5 & 0x0040) /* Rx interrupt */
+ tulip_rx(dev);
+
+ if (csr5 & 0x0001) { /* Tx-done interrupt */
+ int dirty_tx = lp->dirty_tx;
+
+ while (dirty_tx < lp->cur_tx) {
+ int entry = dirty_tx % TX_RING_SIZE;
+ int status = lp->tx_ring[entry].status;
+
+ if (status < 0)
+ break; /* It still hasn't been Txed */
+
+ if (status & 0x8000) {
+ /* There was an major error, log it. */
+ lp->stats.tx_errors++;
+ if (status & 0x4104) lp->stats.tx_aborted_errors++;
+ if (status & 0x0C00) lp->stats.tx_carrier_errors++;
+ if (status & 0x0200) lp->stats.tx_window_errors++;
+ if (status & 0x0002) lp->stats.tx_fifo_errors++;
+ if (status & 0x0080) lp->stats.tx_heartbeat_errors++;
+#ifdef ETHER_STATS
+ if (status & 0x0100) lp->stats.collisions16++;
+#endif
+ } else {
+#ifdef ETHER_STATS
+ if (status & 0x0001) lp->stats.tx_deferred++;
+#endif
+ lp->stats.collisions += (status >> 3) & 15;
+ lp->stats.tx_packets++;
+ }
+
+ /* Free the original skb. */
+ dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE);
+ dirty_tx++;
+ }
+
+#ifndef final_version
+ if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {
+ printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+ dirty_tx, lp->cur_tx, lp->tx_full);
+ dirty_tx += TX_RING_SIZE;
+ }
+#endif
+
+ if (lp->tx_full && dev->tbusy
+ && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
+ /* The ring is no longer full, clear tbusy. */
+ lp->tx_full = 0;
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+
+ lp->dirty_tx = dirty_tx;
+ }
+
+ /* Log errors. */
+ if (csr5 & 0x8000) { /* Abnormal error summary bit. */
+ if (csr5 & 0x0008) lp->stats.tx_errors++; /* Tx babble. */
+ if (csr5 & 0x0100) { /* Missed a Rx frame. */
+ lp->stats.rx_errors++;
+ lp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+ }
+ if (csr5 & 0x0800) {
+ printk("%s: Something Wicked happened! %8.8x.\n",
+ dev->name, csr5);
+ /* Hmmmmm, it's not clear what to do here. */
+ }
+ }
+ if (--boguscnt < 0) {
+ printk("%s: Too much work at interrupt, csr5=0x%8.8x.\n",
+ dev->name, csr5);
+ /* Clear all interrupt sources. */
+ outl(0x0001ffff, ioaddr + CSR5);
+ break;
+ }
+ } while (1);
+
+ if (tulip_debug > 3)
+ printk("%s: exiting interrupt, csr5=%#4.4x.\n",
+ dev->name, inl(ioaddr + CSR5));
+
+ /* Special code for testing *only*. */
+ {
+ static int stopit = 10;
+ if (dev->start == 0 && --stopit < 0) {
+ printk("%s: Emergency stop, looping startup interrupt.\n",
+ dev->name);
+ free_irq(irq);
+ }
+ }
+
+ dev->interrupt = 0;
+ return;
+}
+
+static int
+tulip_rx(struct device *dev)
+{
+ struct tulip_private *lp = (struct tulip_private *)dev->priv;
+ int entry = lp->cur_rx % RX_RING_SIZE;
+ int i;
+
+ if (tulip_debug > 4)
+ printk(" In tulip_rx().\n");
+ /* If we own the next entry, it's a new packet. Send it up. */
+ while (lp->rx_ring[entry].status >= 0) {
+ int status = lp->rx_ring[entry].status;
+
+ if (tulip_debug > 4)
+ printk(" tulip_rx() status was %8.8x.\n", status);
+ if ((status & 0x0300) != 0x0300) {
+ printk("%s: Ethernet frame spanned multiple buffers, status %8.8x!\n",
+ dev->name, status);
+ } else if (status & 0x8000) {
+ /* There was a fatal error. */
+ lp->stats.rx_errors++; /* end of a packet.*/
+ if (status & 0x0890) lp->stats.rx_length_errors++;
+ if (status & 0x0004) lp->stats.rx_frame_errors++;
+ if (status & 0x0002) lp->stats.rx_crc_errors++;
+ if (status & 0x0001) lp->stats.rx_fifo_errors++;
+ } else {
+ /* Malloc up new buffer, compatible with net-2e. */
+ short pkt_len = lp->rx_ring[entry].status >> 16;
+ struct sk_buff *skb;
+
+ skb = alloc_skb(pkt_len, GFP_ATOMIC);
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+ /* Check that at least two ring entries are free.
+ If not, free one and mark stats->rx_dropped++. */
+ for (i=0; i < RX_RING_SIZE; i++)
+ if (lp->rx_ring[(entry+i) % RX_RING_SIZE].status < 0)
+ break;
+
+ if (i > RX_RING_SIZE -2) {
+ lp->stats.rx_dropped++;
+ lp->rx_ring[entry].status = 0x80000000;
+ lp->cur_rx++;
+ }
+ break;
+ }
+ skb->len = pkt_len;
+ skb->dev = dev;
+ memcpy(skb->data, lp->rx_ring[entry].buffer1, pkt_len);
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+
+ lp->rx_ring[entry].status = 0x80000000;
+ entry = (++lp->cur_rx) % RX_RING_SIZE;
+ }
+
+ return 0;
+}
+
+static int
+tulip_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (tulip_debug > 1)
+ printk("%s: Shutting down ethercard, status was %2.2x.\n",
+ dev->name, inl(ioaddr + CSR5));
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ outl(0x00000000, ioaddr + CSR7);
+ /* Stop the chip's Tx and Rx processes. */
+ outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
+
+ tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+
+ return 0;
+}
+
+static struct enet_statistics *
+tulip_get_stats(struct device *dev)
+{
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ short ioaddr = dev->base_addr;
+
+ tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+
+ return &tp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ num_addrs == -1 Promiscuous mode, receive all packets
+ num_addrs == 0 Normal mode, clear multicast list
+ num_addrs > 0 Multicast mode, receive normal and MC packets, and do
+ best-effort filtering.
+ */
+static void
+set_multicast_list(struct device *dev, int num_addrs, void *addrs)
+{
+ short ioaddr = dev->base_addr;
+ int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
+
+ if (num_addrs > 15) {
+ /* Too many to filter perfectly -- accept all multicasts. */
+ outl(csr6 | 0x0080, ioaddr + CSR6);
+ } else if (num_addrs < 0) { /* Set promiscuous. */
+ outl(csr6 | 0x00C0, ioaddr + CSR6);
+ /* Log any net taps. */
+ printk("%s: Promiscuous mode enabled.\n", dev->name);
+ } else {
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ int *setup_frm = tp->setup_frame;
+ unsigned short *eaddrs = addrs;
+ int i;
+
+ /* We have <= 15 addresses that we can use the wonderful
+ 16 address perfect filtering of the Tulip. Note that only
+ the low shortword of setup_frame[] is valid. */
+ outl(csr6 | 0x0000, ioaddr + CSR6);
+ for(i = 0; i < num_addrs; i++) {
+ *setup_frm++ = *eaddrs++;
+ *setup_frm++ = *eaddrs++;
+ *setup_frm++ = *eaddrs++;
+ }
+ /* Fill the rest of the table with our physical address. */
+ eaddrs = (unsigned short *)dev->dev_addr;
+ do {
+ *setup_frm++ = eaddrs[0];
+ *setup_frm++ = eaddrs[1];
+ *setup_frm++ = eaddrs[2];
+ } while (++i < 16);
+
+ /* Now add this frame to the Tx list. */
+ }
+}
+
+static int
+set_mac_address(struct device *dev, void *addr)
+{
+ int i;
+ if (dev->start)
+ return -EBUSY;
+ printk("%s: Setting MAC address to ", dev->name);
+ for (i = 0; i < 6; i++)
+ printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]);
+ printk(".\n");
+ return 0;
+}
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c tulip.c"
+ * c-indent-level: 4
+ * tab-width: 4
+ * End:
+ */
diff --git a/drivers/net/tunnel.c b/drivers/net/tunnel.c
new file mode 100644
index 000000000..b091fbc79
--- /dev/null
+++ b/drivers/net/tunnel.c
@@ -0,0 +1,311 @@
+/* tunnel.c: an IP tunnel driver
+
+ The purpose of this driver is to provide an IP tunnel through
+ which you can tunnel network traffic transparently across subnets.
+
+ This was written by looking at Nick Holloway's dummy driver
+ Thanks for the great code!
+
+ -Sam Lantinga (slouken@cs.ucdavis.edu) 02/01/95
+
+ Minor tweaks:
+ Cleaned up the code a little and added some pre-1.3.0 tweaks.
+ dev->hard_header/hard_header_len changed to use no headers.
+ Comments/bracketing tweaked.
+ Made the tunnels use dev->name not tunnel: when error reporting.
+ Added tx_dropped stat
+
+ -Alan Cox (Alan.Cox@linux.org) 21 March 95
+*/
+
+#include <linux/config.h>
+#ifdef CONFIG_IP_FORWARD
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <netinet/in.h>
+#include <linux/ip.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <net/ip.h>
+
+#include <net/checksum.h> /* If using 1.3.0-pre net code */
+
+#define ip_header_len sizeof(struct iphdr)
+
+static int tunnel_xmit(struct sk_buff *skb, struct device *dev);
+static struct enet_statistics *tunnel_get_stats(struct device *dev);
+
+#ifdef MODULE
+static int tunnel_open(struct device *dev)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int tunnel_close(struct device *dev)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+#endif
+
+
+int tunnel_init(struct device *dev)
+{
+ static int tun_msg=0;
+ if(!tun_msg)
+ {
+ printk ( KERN_INFO "tunnel: version v0.1a\n" );
+ tun_msg=1;
+ }
+
+ /* Fill in fields of the dev structure with ethernet-generic values. */
+ ether_setup(dev);
+
+ /* Custom initialize the device structure. */
+ dev->hard_start_xmit = tunnel_xmit;
+ dev->get_stats = tunnel_get_stats;
+ dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
+ memset(dev->priv, 0, sizeof(struct enet_statistics));
+#ifdef MODULE
+ dev->open = &tunnel_open;
+ dev->stop = &tunnel_close;
+#endif
+ dev->type = ARPHRD_TUNNEL; /* IP tunnel hardware type (Linux 1.1.89) */
+ dev->flags |= IFF_NOARP;
+ dev->flags |= IFF_LOOPBACK; /* Why doesn't tunnel work without this? [ should do now - AC]*/
+ dev->addr_len=0;
+ dev->hard_header_len=0;
+ dev->hard_header=NULL;
+ return 0;
+}
+
+#ifdef TUNNEL_DEBUG
+void print_ip(struct iphdr *ip)
+{
+ unsigned char *ipaddr;
+
+ printk("IP packet:\n");
+ printk("--- header len = %d\n", ip->ihl*4);
+ printk("--- ip version: %d\n", ip->version);
+ printk("--- ip protocol: %d\n", ip->protocol);
+ ipaddr=(unsigned char *)&ip->saddr;
+ printk("--- source address: %u.%u.%u.%u\n",
+ *ipaddr, *(ipaddr+1), *(ipaddr+2), *(ipaddr+3));
+ ipaddr=(unsigned char *)&ip->daddr;
+ printk("--- destination address: %u.%u.%u.%u\n",
+ *ipaddr, *(ipaddr+1), *(ipaddr+2), *(ipaddr+3));
+ printk("--- total packet len: %d\n", ntohs(ip->tot_len));
+}
+#endif
+
+/* This function assumes it is being called from dev_queue_xmit()
+ and that skb is filled properly by that function.
+ We also presume that if we return 0, we need to free skb, but
+ if we return 1, we don't free anything. Right? Wrong?
+*/
+
+static int tunnel_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct enet_statistics *stats;
+ struct sk_buff *skb2; /* The output packet */
+ int newlen; /* The length of skb2->data */
+ struct iphdr *iph; /* Our new IP header */
+
+ /*
+ * Return if there is nothing to do
+ */
+
+ if (skb == NULL || dev == NULL)
+ return 0;
+
+ /*
+ * Make sure we are not busy (check lock variable)
+ */
+
+ stats = (struct enet_statistics *)dev->priv;
+ cli();
+ if (dev->tbusy != 0)
+ {
+ sti();
+ stats->tx_errors++;
+ return(1);
+ }
+ dev->tbusy = 1;
+ sti();
+
+ /*
+ * Perform some sanity checks on the packet
+ */
+
+ if ( ! dev->pa_dstaddr )
+ {
+ printk("%s: packet sent through tunnel to never-never land!\n", dev->name);
+ dev_kfree_skb(skb, FREE_WRITE);
+ dev->tbusy = 0;
+ return(1);
+ }
+
+ iph=(struct iphdr *)skb->data;
+ if ( iph->version != 4 )
+ {
+ /*
+ * Bad IP packet? Possibly an ARP packet
+ */
+ printk("%s: Bad IP packet: ip version %d\n", dev->name, iph->version);
+ dev_kfree_skb(skb, FREE_WRITE);
+ dev->tbusy = 0;
+ return(0);
+ }
+
+
+ /*
+ * Check for routing loops
+ */
+
+ if ( iph->protocol == IPPROTO_IPIP && iph->saddr == dev->pa_addr )
+ {
+ /*
+ * We really should do an ICMP reply here...
+ */
+ printk("%s: Warning: IP routing loop!\n", dev->name);
+ dev->tbusy = 0;
+ dev_kfree_skb(skb, FREE_WRITE);
+ return(0);
+ }
+
+ if ( iph->daddr == dev->pa_addr )
+ {
+ printk("%s: Received inbound packet -- not handled.\n",dev->name);
+ dev_kfree_skb(skb, FREE_WRITE);
+ dev->tbusy = 0;
+ return(0);
+ }
+
+#ifdef TUNNEL_DEBUG
+printk("Old IP Header....\n");
+print_ip(iph);
+#endif
+ /*
+ * Everything is okay:
+ * See if we need to allocate memory for a new packet
+ */
+
+ newlen = (skb->len + ip_header_len);
+ if ( !(skb2 = alloc_skb(newlen, GFP_ATOMIC)) )
+ {
+ printk("%s: No free memory.\n",dev->name);
+ dev_kfree_skb(skb, FREE_WRITE);
+ dev->tbusy = 0;
+ stats->tx_dropped++;
+ return(1);
+ }
+
+ /* Copy the packet to a new buffer, adding a new ip header */
+ skb2->free=1;
+ skb2->len=newlen;
+ iph=skb2->h.iph=(struct iphdr *)skb2->data;
+ memcpy(skb2->h.iph, skb->data, ip_header_len );
+ memcpy(skb2->data + ip_header_len, skb->data, skb->len);
+ /* Free the old packet, we no longer need it */
+ dev_kfree_skb(skb, FREE_WRITE);
+
+ /* Correct the fields in the new ip header */
+ ++iph->ttl; /* Note: ip_forward() decrements ttl, so compensate */
+ iph->saddr = dev->pa_addr;
+ iph->daddr = dev->pa_dstaddr;
+ iph->protocol = IPPROTO_IPIP;
+ iph->ihl = 5;
+ iph->tot_len = htons(skb2->len);
+ iph->frag_off = 0;
+
+ /* Here is where we compute the IP checksum */
+ /* ip_fast_csum() is an inline function from net/inet/ip.h/checksum.h */
+ iph->check = 0;
+ iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+
+#ifdef TUNNEL_DEBUG
+printk("New IP Header....\n");
+print_ip(iph);
+#endif
+ /* Now send the packet on its way */
+#ifdef TUNNEL_DEBUG
+ printk("tunnel: calling ip_forward()\n");
+#endif
+ ip_forward(skb2, dev, 0, iph->daddr, 0);
+
+#ifdef TUNNEL_DEBUG
+ printk("Packet sent through tunnel interface!\n");
+#endif
+ /* Record statistics */
+ stats->tx_packets++;
+
+#ifdef TUNNEL_DEBUG
+ printk("tunnel: Updated usage statistics.\n");
+#endif
+ /* Clean up and return okay. */
+ kfree_skb(skb2, FREE_WRITE);
+ dev->tbusy=0;
+ return 0;
+}
+
+static struct enet_statistics *
+tunnel_get_stats(struct device *dev)
+{
+ return((struct enet_statistics*) dev->priv);
+}
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+
+static int tunnel_probe(struct device *dev)
+{
+ tunnel_init(dev);
+ return 0;
+}
+
+static struct device dev_tunnel = {
+ "tunl0\0 ",
+ 0, 0, 0, 0,
+ 0x0, 0,
+ 0, 0, 0, NULL, tunnel_probe };
+
+int init_module(void)
+{
+ /* Find a name for this unit */
+ int ct= 1;
+
+ while(dev_get(dev_tunnel.name)!=NULL && ct<100)
+ {
+ sprintf(dev_tunnel.name,"tunl%d",ct);
+ ct++;
+ }
+
+#ifdef TUNNEL_DEBUG
+ printk("tunnel: registering device %s\n", dev_tunnel.name);
+#endif
+ if (register_netdev(&dev_tunnel) != 0)
+ return -EIO;
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ unregister_netdev(&dev_tunnel);
+ kfree_s(dev_tunnel.priv,sizeof(struct enet_statistics));
+ dev_tunnel.priv=NULL;
+}
+#endif /* MODULE */
+#endif
diff --git a/drivers/net/wavelan.c b/drivers/net/wavelan.c
new file mode 100644
index 000000000..26eb3e758
--- /dev/null
+++ b/drivers/net/wavelan.c
@@ -0,0 +1,2448 @@
+/*
+ * AT&T GIS (nee NCR) WaveLAN card:
+ * An Ethernet-like radio transceiver
+ * controlled by an Intel 82586 coprocessor.
+ */
+
+#include <linux/config.h>
+
+#if defined(CONFIG_WAVELAN)
+#if defined(MODULE)
+#include <linux/module.h>
+#include <linux/version.h>
+#endif /* defined(MODULE) */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/malloc.h>
+#include <linux/timer.h>
+#define STRUCT_CHECK 1
+#include "i82586.h"
+#include "wavelan.h"
+
+#ifndef WAVELAN_DEBUG
+#define WAVELAN_DEBUG 0
+#endif /* WAVELAN_DEBUG */
+
+#define nels(a) (sizeof(a) / sizeof(a[0]))
+
+#define WATCHDOG_JIFFIES 512 /* TODO: express in HZ. */
+
+typedef struct device device;
+typedef struct enet_statistics en_stats;
+typedef struct net_local net_local;
+typedef struct timer_list timer_list;
+
+struct net_local
+{
+ en_stats stats;
+ unsigned int tx_n_in_use;
+ unsigned char nwid[2];
+ unsigned short hacr;
+ unsigned short rx_head;
+ unsigned short rx_last;
+ unsigned short tx_first_free;
+ unsigned short tx_first_in_use;
+ unsigned int nresets;
+ unsigned int correct_nwid;
+ unsigned int wrong_nwid;
+ unsigned int promiscuous;
+ timer_list watchdog;
+ device *dev;
+ net_local *prev;
+ net_local *next;
+};
+
+extern int wavelan_probe(device *); /* See Space.c */
+
+static char *version = "wavelan.c:v7 95/4/8\n";
+
+/*
+ * Entry point forward declarations.
+ */
+static int wavelan_probe1(device *, unsigned short);
+static int wavelan_open(device *);
+static int wavelan_send_packet(struct sk_buff *, device *);
+static void wavelan_interrupt(int, struct pt_regs *);
+static int wavelan_close(device *);
+static en_stats *wavelan_get_stats(device *);
+static void wavelan_set_multicast_list(device *, int, void *);
+
+/*
+ * Other forward declarations.
+ */
+static void wavelan_cu_show_one(device *, net_local *, int, unsigned short);
+static void wavelan_cu_start(device *);
+static void wavelan_ru_start(device *);
+static void wavelan_watchdog(unsigned long);
+#if 0
+static void wavelan_psa_show(psa_t *);
+static void wavelan_mmc_show(unsigned short);
+#endif /* 0 */
+static void wavelan_scb_show(unsigned short);
+static void wavelan_ru_show(device *);
+static void wavelan_cu_show(device *);
+static void wavelan_dev_show(device *);
+static void wavelan_local_show(device *);
+
+static unsigned int wavelan_debug = WAVELAN_DEBUG;
+static net_local *first_wavelan = (net_local *)0;
+
+static
+unsigned long
+wavelan_splhi(void)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ return flags;
+}
+
+static
+void
+wavelan_splx(unsigned long flags)
+{
+ restore_flags(flags);
+}
+
+static
+unsigned short
+hasr_read(unsigned short ioaddr)
+{
+ return inw(HASR(ioaddr));
+}
+
+static
+void
+hacr_write(unsigned short ioaddr, int hacr)
+{
+ outw(hacr, HACR(ioaddr));
+}
+
+static
+void
+hacr_write_slow(unsigned short ioaddr, int hacr)
+{
+ hacr_write(ioaddr, hacr);
+ /* delay might only be needed sometimes */
+ udelay(1000);
+}
+
+/*
+ * Set the channel attention bit.
+ */
+static
+void
+set_chan_attn(unsigned short ioaddr, unsigned short current_hacr)
+{
+ hacr_write(ioaddr, current_hacr | HACR_CA);
+}
+
+/*
+ * Reset, and then set host adaptor into default mode.
+ */
+static
+void
+wavelan_reset(unsigned short ioaddr)
+{
+ hacr_write_slow(ioaddr, HACR_RESET);
+ hacr_write(ioaddr, HACR_DEFAULT);
+}
+
+static
+void
+wavelan_16_off(unsigned short ioaddr, unsigned short hacr)
+{
+ hacr &= ~HACR_16BITS;
+
+ hacr_write(ioaddr, hacr);
+}
+
+static
+void
+wavelan_16_on(unsigned short ioaddr, unsigned short hacr)
+{
+ hacr |= HACR_16BITS;
+
+ hacr_write(ioaddr, hacr);
+}
+
+static
+void
+wavelan_ints_off(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned long x;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ x = wavelan_splhi();
+
+ lp->hacr &= ~HACR_INTRON;
+ hacr_write(ioaddr, lp->hacr);
+
+ wavelan_splx(x);
+}
+
+static
+void
+wavelan_ints_on(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned long x;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ x = wavelan_splhi();
+
+ lp->hacr |= HACR_INTRON;
+ hacr_write(ioaddr, lp->hacr);
+
+ wavelan_splx(x);
+}
+
+/*
+ * Read bytes from the PSA.
+ */
+static
+void
+psa_read(unsigned short ioaddr, unsigned short hacr, int o, unsigned char *b, int n)
+{
+ wavelan_16_off(ioaddr, hacr);
+
+ while (n-- > 0)
+ {
+ outw(o, PIOR2(ioaddr));
+ o++;
+ *b++ = inb(PIOP2(ioaddr));
+ }
+
+ wavelan_16_on(ioaddr, hacr);
+}
+
+/*
+ * Read bytes from the on-board RAM.
+ */
+static
+void
+obram_read(unsigned short ioaddr, unsigned short o, unsigned char *b, int n)
+{
+ n = (n + 1) / (sizeof(unsigned short) / sizeof(unsigned char));
+
+ outw(o, PIOR1(ioaddr));
+
+ insw(PIOP1(ioaddr), (unsigned short *)b, n);
+}
+
+/*
+ * Write bytes to the on-board RAM.
+ */
+static
+void
+obram_write(unsigned short ioaddr, unsigned short o, unsigned char *b, int n)
+{
+ n = (n + 1) / (sizeof(unsigned short) / sizeof(unsigned char));
+
+ outw(o, PIOR1(ioaddr));
+
+ outsw(PIOP1(ioaddr), (unsigned short *)b, n);
+}
+
+/*
+ * Read bytes from the MMC.
+ */
+static
+void
+mmc_read(unsigned short ioaddr, unsigned short o, unsigned char *b, int n)
+{
+ while (n-- > 0)
+ {
+ while (inw(HASR(ioaddr)) & HASR_MMC_BUSY)
+ ;
+
+ outw(o << 1, MMCR(ioaddr));
+ o++;
+
+ while (inw(HASR(ioaddr)) & HASR_MMC_BUSY)
+ ;
+
+ *b++ = (unsigned char)(inw(MMCR(ioaddr)) >> 8);
+ }
+}
+
+/*
+ * Write bytes to the MMC.
+ */
+static
+void
+mmc_write(unsigned short ioaddr, unsigned short o, unsigned char *b, int n)
+{
+ while (n-- > 0)
+ {
+ while (inw(HASR(ioaddr)) & HASR_MMC_BUSY)
+ ;
+
+ outw((unsigned short)(((unsigned short)*b << 8) | (o << 1) | 1), MMCR(ioaddr));
+ b++;
+ o++;
+ }
+}
+
+/*
+ * Map values from the irq parameter register to irq numbers.
+ */
+static
+int
+wavelan_map_irq(unsigned short ioaddr, unsigned char irqval)
+{
+ int irq;
+ static int irqvals[] =
+ {
+ 0, 0, 0, 0x01,
+ 0x02, 0x04, 0, 0x08,
+ 0, 0, 0x10, 0x20,
+ 0x40, 0, 0, 0x80,
+ };
+
+ for (irq = 0; irq < nels(irqvals); irq++)
+ {
+ if (irqvals[irq] == (int)irqval)
+ return irq;
+ }
+
+ return -1;
+}
+
+/*
+ * Initialize the Modem Management Controller.
+ */
+static
+void
+wavelan_mmc_init(device *dev, psa_t *psa)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ mmw_t m;
+ int configured;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+ memset(&m, 0x00, sizeof(m));
+
+ /*
+ * configured = psa->psa_conf_status & 1;
+ *
+ * For now we use the persistent PSA
+ * information as little as possible, thereby
+ * allowing us to return to the same known state
+ * during a hardware reset.
+ */
+ configured = 0;
+
+ /*
+ * Set default modem control parameters.
+ * See NCR document 407-0024326 Rev. A.
+ */
+ m.mmw_jabber_enable = 0x01;
+ m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN;
+ m.mmw_ifs = 0x20;
+ m.mmw_mod_delay = 0x04;
+ m.mmw_jam_time = 0x38;
+
+ m.mmw_encr_enable = 0;
+ m.mmw_des_io_invert = 0;
+ m.mmw_freeze = 0;
+ m.mmw_decay_prm = 0;
+ m.mmw_decay_updat_prm = 0;
+
+ if (configured)
+ {
+ /*
+ * Use configuration defaults from parameter storage area.
+ */
+ if (psa->psa_undefined & 1)
+ m.mmw_loopt_sel = 0x00;
+ else
+ m.mmw_loopt_sel = MMW_LOOPT_SEL_UNDEFINED;
+
+ m.mmw_thr_pre_set = psa->psa_thr_pre_set & 0x3F;
+ m.mmw_quality_thr = psa->psa_quality_thr & 0x0F;
+ }
+ else
+ {
+ if (lp->promiscuous)
+ m.mmw_loopt_sel = MMW_LOOPT_SEL_UNDEFINED;
+ else
+ m.mmw_loopt_sel = 0x00;
+
+ /*
+ * 0x04 for AT,
+ * 0x01 for MCA.
+ */
+ if (psa->psa_comp_number & 1)
+ m.mmw_thr_pre_set = 0x01;
+ else
+ m.mmw_thr_pre_set = 0x04;
+
+ m.mmw_quality_thr = 0x03;
+ }
+
+ m.mmw_netw_id_l = lp->nwid[1];
+ m.mmw_netw_id_h = lp->nwid[0];
+
+ mmc_write(ioaddr, 0, (unsigned char *)&m, sizeof(m));
+}
+
+static
+void
+wavelan_ack(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned short scb_cs;
+ int i;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), (unsigned char *)&scb_cs, sizeof(scb_cs));
+ scb_cs &= SCB_ST_INT;
+
+ if (scb_cs == 0)
+ return;
+
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ for (i = 1000; i > 0; i--)
+ {
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+ if (scb_cs == 0)
+ break;
+
+ udelay(1000);
+ }
+
+ if (i <= 0)
+ printk("%s: wavelan_ack(): board not accepting command.\n", dev->name);
+}
+
+/*
+ * Set channel attention bit and busy wait until command has
+ * completed, then acknowledge the command completion.
+ */
+static
+int
+wavelan_synchronous_cmd(device *dev, char *str)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned short scb_cmd;
+ ach_t cb;
+ int i;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ scb_cmd = SCB_CMD_CUC & SCB_CMD_CUC_GO;
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cmd, sizeof(scb_cmd));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ for (i = 64; i > 0; i--)
+ {
+ obram_read(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb));
+ if (cb.ac_status & AC_SFLD_C)
+ break;
+
+ udelay(1000);
+ }
+
+ if (i <= 0 || !(cb.ac_status & AC_SFLD_OK))
+ {
+ printk("%s: %s failed; status = 0x%x\n", dev->name, str, cb.ac_status);
+ wavelan_scb_show(ioaddr);
+ return -1;
+ }
+
+ wavelan_ack(dev);
+
+ return 0;
+}
+
+static
+int
+wavelan_hardware_reset(device *dev)
+{
+ unsigned short ioaddr;
+ psa_t psa;
+ net_local *lp;
+ scp_t scp;
+ iscp_t iscp;
+ scb_t scb;
+ ach_t cb;
+ int i;
+ ac_cfg_t cfg;
+ ac_ias_t ias;
+
+ if (wavelan_debug > 0)
+ printk("%s: ->wavelan_hardware_reset(dev=0x%x)\n", dev->name, (unsigned int)dev);
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ lp->nresets++;
+
+ wavelan_reset(ioaddr);
+ lp->hacr = HACR_DEFAULT;
+
+ /*
+ * Clear the onboard RAM.
+ */
+ {
+ unsigned char zeroes[512];
+
+ memset(&zeroes[0], 0x00, sizeof(zeroes));
+
+ for (i = 0; i < I82586_MEMZ; i += sizeof(zeroes))
+ obram_write(ioaddr, i, &zeroes[0], sizeof(zeroes));
+ }
+
+ psa_read(ioaddr, lp->hacr, 0, (unsigned char *)&psa, sizeof(psa));
+
+ wavelan_mmc_init(dev, &psa);
+
+ /*
+ * Construct the command unit structures:
+ * scp, iscp, scb, cb.
+ */
+ memset(&scp, 0x00, sizeof(scp));
+ scp.scp_sysbus = SCP_SY_16BBUS;
+ scp.scp_iscpl = OFFSET_ISCP;
+ obram_write(ioaddr, OFFSET_SCP, (unsigned char *)&scp, sizeof(scp));
+
+ memset(&iscp, 0x00, sizeof(iscp));
+ iscp.iscp_busy = 1;
+ iscp.iscp_offset = OFFSET_SCB;
+ obram_write(ioaddr, OFFSET_ISCP, (unsigned char *)&iscp, sizeof(iscp));
+
+ memset(&scb, 0x00, sizeof(scb));
+ scb.scb_command = SCB_CMD_RESET;
+ scb.scb_cbl_offset = OFFSET_CU;
+ scb.scb_rfa_offset = OFFSET_RU;
+ obram_write(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ for (i = 1000; i > 0; i--)
+ {
+ obram_read(ioaddr, OFFSET_ISCP, (unsigned char *)&iscp, sizeof(iscp));
+
+ if (iscp.iscp_busy == (unsigned short)0)
+ break;
+
+ udelay(1000);
+ }
+
+ if (i <= 0)
+ {
+ printk("%s: wavelan_hardware_reset(): iscp_busy timeout.\n", dev->name);
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
+ return -1;
+ }
+
+ for (i = 15; i > 0; i--)
+ {
+ obram_read(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb));
+
+ if (scb.scb_status == (SCB_ST_CX | SCB_ST_CNA))
+ break;
+
+ udelay(1000);
+ }
+
+ if (i <= 0)
+ {
+ printk("%s: wavelan_hardware_reset(): status: expected 0x%02x, got 0x%02x.\n", dev->name, SCB_ST_CX | SCB_ST_CNA, scb.scb_status);
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
+ return -1;
+ }
+
+ wavelan_ack(dev);
+
+ memset(&cb, 0x00, sizeof(cb));
+ cb.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_diagnose);
+ cb.ac_link = OFFSET_CU;
+ obram_write(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb));
+
+ if (wavelan_synchronous_cmd(dev, "diag()") == -1)
+ {
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
+ return -1;
+ }
+
+ obram_read(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb));
+ if (cb.ac_status & AC_SFLD_FAIL)
+ {
+ printk("%s: wavelan_hardware_reset(): i82586 Self Test failed.\n", dev->name);
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
+ return -1;
+ }
+
+ memset(&cfg, 0x00, sizeof(cfg));
+
+#if 0
+ /*
+ * The default board configuration.
+ */
+ cfg.fifolim_bytecnt = 0x080c;
+ cfg.addrlen_mode = 0x2600;
+ cfg.linprio_interframe = 0x7820; /* IFS=120, ACS=2 */
+ cfg.slot_time = 0xf00c; /* slottime=12 */
+ cfg.hardware = 0x0008; /* tx even w/o CD */
+ cfg.min_frame_len = 0x0040;
+#endif /* 0 */
+
+ /*
+ * For Linux we invert AC_CFG_ALOC(..) so as to conform
+ * to the way that net packets reach us from above.
+ * (See also ac_tx_t.)
+ */
+ cfg.cfg_byte_cnt = AC_CFG_BYTE_CNT(sizeof(ac_cfg_t) - sizeof(ach_t));
+ cfg.cfg_fifolim = AC_CFG_FIFOLIM(8);
+ cfg.cfg_byte8 = AC_CFG_SAV_BF(0) |
+ AC_CFG_SRDY(0);
+ cfg.cfg_byte9 = AC_CFG_ELPBCK(0) |
+ AC_CFG_ILPBCK(0) |
+ AC_CFG_PRELEN(AC_CFG_PLEN_2) |
+ AC_CFG_ALOC(1) |
+ AC_CFG_ADDRLEN(WAVELAN_ADDR_SIZE);
+ cfg.cfg_byte10 = AC_CFG_BOFMET(0) |
+ AC_CFG_ACR(0) |
+ AC_CFG_LINPRIO(0);
+ cfg.cfg_ifs = 32;
+ cfg.cfg_slotl = 0;
+ cfg.cfg_byte13 = AC_CFG_RETRYNUM(15) |
+ AC_CFG_SLTTMHI(2);
+ cfg.cfg_byte14 = AC_CFG_FLGPAD(0) |
+ AC_CFG_BTSTF(0) |
+ AC_CFG_CRC16(0) |
+ AC_CFG_NCRC(0) |
+ AC_CFG_TNCRS(1) |
+ AC_CFG_MANCH(0) |
+ AC_CFG_BCDIS(0) |
+ AC_CFG_PRM(lp->promiscuous);
+ cfg.cfg_byte15 = AC_CFG_ICDS(0) |
+ AC_CFG_CDTF(0) |
+ AC_CFG_ICSS(0) |
+ AC_CFG_CSTF(0);
+/*
+ cfg.cfg_min_frm_len = AC_CFG_MNFRM(64);
+*/
+ cfg.cfg_min_frm_len = AC_CFG_MNFRM(8);
+
+ cfg.cfg_h.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_configure);
+ cfg.cfg_h.ac_link = OFFSET_CU;
+ obram_write(ioaddr, OFFSET_CU, (unsigned char *)&cfg, sizeof(cfg));
+
+ if (wavelan_synchronous_cmd(dev, "reset()-configure") == -1)
+ {
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
+
+ return -1;
+ }
+
+ memset(&ias, 0x00, sizeof(ias));
+ ias.ias_h.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_ia_setup);
+ ias.ias_h.ac_link = OFFSET_CU;
+ memcpy(&ias.ias_addr[0], (unsigned char *)&dev->dev_addr[0], sizeof(ias.ias_addr));
+ obram_write(ioaddr, OFFSET_CU, (unsigned char *)&ias, sizeof(ias));
+
+ if (wavelan_synchronous_cmd(dev, "reset()-address") == -1)
+ {
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
+
+ return -1;
+ }
+
+ wavelan_ints_on(dev);
+
+ if (wavelan_debug > 4)
+ wavelan_scb_show(ioaddr);
+
+ wavelan_ru_start(dev);
+ wavelan_cu_start(dev);
+
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_hardware_reset(): 0\n", dev->name);
+
+ return 0;
+}
+
+#if STRUCT_CHECK == 1
+
+static
+char *
+wavelan_struct_check(void)
+{
+#define SC(t,s,n) if (sizeof(t) != s) return n
+ SC(psa_t, PSA_SIZE, "psa_t");
+ SC(mmw_t, MMW_SIZE, "mmw_t");
+ SC(mmr_t, MMR_SIZE, "mmr_t");
+ SC(ha_t, HA_SIZE, "ha_t");
+#undef SC
+
+ return (char *)0;
+}
+
+#endif /* STRUCT_CHECK == 1 */
+
+/*
+ * Check for a network adaptor of this type.
+ * Return '0' iff one exists.
+ * (There seem to be different interpretations of
+ * the initial value of dev->base_addr.
+ * We follow the example in drivers/net/ne.c.)
+ */
+int
+wavelan_probe(device *dev)
+{
+ int i;
+ int r;
+ short base_addr;
+ static unsigned short iobase[] =
+ {
+#if 0
+ Leave out 0x3C0 for now -- seems to clash
+ with some video controllers.
+ Leave out the others too -- we will always
+ use 0x390 and leave 0x300 for the Ethernet device.
+ 0x300, 0x390, 0x3E0, 0x3C0,
+#endif /* 0 */
+ 0x390,
+ };
+
+ if (wavelan_debug > 0)
+ printk("%s: ->wavelan_probe(dev=0x%x (base_addr=0x%x))\n", dev->name, (unsigned int)dev, (unsigned int)dev->base_addr);
+
+#if STRUCT_CHECK == 1
+ if (wavelan_struct_check() != (char *)0)
+ {
+ printk("%s: structure/compiler botch: \"%s\"\n", dev->name, wavelan_struct_check());
+
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe(): ENODEV\n", dev->name);
+
+ return ENODEV;
+ }
+#endif /* STRUCT_CHECK == 1 */
+
+ base_addr = dev->base_addr;
+
+ if (base_addr < 0)
+ {
+ /*
+ * Don't probe at all.
+ */
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe(): ENXIO\n", dev->name);
+ return ENXIO;
+ }
+
+ if (base_addr > 0x100)
+ {
+ /*
+ * Check a single specified location.
+ */
+ r = wavelan_probe1(dev, base_addr);
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe(): %d\n", dev->name, r);
+ return r;
+ }
+
+ for (i = 0; i < nels(iobase); i++)
+ {
+ if (check_region(iobase[i], sizeof(ha_t)))
+ continue;
+
+ if (wavelan_probe1(dev, iobase[i]) == 0)
+ {
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe(): 0\n", dev->name);
+ return 0;
+ }
+ }
+
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe(): ENODEV\n", dev->name);
+
+ return ENODEV;
+}
+
+static
+int
+wavelan_probe1(device *dev, unsigned short ioaddr)
+{
+ psa_t psa;
+ int irq;
+ int i;
+ net_local *lp;
+
+ if (wavelan_debug > 0)
+ printk("%s: ->wavelan_probe1(dev=0x%x, ioaddr=0x%x)\n", dev->name, (unsigned int)dev, ioaddr);
+
+ wavelan_reset(ioaddr);
+
+ psa_read(ioaddr, HACR_DEFAULT, 0, (unsigned char *)&psa, sizeof(psa));
+
+ /*
+ * Check the first three octets of the MAC address
+ * for the manufacturer's code.
+ */
+ if
+ (
+ psa.psa_univ_mac_addr[0] != SA_ADDR0
+ ||
+ psa.psa_univ_mac_addr[1] != SA_ADDR1
+ ||
+ psa.psa_univ_mac_addr[2] != SA_ADDR2
+ )
+ {
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe1(): ENODEV\n", dev->name);
+ return ENODEV;
+ }
+
+ printk("%s: WaveLAN at %#x,", dev->name, ioaddr);
+
+ if ((irq = wavelan_map_irq(ioaddr, psa.psa_int_req_no)) == -1)
+ {
+ printk(" could not wavelan_map_irq(0x%x, %d).\n", ioaddr, psa.psa_int_req_no);
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe1(): EAGAIN\n", dev->name);
+ return EAGAIN;
+ }
+
+ dev->irq = irq;
+
+ request_region(ioaddr, sizeof(ha_t), "wavelan");
+ dev->base_addr = ioaddr;
+
+ /*
+ * Apparently the third numeric argument to LILO's
+ * `ether=' control line arrives here as `dev->mem_start'.
+ * If it is non-zero we use it instead of the PSA NWID.
+ */
+ if (dev->mem_start != 0)
+ {
+ psa.psa_nwid[0] = (dev->mem_start >> 8) & 0xFF;
+ psa.psa_nwid[1] = (dev->mem_start >> 0) & 0xFF;
+ }
+
+ dev->mem_start = 0x0000;
+ dev->mem_end = 0x0000;
+ dev->if_port = 0;
+
+ memcpy(&dev->dev_addr[0], &psa.psa_univ_mac_addr[0], WAVELAN_ADDR_SIZE);
+
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ printk("%s%02x", (i == 0) ? " " : ":", dev->dev_addr[i]);
+
+ printk(", IRQ %d", dev->irq);
+ printk(", nwid 0x%02x%02x", psa.psa_nwid[0], psa.psa_nwid[1]);
+
+ printk(", PC");
+ switch (psa.psa_comp_number)
+ {
+ case PSA_COMP_PC_AT_915:
+ case PSA_COMP_PC_AT_2400:
+ printk("-AT");
+ break;
+
+ case PSA_COMP_PC_MC_915:
+ case PSA_COMP_PC_MC_2400:
+ printk("-MC");
+ break;
+
+ case PSA_COMP_PCMCIA_915:
+ printk("MCIA");
+ break;
+
+ default:
+ printk("???");
+ break;
+ }
+
+ printk(", ");
+ switch (psa.psa_subband)
+ {
+ case PSA_SUBBAND_915:
+ printk("915");
+ break;
+
+ case PSA_SUBBAND_2425:
+ printk("2425");
+ break;
+
+ case PSA_SUBBAND_2460:
+ printk("2460");
+ break;
+
+ case PSA_SUBBAND_2484:
+ printk("2484");
+ break;
+
+ case PSA_SUBBAND_2430_5:
+ printk("2430.5");
+ break;
+
+ default:
+ printk("???");
+ break;
+ }
+ printk(" MHz");
+
+ printk("\n");
+
+ if (wavelan_debug > 0)
+ printk(version);
+
+ dev->priv = kmalloc(sizeof(net_local), GFP_KERNEL);
+ memset(dev->priv, 0x00, sizeof(net_local));
+ lp = (net_local *)dev->priv;
+
+ if (first_wavelan == (net_local *)0)
+ {
+ first_wavelan = lp;
+ lp->prev = lp;
+ lp->next = lp;
+ }
+ else
+ {
+ lp->prev = first_wavelan->prev;
+ lp->next = first_wavelan;
+ first_wavelan->prev->next = lp;
+ first_wavelan->prev = lp;
+ }
+ lp->dev = dev;
+
+ lp->hacr = HACR_DEFAULT;
+
+ lp->nwid[0] = psa.psa_nwid[0];
+ lp->nwid[1] = psa.psa_nwid[1];
+
+ lp->watchdog.function = wavelan_watchdog;
+ lp->watchdog.data = (unsigned long)dev;
+
+ dev->open = wavelan_open;
+ dev->stop = wavelan_close;
+ dev->hard_start_xmit = wavelan_send_packet;
+ dev->get_stats = wavelan_get_stats;
+ dev->set_multicast_list = &wavelan_set_multicast_list;
+
+ /*
+ * Fill in the fields of the device structure
+ * with ethernet-generic values.
+ */
+ ether_setup(dev);
+
+ dev->mtu = WAVELAN_MTU;
+
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe1(): 0\n", dev->name);
+
+ return 0;
+}
+
+/*
+ * Construct the fd and rbd structures.
+ * Start the receive unit.
+ */
+static
+void
+wavelan_ru_start(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned short scb_cs;
+ fd_t fd;
+ rbd_t rbd;
+ unsigned short rx;
+ unsigned short rx_next;
+ int i;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), (unsigned char *)&scb_cs, sizeof(scb_cs));
+ if ((scb_cs & SCB_ST_RUS) == SCB_ST_RUS_RDY)
+ return;
+
+ lp->rx_head = OFFSET_RU;
+
+ for (i = 0, rx = lp->rx_head; i < NRXBLOCKS; i++, rx = rx_next)
+ {
+ rx_next = (i == NRXBLOCKS - 1) ? lp->rx_head : rx + RXBLOCKZ;
+
+ fd.fd_status = 0;
+ fd.fd_command = (i == NRXBLOCKS - 1) ? FD_COMMAND_EL : 0;
+ fd.fd_link_offset = rx_next;
+ fd.fd_rbd_offset = rx + sizeof(fd);
+ obram_write(ioaddr, rx, (unsigned char *)&fd, sizeof(fd));
+
+ rbd.rbd_status = 0;
+ rbd.rbd_next_rbd_offset = I82586NULL;
+ rbd.rbd_bufl = rx + sizeof(fd) + sizeof(rbd);
+ rbd.rbd_bufh = 0;
+ rbd.rbd_el_size = RBD_EL | (RBD_SIZE & MAXDATAZ);
+ obram_write(ioaddr, rx + sizeof(fd), (unsigned char *)&rbd, sizeof(rbd));
+
+ lp->rx_last = rx;
+ }
+
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_rfa_offset), (unsigned char *)&lp->rx_head, sizeof(lp->rx_head));
+
+ scb_cs = SCB_CMD_RUC_GO;
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ for (i = 1000; i > 0; i--)
+ {
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+ if (scb_cs == 0)
+ break;
+
+ udelay(1000);
+ }
+
+ if (i <= 0)
+ printk("%s: wavelan_ru_start(): board not accepting command.\n", dev->name);
+}
+
+/*
+ * Initialise the transmit blocks.
+ * Start the command unit executing the NOP
+ * self-loop of the first transmit block.
+ */
+static
+void
+wavelan_cu_start(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ int i;
+ unsigned short txblock;
+ unsigned short first_nop;
+ unsigned short scb_cs;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ lp->tx_first_free = OFFSET_CU;
+ lp->tx_first_in_use = I82586NULL;
+
+ for
+ (
+ i = 0, txblock = OFFSET_CU;
+ i < NTXBLOCKS;
+ i++, txblock += TXBLOCKZ
+ )
+ {
+ ac_tx_t tx;
+ ac_nop_t nop;
+ tbd_t tbd;
+ unsigned short tx_addr;
+ unsigned short nop_addr;
+ unsigned short tbd_addr;
+ unsigned short buf_addr;
+
+ tx_addr = txblock;
+ nop_addr = tx_addr + sizeof(tx);
+ tbd_addr = nop_addr + sizeof(nop);
+ buf_addr = tbd_addr + sizeof(tbd);
+
+ tx.tx_h.ac_status = 0;
+ tx.tx_h.ac_command = acmd_transmit | AC_CFLD_I;
+ tx.tx_h.ac_link = nop_addr;
+ tx.tx_tbd_offset = tbd_addr;
+ obram_write(ioaddr, tx_addr, (unsigned char *)&tx, sizeof(tx));
+
+ nop.nop_h.ac_status = 0;
+ nop.nop_h.ac_command = acmd_nop;
+ nop.nop_h.ac_link = nop_addr;
+ obram_write(ioaddr, nop_addr, (unsigned char *)&nop, sizeof(nop));
+
+ tbd.tbd_status = TBD_STATUS_EOF;
+ tbd.tbd_next_bd_offset = I82586NULL;
+ tbd.tbd_bufl = buf_addr;
+ tbd.tbd_bufh = 0;
+ obram_write(ioaddr, tbd_addr, (unsigned char *)&tbd, sizeof(tbd));
+ }
+
+ first_nop = OFFSET_CU + (NTXBLOCKS - 1) * TXBLOCKZ + sizeof(ac_tx_t);
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_cbl_offset), (unsigned char *)&first_nop, sizeof(first_nop));
+
+ scb_cs = SCB_CMD_CUC_GO;
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ for (i = 1000; i > 0; i--)
+ {
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+ if (scb_cs == 0)
+ break;
+
+ udelay(1000);
+ }
+
+ if (i <= 0)
+ printk("%s: wavelan_cu_start(): board not accepting command.\n", dev->name);
+
+ lp->tx_n_in_use = 0;
+ dev->tbusy = 0;
+}
+
+static
+int
+wavelan_open(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned long x;
+ int r;
+
+ if (wavelan_debug > 0)
+ printk("%s: ->wavelan_open(dev=0x%x)\n", dev->name, (unsigned int)dev);
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ if (dev->irq == 0)
+ {
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_open(): -ENXIO\n", dev->name);
+ return -ENXIO;
+ }
+
+ if
+ (
+ irq2dev_map[dev->irq] != (device *)0
+ /* This is always true, but avoid the false IRQ. */
+ ||
+ (irq2dev_map[dev->irq] = dev) == (device *)0
+ ||
+ request_irq(dev->irq, &wavelan_interrupt, 0, "WaveLAN") != 0
+ )
+ {
+ irq2dev_map[dev->irq] = (device *)0;
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_open(): -EAGAIN\n", dev->name);
+ return -EAGAIN;
+ }
+
+ x = wavelan_splhi();
+ if ((r = wavelan_hardware_reset(dev)) != -1)
+ {
+ dev->interrupt = 0;
+ dev->start = 1;
+ }
+ wavelan_splx(x);
+
+ if (r == -1)
+ {
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = (device *)0;
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_open(): -EAGAIN(2)\n", dev->name);
+ return -EAGAIN;
+ }
+
+#if defined(MODULE)
+ MOD_INC_USE_COUNT;
+#endif /* defined(MODULE) */
+
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_open(): 0\n", dev->name);
+
+ return 0;
+}
+
+static
+void
+hardware_send_packet(device *dev, void *buf, short length)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned short txblock;
+ unsigned short txpred;
+ unsigned short tx_addr;
+ unsigned short nop_addr;
+ unsigned short tbd_addr;
+ unsigned short buf_addr;
+ ac_tx_t tx;
+ ac_nop_t nop;
+ tbd_t tbd;
+ unsigned long x;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ x = wavelan_splhi();
+
+ txblock = lp->tx_first_free;
+ txpred = txblock - TXBLOCKZ;
+ if (txpred < OFFSET_CU)
+ txpred += NTXBLOCKS * TXBLOCKZ;
+ lp->tx_first_free += TXBLOCKZ;
+ if (lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+ lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ;
+
+/*
+if (lp->tx_n_in_use > 0)
+ printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]);
+*/
+
+ lp->tx_n_in_use++;
+
+ tx_addr = txblock;
+ nop_addr = tx_addr + sizeof(tx);
+ tbd_addr = nop_addr + sizeof(nop);
+ buf_addr = tbd_addr + sizeof(tbd);
+
+ /*
+ * Transmit command.
+ */
+ tx.tx_h.ac_status = 0;
+ obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status), (unsigned char *)&tx.tx_h.ac_status, sizeof(tx.tx_h.ac_status));
+
+ /*
+ * NOP command.
+ */
+ nop.nop_h.ac_status = 0;
+ obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), (unsigned char *)&nop.nop_h.ac_status, sizeof(nop.nop_h.ac_status));
+ nop.nop_h.ac_link = nop_addr;
+ obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), (unsigned char *)&nop.nop_h.ac_link, sizeof(nop.nop_h.ac_link));
+
+ /*
+ * Transmit buffer descriptor.
+ */
+ tbd.tbd_status = TBD_STATUS_EOF | (TBD_STATUS_ACNT & length);
+ tbd.tbd_next_bd_offset = I82586NULL;
+ tbd.tbd_bufl = buf_addr;
+ tbd.tbd_bufh = 0;
+ obram_write(ioaddr, tbd_addr, (unsigned char *)&tbd, sizeof(tbd));
+
+ /*
+ * Data.
+ */
+ obram_write(ioaddr, buf_addr, buf, length);
+
+ /*
+ * Overwrite the predecessor NOP link
+ * so that it points to this txblock.
+ */
+ nop_addr = txpred + sizeof(tx);
+ nop.nop_h.ac_status = 0;
+ obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), (unsigned char *)&nop.nop_h.ac_status, sizeof(nop.nop_h.ac_status));
+ nop.nop_h.ac_link = txblock;
+ obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), (unsigned char *)&nop.nop_h.ac_link, sizeof(nop.nop_h.ac_link));
+
+ if (lp->tx_first_in_use == I82586NULL)
+ lp->tx_first_in_use = txblock;
+
+ if (lp->tx_n_in_use < NTXBLOCKS - 1)
+ dev->tbusy = 0;
+
+ dev->trans_start = jiffies;
+
+ if (lp->watchdog.next == (timer_list *)0)
+ wavelan_watchdog((unsigned long)dev);
+
+ wavelan_splx(x);
+
+ if (wavelan_debug > 4)
+ {
+ unsigned char *a;
+
+ a = (unsigned char *)buf;
+
+ printk
+ (
+ "%s: tx: dest %02x:%02x:%02x:%02x:%02x:%02x, length %d, tbd.tbd_bufl 0x%x.\n",
+ dev->name,
+ a[0], a[1], a[2], a[3], a[4], a[5],
+ length,
+ buf_addr
+ );
+ }
+}
+
+static
+int
+wavelan_send_packet(struct sk_buff *skb, device *dev)
+{
+ unsigned short ioaddr;
+
+ ioaddr = dev->base_addr;
+
+ if (dev->tbusy)
+ {
+ /*
+ * If we get here, some higher level
+ * has decided we are broken.
+ */
+ int tickssofar;
+
+ tickssofar = jiffies - dev->trans_start;
+
+ /*
+ * But for the moment, we will rely on wavelan_watchdog()
+ * instead as it allows finer control over exactly when we
+ * make the determination of failure.
+ *
+ if (tickssofar < 5)
+ */
+ return 1;
+
+ wavelan_scb_show(ioaddr);
+ wavelan_ru_show(dev);
+ wavelan_cu_show(dev);
+ wavelan_dev_show(dev);
+ wavelan_local_show(dev);
+
+ printk("%s: transmit timed out -- resetting board.\n", dev->name);
+
+ (void)wavelan_hardware_reset(dev);
+ }
+
+ /*
+ * If some higher layer thinks we've missed
+ * a tx-done interrupt we are passed NULL.
+ * Caution: dev_tint() handles the cli()/sti() itself.
+ */
+ if (skb == (struct sk_buff *)0)
+ {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /*
+ * Block a timer-based transmit from overlapping.
+ */
+ if (set_bit(0, (void *)&dev->tbusy) == 0)
+ {
+ short length;
+ unsigned char *buf;
+
+ length = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
+ buf = skb->data;
+
+ hardware_send_packet(dev, buf, length);
+ }
+ else
+ printk("%s: Transmitter access conflict.\n", dev->name);
+
+ dev_kfree_skb(skb, FREE_WRITE);
+
+ return 0;
+}
+
+#if 0
+static
+int
+addrcmp(unsigned char *a0, unsigned char *a1)
+{
+ int i;
+
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ {
+ if (a0[i] != a1[i])
+ return a0[i] - a1[i];
+ }
+
+ return 0;
+}
+#endif /* 0 */
+
+/*
+ * Transfer as many packets as we can
+ * from the device RAM.
+ * Called by the interrupt handler.
+ */
+static
+void
+wavelan_receive(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ int nreaped;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+ nreaped = 0;
+
+ for (;;)
+ {
+ fd_t fd;
+ rbd_t rbd;
+ ushort pkt_len;
+ int sksize;
+ struct sk_buff *skb;
+
+ obram_read(ioaddr, lp->rx_head, (unsigned char *)&fd, sizeof(fd));
+
+ if ((fd.fd_status & FD_STATUS_C) != FD_STATUS_C)
+ break;
+
+ nreaped++;
+
+ if
+ (
+ (fd.fd_status & (FD_STATUS_B | FD_STATUS_OK))
+ !=
+ (FD_STATUS_B | FD_STATUS_OK)
+ )
+ {
+ /*
+ * Not sure about this one -- it does not seem
+ * to be an error so we will keep quiet about it.
+ if ((fd.fd_status & FD_STATUS_B) != FD_STATUS_B)
+ printk("%s: frame not consumed by RU.\n", dev->name);
+ */
+
+ if ((fd.fd_status & FD_STATUS_OK) != FD_STATUS_OK)
+ printk("%s: frame not received successfully.\n", dev->name);
+ }
+
+ if ((fd.fd_status & (FD_STATUS_S6 | FD_STATUS_S7 | FD_STATUS_S8 | FD_STATUS_S9 | FD_STATUS_S10 | FD_STATUS_S11)) != 0)
+ {
+ lp->stats.rx_errors++;
+
+ if ((fd.fd_status & FD_STATUS_S6) != 0)
+ printk("%s: no EOF flag.\n", dev->name);
+
+ if ((fd.fd_status & FD_STATUS_S7) != 0)
+ {
+ lp->stats.rx_length_errors++;
+ printk("%s: frame too short.\n", dev->name);
+ }
+
+ if ((fd.fd_status & FD_STATUS_S8) != 0)
+ {
+ lp->stats.rx_over_errors++;
+ printk("%s: rx DMA overrun.\n", dev->name);
+ }
+
+ if ((fd.fd_status & FD_STATUS_S9) != 0)
+ {
+ lp->stats.rx_fifo_errors++;
+ printk("%s: ran out of resources.\n", dev->name);
+ }
+
+ if ((fd.fd_status & FD_STATUS_S10) != 0)
+ {
+ lp->stats.rx_frame_errors++;
+ printk("%s: alignment error.\n", dev->name);
+ }
+
+ if ((fd.fd_status & FD_STATUS_S11) != 0)
+ {
+ lp->stats.rx_crc_errors++;
+ printk("%s: CRC error.\n", dev->name);
+ }
+ }
+
+ if (fd.fd_rbd_offset == I82586NULL)
+ printk("%s: frame has no data.\n", dev->name);
+ else
+ {
+ obram_read(ioaddr, fd.fd_rbd_offset, (unsigned char *)&rbd, sizeof(rbd));
+
+ if ((rbd.rbd_status & RBD_STATUS_EOF) != RBD_STATUS_EOF)
+ printk("%s: missing EOF flag.\n", dev->name);
+
+ if ((rbd.rbd_status & RBD_STATUS_F) != RBD_STATUS_F)
+ printk("%s: missing F flag.\n", dev->name);
+
+ pkt_len = rbd.rbd_status & RBD_STATUS_ACNT;
+
+#if 0
+ {
+ unsigned char addr[WAVELAN_ADDR_SIZE];
+ int i;
+ static unsigned char toweraddr[WAVELAN_ADDR_SIZE] =
+ {
+ 0x08, 0x00, 0x0e, 0x20, 0x3e, 0xd3,
+ };
+
+ obram_read(ioaddr, rbd.rbd_bufl + sizeof(addr), &addr[0], sizeof(addr));
+ if
+ (
+ /*
+ addrcmp(&addr[0], &dev->dev_addr[0]) != 0
+ &&
+ */
+ addrcmp(&addr[0], toweraddr) != 0
+ )
+ {
+ printk("%s: foreign MAC source addr=", dev->name);
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ printk("%s%02x", (i == 0) ? "" : ":", addr[i]);
+ printk("\n");
+ }
+ }
+#endif /* 0 */
+
+ if (wavelan_debug > 5)
+ {
+ unsigned char addr[WAVELAN_ADDR_SIZE];
+ unsigned short ltype;
+ int i;
+
+#if 0
+ printk("%s: fd_dest=", dev->name);
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ printk("%s%02x", (i == 0) ? "" : ":", fd.fd_dest[i]);
+ printk("\n");
+
+ printk("%s: fd_src=", dev->name);
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ printk("%s%02x", (i == 0) ? "" : ":", fd.fd_src[i]);
+ printk("\n");
+ printk("%s: fd_length=%d\n", dev->name, fd.fd_length);
+#endif /* 0 */
+
+ obram_read(ioaddr, rbd.rbd_bufl, &addr[0], sizeof(addr));
+ printk("%s: dest=", dev->name);
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ printk("%s%02x", (i == 0) ? "" : ":", addr[i]);
+ printk("\n");
+
+ obram_read(ioaddr, rbd.rbd_bufl + sizeof(addr), &addr[0], sizeof(addr));
+ printk("%s: src=", dev->name);
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ printk("%s%02x", (i == 0) ? "" : ":", addr[i]);
+ printk("\n");
+
+ obram_read(ioaddr, rbd.rbd_bufl + sizeof(addr) * 2, (unsigned char *)&ltype, sizeof(ltype));
+ printk("%s: ntohs(length/type)=0x%04x\n", dev->name, ntohs(ltype));
+ }
+
+ sksize = pkt_len;
+
+ if ((skb = alloc_skb(sksize, GFP_ATOMIC)) == (struct sk_buff *)0)
+ {
+ printk("%s: could not alloc_skb(%d, GFP_ATOMIC).\n", dev->name, sksize);
+ lp->stats.rx_dropped++;
+ }
+ else
+ {
+ skb->len = pkt_len;
+ skb->dev = dev;
+
+ obram_read(ioaddr, rbd.rbd_bufl, skb->data, pkt_len);
+
+ if (wavelan_debug > 5)
+ {
+ int i;
+ int maxi;
+
+ printk("%s: pkt_len=%d, data=\"", dev->name, pkt_len);
+
+ if ((maxi = pkt_len) > 16)
+ maxi = 16;
+
+ for (i = 0; i < maxi; i++)
+ {
+ unsigned char c;
+
+ c = skb->data[i];
+ if (c >= ' ' && c <= '~')
+ printk(" %c", skb->data[i]);
+ else
+ printk("%02x", skb->data[i]);
+ }
+
+ if (maxi < pkt_len)
+ printk("..");
+
+ printk("\"\n\n");
+ }
+
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+
+ lp->stats.rx_packets++;
+ }
+ }
+
+ fd.fd_status = 0;
+ obram_write(ioaddr, fdoff(lp->rx_head, fd_status), (unsigned char *)&fd.fd_status, sizeof(fd.fd_status));
+
+ fd.fd_command = FD_COMMAND_EL;
+ obram_write(ioaddr, fdoff(lp->rx_head, fd_command), (unsigned char *)&fd.fd_command, sizeof(fd.fd_command));
+
+ fd.fd_command = 0;
+ obram_write(ioaddr, fdoff(lp->rx_last, fd_command), (unsigned char *)&fd.fd_command, sizeof(fd.fd_command));
+
+ lp->rx_last = lp->rx_head;
+ lp->rx_head = fd.fd_link_offset;
+ }
+
+/*
+ if (nreaped > 1)
+ printk("r%d", nreaped);
+*/
+}
+
+/*
+ * Command completion interrupt.
+ * Reclaim as many freed tx buffers as we can.
+ */
+static
+int
+wavelan_complete(device *dev, unsigned short ioaddr, net_local *lp)
+{
+ int nreaped;
+
+ nreaped = 0;
+
+ for (;;)
+ {
+ unsigned short tx_status;
+
+ if (lp->tx_first_in_use == I82586NULL)
+ break;
+
+ obram_read(ioaddr, acoff(lp->tx_first_in_use, ac_status), (unsigned char *)&tx_status, sizeof(tx_status));
+
+ if ((tx_status & AC_SFLD_C) == 0)
+ break;
+
+ nreaped++;
+
+ --lp->tx_n_in_use;
+
+/*
+if (lp->tx_n_in_use > 0)
+ printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]);
+*/
+
+ if (lp->tx_n_in_use <= 0)
+ lp->tx_first_in_use = I82586NULL;
+ else
+ {
+ lp->tx_first_in_use += TXBLOCKZ;
+ if (lp->tx_first_in_use >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+ lp->tx_first_in_use -= NTXBLOCKS * TXBLOCKZ;
+ }
+
+ if (tx_status & AC_SFLD_OK)
+ {
+ int ncollisions;
+
+ lp->stats.tx_packets++;
+ ncollisions = tx_status & AC_SFLD_MAXCOL;
+ lp->stats.collisions += ncollisions;
+ /*
+ if (ncollisions > 0)
+ printk("%s: tx completed after %d collisions.\n", dev->name, ncollisions);
+ */
+ }
+ else
+ {
+ lp->stats.tx_errors++;
+ if (tx_status & AC_SFLD_S10)
+ {
+ lp->stats.tx_carrier_errors++;
+ if (wavelan_debug > 0)
+ printk("%s: tx error: no CS.\n", dev->name);
+ }
+ if (tx_status & AC_SFLD_S9)
+ {
+ lp->stats.tx_carrier_errors++;
+ printk("%s: tx error: lost CTS.\n", dev->name);
+ }
+ if (tx_status & AC_SFLD_S8)
+ {
+ lp->stats.tx_fifo_errors++;
+ printk("%s: tx error: slow DMA.\n", dev->name);
+ }
+ if (tx_status & AC_SFLD_S6)
+ {
+ lp->stats.tx_heartbeat_errors++;
+ if (wavelan_debug > 0)
+ printk("%s: tx error: heart beat.\n", dev->name);
+ }
+ if (tx_status & AC_SFLD_S5)
+ {
+ lp->stats.tx_aborted_errors++;
+ if (wavelan_debug > 0)
+ printk("%s: tx error: too many collisions.\n", dev->name);
+ }
+ }
+
+ if (wavelan_debug > 5)
+ printk("%s: tx completed, tx_status 0x%04x.\n", dev->name, tx_status);
+ }
+
+/*
+ if (nreaped > 1)
+ printk("c%d", nreaped);
+*/
+
+ /*
+ * Inform upper layers.
+ */
+ if (lp->tx_n_in_use < NTXBLOCKS - 1)
+ {
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+
+ return nreaped;
+}
+
+static
+void
+wavelan_watchdog(unsigned long a)
+{
+ device *dev;
+ net_local *lp;
+ unsigned short ioaddr;
+ unsigned long x;
+ unsigned int nreaped;
+
+ x = wavelan_splhi();
+
+ dev = (device *)a;
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ if (lp->tx_n_in_use <= 0)
+ {
+ wavelan_splx(x);
+ return;
+ }
+
+ lp->watchdog.expires = WATCHDOG_JIFFIES;
+ add_timer(&lp->watchdog);
+
+ if (jiffies - dev->trans_start < WATCHDOG_JIFFIES)
+ {
+ wavelan_splx(x);
+ return;
+ }
+
+ nreaped = wavelan_complete(dev, ioaddr, lp);
+
+ printk("%s: warning: wavelan_watchdog(): %d reaped, %d remain.\n", dev->name, nreaped, lp->tx_n_in_use);
+ /*
+ wavelan_scb_show(ioaddr);
+ wavelan_ru_show(dev);
+ wavelan_cu_show(dev);
+ wavelan_dev_show(dev);
+ wavelan_local_show(dev);
+ */
+
+ wavelan_splx(x);
+}
+
+static
+void
+wavelan_interrupt(int irq, struct pt_regs *regs)
+{
+ device *dev;
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned short hasr;
+ unsigned short status;
+ unsigned short ack_cmd;
+
+ if ((dev = (device *)(irq2dev_map[irq])) == (device *)0)
+ {
+ printk("wavelan_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ dev->interrupt = 1;
+
+ if ((hasr = hasr_read(ioaddr)) & HASR_MMC_INTR)
+ {
+ unsigned char dce_status;
+
+ /*
+ * Interrupt from the modem management controller.
+ * This will clear it -- ignored for now.
+ */
+ mmc_read(ioaddr, mmroff(0, mmr_dce_status), &dce_status, sizeof(dce_status));
+ if (wavelan_debug > 0)
+ printk("%s: warning: wavelan_interrupt(): unexpected mmc interrupt: status 0x%04x.\n", dev->name, dce_status);
+ }
+
+ if ((hasr & HASR_82586_INTR) == 0)
+ {
+ dev->interrupt = 0;
+ if (wavelan_debug > 0)
+ printk("%s: warning: wavelan_interrupt() but (hasr & HASR_82586_INTR) == 0.\n", dev->name);
+ return;
+ }
+
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), (unsigned char *)&status, sizeof(status));
+
+ /*
+ * Acknowledge the interrupt(s).
+ */
+ ack_cmd = status & SCB_ST_INT;
+
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&ack_cmd, sizeof(ack_cmd));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ if (wavelan_debug > 5)
+ printk("%s: interrupt, status 0x%04x.\n", dev->name, status);
+
+ if ((status & SCB_ST_CX) == SCB_ST_CX)
+ {
+ /*
+ * Command completed.
+ */
+ if (wavelan_debug > 5)
+ printk("%s: command completed.\n", dev->name);
+ (void)wavelan_complete(dev, ioaddr, lp);
+ }
+
+ if ((status & SCB_ST_FR) == SCB_ST_FR)
+ {
+ /*
+ * Frame received.
+ */
+ if (wavelan_debug > 5)
+ printk("%s: received packet.\n", dev->name);
+ wavelan_receive(dev);
+ }
+
+ if
+ (
+ (status & SCB_ST_CNA) == SCB_ST_CNA
+ ||
+ (((status & SCB_ST_CUS) != SCB_ST_CUS_ACTV) && dev->start)
+ )
+ {
+ printk("%s: warning: CU inactive -- restarting.\n", dev->name);
+
+ (void)wavelan_hardware_reset(dev);
+ }
+
+ if
+ (
+ (status & SCB_ST_RNR) == SCB_ST_RNR
+ ||
+ (((status & SCB_ST_RUS) != SCB_ST_RUS_RDY) && dev->start)
+ )
+ {
+ printk("%s: warning: RU not ready -- restarting.\n", dev->name);
+
+ (void)wavelan_hardware_reset(dev);
+ }
+
+ dev->interrupt = 0;
+}
+
+static
+int
+wavelan_close(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned short scb_cmd;
+
+ if (wavelan_debug > 0)
+ printk("%s: ->wavelan_close(dev=0x%x)\n", dev->name, (unsigned int)dev);
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /*
+ * Flush the Tx and disable Rx.
+ */
+ scb_cmd = (SCB_CMD_CUC & SCB_CMD_CUC_SUS) | (SCB_CMD_RUC & SCB_CMD_RUC_SUS);
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cmd, sizeof(scb_cmd));
+ set_chan_attn(ioaddr, lp->hacr);
+
+ wavelan_ints_off(dev);
+
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = (device *)0;
+
+ /*
+ * Release the ioport-region.
+ */
+ release_region(ioaddr, sizeof(ha_t));
+
+#if defined(MODULE)
+ MOD_DEC_USE_COUNT;
+#endif /* defined(MODULE) */
+
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_close(): 0\n", dev->name);
+
+ return 0;
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static
+en_stats *
+wavelan_get_stats(device *dev)
+{
+ net_local *lp;
+
+ lp = (net_local *)dev->priv;
+
+ return &lp->stats;
+}
+
+static
+void
+wavelan_set_multicast_list(device *dev, int num_addrs, void *addrs)
+{
+ net_local *lp;
+ unsigned long x;
+
+ if (wavelan_debug > 0)
+ printk("%s: ->wavelan_set_multicast_list(dev=0x%x, num_addrs=%d, addrs=0x%x)\n", dev->name, (unsigned int)dev, num_addrs, (unsigned int)addrs);
+
+ lp = (net_local *)dev->priv;
+
+ switch (num_addrs)
+ {
+ case -1:
+ /*
+ * Promiscuous mode: receive all packets.
+ */
+ lp->promiscuous = 1;
+ x = wavelan_splhi();
+ (void)wavelan_hardware_reset(dev);
+ wavelan_splx(x);
+ break;
+
+ case 0:
+ /*
+ * Normal mode: disable promiscuous mode,
+ * clear multicast list.
+ */
+ lp->promiscuous = 0;
+ x = wavelan_splhi();
+ (void)wavelan_hardware_reset(dev);
+ wavelan_splx(x);
+ break;
+
+ default:
+ /*
+ * Multicast mode: receive normal and
+ * multicast packets and do best-effort filtering.
+ */
+ break;
+ }
+
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_set_multicast_list()\n", dev->name);
+}
+
+/*
+ * Extra WaveLAN-specific device data.
+ * "cat /proc/net/wavelan" -- see fs/proc/net.c.
+ */
+static
+int
+sprintf_stats(char *buffer, device *dev)
+{
+ net_local *lp;
+ unsigned char v;
+ mmr_t m;
+
+ lp = (net_local *)dev->priv;
+
+ if (lp == (net_local *)0)
+ return sprintf(buffer, "%6s: No statistics available.\n", dev->name);
+
+ v = (unsigned char)1;
+ mmc_write(dev->base_addr, mmwoff(0, mmw_freeze), &v, sizeof(v));
+
+ mmc_read(dev->base_addr, mmroff(0, mmr_dce_status), &m.mmr_dce_status, sizeof(m.mmr_dce_status));
+ mmc_read(dev->base_addr, mmroff(0, mmr_correct_nwid_h), &m.mmr_correct_nwid_h, sizeof(m.mmr_correct_nwid_h));
+ mmc_read(dev->base_addr, mmroff(0, mmr_correct_nwid_l), &m.mmr_correct_nwid_l, sizeof(m.mmr_correct_nwid_l));
+ mmc_read(dev->base_addr, mmroff(0, mmr_wrong_nwid_h), &m.mmr_wrong_nwid_h, sizeof(m.mmr_wrong_nwid_h));
+ mmc_read(dev->base_addr, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l, sizeof(m.mmr_wrong_nwid_l));
+ mmc_read(dev->base_addr, mmroff(0, mmr_signal_lvl), &m.mmr_signal_lvl, sizeof(m.mmr_signal_lvl));
+ mmc_read(dev->base_addr, mmroff(0, mmr_silence_lvl), &m.mmr_silence_lvl, sizeof(m.mmr_silence_lvl));
+ mmc_read(dev->base_addr, mmroff(0, mmr_sgnl_qual), &m.mmr_sgnl_qual, sizeof(m.mmr_sgnl_qual));
+
+ v = (unsigned char)0;
+ mmc_write(dev->base_addr, mmwoff(0, mmw_freeze), &v, sizeof(v));
+
+ lp->correct_nwid += (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l;
+ lp->wrong_nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
+
+ return sprintf
+ (
+ buffer,
+ "%6s: %02x %08x %08x %02x %02x %02x %02x %u\n",
+ dev->name,
+ m.mmr_dce_status,
+ lp->correct_nwid,
+ lp->wrong_nwid,
+ m.mmr_signal_lvl,
+ m.mmr_silence_lvl,
+ m.mmr_sgnl_qual,
+ lp->tx_n_in_use,
+ lp->nresets
+ );
+}
+
+int
+wavelan_get_info(char *buffer, char **start, off_t offset, int length)
+{
+ int len;
+ off_t begin;
+ off_t pos;
+ int size;
+ unsigned long x;
+
+ len = 0;
+ begin = 0;
+ pos = 0;
+
+ size = sprintf(buffer, "%s", "Iface | dce +nwid -nwid lvl slnc qual ntxq nrst\n");
+
+ pos += size;
+ len += size;
+
+ x = wavelan_splhi();
+
+ if (first_wavelan != (net_local *)0)
+ {
+ net_local *lp;
+
+ lp = first_wavelan;
+ do
+ {
+ size = sprintf_stats(buffer + len, lp->dev);
+
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset)
+ {
+ len = 0;
+ begin = pos;
+ }
+
+ if (pos > offset + length)
+ break;
+ }
+ while ((lp = lp->next) != first_wavelan);
+ }
+
+ wavelan_splx(x);
+
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin); /* Start slop */
+ if (len > length)
+ len = length; /* Ending slop */
+
+ return len;
+}
+
+#if defined(MODULE)
+char kernel_version[] = UTS_RELEASE;
+static struct device dev_wavelan =
+{
+ " " /* "wavelan" */,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, wavelan_probe
+};
+
+int
+init_module(void)
+{
+ if (register_netdev(&dev_wavelan) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ if (MOD_IN_USE)
+ printk("wavelan: device busy, remove delayed\n");
+ else
+ {
+ unregister_netdev(&dev_wavelan);
+ kfree_s(dev_wavelan.priv, sizeof(struct net_local));
+ dev_wavelan.priv = NULL;
+ }
+}
+#endif /* defined(MODULE) */
+
+static
+void
+wavelan_cu_show_one(device *dev, net_local *lp, int i, unsigned short p)
+{
+ unsigned short ioaddr;
+ ac_tx_t actx;
+
+ ioaddr = dev->base_addr;
+
+ printk("%d: 0x%x:", i, p);
+
+ obram_read(ioaddr, p, (unsigned char *)&actx, sizeof(actx));
+ printk(" status=0x%x,", actx.tx_h.ac_status);
+ printk(" command=0x%x,", actx.tx_h.ac_command);
+
+/*
+ {
+ tbd_t tbd;
+
+ obram_read(ioaddr, actx.tx_tbd_offset, (unsigned char *)&tbd, sizeof(tbd));
+ printk(" tbd_status=0x%x,", tbd.tbd_status);
+ }
+*/
+
+ printk("|");
+}
+
+#if 0
+static
+void
+wavelan_psa_show(psa_t *p)
+{
+ printk("psa:");
+
+ printk("psa_io_base_addr_1: 0x%02x,", p->psa_io_base_addr_1);
+ printk("psa_io_base_addr_2: 0x%02x,", p->psa_io_base_addr_2);
+ printk("psa_io_base_addr_3: 0x%02x,", p->psa_io_base_addr_3);
+ printk("psa_io_base_addr_4: 0x%02x,", p->psa_io_base_addr_4);
+ printk("psa_rem_boot_addr_1: 0x%02x,", p->psa_rem_boot_addr_1);
+ printk("psa_rem_boot_addr_2: 0x%02x,", p->psa_rem_boot_addr_2);
+ printk("psa_rem_boot_addr_3: 0x%02x,", p->psa_rem_boot_addr_3);
+ printk("psa_holi_params: 0x%02x,", p->psa_holi_params);
+ printk("psa_int_req_no: %d,", p->psa_int_req_no);
+ printk
+ (
+ "psa_univ_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x,",
+ p->psa_univ_mac_addr[0],
+ p->psa_univ_mac_addr[1],
+ p->psa_univ_mac_addr[2],
+ p->psa_univ_mac_addr[3],
+ p->psa_univ_mac_addr[4],
+ p->psa_univ_mac_addr[5]
+ );
+ printk
+ (
+ "psa_local_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x,",
+ p->psa_local_mac_addr[0],
+ p->psa_local_mac_addr[1],
+ p->psa_local_mac_addr[2],
+ p->psa_local_mac_addr[3],
+ p->psa_local_mac_addr[4],
+ p->psa_local_mac_addr[5]
+ );
+ printk("psa_univ_local_sel: %d,", p->psa_univ_local_sel);
+ printk("psa_comp_number: %d,", p->psa_comp_number);
+ printk("psa_thr_pre_set: 0x%02x,", p->psa_thr_pre_set);
+ printk("psa_feature_select/decay_prm: 0x%02x,", p->psa_feature_select);
+ printk("psa_subband/decay_update_prm: %d,", p->psa_subband);
+ printk("psa_quality_thr: 0x%02x,", p->psa_quality_thr);
+ printk("psa_mod_delay: 0x%02x,", p->psa_mod_delay);
+ printk("psa_nwid: 0x%02x%02x,", p->psa_nwid[0], p->psa_nwid[1]);
+ printk("psa_undefined: %d,", p->psa_undefined);
+ printk("psa_encryption_select: %d,", p->psa_encryption_select);
+ printk
+ (
+ "psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x,",
+ p->psa_encryption_key[0],
+ p->psa_encryption_key[1],
+ p->psa_encryption_key[2],
+ p->psa_encryption_key[3],
+ p->psa_encryption_key[4],
+ p->psa_encryption_key[5],
+ p->psa_encryption_key[6],
+ p->psa_encryption_key[7]
+ );
+ printk("psa_databus_width: %d,", p->psa_databus_width);
+ printk("psa_call_code/auto_squelch: 0x%02x,", p->psa_call_code);
+ printk("psa_no_of_retries: %d,", p->psa_no_of_retries);
+ printk("psa_acr: %d,", p->psa_acr);
+ printk("psa_dump_count: %d,", p->psa_dump_count);
+ printk("psa_nwid_prefix: 0x%02x,", p->psa_nwid_prefix);
+ printk("psa_conf_status: %d,", p->psa_conf_status);
+ printk("psa_crc: 0x%02x%02x,", p->psa_crc[0], p->psa_crc[1]);
+ printk("psa_crc_status: 0x%02x,", p->psa_crc_status);
+
+ printk("\n");
+}
+
+static
+void
+wavelan_mmc_show(unsigned short ioaddr)
+{
+ mmr_t m;
+
+ mmc_read(ioaddr, 0, (unsigned char *)&m, sizeof(m));
+
+ printk("mmr:");
+ printk(" des_status: 0x%x", m.mmr_des_status);
+ printk(" des_avail: 0x%x", m.mmr_des_avail);
+ printk(" des_io_invert: 0x%x", m.mmr_des_io_invert);
+ printk
+ (
+ " dce_status: 0x%x[%s%s%s%s]",
+ m.mmr_dce_status & 0x0F,
+ (m.mmr_dce_status & MMR_DCE_STATUS_ENERG_DET) ? "energy detected," : "",
+ (m.mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ? "loop test indicated," : "",
+ (m.mmr_dce_status & MMR_DCE_STATUS_XMTITR_IND) ? "transmitter on," : "",
+ (m.mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ? "jabber timer expired," : ""
+ );
+ printk(" correct_nwid: %d", m.mmr_correct_nwid_h << 8 | m.mmr_correct_nwid_l);
+ printk(" wrong_nwid: %d", (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l);
+ printk(" thr_pre_set: 0x%x", m.mmr_thr_pre_set);
+ printk(" signal_lvl: %d", m.mmr_signal_lvl);
+ printk(" silence_lvl: %d", m.mmr_silence_lvl);
+ printk(" sgnl_qual: 0x%x", m.mmr_sgnl_qual);
+ printk(" netw_id_l: %x", m.mmr_netw_id_l);
+
+ printk("\n");
+}
+#endif /* 0 */
+
+static
+void
+wavelan_scb_show(unsigned short ioaddr)
+{
+ scb_t scb;
+
+ obram_read(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb));
+
+ printk("scb:");
+
+ printk(" status:");
+ printk
+ (
+ " stat 0x%x[%s%s%s%s]",
+ (scb.scb_status & (SCB_ST_CX | SCB_ST_FR | SCB_ST_CNA | SCB_ST_RNR)) >> 12,
+ (scb.scb_status & SCB_ST_CX) ? "cmd completion interrupt," : "",
+ (scb.scb_status & SCB_ST_FR) ? "frame received," : "",
+ (scb.scb_status & SCB_ST_CNA) ? "cmd unit not active," : "",
+ (scb.scb_status & SCB_ST_RNR) ? "rcv unit not ready," : ""
+ );
+ printk
+ (
+ " cus 0x%x[%s%s%s]",
+ (scb.scb_status & SCB_ST_CUS) >> 8,
+ ((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_IDLE) ? "idle" : "",
+ ((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_SUSP) ? "suspended" : "",
+ ((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_ACTV) ? "active" : ""
+ );
+ printk
+ (
+ " rus 0x%x[%s%s%s%s]",
+ (scb.scb_status & SCB_ST_RUS) >> 4,
+ ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_IDLE) ? "idle" : "",
+ ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_SUSP) ? "suspended" : "",
+ ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_NRES) ? "no resources" : "",
+ ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_RDY) ? "ready" : ""
+ );
+
+ printk(" command:");
+ printk
+ (
+ " ack 0x%x[%s%s%s%s]",
+ (scb.scb_command & (SCB_CMD_ACK_CX | SCB_CMD_ACK_FR | SCB_CMD_ACK_CNA | SCB_CMD_ACK_RNR)) >> 12,
+ (scb.scb_command & SCB_CMD_ACK_CX) ? "ack cmd completion," : "",
+ (scb.scb_command & SCB_CMD_ACK_FR) ? "ack frame received," : "",
+ (scb.scb_command & SCB_CMD_ACK_CNA) ? "ack CU not active," : "",
+ (scb.scb_command & SCB_CMD_ACK_RNR) ? "ack RU not ready," : ""
+ );
+ printk
+ (
+ " cuc 0x%x[%s%s%s%s%s]",
+ (scb.scb_command & SCB_CMD_CUC) >> 8,
+ ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_NOP) ? "nop" : "",
+ ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_GO) ? "start cbl_offset" : "",
+ ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_RES) ? "resume execution" : "",
+ ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_SUS) ? "suspend execution" : "",
+ ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_ABT) ? "abort execution" : ""
+ );
+ printk
+ (
+ " ruc 0x%x[%s%s%s%s%s]",
+ (scb.scb_command & SCB_CMD_RUC) >> 4,
+ ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_NOP) ? "nop" : "",
+ ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_GO) ? "start rfa_offset" : "",
+ ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_RES) ? "resume reception" : "",
+ ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_SUS) ? "suspend reception" : "",
+ ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_ABT) ? "abort reception" : ""
+ );
+
+ printk(" cbl_offset 0x%x", scb.scb_cbl_offset);
+ printk(" rfa_offset 0x%x", scb.scb_rfa_offset);
+
+ printk(" crcerrs %d", scb.scb_crcerrs);
+ printk(" alnerrs %d", scb.scb_alnerrs);
+ printk(" rscerrs %d", scb.scb_rscerrs);
+ printk(" ovrnerrs %d", scb.scb_ovrnerrs);
+
+ printk("\n");
+}
+
+static
+void
+wavelan_ru_show(device *dev)
+{
+ net_local *lp;
+
+ lp = (net_local *)dev->priv;
+
+ printk("ru:");
+ /*
+ * Not implemented yet...
+ */
+ printk("\n");
+}
+
+static
+void
+wavelan_cu_show(device *dev)
+{
+ net_local *lp;
+ unsigned int i;
+ unsigned short p;
+
+ lp = (net_local *)dev->priv;
+
+ printk("cu:");
+ printk("\n");
+
+ for (i = 0, p = lp->tx_first_in_use; i < NTXBLOCKS; i++)
+ {
+ wavelan_cu_show_one(dev, lp, i, p);
+
+ p += TXBLOCKZ;
+ if (p >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+ p -= NTXBLOCKS * TXBLOCKZ;
+ }
+}
+
+static
+void
+wavelan_dev_show(device *dev)
+{
+ printk("dev:");
+ printk(" start=%d,", dev->start);
+ printk(" tbusy=%d,", dev->tbusy);
+ printk(" interrupt=%d,", dev->interrupt);
+ printk(" trans_start=%ld,", dev->trans_start);
+ printk(" flags=0x%x,", dev->flags);
+ printk("\n");
+}
+
+static
+void
+wavelan_local_show(device *dev)
+{
+ net_local *lp;
+
+ lp = (net_local *)dev->priv;
+
+ printk("local:");
+ printk(" tx_n_in_use=%d,", lp->tx_n_in_use);
+ printk(" hacr=0x%x,", lp->hacr);
+ printk(" rx_head=0x%x,", lp->rx_head);
+ printk(" rx_last=0x%x,", lp->rx_last);
+ printk(" tx_first_free=0x%x,", lp->tx_first_free);
+ printk(" tx_first_in_use=0x%x,", lp->tx_first_in_use);
+ printk("\n");
+}
+
+/*
+ * This software may only be used and distributed
+ * according to the terms of the GNU Public License.
+ *
+ * This software was developed as a component of the
+ * Linux operating system.
+ * It is based on other device drivers and information
+ * either written or supplied by:
+ * Ajay Bakre (bakre@paul.rutgers.edu),
+ * Donald Becker (becker@cesdis.gsfc.nasa.gov),
+ * Loeke Brederveld (Loeke.Brederveld@Utrecht.NCR.com),
+ * Anders Klemets (klemets@it.kth.se),
+ * Vladimir V. Kolpakov (w@stier.koenig.ru),
+ * Marc Meertens (Marc.Meertens@Utrecht.NCR.com),
+ * Pauline Middelink (middelin@polyware.iaf.nl),
+ * Robert Morris (rtm@das.harvard.edu),
+ * Girish Welling (welling@paul.rutgers.edu),
+ *
+ * Thanks go also to:
+ * James Ashton (jaa101@syseng.anu.edu.au),
+ * Alan Cox (iialan@iiit.swan.ac.uk),
+ * Allan Creighton (allanc@cs.usyd.edu.au),
+ * Matthew Geier (matthew@cs.usyd.edu.au),
+ * Remo di Giovanni (remo@cs.usyd.edu.au),
+ * Eckhard Grah (grah@wrcs1.urz.uni-wuppertal.de),
+ * Vipul Gupta (vgupta@cs.binghamton.edu),
+ * Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM),
+ * Tim Nicholson (tim@cs.usyd.edu.au),
+ * Ian Parkin (ian@cs.usyd.edu.au),
+ * John Rosenberg (johnr@cs.usyd.edu.au),
+ * George Rossi (george@phm.gov.au),
+ * Arthur Scott (arthur@cs.usyd.edu.au),
+ * Peter Storey,
+ * for their assistance and advice.
+ *
+ * Please send bug reports, updates, comments to:
+ *
+ * Bruce Janson Email: bruce@cs.usyd.edu.au
+ * Basser Department of Computer Science Phone: +61-2-351-3423
+ * University of Sydney, N.S.W., 2006, AUSTRALIA Fax: +61-2-351-3838
+ */
+#endif /* defined(CONFIG_WAVELAN) */
diff --git a/drivers/net/wavelan.h b/drivers/net/wavelan.h
new file mode 100644
index 000000000..4e1f514ff
--- /dev/null
+++ b/drivers/net/wavelan.h
@@ -0,0 +1,254 @@
+#if defined(CONFIG_WAVELAN)
+#define WAVELAN_ADDR_SIZE 6 /* Size of a MAC address */
+#define SA_ADDR0 0x08 /* First octet of WaveLAN MAC addresses */
+#define SA_ADDR1 0x00 /* Second octet of WaveLAN MAC addresses */
+#define SA_ADDR2 0x0E /* Third octet of WaveLAN MAC addresses */
+#define WAVELAN_MTU 1500 /* Maximum size of WaveLAN packet */
+
+/*
+ * Parameter Storage Area (PSA).
+ */
+typedef struct psa_t psa_t;
+struct psa_t
+{
+ unsigned char psa_io_base_addr_1; /* Base address 1 ??? */
+ unsigned char psa_io_base_addr_2; /* Base address 2 */
+ unsigned char psa_io_base_addr_3; /* Base address 3 */
+ unsigned char psa_io_base_addr_4; /* Base address 4 */
+ unsigned char psa_rem_boot_addr_1; /* Remote Boot Address 1 */
+ unsigned char psa_rem_boot_addr_2; /* Remote Boot Address 2 */
+ unsigned char psa_rem_boot_addr_3; /* Remote Boot Address 3 */
+ unsigned char psa_holi_params; /* HOst Lan Interface (HOLI) Parameters */
+ unsigned char psa_int_req_no; /* Interrupt Request Line */
+ unsigned char psa_unused0[7]; /* unused */
+ unsigned char psa_univ_mac_addr[WAVELAN_ADDR_SIZE]; /* Universal (factory) MAC Address */
+ unsigned char psa_local_mac_addr[WAVELAN_ADDR_SIZE]; /* Local MAC Address */
+ unsigned char psa_univ_local_sel; /* Universal Local Selection */
+#define PSA_UNIVERSAL 0 /* Universal (factory) */
+#define PSA_LOCAL 1 /* Local */
+ unsigned char psa_comp_number; /* Compatibility Number: */
+#define PSA_COMP_PC_AT_915 0 /* PC-AT 915 MHz */
+#define PSA_COMP_PC_MC_915 1 /* PC-MC 915 MHz */
+#define PSA_COMP_PC_AT_2400 2 /* PC-AT 2.4 GHz */
+#define PSA_COMP_PC_MC_2400 3 /* PC-MC 2.4 GHz */
+#define PSA_COMP_PCMCIA_915 4 /* PCMCIA 915 MHz */
+ unsigned char psa_thr_pre_set; /* Modem Threshold Preset */
+ unsigned char psa_feature_select; /* ??? */
+#if 0
+ <alias for above>
+ unsigned char psa_decay_prm; /* Modem Decay */
+#endif /* 0 */
+ unsigned char psa_subband; /* Subband */
+#define PSA_SUBBAND_915 0 /* 915 MHz */
+#define PSA_SUBBAND_2425 1 /* 2425 MHz */
+#define PSA_SUBBAND_2460 2 /* 2460 MHz */
+#define PSA_SUBBAND_2484 3 /* 2484 MHz */
+#define PSA_SUBBAND_2430_5 4 /* 2430.5 MHz */
+#if 0
+ <alias for above>
+ unsigned char psa_decay_updat_prm; /* Modem Decay Update ??? */
+#endif /* 0 */
+ unsigned char psa_quality_thr; /* Modem Quality Threshold */
+ unsigned char psa_mod_delay; /* Modem Delay ??? */
+ unsigned char psa_nwid[2]; /* Network ID */
+ unsigned char psa_undefined; /* undefined */
+ unsigned char psa_encryption_select; /* Encryption On Off */
+ unsigned char psa_encryption_key[8]; /* Encryption Key */
+ unsigned char psa_databus_width; /* 8/16 bit bus width */
+ unsigned char psa_call_code; /* ??? */
+#if 0
+ <alias for above>
+ unsigned char psa_auto_squelch; /* Automatic Squelch level On off ??? */
+#endif /* 0 */
+ unsigned char psa_no_of_retries; /* LAN Cont. No of retries */
+ unsigned char psa_acr; /* LAN Cont. ACR */
+ unsigned char psa_dump_count; /* number of Dump Commands in TFB */
+ unsigned char psa_unused1[4]; /* unused */
+ unsigned char psa_nwid_prefix; /* ??? */
+ unsigned char psa_unused2[3]; /* unused */
+ unsigned char psa_conf_status; /* Card Configuration Status */
+ unsigned char psa_crc[2]; /* CRC over PSA */
+ unsigned char psa_crc_status; /* CRC Valid Flag */
+};
+#if STRUCT_CHECK == 1
+#define PSA_SIZE 64
+#endif /* STRUCT_CHECK == 1 */
+
+/*
+ * Modem Management Controller (MMC) write structure.
+ */
+typedef struct mmw_t mmw_t;
+struct mmw_t
+{
+ unsigned char mmw_encr_key[8]; /* encryption key */
+ unsigned char mmw_encr_enable; /* enable/disable encryption */
+ unsigned char mmw_unused0[1]; /* unused */
+ unsigned char mmw_des_io_invert; /* ??? */
+ unsigned char mmw_unused1[5]; /* unused */
+ unsigned char mmw_loopt_sel; /* looptest selection */
+#define MMW_LOOPT_SEL_UNDEFINED 0x40 /* undefined */
+#define MMW_LOOPT_SEL_INT 0x20 /* activate Attention Request */
+#define MMW_LOOPT_SEL_LS 0x10 /* looptest without collision avoidance */
+#define MMW_LOOPT_SEL_LT3A 0x08 /* looptest 3a */
+#define MMW_LOOPT_SEL_LT3B 0x04 /* looptest 3b */
+#define MMW_LOOPT_SEL_LT3C 0x02 /* looptest 3c */
+#define MMW_LOOPT_SEL_LT3D 0x01 /* looptest 3d */
+ unsigned char mmw_jabber_enable; /* jabber timer enable */
+ unsigned char mmw_freeze; /* freeze / unfreeze signal level */
+ unsigned char mmw_anten_sel; /* antenna selection */
+#define MMW_ANTEN_SEL_SEL 0x01 /* direct antenna selection */
+#define MMW_ANTEN_SEL_ALG_EN 0x02 /* antenna selection algorithm enable */
+ unsigned char mmw_ifs; /* inter frame spacing */
+ unsigned char mmw_mod_delay; /* modem delay */
+ unsigned char mmw_jam_time; /* jamming time */
+ unsigned char mmw_unused2[1]; /* unused */
+ unsigned char mmw_thr_pre_set; /* level threshold preset */
+ unsigned char mmw_decay_prm; /* decay parameters */
+ unsigned char mmw_decay_updat_prm; /* decay update parameters */
+ unsigned char mmw_quality_thr; /* quality (z-quotient) threshold */
+ unsigned char mmw_netw_id_l; /* NWID low order byte */
+ unsigned char mmw_netw_id_h; /* NWID high order byte */
+};
+#if STRUCT_CHECK == 1
+#define MMW_SIZE 30
+#endif /* STRUCT_CHECK == 1 */
+
+#define mmwoff(p,f) (unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0)
+
+/*
+ * Modem Management Controller (MMC) read structure.
+ */
+typedef struct mmr_t mmr_t;
+struct mmr_t
+{
+ unsigned char mmr_unused0[8]; /* unused */
+ unsigned char mmr_des_status; /* encryption status */
+ unsigned char mmr_des_avail; /* encryption available (0x55 read) */
+ unsigned char mmr_des_io_invert; /* des I/O invert register */
+ unsigned char mmr_unused1[5]; /* unused */
+ unsigned char mmr_dce_status; /* DCE status */
+#define MMR_DCE_STATUS_ENERG_DET 0x01 /* energy detected */
+#define MMR_DCE_STATUS_LOOPT_IND 0x02 /* loop test indicated */
+#define MMR_DCE_STATUS_XMTITR_IND 0x04 /* transmitter on */
+#define MMR_DCE_STATUS_JBR_EXPIRED 0x08 /* jabber timer expired */
+ unsigned char mmr_unused2[3]; /* unused */
+ unsigned char mmr_correct_nwid_l; /* no. of correct NWID's rxd (low) */
+ unsigned char mmr_correct_nwid_h; /* no. of correct NWID's rxd (high) */
+ unsigned char mmr_wrong_nwid_l; /* count of wrong NWID's received (low) */
+ unsigned char mmr_wrong_nwid_h; /* count of wrong NWID's received (high) */
+ unsigned char mmr_thr_pre_set; /* level threshold preset */
+ unsigned char mmr_signal_lvl; /* signal level */
+ unsigned char mmr_silence_lvl; /* silence level */
+ unsigned char mmr_sgnl_qual; /* signal quality */
+#define MMR_SGNL_QUAL_0 0x01 /* signal quality 0 */
+#define MMR_SGNL_QUAL_1 0x02 /* signal quality 1 */
+#define MMR_SGNL_QUAL_2 0x04 /* signal quality 2 */
+#define MMR_SGNL_QUAL_3 0x08 /* signal quality 3 */
+#define MMR_SGNL_QUAL_S_A 0x80 /* currently selected antenna */
+ unsigned char mmr_netw_id_l; /* NWID low order byte ??? */
+ unsigned char mmr_unused3[1]; /* unused */
+};
+#if STRUCT_CHECK == 1
+#define MMR_SIZE 30
+#endif /* STRUCT_CHECK == 1 */
+
+#define MMR_LEVEL_MASK 0x3F
+
+#define mmroff(p,f) (unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0)
+
+/*
+ * Host Adaptor structure.
+ * (base is board port address).
+ */
+typedef union hacs_u hacs_u;
+union hacs_u
+{
+ unsigned short hu_command; /* Command register */
+#define HACR_RESET 0x0001 /* Reset board */
+#define HACR_CA 0x0002 /* Set Channel Attention for 82586 */
+#define HACR_16BITS 0x0004 /* 16 bits operation (0 => 8bits) */
+#define HACR_OUT0 0x0008 /* General purpose output pin 0 */
+ /* not used - must be 1 */
+#define HACR_OUT1 0x0010 /* General purpose output pin 1 */
+ /* not used - must be 1 */
+#define HACR_82586_INT_ENABLE 0x0020 /* Enable 82586 interrupts */
+#define HACR_MMC_INT_ENABLE 0x0040 /* Enable MMC interrupts */
+#define HACR_INTR_CLR_ENABLE 0x0080 /* Enable interrupt status read/clear */
+ unsigned short hu_status; /* Status Register */
+#define HASR_82586_INTR 0x0001 /* Interrupt request from 82586 */
+#define HASR_MMC_INTR 0x0002 /* Interrupt request from MMC */
+#define HASR_MMC_BUSY 0x0004 /* MMC busy indication */
+#define HASR_PSA_BUSY 0x0008 /* LAN parameter storage area busy */
+};
+
+typedef struct ha_t ha_t;
+struct ha_t
+{
+ hacs_u ha_cs; /* Command and status registers */
+#define ha_command ha_cs.hu_command
+#define ha_status ha_cs.hu_status
+ unsigned short ha_mmcr; /* Modem Management Ctrl Register */
+ unsigned short ha_pior0; /* Program I/O Address Register Port 0 */
+ unsigned short ha_piop0; /* Program I/O Port 0 */
+ unsigned short ha_pior1; /* Program I/O Address Register Port 1 */
+ unsigned short ha_piop1; /* Program I/O Port 1 */
+ unsigned short ha_pior2; /* Program I/O Address Register Port 2 */
+ unsigned short ha_piop2; /* Program I/O Port 2 */
+};
+#if STRUCT_CHECK == 1
+#define HA_SIZE 16
+#endif /* STRUCT_CHECK == 1 */
+
+#define hoff(p,f) (unsigned short)((void *)(&((ha_t *)((void *)0 + (p)))->f) - (void *)0)
+#define HACR(p) hoff(p, ha_command)
+#define HASR(p) hoff(p, ha_status)
+#define MMCR(p) hoff(p, ha_mmcr)
+#define PIOR0(p) hoff(p, ha_pior0)
+#define PIOP0(p) hoff(p, ha_piop0)
+#define PIOR1(p) hoff(p, ha_pior1)
+#define PIOP1(p) hoff(p, ha_piop1)
+#define PIOR2(p) hoff(p, ha_pior2)
+#define PIOP2(p) hoff(p, ha_piop2)
+
+/*
+ * Program I/O Mode Register values.
+ */
+#define STATIC_PIO 0 /* Mode 1: static mode */
+ /* RAM access ??? */
+#define AUTOINCR_PIO 1 /* Mode 2: auto increment mode */
+ /* RAM access ??? */
+#define AUTODECR_PIO 2 /* Mode 3: auto decrement mode */
+ /* RAM access ??? */
+#define PARAM_ACCESS_PIO 3 /* Mode 4: LAN parameter access mode */
+ /* Parameter access. */
+#define PIO_MASK 3 /* register mask */
+#define PIOM(cmd,piono) ((u_short)cmd << 10 << (piono * 2))
+
+#define HACR_DEFAULT (HACR_OUT0 | HACR_OUT1 | HACR_16BITS | PIOM(STATIC_PIO, 0) | PIOM(AUTOINCR_PIO, 1) | PIOM(PARAM_ACCESS_PIO, 2))
+#define HACR_INTRON (HACR_82586_INT_ENABLE | HACR_MMC_INT_ENABLE | HACR_INTR_CLR_ENABLE)
+
+#define MAXDATAZ (WAVELAN_ADDR_SIZE + WAVELAN_ADDR_SIZE + 2 + WAVELAN_MTU)
+
+/*
+ * Onboard 64k RAM layout.
+ * (Offsets from 0x0000.)
+ */
+#define OFFSET_RU 0x0000
+#define OFFSET_CU 0x8000
+#define OFFSET_SCB (OFFSET_ISCP - sizeof(scb_t))
+#define OFFSET_ISCP (OFFSET_SCP - sizeof(iscp_t))
+#define OFFSET_SCP I82586_SCP_ADDR
+
+#define RXBLOCKZ (sizeof(fd_t) + sizeof(rbd_t) + MAXDATAZ)
+#define TXBLOCKZ (sizeof(ac_tx_t) + sizeof(ac_nop_t) + sizeof(tbd_t) + MAXDATAZ)
+
+#define NRXBLOCKS ((OFFSET_CU - OFFSET_RU) / RXBLOCKZ)
+#define NTXBLOCKS ((OFFSET_SCB - OFFSET_CU) / TXBLOCKZ)
+
+/*
+ * This software may only be used and distributed
+ * according to the terms of the GNU Public License.
+ *
+ * For more details, see wavelan.c.
+ */
+#endif /* defined(CONFIG_WAVELAN) */
diff --git a/drivers/net/wd.c b/drivers/net/wd.c
index 9dba2ea2a..d59a68ab8 100644
--- a/drivers/net/wd.c
+++ b/drivers/net/wd.c
@@ -20,7 +20,6 @@
static char *version =
"wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
@@ -236,7 +235,7 @@ int wd_probe1(struct device *dev, int ioaddr)
}
/* OK, were are certain this is going to work. Setup the device. */
- snarf_region(ioaddr, WD_IO_EXTENT);
+ request_region(ioaddr, WD_IO_EXTENT,"wd");
ethdev_init(dev);
ei_status.name = model_name;
@@ -262,6 +261,13 @@ int wd_probe1(struct device *dev, int ioaddr)
dev->stop = &wd_close_card;
NS8390_init(dev, 0);
+#if 1
+ /* Enable interrupt generation on softconfig cards -- M.U */
+ /* .. but possibly potentially unsafe - Donald */
+ if (inb(ioaddr+14) & 0x20)
+ outb(inb(ioaddr+4)|0x80, ioaddr+4);
+#endif
+
return 0;
}
@@ -362,6 +368,7 @@ wd_close_card(struct device *dev)
if (ei_debug > 1)
printk("%s: Shutting down ethercard.\n", dev->name);
NS8390_init(dev, 0);
+ dev->start = 0;
/* Change from 16-bit to 8-bit shared memory so reboot works. */
outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
diff --git a/drivers/net/z8530.h b/drivers/net/z8530.h
new file mode 100644
index 000000000..bfa7339af
--- /dev/null
+++ b/drivers/net/z8530.h
@@ -0,0 +1,220 @@
+
+/* 8530 Serial Communications Controller Register definitions */
+#define FLAG 0x7e
+
+/* Write Register 0 */
+#define R0 0 /* Register selects */
+#define R1 1
+#define R2 2
+#define R3 3
+#define R4 4
+#define R5 5
+#define R6 6
+#define R7 7
+#define R8 8
+#define R9 9
+#define R10 10
+#define R11 11
+#define R12 12
+#define R13 13
+#define R14 14
+#define R15 15
+
+#define NULLCODE 0 /* Null Code */
+#define POINT_HIGH 0x8 /* Select upper half of registers */
+#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */
+#define SEND_ABORT 0x18 /* HDLC Abort */
+#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */
+#define RES_Tx_P 0x28 /* Reset TxINT Pending */
+#define ERR_RES 0x30 /* Error Reset */
+#define RES_H_IUS 0x38 /* Reset highest IUS */
+
+#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */
+#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */
+#define RES_EOM_L 0xC0 /* Reset EOM latch */
+
+/* Write Register 1 */
+
+#define EXT_INT_ENAB 0x1 /* Ext Int Enable */
+#define TxINT_ENAB 0x2 /* Tx Int Enable */
+#define PAR_SPEC 0x4 /* Parity is special condition */
+
+#define RxINT_DISAB 0 /* Rx Int Disable */
+#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */
+#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */
+#define INT_ERR_Rx 0x18 /* Int on error only */
+
+#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */
+#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */
+#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */
+
+/* Write Register #2 (Interrupt Vector) */
+
+/* Write Register 3 */
+
+#define RxENABLE 0x1 /* Rx Enable */
+#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */
+#define ADD_SM 0x4 /* Address Search Mode (SDLC) */
+#define RxCRC_ENAB 0x8 /* Rx CRC Enable */
+#define ENT_HM 0x10 /* Enter Hunt Mode */
+#define AUTO_ENAB 0x20 /* Auto Enables */
+#define Rx5 0x0 /* Rx 5 Bits/Character */
+#define Rx7 0x40 /* Rx 7 Bits/Character */
+#define Rx6 0x80 /* Rx 6 Bits/Character */
+#define Rx8 0xc0 /* Rx 8 Bits/Character */
+
+/* Write Register 4 */
+
+#define PAR_ENA 0x1 /* Parity Enable */
+#define PAR_EVEN 0x2 /* Parity Even/Odd* */
+
+#define SYNC_ENAB 0 /* Sync Modes Enable */
+#define SB1 0x4 /* 1 stop bit/char */
+#define SB15 0x8 /* 1.5 stop bits/char */
+#define SB2 0xc /* 2 stop bits/char */
+
+#define MONSYNC 0 /* 8 Bit Sync character */
+#define BISYNC 0x10 /* 16 bit sync character */
+#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */
+#define EXTSYNC 0x30 /* External Sync Mode */
+
+#define X1CLK 0x0 /* x1 clock mode */
+#define X16CLK 0x40 /* x16 clock mode */
+#define X32CLK 0x80 /* x32 clock mode */
+#define X64CLK 0xC0 /* x64 clock mode */
+
+/* Write Register 5 */
+
+#define TxCRC_ENAB 0x1 /* Tx CRC Enable */
+#define RTS 0x2 /* RTS */
+#define SDLC_CRC 0x4 /* SDLC/CRC-16 */
+#define TxENAB 0x8 /* Tx Enable */
+#define SND_BRK 0x10 /* Send Break */
+#define Tx5 0x0 /* Tx 5 bits (or less)/character */
+#define Tx7 0x20 /* Tx 7 bits/character */
+#define Tx6 0x40 /* Tx 6 bits/character */
+#define Tx8 0x60 /* Tx 8 bits/character */
+#define DTR 0x80 /* DTR */
+
+/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
+
+/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
+
+/* Write Register 8 (transmit buffer) */
+
+/* Write Register 9 (Master interrupt control) */
+#define VIS 1 /* Vector Includes Status */
+#define NV 2 /* No Vector */
+#define DLC 4 /* Disable Lower Chain */
+#define MIE 8 /* Master Interrupt Enable */
+#define STATHI 0x10 /* Status high */
+#define NORESET 0 /* No reset on write to R9 */
+#define CHRB 0x40 /* Reset channel B */
+#define CHRA 0x80 /* Reset channel A */
+#define FHWRES 0xc0 /* Force hardware reset */
+
+/* Write Register 10 (misc control bits) */
+#define BIT6 1 /* 6 bit/8bit sync */
+#define LOOPMODE 2 /* SDLC Loop mode */
+#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */
+#define MARKIDLE 8 /* Mark/flag on idle */
+#define GAOP 0x10 /* Go active on poll */
+#define NRZ 0 /* NRZ mode */
+#define NRZI 0x20 /* NRZI mode */
+#define FM1 0x40 /* FM1 (transition = 1) */
+#define FM0 0x60 /* FM0 (transition = 0) */
+#define CRCPS 0x80 /* CRC Preset I/O */
+
+/* Write Register 11 (Clock Mode control) */
+#define TRxCXT 0 /* TRxC = Xtal output */
+#define TRxCTC 1 /* TRxC = Transmit clock */
+#define TRxCBR 2 /* TRxC = BR Generator Output */
+#define TRxCDP 3 /* TRxC = DPLL output */
+#define TRxCOI 4 /* TRxC O/I */
+#define TCRTxCP 0 /* Transmit clock = RTxC pin */
+#define TCTRxCP 8 /* Transmit clock = TRxC pin */
+#define TCBR 0x10 /* Transmit clock = BR Generator output */
+#define TCDPLL 0x18 /* Transmit clock = DPLL output */
+#define RCRTxCP 0 /* Receive clock = RTxC pin */
+#define RCTRxCP 0x20 /* Receive clock = TRxC pin */
+#define RCBR 0x40 /* Receive clock = BR Generator output */
+#define RCDPLL 0x60 /* Receive clock = DPLL output */
+#define RTxCX 0x80 /* RTxC Xtal/No Xtal */
+
+/* Write Register 12 (lower byte of baud rate generator time constant) */
+
+/* Write Register 13 (upper byte of baud rate generator time constant) */
+
+/* Write Register 14 (Misc control bits) */
+#define BRENABL 1 /* Baud rate generator enable */
+#define BRSRC 2 /* Baud rate generator source */
+#define DTRREQ 4 /* DTR/Request function */
+#define AUTOECHO 8 /* Auto Echo */
+#define LOOPBAK 0x10 /* Local loopback */
+#define SEARCH 0x20 /* Enter search mode */
+#define RMC 0x40 /* Reset missing clock */
+#define DISDPLL 0x60 /* Disable DPLL */
+#define SSBR 0x80 /* Set DPLL source = BR generator */
+#define SSRTxC 0xa0 /* Set DPLL source = RTxC */
+#define SFMM 0xc0 /* Set FM mode */
+#define SNRZI 0xe0 /* Set NRZI mode */
+
+/* Write Register 15 (external/status interrupt control) */
+#define ZCIE 2 /* Zero count IE */
+#define DCDIE 8 /* DCD IE */
+#define SYNCIE 0x10 /* Sync/hunt IE */
+#define CTSIE 0x20 /* CTS IE */
+#define TxUIE 0x40 /* Tx Underrun/EOM IE */
+#define BRKIE 0x80 /* Break/Abort IE */
+
+
+/* Read Register 0 */
+#define Rx_CH_AV 0x1 /* Rx Character Available */
+#define ZCOUNT 0x2 /* Zero count */
+#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */
+#define DCD 0x8 /* DCD */
+#define SYNC_HUNT 0x10 /* Sync/hunt */
+#define CTS 0x20 /* CTS */
+#define TxEOM 0x40 /* Tx underrun */
+#define BRK_ABRT 0x80 /* Break/Abort */
+
+/* Read Register 1 */
+#define ALL_SNT 0x1 /* All sent */
+/* Residue Data for 8 Rx bits/char programmed */
+#define RES3 0x8 /* 0/3 */
+#define RES4 0x4 /* 0/4 */
+#define RES5 0xc /* 0/5 */
+#define RES6 0x2 /* 0/6 */
+#define RES7 0xa /* 0/7 */
+#define RES8 0x6 /* 0/8 */
+#define RES18 0xe /* 1/8 */
+#define RES28 0x0 /* 2/8 */
+/* Special Rx Condition Interrupts */
+#define PAR_ERR 0x10 /* Parity error */
+#define Rx_OVR 0x20 /* Rx Overrun Error */
+#define CRC_ERR 0x40 /* CRC/Framing Error */
+#define END_FR 0x80 /* End of Frame (SDLC) */
+
+/* Read Register 2 (channel b only) - Interrupt vector */
+
+/* Read Register 3 (interrupt pending register) ch a only */
+#define CHBEXT 0x1 /* Channel B Ext/Stat IP */
+#define CHBTxIP 0x2 /* Channel B Tx IP */
+#define CHBRxIP 0x4 /* Channel B Rx IP */
+#define CHAEXT 0x8 /* Channel A Ext/Stat IP */
+#define CHATxIP 0x10 /* Channel A Tx IP */
+#define CHARxIP 0x20 /* Channel A Rx IP */
+
+/* Read Register 8 (receive data register) */
+
+/* Read Register 10 (misc status bits) */
+#define ONLOOP 2 /* On loop */
+#define LOOPSEND 0x10 /* Loop sending */
+#define CLK2MIS 0x40 /* Two clocks missing */
+#define CLK1MIS 0x80 /* One clock missing */
+
+/* Read Register 12 (lower byte of baud rate generator constant) */
+
+/* Read Register 13 (upper byte of baud rate generator constant) */
+
+/* Read Register 15 (value of WR 15) */
diff --git a/drivers/net/znet.c b/drivers/net/znet.c
index 1111f41b1..9c0fb6fdb 100644
--- a/drivers/net/znet.c
+++ b/drivers/net/znet.c
@@ -62,7 +62,6 @@ static char *version = "znet.c:v1.02 9/23/94 becker@cesdis.gsfc.nasa.gov\n";
functionality from the serial subsystem.
*/
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
@@ -183,7 +182,7 @@ struct netidblk {
int znet_probe(struct device *dev);
static int znet_open(struct device *dev);
static int znet_send_packet(struct sk_buff *skb, struct device *dev);
-static void znet_interrupt(int reg_ptr);
+static void znet_interrupt(int irq, struct pt_regs *regs);
static void znet_rx(struct device *dev);
static int znet_close(struct device *dev);
static struct enet_statistics *net_get_stats(struct device *dev);
@@ -403,9 +402,8 @@ static int znet_send_packet(struct sk_buff *skb, struct device *dev)
}
/* The ZNET interrupt handler. */
-static void znet_interrupt(int reg_ptr)
+static void znet_interrupt(int irq, struct pt_regs * regs)
{
- int irq = pt_regs2irq(reg_ptr);
struct device *dev = irq2dev_map[irq];
int ioaddr;
int boguscnt = 20;
@@ -548,18 +546,15 @@ static void znet_rx(struct device *dev)
lp->stats.rx_length_errors++;
} else {
/* Malloc up new buffer. */
- int sksize = sizeof(struct sk_buff) + pkt_len;
struct sk_buff *skb;
- skb = alloc_skb(sksize, GFP_ATOMIC);
+ skb = alloc_skb(pkt_len, GFP_ATOMIC);
if (skb == NULL) {
if (znet_debug)
printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
lp->stats.rx_dropped++;
break;
}
- skb->mem_len = sksize;
- skb->mem_addr = skb;
skb->len = pkt_len;
skb->dev = dev;
@@ -576,18 +571,9 @@ static void znet_rx(struct device *dev)
packet[1], packet[2], packet[3]);
}
}
-
-#ifdef HAVE_NETIF_RX
- netif_rx(skb);
-#else
- skb->lock = 0;
- if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
- kfree_s(skb, sksize);
- lp->stats.rx_dropped++;
- break;
- }
-#endif
- lp->stats.rx_packets++;
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
}
zn.rx_cur = this_rfp_ptr;
if (zn.rx_cur >= zn.rx_end)
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
new file mode 100644
index 000000000..29b4c67f9
--- /dev/null
+++ b/drivers/pci/Makefile
@@ -0,0 +1,40 @@
+#
+# Makefile for the PCI bus specific drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now inherited from the
+# parent makefile.
+#
+
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+.s.o:
+ $(AS) -c -o $*.o $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+OBJS := pci.o
+SRCS := pci.c
+
+all: pci.a
+
+pci.a: $(OBJS)
+ rm -f pci.a
+ $(AR) rcs pci.a $(OBJS)
+ sync
+
+dep:
+ $(CPP) -M $(SRCS) > .depend
+
+modules:
+dummy:
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
new file mode 100644
index 000000000..d97923486
--- /dev/null
+++ b/drivers/pci/pci.c
@@ -0,0 +1,752 @@
+/*
+ * drivers/pci/pci.c
+ *
+ * PCI services that are built on top of the BIOS32 service.
+ *
+ * Copyright 1993, 1994, 1995 Drew Eckhardt, Frederic Potter,
+ * David Mosberger-Tang
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+
+#include <asm/page.h>
+
+struct pci_bus pci_root;
+struct pci_dev *pci_devices = 0;
+
+
+/*
+ * The bridge_id field is an offset of an item into the array
+ * BRIDGE_MAPPING_TYPE. 0xff indicates that the device is not a PCI
+ * bridge, or that we don't know for the moment how to configure it.
+ * I'm trying to do my best so that the kernel stays small. Different
+ * chipset can have same optimization structure. i486 and pentium
+ * chipsets from the same manufacturer usually have the same
+ * structure.
+ */
+#define DEVICE(vid,did,name) \
+ {PCI_VENDOR_ID_##vid, PCI_DEVICE_ID_##did, (name), 0xff}
+
+#define BRIDGE(vid,did,name,bridge) \
+ {PCI_VENDOR_ID_##vid, PCI_DEVICE_ID_##did, (name), (bridge)}
+
+struct pci_dev_info dev_info[] = {
+ DEVICE( NCR, NCR_53C810, "53c810"),
+ DEVICE( NCR, NCR_53C815, "53c815"),
+ DEVICE( NCR, NCR_53C820, "53c820"),
+ DEVICE( NCR, NCR_53C825, "53c825"),
+ DEVICE( ADAPTEC, ADAPTEC_2940, "2940"),
+ DEVICE( ADAPTEC, ADAPTEC_294x, "294x"),
+ DEVICE( DPT, DPT, "SmartCache/Raid"),
+ DEVICE( S3, S3_864_1, "Vision 864-P"),
+ DEVICE( S3, S3_864_2, "Vision 864-P"),
+ DEVICE( S3, S3_868, "Vision 868"),
+ DEVICE( S3, S3_928, "Vision 928-P"),
+ DEVICE( S3, S3_964_1, "Vision 964-P"),
+ DEVICE( S3, S3_964_2, "Vision 964-P"),
+ DEVICE( S3, S3_811, "Trio32/Trio64"),
+ DEVICE( S3, S3_968, "Vision 968"),
+ DEVICE( OPTI, OPTI_82C822, "82C822"),
+ DEVICE( OPTI, OPTI_82C621, "82C621"),
+ DEVICE( OPTI, OPTI_82C557, "82C557"),
+ DEVICE( OPTI, OPTI_82C558, "82C558"),
+ BRIDGE( UMC, UMC_UM8881F, "UM8881F", 0x02),
+ BRIDGE( UMC, UMC_UM8891A, "UM8891A", 0x01),
+ DEVICE( UMC, UMC_UM8886F, "UM8886F"),
+ DEVICE( UMC, UMC_UM8673F, "UM8673F"),
+ DEVICE( DEC, DEC_TULIP, "DC21040"),
+ DEVICE( DEC, DEC_TULIP_FAST, "DC21040"),
+ DEVICE( DEC, DEC_FDDI, "DEFPA"),
+ DEVICE( DEC, DEC_BRD, "DC21050"),
+ DEVICE( MATROX, MATROX_MGA_2, "Atlas PX2085"),
+ DEVICE( MATROX, MATROX_MGA_IMP, "MGA Impression"),
+ DEVICE( INTEL, INTEL_82378, "82378IB"),
+ BRIDGE( INTEL, INTEL_82424, "82424ZX Saturn", 0x00),
+ DEVICE( INTEL, INTEL_82375, "82375EB"),
+ BRIDGE( INTEL, INTEL_82434, "82434LX Mercury/Neptune", 0x00),
+ DEVICE( INTEL, INTEL_82430, "82430ZX Aries"),
+ DEVICE( SMC, SMC_37C665, "FDC 37C665"),
+ DEVICE( ATI, ATI_M32, "Mach 32"),
+ DEVICE( ATI, ATI_M64, "Mach 64"),
+ DEVICE( WEITEK, WEITEK_P9000, "P9000"),
+ DEVICE( WEITEK, WEITEK_P9100, "P9100"),
+ DEVICE( CIRRUS, CIRRUS_5430, "GD 5430"),
+ DEVICE( CIRRUS, CIRRUS_5434_4, "GD 5434"),
+ DEVICE( CIRRUS, CIRRUS_5434_8, "GD 5434"),
+ DEVICE( CIRRUS, CIRRUS_6729, "CL 6729"),
+ DEVICE( BUSLOGIC, BUSLOGIC_946C, "946C"),
+ DEVICE( BUSLOGIC, BUSLOGIC_946C_2,"946C"),
+ DEVICE( N9, N9_I128, "Imagine 128"),
+ DEVICE( AI, AI_M1435, "M1435"),
+ DEVICE( AL, AL_M1445, "M1445"),
+ DEVICE( AL, AL_M1449, "M1449"),
+ DEVICE( AL, AL_M1451, "M1451"),
+ DEVICE( TSENG, TSENG_W32P_2, "ET4000W32P"),
+ DEVICE( TSENG, TSENG_W32P_b, "ET4000W32P rev B"),
+ DEVICE( TSENG, TSENG_W32P_c, "ET4000W32P rev C"),
+ DEVICE( TSENG, TSENG_W32P_d, "ET4000W32P rev D"),
+ DEVICE( CMD, CMD_640, "640A"),
+ DEVICE( VISION, VISION_QD8500, "QD-8500"),
+ DEVICE( VISION, VISION_QD8580, "QD-8580"),
+ DEVICE( AMD, AMD_LANCE, "79C970"),
+ DEVICE( AMD, AMD_SCSI, "53C974"),
+ DEVICE( VLSI, VLSI_82C593, "82C593-FC1"),
+ DEVICE( VLSI, VLSI_82C592, "82C592-FC1"),
+ DEVICE( ADL, ADL_2301, "2301"),
+ DEVICE( SYMPHONY, SYMPHONY_101, "82C101"),
+ DEVICE( TRIDENT, TRIDENT_9420, "TG 9420"),
+ DEVICE( TRIDENT, TRIDENT_9440, "TG 9440"),
+ DEVICE( CONTAQ, CONTAQ_82C599, "82C599"),
+ DEVICE( NS, NS_87410, "87410"),
+ DEVICE( VIA, VIA_82C505, "VT 82C505"),
+ DEVICE( VIA, VIA_82C576, "VT 82C576 3V"),
+ DEVICE( VIA, VIA_82C561, "VT 82C561"),
+ DEVICE( SI, SI_496, "85C496"),
+ DEVICE( SI, SI_501, "85C501"),
+ DEVICE( SI, SI_503, "85C503"),
+ DEVICE( SI, SI_601, "85C601"),
+ DEVICE( LEADTEK, LEADTEK_805, "S3 805"),
+ DEVICE( IMS, IMS_8849, "8849"),
+ DEVICE( ZEINET, ZEINET_1221, "1221"),
+ DEVICE( EF, EF_ATM, "155P-MF1"),
+ DEVICE( HER, HER_STING, "Stingray"),
+ DEVICE( HER, HER_STING, "Stingray"),
+ DEVICE( ATRONICS, ATRONICS_2015, "IDE-2015PL"),
+ DEVICE( CT, CT_65545, "65545"),
+ DEVICE( FD, FD_36C70, "TMC-18C30"),
+ DEVICE( WINBOND, WINBOND_83769, "W83769F"),
+ DEVICE( 3COM, 3COM_3C590, "3C590 10bT"),
+ DEVICE( 3COM, 3COM_3C595TX, "3C595 100bTX"),
+ DEVICE( 3COM, 3COM_3C595T4, "3C595 100bT4"),
+ DEVICE( 3COM, 3COM_3C595MII, "3C595 100b-MII"),
+ DEVICE( PROMISE, PROMISE_5300, "DC5030"),
+ DEVICE( QLOGIC, QLOGIC_ISP1020, "ISP1020"),
+ DEVICE( QLOGIC, QLOGIC_ISP1022, "ISP1022"),
+ DEVICE( X, X_AGX016, "ITT AGX016")
+};
+
+
+#ifdef CONFIG_PCI_OPTIMIZE
+
+/*
+ * An item of this structure has the following meaning:
+ * for each optimization, the register address, the mask
+ * and value to write to turn it on.
+ * There are 5 optimizations for the moment:
+ * Cache L2 write back best than write through
+ * Posted Write for CPU to PCI enable
+ * Posted Write for CPU to MEMORY enable
+ * Posted Write for PCI to MEMORY enable
+ * PCI Burst enable
+ *
+ * Half of the bios I've meet don't allow you to turn that on, and you
+ * can gain more than 15% on graphic accesses using those
+ * optimizations...
+ */
+struct optimization_type {
+ char *type;
+ char *off;
+ char *on;
+} bridge_optimization[] = {
+ {"Cache L2", "write trough", "write back"},
+ {"CPU-PCI posted write", "off", "on"},
+ {"CPU-Memory posted write", "off", "on"},
+ {"PCI-Memory posted write", "off", "on"},
+ {"PCI burst", "off", "on"}
+};
+
+#define NUM_OPTIMIZATIONS \
+ (sizeof(bridge_optimization) / sizeof(bridge_optimization[0]))
+
+struct bridge_mapping_type {
+ unsigned char addr; /* config space address */
+ unsigned char mask;
+ unsigned char value;
+} bridge_mapping[] = {
+ /*
+ * Intel Neptune/Mercury/Saturn:
+ * If the internal cache is write back,
+ * the L2 cache must be write through!
+ * I've to check out how to control that
+ * for the moment, we won't touch the cache
+ */
+ {0x0 ,0x02 ,0x02 },
+ {0x53 ,0x02 ,0x02 },
+ {0x53 ,0x01 ,0x01 },
+ {0x54 ,0x01 ,0x01 },
+ {0x54 ,0x02 ,0x02 },
+
+ /*
+ * UMC 8891A Pentium chipset:
+ * Why did you think UMC was cheaper ??
+ */
+ {0x50 ,0x10 ,0x00 },
+ {0x51 ,0x40 ,0x40 },
+ {0x0 ,0x0 ,0x0 },
+ {0x0 ,0x0 ,0x0 },
+ {0x0 ,0x0 ,0x0 },
+
+ /*
+ * UMC UM8881F
+ * This is a dummy entry for my tests.
+ * I have this chipset and no docs....
+ */
+ {0x0 ,0x1 ,0x1 },
+ {0x0 ,0x2 ,0x0 },
+ {0x0 ,0x0 ,0x0 },
+ {0x0 ,0x0 ,0x0 },
+ {0x0 ,0x0 ,0x0 }
+};
+
+#endif /* CONFIG_PCI_OPTIMIZE */
+
+
+/*
+ * If performance ever becomes important, device_info[] could be
+ * sorted by key and this could be replaced by a binary search.
+ */
+struct pci_dev_info *pci_lookup_dev(unsigned int vendor, unsigned int dev)
+{
+ int i;
+
+ for (i = 0; i < sizeof(dev_info)/sizeof(dev_info[0]); ++i) {
+ if (dev_info[i].vendor == vendor &&
+ dev_info[i].device == dev)
+ {
+ return &dev_info[i];
+ }
+ }
+ return 0;
+}
+
+
+char *pci_strbioserr (int error)
+{
+ switch (error) {
+ case PCIBIOS_SUCCESSFUL: return "SUCCESSFUL";
+ case PCIBIOS_FUNC_NOT_SUPPORTED: return "FUNC_NOT_SUPPORTED";
+ case PCIBIOS_BAD_VENDOR_ID: return "SUCCESSFUL";
+ case PCIBIOS_DEVICE_NOT_FOUND: return "DEVICE_NOT_FOUND";
+ case PCIBIOS_BAD_REGISTER_NUMBER: return "BAD_REGISTER_NUMBER";
+ case PCIBIOS_SET_FAILED: return "SET_FAILED";
+ case PCIBIOS_BUFFER_TOO_SMALL: return "BUFFER_TOO_SMALL";
+ default: return "Unknown error status";
+ }
+}
+
+
+const char *pci_strclass (unsigned int class)
+{
+ switch (class >> 8) {
+ case PCI_CLASS_NOT_DEFINED: return "Non-VGA device";
+ case PCI_CLASS_NOT_DEFINED_VGA: return "VGA compatible device";
+
+ case PCI_CLASS_STORAGE_SCSI: return "SCSI storage controller";
+ case PCI_CLASS_STORAGE_IDE: return "IDE controller";
+ case PCI_CLASS_STORAGE_FLOPPY: return "Floppy disk controller";
+ case PCI_CLASS_STORAGE_IPI: return "IPI bus controller";
+ case PCI_CLASS_STORAGE_OTHER: return "Unknown mass storage controller";
+
+ case PCI_CLASS_NETWORK_ETHERNET: return "Ethernet controller";
+ case PCI_CLASS_NETWORK_TOKEN_RING: return "Token ring network controller";
+ case PCI_CLASS_NETWORK_FDDI: return "FDDI network controller";
+ case PCI_CLASS_NETWORK_OTHER: return "Network controller";
+
+ case PCI_CLASS_DISPLAY_VGA: return "VGA compatible controller";
+ case PCI_CLASS_DISPLAY_XGA: return "XGA compatible controller";
+ case PCI_CLASS_DISPLAY_OTHER: return "Display controller";
+
+ case PCI_CLASS_MULTIMEDIA_VIDEO: return "Multimedia video controller";
+ case PCI_CLASS_MULTIMEDIA_AUDIO: return "Multimedia audio controller";
+ case PCI_CLASS_MULTIMEDIA_OTHER: return "Multimedia controller";
+
+ case PCI_CLASS_MEMORY_RAM: return "RAM memory";
+ case PCI_CLASS_MEMORY_FLASH: return "FLASH memory";
+ case PCI_CLASS_MEMORY_OTHER: return "Memory";
+
+ case PCI_CLASS_BRIDGE_HOST: return "Host bridge";
+ case PCI_CLASS_BRIDGE_ISA: return "ISA bridge";
+ case PCI_CLASS_BRIDGE_EISA: return "EISA bridge";
+ case PCI_CLASS_BRIDGE_MC: return "MicroChannel bridge";
+ case PCI_CLASS_BRIDGE_PCI: return "PCI bridge";
+ case PCI_CLASS_BRIDGE_PCMCIA: return "PCMCIA bridge";
+ case PCI_CLASS_BRIDGE_OTHER: return "Bridge";
+
+ default: return "Unknown class";
+ }
+}
+
+
+const char *pci_strvendor(unsigned int vendor)
+{
+ switch (vendor) {
+ case PCI_VENDOR_ID_NCR: return "NCR";
+ case PCI_VENDOR_ID_ADAPTEC: return "Adaptec";
+ case PCI_VENDOR_ID_DPT: return "DPT";
+ case PCI_VENDOR_ID_S3: return "S3 Inc.";
+ case PCI_VENDOR_ID_OPTI: return "OPTI";
+ case PCI_VENDOR_ID_UMC: return "UMC";
+ case PCI_VENDOR_ID_DEC: return "DEC";
+ case PCI_VENDOR_ID_MATROX: return "Matrox";
+ case PCI_VENDOR_ID_INTEL: return "Intel";
+ case PCI_VENDOR_ID_SMC: return "SMC";
+ case PCI_VENDOR_ID_ATI: return "ATI";
+ case PCI_VENDOR_ID_WEITEK: return "Weitek";
+ case PCI_VENDOR_ID_CIRRUS: return "Cirrus Logic";
+ case PCI_VENDOR_ID_BUSLOGIC: return "Bus Logic";
+ case PCI_VENDOR_ID_N9: return "Number Nine";
+ case PCI_VENDOR_ID_AI: return "Acer Incorporated";
+ case PCI_VENDOR_ID_AL: return "Acer Labs";
+ case PCI_VENDOR_ID_TSENG: return "Tseng'Lab";
+ case PCI_VENDOR_ID_CMD: return "CMD";
+ case PCI_VENDOR_ID_VISION: return "Vision";
+ case PCI_VENDOR_ID_AMD: return "AMD";
+ case PCI_VENDOR_ID_VLSI: return "VLSI";
+ case PCI_VENDOR_ID_ADL: return "Advance Logic";
+ case PCI_VENDOR_ID_SYMPHONY: return "Symphony";
+ case PCI_VENDOR_ID_TRIDENT: return "Trident";
+ case PCI_VENDOR_ID_CONTAQ: return "Contaq";
+ case PCI_VENDOR_ID_NS: return "NS";
+ case PCI_VENDOR_ID_VIA: return "VIA Technologies";
+ case PCI_VENDOR_ID_SI: return "Silicon Integrated Systems";
+ case PCI_VENDOR_ID_LEADTEK: return "Leadtek Research";
+ case PCI_VENDOR_ID_IMS: return "IMS";
+ case PCI_VENDOR_ID_ZEINET: return "ZeiNet";
+ case PCI_VENDOR_ID_EF: return "Efficient Networks";
+ case PCI_VENDOR_ID_HER: return "Hercules";
+ case PCI_VENDOR_ID_ATRONICS: return "Atronics";
+ case PCI_VENDOR_ID_CT: return "Chips & Technologies";
+ case PCI_VENDOR_ID_FD: return "Future Domain";
+ case PCI_VENDOR_ID_WINBOND: return "Winbond";
+ case PCI_VENDOR_ID_3COM: return "3Com";
+ case PCI_VENDOR_ID_PROMISE: return "Promise Technology";
+ case PCI_VENDOR_ID_QLOGIC: return "Q Logic";
+ default: return "Unknown vendor";
+ }
+}
+
+
+const char *pci_strdev(unsigned int vendor, unsigned int device)
+{
+ struct pci_dev_info *info;
+
+ info = pci_lookup_dev(vendor, device);
+ return info ? info->name : "Unknown device";
+}
+
+
+
+/*
+ * Turn on/off PCI bridge optimization. This should allow benchmarking.
+ */
+static void burst_bridge(unsigned char bus, unsigned char devfn,
+ unsigned char pos, int turn_on)
+{
+#ifdef CONFIG_PCI_OPTIMIZE
+ struct bridge_mapping_type *bmap;
+ unsigned char val;
+ int i;
+
+ pos *= NUM_OPTIMIZATIONS;
+ printk("PCI bridge optimization.\n");
+ for (i = 0; i < NUM_OPTIMIZATIONS; i++) {
+ printk(" %s: ", bridge_optimization[i].type);
+ bmap = &bridge_mapping[pos + i];
+ if (!bmap->addr) {
+ printk("Not supported.");
+ } else {
+ pcibios_read_config_byte(bus, devfn, bmap->addr, &val);
+ if ((val & bmap->mask) == bmap->value) {
+ printk("%s.", bridge_optimization[i].on);
+ if (!turn_on) {
+ pcibios_write_config_byte(bus, devfn,
+ bmap->addr,
+ (val | bmap->mask)
+ - bmap->value);
+ printk("Changed! Now %s.", bridge_optimization[i].off);
+ }
+ } else {
+ printk("%s.", bridge_optimization[i].off);
+ if (turn_on) {
+ pcibios_write_config_byte(bus, devfn,
+ bmap->addr,
+ (val & (0xff - bmap->mask))
+ + bmap->value);
+ printk("Changed! Now %s.", bridge_optimization[i].on);
+ }
+ }
+ }
+ printk("\n");
+ }
+#endif /* CONFIG_PCI_OPTIMIZE */
+}
+
+
+/*
+ * Convert some of the configuration space registers of the device at
+ * address (bus,devfn) into a string (possibly several lines each).
+ * The configuration string is stored starting at buf[len]. If the
+ * string would exceed the size of the buffer (SIZE), 0 is returned.
+ */
+static int sprint_dev_config(struct pci_dev *dev, char *buf, int size)
+{
+ unsigned long base;
+ unsigned int l, class_rev, bus, devfn;
+ unsigned short vendor, device, status;
+ unsigned char bist, latency, min_gnt, max_lat;
+ int reg, len = 0;
+ const char *str;
+
+ bus = dev->bus->number;
+ devfn = dev->devfn;
+
+ pcibios_read_config_dword(bus, devfn, PCI_CLASS_REVISION, &class_rev);
+ pcibios_read_config_word (bus, devfn, PCI_VENDOR_ID, &vendor);
+ pcibios_read_config_word (bus, devfn, PCI_DEVICE_ID, &device);
+ pcibios_read_config_word (bus, devfn, PCI_STATUS, &status);
+ pcibios_read_config_byte (bus, devfn, PCI_BIST, &bist);
+ pcibios_read_config_byte (bus, devfn, PCI_LATENCY_TIMER, &latency);
+ pcibios_read_config_byte (bus, devfn, PCI_MIN_GNT, &min_gnt);
+ pcibios_read_config_byte (bus, devfn, PCI_MAX_LAT, &max_lat);
+ if (len + 80 > size) {
+ return -1;
+ }
+ len += sprintf(buf + len, " Bus %2d, device %3d, function %2d:\n",
+ bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
+
+ if (len + 80 > size) {
+ return -1;
+ }
+ len += sprintf(buf + len, " %s: %s %s (rev %d).\n ",
+ pci_strclass(class_rev >> 8), pci_strvendor(vendor),
+ pci_strdev(vendor, device), class_rev & 0xff);
+
+ if (!pci_lookup_dev(vendor, device)) {
+ len += sprintf(buf + len,
+ "Vendor id=%x. Device id=%x.\n ",
+ vendor, device);
+ }
+
+ str = 0; /* to keep gcc shut... */
+ switch (status & PCI_STATUS_DEVSEL_MASK) {
+ case PCI_STATUS_DEVSEL_FAST: str = "Fast devsel. "; break;
+ case PCI_STATUS_DEVSEL_MEDIUM: str = "Medium devsel. "; break;
+ case PCI_STATUS_DEVSEL_SLOW: str = "Slow devsel. "; break;
+ }
+ if (len + strlen(str) > size) {
+ return -1;
+ }
+ len += sprintf(buf + len, str);
+
+ if (status & PCI_STATUS_FAST_BACK) {
+# define fast_b2b_capable "Fast back-to-back capable. "
+ if (len + strlen(fast_b2b_capable) > size) {
+ return -1;
+ }
+ len += sprintf(buf + len, fast_b2b_capable);
+# undef fast_b2b_capable
+ }
+
+ if (bist & PCI_BIST_CAPABLE) {
+# define BIST_capable "BIST capable. "
+ if (len + strlen(BIST_capable) > size) {
+ return -1;
+ }
+ len += sprintf(buf + len, BIST_capable);
+# undef BIST_capable
+ }
+
+ if (dev->irq) {
+ if (len + 40 > size) {
+ return -1;
+ }
+ len += sprintf(buf + len, "IRQ %d. ", dev->irq);
+ }
+
+ if (dev->master) {
+ if (len + 80 > size) {
+ return -1;
+ }
+ len += sprintf(buf + len, "Master Capable. ");
+ if (latency)
+ len += sprintf(buf + len, "Latency=%d. ", latency);
+ else
+ len += sprintf(buf + len, "No bursts. ");
+ if (min_gnt)
+ len += sprintf(buf + len, "Min Gnt=%d.", min_gnt);
+ if (max_lat)
+ len += sprintf(buf + len, "Max Lat=%d.", max_lat);
+ }
+
+ for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) {
+ if (len + 40 > size) {
+ return -1;
+ }
+ pcibios_read_config_dword(bus, devfn, reg, &l);
+ base = l;
+ if (!base) {
+ continue;
+ }
+
+ if (base & PCI_BASE_ADDRESS_SPACE_IO) {
+ len += sprintf(buf + len,
+ "\n I/O at 0x%lx.",
+ base & PCI_BASE_ADDRESS_IO_MASK);
+ } else {
+ const char *pref, *type = "unknown";
+
+ if (base & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+ pref = "P";
+ } else {
+ pref = "Non-p";
+ }
+ switch (base & PCI_BASE_ADDRESS_MEM_TYPE_MASK) {
+ case PCI_BASE_ADDRESS_MEM_TYPE_32:
+ type = "32 bit"; break;
+ case PCI_BASE_ADDRESS_MEM_TYPE_1M:
+ type = "20 bit"; break;
+ case PCI_BASE_ADDRESS_MEM_TYPE_64:
+ type = "64 bit";
+ /* read top 32 bit address of base addr: */
+ reg += 4;
+ pcibios_read_config_dword(bus, devfn, reg, &l);
+ base |= ((u64) l) << 32;
+ break;
+ }
+ len += sprintf(buf + len,
+ "\n %srefetchable %s memory at "
+ "0x%lx.", pref, type,
+ base & PCI_BASE_ADDRESS_MEM_MASK);
+ }
+ }
+
+ len += sprintf(buf + len, "\n");
+ return len;
+}
+
+
+/*
+ * Return list of PCI devices as a character string for /proc/pci.
+ * BUF is a buffer that is PAGE_SIZE bytes long.
+ */
+int get_pci_list(char *buf)
+{
+ int nprinted, len, size;
+ struct pci_dev *dev;
+# define MSG "\nwarning: page-size limit reached!\n"
+
+ /* reserve same for truncation warning message: */
+ size = PAGE_SIZE - (strlen(MSG) + 1);
+ len = sprintf(buf, "PCI devices found:\n");
+
+ for (dev = pci_devices; dev; dev = dev->next) {
+ nprinted = sprint_dev_config(dev, buf + len, size - len);
+ if (nprinted < 0) {
+ return len + sprintf(buf + len, MSG);
+ }
+ len += nprinted;
+ }
+ return len;
+}
+
+
+/*
+ * pci_malloc() returns initialized memory of size SIZE. Can be
+ * used only while pci_init() is active.
+ */
+static void *pci_malloc(long size, unsigned long *mem_startp)
+{
+ void *mem;
+
+#ifdef DEBUG
+ printk("...pci_malloc(size=%ld,mem=%p)", size, *mem_startp);
+#endif
+ mem = (void*) *mem_startp;
+ *mem_startp += (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
+ memset(mem, 0, size);
+ return mem;
+}
+
+
+static unsigned int scan_bus(struct pci_bus *bus, unsigned long *mem_startp)
+{
+ unsigned int devfn, l, max;
+ unsigned char cmd, tmp, hdr_type = 0;
+ struct pci_dev_info *info;
+ struct pci_dev *dev;
+ struct pci_bus *child;
+
+#ifdef DEBUG
+ printk("...scan_bus(busno=%d,mem=%p)\n", bus->number, *mem_startp);
+#endif
+
+ max = bus->secondary;
+ for (devfn = 0; devfn < 0xff; ++devfn) {
+ if (PCI_FUNC(devfn) == 0) {
+ pcibios_read_config_byte(bus->number, devfn,
+ PCI_HEADER_TYPE, &hdr_type);
+ } else if (!(hdr_type & 0x80)) {
+ /* not a multi-function device */
+ continue;
+ }
+
+ pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID,
+ &l);
+ /* some broken boards return 0 if a slot is empty: */
+ if (l == 0xffffffff || l == 0x00000000) {
+ hdr_type = 0;
+ continue;
+ }
+
+ dev = pci_malloc(sizeof(*dev), mem_startp);
+ dev->bus = bus;
+ /*
+ * Put it into the simple chain of devices on this
+ * bus. It is used to find devices once everything is
+ * set up.
+ */
+ dev->next = pci_devices;
+ pci_devices = dev;
+
+ dev->devfn = devfn;
+ dev->vendor = l & 0xffff;
+ dev->device = (l >> 16) & 0xffff;
+
+ /*
+ * Check to see if we now about this device and report
+ * a message at boot time. This is the only way to
+ * learn about new hardware...
+ */
+ info = pci_lookup_dev(dev->vendor, dev->device);
+ if (!info) {
+ printk("Warning : Unknown PCI device. Please read include/linux/pci.h \n");
+ } else {
+ /* Some BIOS' are lazy. Let's do their job: */
+ if (info->bridge_type != 0xff) {
+ burst_bridge(bus->number, devfn,
+ info->bridge_type, 1);
+ }
+ }
+
+ /* non-destructively determine if device can be a master: */
+ pcibios_read_config_byte(bus->number, devfn, PCI_COMMAND,
+ &cmd);
+ pcibios_write_config_byte(bus->number, devfn, PCI_COMMAND,
+ cmd | PCI_COMMAND_MASTER);
+ pcibios_read_config_byte(bus->number, devfn, PCI_COMMAND,
+ &tmp);
+ dev->master = ((tmp & PCI_COMMAND_MASTER) != 0);
+ pcibios_write_config_byte(bus->number, devfn, PCI_COMMAND,
+ cmd);
+
+ /* read irq level (may be changed during pcibios_fixup()): */
+ pcibios_read_config_byte(bus->number, devfn,
+ PCI_INTERRUPT_LINE, &dev->irq);
+
+ /* check to see if this device is a PCI-PCI bridge: */
+ pcibios_read_config_dword(bus->number, devfn,
+ PCI_CLASS_REVISION, &l);
+ l = l >> 8; /* upper 3 bytes */
+ dev->class = l;
+ /*
+ * Now insert it into the list of devices held
+ * by the parent bus.
+ */
+ dev->sibling = bus->devices;
+ bus->devices = dev;
+
+ if (dev->class >> 8 == PCI_CLASS_BRIDGE_PCI) {
+ unsigned int buses;
+
+ /*
+ * Insert it into the tree of buses.
+ */
+ child = pci_malloc(sizeof(*child), mem_startp);
+ child->next = bus->children;
+ bus->children = child;
+ child->self = dev;
+ child->parent = bus;
+
+ /*
+ * Set up the primary, secondary and subordinate
+ * bus numbers.
+ */
+ child->number = child->secondary = ++max;
+ child->primary = bus->secondary;
+ child->subordinate = 0xff;
+ /*
+ * Clear all status bits and turn off memory,
+ * I/O and master enables.
+ */
+ pcibios_write_config_word(bus->number, devfn,
+ PCI_COMMAND, 0x0000);
+ pcibios_write_config_word(bus->number, devfn,
+ PCI_STATUS, 0xffff);
+ /*
+ * Configure the bus numbers for this bridge:
+ */
+ pcibios_read_config_dword(bus->number, devfn, 0x18,
+ &buses);
+ buses &= 0xff000000;
+ buses |= (((unsigned int)(child->primary) << 0) |
+ ((unsigned int)(child->secondary) << 8) |
+ ((unsigned int)(child->subordinate) << 16));
+ pcibios_write_config_dword(bus->number, devfn, 0x18,
+ buses);
+ /*
+ * Now we can scan all subordinate buses:
+ */
+ max = scan_bus(child, mem_startp);
+ /*
+ * Set the subordinate bus number to its real
+ * value:
+ */
+ child->subordinate = max;
+ buses = (buses & 0xff00ffff)
+ | ((unsigned int)(child->subordinate) << 16);
+ pcibios_write_config_dword(bus->number, devfn, 0x18,
+ buses);
+ }
+ }
+ /*
+ * We've scanned the bus and so we know all about what's on
+ * the other side of any bridges that may be on this bus plus
+ * any devices.
+ *
+ * Return how far we've got finding sub-buses.
+ */
+ return max;
+}
+
+
+unsigned long pci_init (unsigned long mem_start, unsigned long mem_end)
+{
+ mem_start = pcibios_init(mem_start, mem_end);
+
+ if (!pcibios_present()) {
+ printk("pci_init: no BIOS32 detected\n");
+ return mem_start;
+ }
+
+ printk("Probing PCI hardware.\n");
+
+ memset(&pci_root, 0, sizeof(pci_root));
+ pci_root.subordinate = scan_bus(&pci_root, &mem_start);
+
+ /* give BIOS a chance to apply platform specific fixes: */
+ mem_start = pcibios_fixup(mem_start, mem_end);
+
+#ifdef DEBUG
+ {
+ int len = get_pci_list(mem_start);
+ if (len) {
+ ((char*)mem_start)[len] = '\0';
+ printk("%s\n", mem_start);
+ }
+ }
+#endif
+ return mem_start;
+}
diff --git a/drivers/scsi/53c7,8xx.c b/drivers/scsi/53c7,8xx.c
index 99a7d360d..764eb6fe0 100644
--- a/drivers/scsi/53c7,8xx.c
+++ b/drivers/scsi/53c7,8xx.c
@@ -5,8 +5,8 @@
* weed out brain damaged main boards.
*/
-#define PERM_OPTIONS (OPTION_IO_MAPPED|OPTION_DEBUG_TEST1)
+#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.
@@ -20,7 +20,7 @@
* Hannover, Germany
* hm@ix.de
*
- * Copyright 1993, 1994 Drew Eckhardt
+ * Copyright 1993, 1994, 1995 Drew Eckhardt
* Visionary Computing
* (Unix and Linux consulting and custom programming)
* drew@Colorado.EDU
@@ -28,8 +28,6 @@
*
* TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation.
*
- * PRE-ALPHA
- *
* For more information, please consult
*
*
@@ -156,6 +154,11 @@
*
*/
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <asm/dma.h>
#include <asm/io.h>
#include <asm/system.h>
#include <linux/delay.h>
@@ -165,6 +168,7 @@
#include <linux/bios32.h>
#include <linux/pci.h>
#include <linux/string.h>
+#include <linux/mm.h>
#include "../block/blk.h"
#include "scsi.h"
#include "hosts.h"
@@ -175,7 +179,9 @@
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 int NCR53c8xx_dsa_len;
+static void NCR53c7x0_intr(int irq, struct pt_regs * regs);
+static int halt (struct Scsi_Host *host);
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);
@@ -189,23 +195,11 @@ static int NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct
NCR53c7x0_cmd *cmd);
static void NCR53c8x0_soft_reset (struct Scsi_Host *host);
+static int perm_options = PERM_OPTIONS;
+
static struct Scsi_Host *first_host = NULL; /* Head of list of NCR boards */
static Scsi_Host_Template *the_template = NULL;
-/* Allocate 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 :
@@ -267,8 +261,7 @@ static char scan_scsis_buf[512];
*
* For the very similar chips, we should probably hack the fixup code
* and interrupt code so that it works everywhere, but I suspect the
- * NCR53c700 is going
- * to need it's own fixup routine.
+ * NCR53c700 is going to need it's own fixup routine.
*/
/*
@@ -276,16 +269,17 @@ static char scan_scsis_buf[512];
*/
struct pci_chip {
- short pci_device_id;
+ unsigned short pci_device_id;
int chip;
int max_revision;
int min_revision;
};
-static struct pci_chip pci_chip_ids[3] = {
+static struct pci_chip pci_chip_ids[] = {
{PCI_DEVICE_ID_NCR_53C810, 810, 1, 1},
+ {PCI_DEVICE_ID_NCR_53C815, 815, 2, 3},
{PCI_DEVICE_ID_NCR_53C820, 820, -1, -1},
- {PCI_DEVICE_ID_NCR_53C825, 825, -1, -1},
+ {PCI_DEVICE_ID_NCR_53C825, 825, -1, -1}
};
#define NPCI_CHIP_IDS (sizeof (pci_chip_ids) / sizeof(pci_chip_ids[0]))
@@ -401,7 +395,8 @@ setup_wrapper(825)
* field of the hostdata structure MUST have been set.
*/
-static int NCR53c7x0_init (struct Scsi_Host *host) {
+static int
+NCR53c7x0_init (struct Scsi_Host *host) {
NCR53c7x0_local_declare();
/* unsigned char tmp; */
int i, j, ccf;
@@ -413,6 +408,7 @@ static int NCR53c7x0_init (struct Scsi_Host *host) {
switch (hostdata->chip) {
case 810:
case 815:
+ case 820:
case 825:
hostdata->dstat_sir_intr = NCR53c8x0_dstat_sir_intr;
hostdata->init_save_regs = NULL;
@@ -430,13 +426,19 @@ static int NCR53c7x0_init (struct Scsi_Host *host) {
return -1;
}
+ /* Assign constants accessed by NCR */
+ hostdata->NCR53c7xx_zero = 0;
+ hostdata->NCR53c7xx_msg_reject = MESSAGE_REJECT;
+ hostdata->NCR53c7xx_msg_abort = ABORT;
+ hostdata->NCR53c7xx_msg_nop = NOP;
+
/*
* Set up an interrupt handler if we aren't already sharing an IRQ
* with another board.
*/
- for (search = first_host; search && (search->hostt == the_template) &&
- (search->irq != host->irq); search=search->next);
+ for (search = first_host; search && ((search->hostt != the_template) ||
+ (search->irq != host->irq)); search=search->next);
if (!search) {
if (request_irq(host->irq, NCR53c7x0_intr, SA_INTERRUPT, "53c7,8xx")) {
@@ -459,6 +461,10 @@ static int NCR53c7x0_init (struct Scsi_Host *host) {
hostdata->istat = ((hostdata->chip / 100) == 8) ?
ISTAT_REG_800 : ISTAT_REG_700;
+/* Only the ISTAT register is readable when the NCR is running, so make
+ sure it's halted. */
+ halt(host);
+
/*
* XXX - the NCR53c700 uses bitfielded registers for SCID, SDID, etc,
* as does the 710 with one bit per SCSI ID. Conversely, the NCR
@@ -503,9 +509,9 @@ static int NCR53c7x0_init (struct Scsi_Host *host) {
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");
+ printk ("scsi%d : using %s interrupts\n", host->host_no,
+ (hostdata->saved_dcntl & DCNTL_800_IRQM) ? "edge triggered" :
+ "level active");
/*
* DMODE controls DMA burst length, and on 700 series chips,
@@ -527,8 +533,9 @@ static int NCR53c7x0_init (struct Scsi_Host *host) {
case DMODE_BL_4: i = 4; break;
case DMODE_BL_8: i = 8; break;
case DMODE_BL_16: i = 16; break;
+ default: i = 0;
}
- printk ("scsi%d ; burst length %d\n", host->host_no, i);
+ printk ("scsi%d : burst length %d\n", host->host_no, i);
}
}
@@ -572,6 +579,7 @@ static int NCR53c7x0_init (struct Scsi_Host *host) {
*/
for (i = 0; i < 8; ++i) {
+ hostdata->cmd_allocated[i] = 0;
for (j = 0; j < 8; ++j)
hostdata->busy[i][j] = 0;
/*
@@ -600,8 +608,8 @@ static int NCR53c7x0_init (struct Scsi_Host *host) {
hostdata->issue_queue = hostdata->running_list =
hostdata->finished_queue = NULL;
- hostdata->issue_dsa_head =
- hostdata->issue_dsa_tail = NULL;
+ hostdata->issue_dsa_head = NULL;
+ hostdata->issue_dsa_tail = NULL;
if (hostdata->init_save_regs)
hostdata->init_save_regs (host);
@@ -668,11 +676,11 @@ static int normal_init (Scsi_Host_Template *tpnt, int board, int chip,
struct Scsi_Host *instance;
struct NCR53c7x0_hostdata *hostdata;
char chip_str[80];
- int script_len = 0, size = 0;
+ int script_len = 0, dsa_len = 0, size = 0, max_cmd_size = 0;
int ok = 0;
- options |= PERM_OPTIONS;
+ options |= perm_options;
switch (chip) {
case 825:
@@ -680,6 +688,7 @@ static int normal_init (Scsi_Host_Template *tpnt, int board, int chip,
case 815:
case 810:
script_len = NCR53c8xx_script_len;
+ dsa_len = NCR53c8xx_dsa_len;
options |= OPTION_INTFLY;
sprintf (chip_str, "NCR53c%d", chip);
break;
@@ -698,7 +707,7 @@ static int normal_init (Scsi_Host_Template *tpnt, int board, int chip,
if ((chip / 100 == 8) && !pci_valid)
printk ("scsi-ncr53c7,8xx : for better reliability and performance, please use the\n"
" PCI override instead.\n"
- " Syntax : ncr53c8{10,20,25}=pci,<bus>,<device>,<function>\n"
+ " Syntax : ncr53c8{10,15,20,25}=pci,<bus>,<device>,<function>\n"
" <bus> and <device> are usually 0.\n");
if (options & OPTION_DEBUG_PROBE_ONLY) {
@@ -706,9 +715,50 @@ static int normal_init (Scsi_Host_Template *tpnt, int board, int chip,
return -1;
}
- size = sizeof(struct NCR53c7x0_hostdata) + script_len;
+ max_cmd_size = sizeof(struct NCR53c7x0_cmd) + dsa_len +
+ /* Size of dynamic part of command structure : */
+ 2 * /* Worst case : we don't know if we need DATA IN or DATA out */
+ ( 2 * /* Current instructions per scatter/gather segment */
+ tpnt->sg_tablesize +
+ 3 /* Current startup / termination required per phase */
+ ) *
+ 8 /* Each instruction is eight bytes */;
+ /* Note that alignment will be guaranteed, since we put the command
+ allocated at probe time after the fixed-up SCSI script, which
+ consists of 32 bit words, aligned on a 32 bit boundary. */
+
+ /* Allocate fixed part of hostdata, dynamic part to hold appropriate
+ SCSI SCRIPT(tm) plus a single, maximum-sized NCR53c7x0_cmd structure.
+
+ We need a NCR53c7x0_cmd structure for scan_scsis() when we are
+ not loaded as a module, and when we're loaded as a module, we
+ can't use a non-dynamically allocated structure because modules
+ are vmalloc()'d, which can allow structures to cross page
+ boundaries and breaks our physical/virtual address assumptions
+ for DMA.
+
+ So, we stick it past the end of our hostdata structure.
+
+ ASSUMPTION :
+ Regardless of how many simultaneous SCSI commands we allow,
+ the probe code only executes a _single_ instruction at a time,
+ so we only need one here, and don't need to allocate NCR53c7x0_cmd
+ structures for each target until we are no longer in scan_scsis
+ and kmalloc() has become functional (memory_init() happens
+ after all device driver initialization).
+ */
+
+ size = sizeof(struct NCR53c7x0_hostdata) + script_len + max_cmd_size;
instance = scsi_register (tpnt, size);
+ if (!instance)
+ return -1;
+
+
+ /* FIXME : if we ever support an ISA NCR53c7xx based board, we
+ need to check if the chip is running in a 16 bit mode, and if so
+ unregister it if it is past the 16M (0x1000000) mark */
+
hostdata = (struct NCR53c7x0_hostdata *)
instance->hostdata;
hostdata->size = size;
@@ -764,19 +814,30 @@ static int normal_init (Scsi_Host_Template *tpnt, int board, int chip,
instance->dma_channel = dma;
hostdata->options = options;
-
+ hostdata->dsa_size = dsa_len;
+ hostdata->max_cmd_size = max_cmd_size;
+ hostdata->num_cmds = 1;
+ /* Initialize single command */
+ hostdata->free = (struct NCR53c7x0_cmd *)
+ (hostdata->script + hostdata->script_count);
+ hostdata->free->real = (void *) hostdata->free;
+ hostdata->free->size = max_cmd_size;
+ hostdata->free->free = NULL;
+ hostdata->free->next = NULL;
+
+
return NCR53c7x0_init(instance);
}
/*
- * Function : static int pci_init(Scsi_Host_Template *tpnt, int board,
+ * Function : static int ncr_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.
+ * and whether bus mastering, etc. are OK.
*
* Useful where a new NCR chip is backwards compatible with
* a supported chip, but the DEVICE ID has changed so it
@@ -790,12 +851,13 @@ static int normal_init (Scsi_Host_Template *tpnt, int board, int chip,
*
*/
-static int pci_init (Scsi_Host_Template *tpnt, int board, int chip,
+static int ncr_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 error, expected_chip;
+ int expected_id = -1, max_revision = -1, min_revision = -1;
int i;
printk("scsi-ncr53c7,8xx : at PCI bus %d, device %d, function %d\n",
@@ -850,6 +912,8 @@ static int pci_init (Scsi_Host_Template *tpnt, int board, int chip,
io_port = 0;
} else
io_port &= PCI_BASE_ADDRESS_IO_MASK;
+ } else {
+ io_port = 0;
}
if (command & PCI_COMMAND_MEMORY) {
@@ -859,6 +923,8 @@ static int pci_init (Scsi_Host_Template *tpnt, int board, int chip,
base = 0;
} else
base &= PCI_BASE_ADDRESS_MEM_MASK;
+ } else {
+ base = 0;
}
if (!io_port && !base) {
@@ -912,7 +978,6 @@ static int pci_init (Scsi_Host_Template *tpnt, int board, int chip,
*/
int NCR53c7xx_detect(Scsi_Host_Template *tpnt) {
- short current_chip;
int i;
int current_override;
int count; /* Number of boards detected */
@@ -923,7 +988,7 @@ int NCR53c7xx_detect(Scsi_Host_Template *tpnt) {
for (current_override = count = 0; current_override < OVERRIDE_LIMIT;
++current_override) {
if (overrides[current_override].pci ?
- !pci_init (tpnt, overrides[current_override].board,
+ !ncr_init (tpnt, overrides[current_override].board,
overrides[current_override].chip,
(unsigned char) overrides[current_override].data.pci.bus,
(((overrides[current_override].data.pci.device
@@ -948,7 +1013,7 @@ int NCR53c7xx_detect(Scsi_Host_Template *tpnt) {
!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,
+ !ncr_init (tpnt, BOARD_GENERIC, pci_chip_ids[i].chip,
pci_bus, pci_device_fn, /* no options */ 0);
++count, ++pci_index);
}
@@ -959,6 +1024,7 @@ int NCR53c7xx_detect(Scsi_Host_Template *tpnt) {
#include "53c8xx_d.h"
static int NCR53c8xx_script_len = sizeof (SCRIPT);
+static int NCR53c8xx_dsa_len = A_dsa_end + Ent_dsa_zero - Ent_dsa_code_template;
/*
* Function : static void NCR53c8x0_init_fixup (struct Scsi_Host *host)
@@ -969,7 +1035,8 @@ static int NCR53c8xx_script_len = sizeof (SCRIPT);
*
*/
-static void NCR53c8x0_init_fixup (struct Scsi_Host *host) {
+static void
+NCR53c8x0_init_fixup (struct Scsi_Host *host) {
NCR53c7x0_local_declare();
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata;
@@ -988,6 +1055,16 @@ static void NCR53c8x0_init_fixup (struct Scsi_Host *host) {
for (i = 0; i < PATCHES; ++i)
hostdata->script[LABELPATCHES[i]] +=
(unsigned long) hostdata->script;
+ /* Fixup addresses of constants that used to be EXTERNAL */
+
+ patch_abs_32 (hostdata->script, 0, NCR53c7xx_msg_abort,
+ (long) &(hostdata->NCR53c7xx_msg_abort));
+ patch_abs_32 (hostdata->script, 0, NCR53c7xx_msg_reject,
+ (long) &(hostdata->NCR53c7xx_msg_reject));
+ patch_abs_32 (hostdata->script, 0, NCR53c7xx_zero,
+ (long) &(hostdata->NCR53c7xx_zero));
+ patch_abs_32 (hostdata->script, 0, NCR53c7xx_sink,
+ (long) &(hostdata->NCR53c7xx_sink));
/*
* Fixup absolutes set at boot-time.
@@ -1017,10 +1094,6 @@ static void NCR53c8x0_init_fixup (struct Scsi_Host *host) {
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);
@@ -1039,11 +1112,15 @@ static void NCR53c8x0_init_fixup (struct Scsi_Host *host) {
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, 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, 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));
@@ -1125,17 +1202,18 @@ static int NCR53c8xx_run_tests (struct Scsi_Host *host) {
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata;
unsigned long timeout, start;
- int old_level, failed, i;
+ int failed, i;
+ unsigned long flags;
+
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);
+ save_flags(flags);
+ cli();
if (!hostdata->idle) {
printk ("scsi%d : chip not idle, aborting tests\n", host->host_no);
- splx(old_level);
+ restore_flags(flags);
return -1;
}
@@ -1161,10 +1239,11 @@ static int NCR53c8xx_run_tests (struct Scsi_Host *host) {
printk ("scsi%d : test 1", host->host_no);
NCR53c7x0_write32 (DSP_REG, start);
printk (" started\n");
- splx(7);
+ sti();
timeout = jiffies + 50; /* arbitrary */
- while ((hostdata->test_completed == -1) && jiffies < timeout);
+ while ((hostdata->test_completed == -1) && jiffies < timeout)
+ barrier();
failed = 1;
if (hostdata->test_completed == -1)
@@ -1197,7 +1276,7 @@ static int NCR53c8xx_run_tests (struct Scsi_Host *host) {
(unsigned long) hostdata->script, start);
printk ("scsi%d : DSPS = 0x%lx\n", host->host_no,
(unsigned long) NCR53c7x0_read32(DSPS_REG));
- splx(old_level);
+ restore_flags(flags);
return -1;
}
hostdata->test_running = 0;
@@ -1233,10 +1312,10 @@ static int NCR53c8xx_run_tests (struct Scsi_Host *host) {
dsa[11] = (unsigned long) &msg;
for (i = 0; i < 3; ++i) {
- splx(0);
+ cli();
if (!hostdata->idle) {
printk ("scsi%d : chip not idle, aborting tests\n", host->host_no);
- splx(old_level);
+ restore_flags(flags);
return -1;
}
@@ -1249,10 +1328,11 @@ static int NCR53c8xx_run_tests (struct Scsi_Host *host) {
hostdata->state = STATE_RUNNING;
NCR53c7x0_write32 (DSA_REG, (unsigned long) dsa);
NCR53c7x0_write32 (DSP_REG, start);
- splx(7);
+ sti();
timeout = jiffies + 500; /* arbitrary */
- while ((hostdata->test_completed == -1) && jiffies < timeout);
+ while ((hostdata->test_completed == -1) && jiffies < timeout)
+ barrier();
NCR53c7x0_write32 (DSA_REG, 0);
if (hostdata->test_completed == 2) {
@@ -1269,12 +1349,12 @@ static int NCR53c8xx_run_tests (struct Scsi_Host *host) {
host->host_no, i);
if (!hostdata->idle) {
printk("scsi%d : not idle\n", host->host_no);
- splx(old_level);
+ restore_flags(flags);
return -1;
}
} else if (hostdata->test_completed == -1) {
printk ("scsi%d : test 2 timed out\n", host->host_no);
- splx(old_level);
+ restore_flags(flags);
return -1;
}
hostdata->test_running = 0;
@@ -1285,9 +1365,8 @@ static int NCR53c8xx_run_tests (struct Scsi_Host *host) {
}
}
}
- printk ("scsi%d : tests complete.\n", host->host_no);
- splx(old_level);
+ restore_flags(flags);
return 0;
}
@@ -1344,11 +1423,12 @@ static void abnormal_finished (struct NCR53c7x0_cmd *cmd, int result) {
struct Scsi_Host *host = c->host;
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata;
- int old_level;
+ unsigned long flags;
char **prev, *search;
int i;
- old_level = splx(0);
+ save_flags(flags);
+ cli();
for (i = 0; i < 2; ++i) {
for (search = (char *) (i ? hostdata->issue_dsa_head :
hostdata->reconnect_dsa_head), prev = (char **) (i ?
@@ -1370,21 +1450,14 @@ static void abnormal_finished (struct NCR53c7x0_cmd *cmd, int result) {
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;
- }
+ cmd->next = hostdata->free;
+ hostdata->free = cmd;
c->host_scribble = NULL;
c->result = result;
c->scsi_done(c);
- splx(old_level);
+ restore_flags(flags);
}
/*
@@ -1409,7 +1482,7 @@ static void intr_break (struct Scsi_Host *host, struct
unsigned long *dsp;
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata;
- int old_level;
+ unsigned long flags;
NCR53c7x0_local_setup(host);
/*
@@ -1417,8 +1490,8 @@ static void intr_break (struct Scsi_Host *host, struct
* dump the appropriate debugging information to standard
* output.
*/
-
- old_level = splx(0);
+ save_flags(flags);
+ cli();
dsp = (unsigned long *) NCR53c7x0_read32(DSP_REG);
for (bp = hostdata->breakpoints; bp && bp->address != dsp;
bp = bp->next);
@@ -1440,7 +1513,7 @@ static void intr_break (struct Scsi_Host *host, struct
* instruction in bytes.
*/
- splx(old_level);
+ restore_flags(flags);
}
/*
@@ -1555,7 +1628,7 @@ static void synchronous (struct Scsi_Host *host, int target, char *msg) {
hostdata->sync[target].select_indirect = (scntl3 << 24) | (target << 16) |
(sxfer << 8);
- script = hostdata->sync[target].script;
+ script = (long *) hostdata->sync[target].script;
/* XXX - add NCR53c7x0 code to reprogram SCF bits if we want to */
if ((hostdata->chip / 100) == 8) {
@@ -1610,7 +1683,7 @@ static int NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct
printk ("scsi%d : message", host->host_no);
if (cmd)
printk (" from target %d lun %d", c->target, c->lun);
- print_msg (hostdata->msg_buf);
+ print_msg ((unsigned char *) hostdata->msg_buf);
printk("\n");
switch (hostdata->msg_buf[0]) {
/*
@@ -1645,7 +1718,8 @@ static int NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct
*/
if (cmd->flags & CMD_FLAG_SDTR) {
cmd->flags &= ~CMD_FLAG_SDTR;
- synchronous (host, c->target, hostdata->msg_buf);
+ synchronous (host, c->target, (unsigned char *)
+ hostdata->msg_buf);
hostdata->dsp = hostdata->script + hostdata->E_accept_message /
sizeof(long);
hostdata->dsp_changed = 1;
@@ -1653,7 +1727,8 @@ static int NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct
} else {
if (hostdata->options & OPTION_SYNCHRONOUS) {
cmd->flags |= CMD_FLAG_DID_SDTR;
- synchronous (host, c->target, hostdata->msg_buf);
+ synchronous (host, c->target, (unsigned char *)
+ hostdata->msg_buf);
} else {
hostdata->msg_buf[4] = 0; /* 0 offset = async */
}
@@ -1928,15 +2003,16 @@ static int debugger_fn_bc (struct Scsi_Host *host, struct debugger_token *token,
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
instance->hostdata;
struct NCR53c7x0_break *bp, **prev;
- int old_level;
- old_level = splx(0);
+ unsigned long flags;
+ save_flags(flags);
+ cli();
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);
+ restore_flags(flags);
return -EIO;
}
@@ -1949,7 +2025,7 @@ static int debugger_fn_bc (struct Scsi_Host *host, struct debugger_token *token,
if (prev)
*prev = bp->next;
- splx(old_level);
+ restore_flags(flags);
return 0;
}
@@ -1961,7 +2037,7 @@ static int debugger_fn_bl (struct Scsi_Host *host, struct debugger_token *token,
struct NCR53c7x0_break *bp;
char buf[80];
size_t len;
- int old_level;
+ unsigned long flags;
/*
* XXX - we need to insure that the processor is halted
* here in order to prevent a race condition. So, if the
@@ -1972,7 +2048,8 @@ static int debugger_fn_bl (struct Scsi_Host *host, struct debugger_token *token,
host->host_no);
debugger_kernel_write (host, buf, strlen(buf));
- old_level=splx(0);
+ save_flags(flags);
+ cli();
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",
@@ -1987,7 +2064,7 @@ static int debugger_fn_bl (struct Scsi_Host *host, struct debugger_token *token,
len = strlen(buf);
debugger_kernel_write (host, buf, len);
}
- splx(old_level);
+ restore_flags(flags);
return 0;
}
@@ -1998,20 +2075,21 @@ static int debugger_fn_bs (struct Scsi_Host *host, struct debugger_token *token,
struct NCR53c7x0_break *bp;
char buf[80];
size_t len;
- int old_level;
- old_level=splx(0);
+ unsigned long flags;
+ save_flags(flags);
+ cli();
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);
+ restore_flags(flags);
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);
+ restore_flags(flags);
return -1;
}
@@ -2023,7 +2101,7 @@ static int debugger_fn_bs (struct Scsi_Host *host, struct debugger_token *token,
hostdata->breakpoints = bp->next;
memcpy ((void *) bp->address, (void *) hostdata->E_debug_break, 8);
- splx(old_level);
+ restore_flags(flags);
return 0;
}
@@ -2112,9 +2190,10 @@ 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);
+ int copy, left;
+ unsigned long flags;
+ save_flags(flags);
+ cli();
while (buflen) {
left = (hostdata->debug_buf + hostdata->debug_size - 1) -
hostdata->debug_write;
@@ -2127,7 +2206,7 @@ static debugger_kernel_write (struct Scsi_Host *host, char *buf, size_t
(hostdata->debug_buf + hostdata->debug_size))
hosdata->debug_write = hostdata->debug_buf;
}
- (void) splx(old_level);
+ restore_flags(flags);
}
#endif /* def NCRDEBUG */
@@ -2144,7 +2223,8 @@ static debugger_kernel_write (struct Scsi_Host *host, char *buf, size_t
*
*/
-static void NCR53c8x0_soft_reset (struct Scsi_Host *host) {
+static void
+NCR53c8x0_soft_reset (struct Scsi_Host *host) {
NCR53c7x0_local_declare();
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata;
@@ -2167,14 +2247,18 @@ static void NCR53c8x0_soft_reset (struct Scsi_Host *host) {
/*
- * Respond to selection and reselection by targets and
- * use our _initiator_ SCSI ID for arbitration.
+ * Respond to reselection by targets and use our _initiator_ SCSI ID
+ * for arbitration. If notyet, also respond to SCSI selection.
*
* XXX - Note : we must reprogram this when reselecting as
* a target.
*/
+#ifdef notyet
NCR53c7x0_write8(SCID_REG, (host->this_id & 7)|SCID_800_RRE|SCID_800_SRE);
+#else
+ NCR53c7x0_write8(SCID_REG, (host->this_id & 7)|SCID_800_RRE);
+#endif
NCR53c7x0_write8(RESPID_REG_800, hostdata->this_id_mask);
/*
@@ -2202,8 +2286,7 @@ static void NCR53c8x0_soft_reset (struct Scsi_Host *host) {
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);
+ SIEN_PAR : 0) | SIEN_RST | SIEN_UDC | SIEN_SGE | SIEN_MA);
NCR53c7x0_write8(SIEN1_REG_800, SIEN1_800_STO | SIEN1_800_HTH);
/*
@@ -2223,11 +2306,13 @@ static void NCR53c8x0_soft_reset (struct Scsi_Host *host) {
/*
* 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.
+ * Purpose : If we have not already allocated enough NCR53c7x0_cmd
+ * structures to satisfy any allowable number of simultaneous
+ * commands for this host; do so (using either scsi_malloc()
+ * or kmalloc() depending on configuration), and add them to the
+ * hostdata free list. Take the first structure off the free list,
+ * initialize it based on the Scsi_Cmnd structure passed in,
+ * including dsa and Linux field initialization, and dsa code relocation.
*
* Inputs : cmd - SCSI command
*
@@ -2235,26 +2320,86 @@ static void NCR53c8x0_soft_reset (struct Scsi_Host *host) {
* NULL on failure.
*/
-static struct NCR53c7x0_cmd *create_cmd (Scsi_Cmnd *cmd) {
+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 */
+ struct NCR53c7x0_cmd *tmp = NULL; /* 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) */
+ i; /* Counter */
unsigned long *cmd_datain, /* Address of datain/dataout code */
*cmd_dataout; /* Incremented as we assemble */
+#ifdef notyet
void *real; /* Real address */
+ int size; /* Size of *tmp */
+ int alignment; /* Alignment adjustment (0 - 4) */
+#endif
+ unsigned long flags;
NCR53c7x0_local_setup(cmd->host);
+/* FIXME : when we start doing multiple simultaneous commands per LUN,
+ we will need to either
+ - Do an attach_slave() and detach_slave() the right way (allocate
+ memory in attach_slave() as we do in scsi_register).
+ - Make sure this code works
+ with the former being cleaner. At the same time, we can also go with
+ a per-device host_scribble, and introduce a NCR53c7x0_device structure
+ to replace the messy fixed length arrays we're starting to use. */
+
+#ifdef notyet
+
+ if (hostdata->num_commands < host->can_queue &&
+ !in_scan_scsis &&
+ !(hostdata->cmd_allocated[cmd->target] & (1 << cmd->lun))) {
+ for (i = host->hostt->cmd_per_lun - 1; i >= 0 --i) {
+#ifdef SCSI_MALLOC
+ /* scsi_malloc must allocate with a 512 byte granularity, but always
+ returns buffers which are aligned on a 512 boundary */
+ size = (hostdata->max_cmd_size + 511) / 512 * 512;
+ tmp = (struct NCR53c7x0_cmd *) scsi_malloc (size);
+ if (!tmp)
+ break;
+ tmp->real = (void *) tmp;
+#else
+ /* kmalloc() can allocate any size, but historically has returned
+ unaligned addresses, so we need to allow for alignment */
+ size = hostdata->max_cmd_size + 4;
+ real = kmalloc (size, GFP_ATOMIC);
+ alignment = 4 - (((unsigned) real) & 3);
+ tmp = (struct NCR53c7x0_cmd *) (((char *) real) + alignment);
+ if (!tmp)
+ break;
+ tmp->real = real;
+#endif /* def SCSI_MALLOC */
+ tmp->size = size;
+ /* Insert all but last into list */
+ if (i > 0) {
+ tmp->next = hostdata->free;
+ hostdata->free = tmp;
+ }
+ }
+ }
+#endif /* def notyet */
+ if (!tmp) {
+ save_flags(flags);
+ cli();
+ tmp = (struct NCR53c7x0_cmd *) hostdata->free;
+ if (tmp) {
+ hostdata->free = tmp->next;
+ restore_flags(flags);
+ } else {
+ restore_flags(flags);
+ return NULL;
+ }
+ }
+
/*
- * Decide weather we need to generate commands for DATA IN,
+ * Decide whether we need to generate commands for DATA IN,
* DATA OUT, neither, or both based on the SCSI command
*/
@@ -2303,10 +2448,6 @@ static struct NCR53c7x0_cmd *create_cmd (Scsi_Cmnd *cmd) {
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
@@ -2324,63 +2465,11 @@ static struct NCR53c7x0_cmd *create_cmd (Scsi_Cmnd *cmd) {
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;
@@ -2414,7 +2503,7 @@ static struct NCR53c7x0_cmd *create_cmd (Scsi_Cmnd *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
+ * XXX - we need to figure this size based on whether
* or not we'll be using any additional messages.
*/
patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1);
@@ -2442,7 +2531,7 @@ static struct NCR53c7x0_cmd *create_cmd (Scsi_Cmnd *cmd) {
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);
+ &(hostdata->NCR53c7xx_msg_nop));
/*
@@ -2466,8 +2555,8 @@ static struct NCR53c7x0_cmd *create_cmd (Scsi_Cmnd *cmd) {
#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
+ * XXX - I'm undecided whether all of this nonsense is faster
+ * in the long run, or whether 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,
@@ -2496,8 +2585,8 @@ static struct NCR53c7x0_cmd *create_cmd (Scsi_Cmnd *cmd) {
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);
+ cmd_datain[3] = (unsigned long) hostdata->script +
+ hostdata->E_msg_in;
#if 0
print_insn (host, cmd_datain, "dynamic ", 1);
print_insn (host, cmd_datain + 2, "dynamic ", 1);
@@ -2510,8 +2599,8 @@ static struct NCR53c7x0_cmd *create_cmd (Scsi_Cmnd *cmd) {
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);
+ cmd_dataout[3] = (unsigned long) hostdata->script +
+ hostdata->E_msg_in;
#if 0
print_insn (host, cmd_dataout, "dynamic ", 1);
print_insn (host, cmd_dataout + 2, "dynamic ", 1);
@@ -2528,8 +2617,8 @@ static struct NCR53c7x0_cmd *create_cmd (Scsi_Cmnd *cmd) {
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);
+ cmd_datain[1] = (unsigned long) hostdata->script +
+ hostdata->E_other_transfer;
#if 0
print_insn (host, cmd_datain, "dynamic jump ", 1);
#endif
@@ -2547,8 +2636,8 @@ static struct NCR53c7x0_cmd *create_cmd (Scsi_Cmnd *cmd) {
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);
+ cmd_dataout[1] = (unsigned long) hostdata->script +
+ hostdata->E_other_transfer;
#if 0
print_insn (host, cmd_dataout, "dynamic jump ", 1);
#endif
@@ -2583,7 +2672,7 @@ int NCR53c7xx_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)) {
struct Scsi_Host *host = cmd->host;
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata;
- int old_level;
+ unsigned long flags;
unsigned char target_was_busy;
NCR53c7x0_local_setup(host);
@@ -2651,7 +2740,8 @@ int NCR53c7xx_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)) {
* valid while the condition exists.
*/
- old_level = splx(0);
+ save_flags(flags);
+ cli();
/*
* Consider a target busy if there are _any_ commands running
@@ -2744,7 +2834,7 @@ int NCR53c7xx_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)) {
tmp->next; tmp = (struct NCR53c7x0_cmd *) tmp->next);
tmp->next = tmp;
}
- splx(old_level);
+ restore_flags(flags);
return 0;
}
@@ -2790,7 +2880,7 @@ static void intr_scsi (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) {
/* 250ms selection timeout */
if ((((hostdata->chip / 100) == 8) && (sist1 & SIST1_800_STO)) ||
- (((hostdata->chip / 100) != 8) && sstat0_sist0 && SSTAT0_700_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);
@@ -2828,9 +2918,9 @@ static void intr_scsi (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) {
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) {
+ printk("scsi%d : target %d lun %d unexpected disconnect\n",
+ host->host_no, cmd->cmd->target, cmd->cmd->lun);
abnormal_finished(cmd, DID_ERROR << 16);
}
hostdata->dsp = hostdata->script + hostdata->E_schedule /
@@ -2908,7 +2998,7 @@ static void intr_scsi (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) {
}
/*
- * Function : static void NCR53c7x0_intr (int irq)
+ * Function : static void NCR53c7x0_intr (int irq, struct pt_regs * regs)
*
* Purpose : handle NCR53c7x0 interrupts for all NCR devices sharing
* the same IRQ line.
@@ -2918,7 +3008,7 @@ static void intr_scsi (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) {
* this handler.
*/
-static void NCR53c7x0_intr (int irq) {
+static void NCR53c7x0_intr (int irq, struct pt_regs * regs) {
NCR53c7x0_local_declare();
struct Scsi_Host *host; /* Host we are looking at */
unsigned char istat; /* Values of interrupt regs */
@@ -2930,7 +3020,7 @@ static void NCR53c7x0_intr (int irq) {
should terminate */
int interrupted = 0; /* This HA generated
an interrupt */
- int old_level;
+ unsigned long flags;
#ifdef NCR_DEBUG
char buf[80]; /* Debugging sprintf buffer */
@@ -2991,12 +3081,14 @@ static void NCR53c7x0_intr (int irq) {
*/
- old_level = splx(0);
+ save_flags(flags);
+ cli();
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) {
+ &(hostdata->running_list), cmd =
+ (struct NCR53c7x0_cmd *) hostdata->running_list; cmd ;
+ cmd_prev_ptr = (struct NCR53c7x0_cmd **) &(cmd->next),
+ cmd = (struct NCR53c7x0_cmd *) cmd->next) {
Scsi_Cmnd *tmp;
if (!cmd) {
@@ -3025,7 +3117,7 @@ restart:
if (cmd->prev)
cmd->prev->next = cmd->next;
if (cmd_prev_ptr)
- *cmd_prev_ptr = cmd->next;
+ *cmd_prev_ptr = (struct NCR53c7x0_cmd *) cmd->next;
#ifdef LUN_BUSY
/* Check for next command for target, add to issue queue */
@@ -3034,16 +3126,8 @@ restart:
#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;
- }
-
+ cmd->next = hostdata->free;
+ hostdata->free = cmd;
tmp->host_scribble = NULL;
@@ -3061,7 +3145,7 @@ restart:
goto restart;
}
- splx(old_level);
+ restore_flags(flags);
if (!search_found) {
printk ("scsi%d : WARNING : INTFLY with no completed commands.\n",
@@ -3184,7 +3268,8 @@ restart:
*
*/
-static int abort_connected (struct Scsi_Host *host) {
+static int
+abort_connected (struct Scsi_Host *host) {
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata;
@@ -3378,15 +3463,13 @@ 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 char dstat; /* DSTAT */
unsigned long *dsp,
*next_dsp, /* Current dsp */
- *dsa;
-
-
- int ipl, /* Old ipl from splx(0) */
- tmp;
+ *dsa,
+ dbc_dcmd; /* DCMD (high eight bits) + DBC */
+ int tmp;
+ unsigned long flags;
NCR53c7x0_local_setup(host);
if (!hostdata->dstat_valid) {
@@ -3440,12 +3523,13 @@ static void intr_dma (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) {
if (hostdata->options & OPTION_DEBUG_TRACE) {
} else if (hostdata->options & OPTION_DEBUG_SINGLE) {
print_insn (host, dsp, "s ", 0);
- ipl = splx(0);
+ save_flags(flags);
+ cli();
/* 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);
+ restore_flags(flags);
} else {
printk("scsi%d : unexpected single step interrupt at\n"
" ", host->host_no);
@@ -3634,9 +3718,10 @@ int NCR53c7xx_abort (Scsi_Cmnd *cmd) {
struct Scsi_Host *host = cmd->host;
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata;
- int old_level;
+ unsigned long flags;
struct NCR53c7x0_cmd *curr, **prev;
- old_level = splx(0);
+ save_flags(flags);
+ cli();
/*
* The command could be hiding in the issue_queue. This would be very
@@ -3648,28 +3733,23 @@ int NCR53c7xx_abort (Scsi_Cmnd *cmd) {
* 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);
+ for (curr = (struct NCR53c7x0_cmd *) hostdata->issue_queue,
+ prev = (struct NCR53c7x0_cmd **) &(hostdata->issue_queue);
+ curr && curr->cmd != cmd; prev = (struct NCR53c7x0_cmd **)
+ &(curr->next), curr = (struct NCR53c7x0_cmd *) curr->next);
if (curr) {
- *prev = curr->next;
+ *prev = (struct NCR53c7x0_cmd *) 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;
- }
+ curr->next = hostdata->free;
+ hostdata->free = curr;
cmd->result = 0;
cmd->scsi_done(cmd);
- splx(old_level);
+ restore_flags(flags);
return SCSI_ABORT_SUCCESS;
}
@@ -3678,11 +3758,13 @@ int NCR53c7xx_abort (Scsi_Cmnd *cmd) {
* 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);
+ for (curr = (struct NCR53c7x0_cmd *) hostdata->running_list,
+ prev = (struct NCR53c7x0_cmd **) &(hostdata->running_list);
+ curr && curr->cmd != cmd; prev = (struct NCR53c7x0_cmd **)
+ &(curr->next), curr = (struct NCR53c7x0_cmd *) curr->next);
if (curr) {
- splx(old_level);
+ restore_flags(flags);
printk ("scsi%d : DANGER : command in running list, can not abort.\n",
cmd->host->host_no);
return SCSI_ABORT_SNOOZE;
@@ -3695,16 +3777,8 @@ int NCR53c7xx_abort (Scsi_Cmnd *cmd) {
*/
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;
- }
+ curr->next = hostdata->free;
+ hostdata->free = curr;
if (((cmd->result & 0xff00) == 0xff00) ||
((cmd->result & 0xff) == 0xff)) {
@@ -3714,7 +3788,7 @@ int NCR53c7xx_abort (Scsi_Cmnd *cmd) {
host->host_no);
}
cmd->scsi_done(cmd);
- splx(old_level);
+ restore_flags(flags);
return SCSI_ABORT_SNOOZE;
}
@@ -3729,17 +3803,47 @@ int NCR53c7xx_abort (Scsi_Cmnd *cmd) {
* Returns : 0 on success.
*/
-int NCR53c7xx_reset (Scsi_Cmnd *cmd) {
+int
+NCR53c7xx_reset (Scsi_Cmnd *cmd) {
NCR53c7x0_local_declare();
- struct Scsi_Host *host = cmd ? cmd->host : NULL;
+ unsigned long flags;
+ int found;
+ struct NCR53c7x0_cmd * c;
+ Scsi_Cmnd *tmp;
+ struct Scsi_Host *host = cmd->host;
struct NCR53c7x0_hostdata *hostdata = host ?
- (struct NCR53c7x0_hostdata *) host->hostdata : NULL;
- if (host) NCR53c7x0_local_setup(host);
-
+ (struct NCR53c7x0_hostdata *) host->hostdata : NULL;
+ NCR53c7x0_local_setup(host);
+ save_flags(flags);
+ halt (host);
+ NCR53c7x0_write8(SCNTL1_REG, SCNTL1_RST);
+ udelay(25); /* Minimum amount of time to assert RST */
+ NCR53c7x0_write8(SCNTL1_REG, SCNTL1_RST);
+ for (c = (struct NCR53c7x0_cmd *) hostdata->running_list, found = 0; c;
+ c = (struct NCR53c7x0_cmd *) c->next) {
+ tmp = c->cmd;
+ c->next = hostdata->free;
+ hostdata->free = c;
+
+ if (tmp == cmd)
+ found = 1;
+ tmp->result = DID_RESET << 16;
+ tmp->scsi_done(tmp);
+ }
+ if (!found) {
+ c = (struct NCR53c7x0_cmd *) cmd->host_scribble;
+ if (c) {
+ c->next = hostdata->free;
+ hostdata->free = c;
+ }
+ cmd->result = DID_RESET << 16;
+ cmd->scsi_done(cmd);
+ }
+ restore_flags(flags);
printk ("scsi%d : DANGER : NCR53c7xx_reset is NOP\n",
cmd->host->host_no);
- return SCSI_RESET_SNOOZE;
+ return SCSI_RESET_SUCCESS;
}
/*
@@ -3750,13 +3854,11 @@ int NCR53c7xx_reset (Scsi_Cmnd *cmd) {
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" ,
+ " + %ld : dsa_msgout length = %lu, data = 0x%lx\n" ,
host->host_no, (unsigned) dsa, hostdata->dsa_msgout,
dsa[hostdata->dsa_msgout / sizeof(long)],
dsa[hostdata->dsa_msgout / sizeof(long) + 1]);
@@ -3770,3 +3872,108 @@ static void print_dsa (struct Scsi_Host *host, unsigned long *dsa) {
}
}
+/*
+ * Function : static int shutdown (struct Scsi_Host *host)
+ *
+ * Purpose : does a clean (we hope) shutdown of the NCR SCSI
+ * chip. Use prior to dumping core, unloading the NCR driver,
+ * etc.
+ *
+ * Returns : 0 on success
+ */
+#ifdef MODULE
+static int
+shutdown (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ unsigned long flags;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ NCR53c7x0_local_setup(host);
+ save_flags (flags);
+ cli();
+ halt (host);
+ hostdata->soft_reset(host);
+/*
+ * For now, we take the simplest solution : reset the SCSI bus. Eventually,
+ * - If a command is connected, kill it with an ABORT message
+ * - If commands are disconnected, connect to each target/LUN and
+ * do a ABORT, followed by a SOFT reset, followed by a hard
+ * reset.
+ */
+ NCR53c7x0_write8(SCNTL1_REG, SCNTL1_RST);
+ udelay(25); /* Minimum amount of time to assert RST */
+ NCR53c7x0_write8(SCNTL1_REG, SCNTL1_RST);
+ restore_flags (flags);
+ return 0;
+}
+#endif
+
+
+/*
+ * Function : static int halt (struct Scsi_Host *host)
+ *
+ * Purpose : halts the SCSI SCRIPTS(tm) processor on the NCR chip
+ *
+ * Inputs : host - SCSI chip to halt
+ *
+ * Returns : 0 on success
+ */
+
+static int
+halt (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ unsigned long flags;
+ unsigned char istat, tmp;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ NCR53c7x0_local_setup(host);
+
+ save_flags(flags);
+ cli();
+ NCR53c7x0_write8(hostdata->istat, ISTAT_ABRT);
+ /* Eat interrupts until we find what we're looking for */
+ for (;;) {
+ istat = NCR53c7x0_read8 (hostdata->istat);
+ if (istat & ISTAT_SIP) {
+ if ((hostdata->chip / 100) == 8) {
+ tmp = NCR53c7x0_read8(SIST0_REG_800);
+ udelay(1);
+ tmp = NCR53c7x0_read8(SIST1_REG_800);
+ } else {
+ tmp = NCR53c7x0_read8(SSTAT0_REG);
+ }
+ } else if (istat & ISTAT_DIP) {
+ NCR53c7x0_write8(hostdata->istat, 0);
+ tmp = NCR53c7x0_read8(DSTAT_REG);
+ if (tmp & DSTAT_ABRT)
+ break;
+ else
+ panic("scsi%d: could not halt NCR chip\n", host->host_no);
+ }
+ }
+ hostdata->state = STATE_HALTED;
+ restore_flags(flags);
+ return 0;
+}
+
+#ifdef MODULE
+int NCR53c7x0_release(struct Scsi_Host *host) {
+ shutdown (host);
+/* FIXME : need to recursively free tpnt structure */
+ if (host->irq != IRQ_NONE)
+ {
+ int irq_count;
+ struct Scsi_Host *tmp;
+ for (irq_count = 0, tmp = first_host; tmp; tmp = tmp->next)
+ if (tmp->hostt == the_template && tmp->irq == host->irq)
+ ++irq_count;
+ if (irq_count == 1)
+ free_irq(host->irq);
+ }
+ if (host->dma_channel != DMA_NONE)
+ free_dma(host->dma_channel);
+ return 1;
+}
+Scsi_Host_Template driver_template = NCR53c7xx;
+#include "scsi_module.c"
+#endif /* def MODULE */
diff --git a/drivers/scsi/53c7,8xx.h b/drivers/scsi/53c7,8xx.h
index 1eee78799..6a3615914 100644
--- a/drivers/scsi/53c7,8xx.h
+++ b/drivers/scsi/53c7,8xx.h
@@ -46,20 +46,27 @@
* array.
*/
-#ifdef HOSTS_C
+#if defined(HOSTS_C) || defined(MODULE)
#include <linux/scsicam.h>
extern int NCR53c7xx_abort(Scsi_Cmnd *);
extern int NCR53c7xx_detect(Scsi_Host_Template *tpnt);
extern int NCR53c7xx_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
extern int NCR53c7xx_reset(Scsi_Cmnd *);
-
-#define NCR53c7xx {NULL, NULL, "NCR53c{7,8}xx (rel 3)", NCR53c7xx_detect, \
- NULL, NULL, \
- 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}
+#ifdef MODULE
+extern int NCR53c7xx_release(struct Scsi_Host *);
#else
+#define NCR53c7xx_release NULL
+#endif
+
+#define NCR53c7xx {NULL, NULL, "NCR53c{7,8}xx (rel 4)", NCR53c7xx_detect, \
+ NULL, /* info */ NULL, /* command, deprecated */ NULL, \
+ NCR53c7xx_queue_command, NCR53c7xx_abort, NCR53c7xx_reset, \
+ NULL /* slave attach */, scsicam_bios_param, /* can queue */ 1, \
+ /* id */ 7, 127 /* old SG_ALL */, /* cmd per lun */ 1 , \
+ /* present */ 0, /* unchecked isa dma */ 0, DISABLE_CLUSTERING}
+#endif /* defined(HOSTS_C) || defined(MODULE) */
+
+#ifndef HOSTS_C
/* Register addresses, ordered numerically */
@@ -932,6 +939,9 @@ struct NCR53c7x0_table_indirect {
struct NCR53c7x0_cmd {
void *real; /* Real, unaligned address */
+ void (* free)(void *); /* Command to deallocate; NULL
+ for structures allocated with
+ scsi_register, etc. */
Scsi_Cmnd *cmd; /* Associated Scsi_Cmnd
structure, Scsi_Cmnd points
at NCR53c7x0_cmd using
@@ -949,7 +959,11 @@ struct NCR53c7x0_cmd {
*/
- struct NCR53c7x0_cmd *next, *prev; /* Linux maintained lists */
+ volatile struct NCR53c7x0_cmd *next, *prev;
+ /* Linux maintained lists. Note that
+ hostdata->free is a singly linked
+ list; the rest are doubly linked */
+
unsigned long *data_transfer_start; /* Start of data transfer routines */
@@ -986,10 +1000,12 @@ struct NCR53c7x0_break {
/* Indicates that the NCR is executing other code. */
#define STATE_RUNNING 2
/*
- * Indicates that the NCR was being aborted. Only used when running
- * NCR53c700 compatible scripts.
+ * Indicates that the NCR was being aborted.
*/
#define STATE_ABORTING 3
+/*
+ * Indicates that the NCR was successfully aborted. */
+#define STATE_ABORTED 4
/*
@@ -1072,6 +1088,7 @@ struct NCR53c7x0_hostdata {
int (* dstat_sir_intr)(struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd);
+ long dsa_size; /* Size of DSA structure */
/*
* Location of DSA fields for the SCSI SCRIPT corresponding to this
@@ -1160,11 +1177,13 @@ struct NCR53c7x0_hostdata {
*breakpoint_current; /* Current breakpoint being stepped
through, NULL if we are running
normally. */
+#ifdef NCR_DEBUG
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 */
+#endif /* def NCR_DEBUG */
/* XXX - primitive debugging junk, remove when working ? */
int debug_print_limit; /* Number of commands to print
@@ -1203,6 +1222,21 @@ struct NCR53c7x0_hostdata {
nexus, ONLY valid for
NCR53c700/NCR53c700-66
*/
+
+ volatile struct NCR53c7x0_cmd *spare; /* pointer to spare,
+ allocated at probe time,
+ which we can use for
+ initialization */
+ volatile struct NCR53c7x0_cmd *free;
+ int max_cmd_size; /* Maximum size of NCR53c7x0_cmd
+ based on number of
+ scatter/gather segments, etc.
+ */
+ volatile int num_cmds; /* Number of commands
+ allocated */
+ volatile unsigned char cmd_allocated[8]; /* Have we allocated commands
+ for this target yet? If not,
+ do so ASAP */
volatile unsigned char busy[8][8]; /* number of commands
executing on each target
*/
@@ -1226,13 +1260,21 @@ struct NCR53c7x0_hostdata {
volatile unsigned char msg_buf[16]; /* buffer for messages
other than the command
complete message */
- volatile struct NCR53c7x0_cmd *reconnect_dsa_head;
+ volatile unsigned char *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 */
+ /* These were static variables before we moved them */
+
+ long NCR53c7xx_zero;
+ long NCR53c7xx_sink;
+ char NCR53c7xx_msg_reject;
+ char NCR53c7xx_msg_abort;
+ char NCR53c7xx_msg_nop;
+
int script_count; /* Size of script in longs */
unsigned long script[0]; /* Relocated SCSI script */
diff --git a/drivers/scsi/53c7,8xx.scr b/drivers/scsi/53c7,8xx.scr
index 0174526f5..52ab39b11 100644
--- a/drivers/scsi/53c7,8xx.scr
+++ b/drivers/scsi/53c7,8xx.scr
@@ -221,10 +221,10 @@ 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
+ABSOLUTE NCR53c7xx_msg_abort = 0 ; Pointer to abort message
+ABSOLUTE NCR53c7xx_msg_reject = 0 ; Pointer to reject message
+ABSOLUTE NCR53c7xx_zero = 0 ; long with zero in it, use for source
+ABSOLUTE NCR53c7xx_sink = 0 ; long to dump worthless data in
; Pointer to final bytes of multi-byte messages
ABSOLUTE msg_buf = 0
@@ -233,7 +233,7 @@ ABSOLUTE msg_buf = 0
ABSOLUTE reselected_identify = 0
ABSOLUTE reselected_tag = 0
-; Request sense command pointer, its a 6 byte command, should
+; Request sense command pointer, it's a 6 byte command, should
; be constant for all commands since we always want 16 bytes of
; sense and we don't need to change any fields as we did under
; SCSI-I when we actually cared about the LUN field.
@@ -830,7 +830,7 @@ reselected_not_end:
; 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 os the address.
+ ; 32 bit width of the address.
;
; A potential work around would be to have a known alignment
; of the DSA structure such that the base address plus
@@ -881,13 +881,23 @@ selected:
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, IF 0x40
+ MOVE SIST0 & 0x20 TO SFBR
+ JUMP selected, IF 0x20
+; FIXME : Something bogus happened, and we shouldn't fail silently.
JUMP schedule
select_failed:
- MOVE ISTAT & 0x20 TO SFBR
- JUMP reselected, IF NOT 0x20
- MOVE ISTAT & 0xdf TO ISTAT
+; If SIGP is set, the user just gave us another command, and
+; we should restart or return to the scheduler.
+; Reading CTEST2 clears the SIG_P bit in the ISTAT register.
+ MOVE CTEST2 & 0x40 TO SFBR
+ JUMP select, IF 0x40
+; Otherwise, mask the selected and reselected bits off SIST0
+ MOVE SIST0 & 0x30 TO SFBR
+ JUMP selected, IF 0x20
+ JUMP reselected, IF 0x10
+; FIXME : Something bogus happened, and we shouldn't fail silently.
JUMP schedule
;
@@ -980,7 +990,10 @@ no_source_data:
; If DSP points here, and a phase mismatch is encountered, we need to
; do a bus reset.
;
+
+ MOVE SCNTL2 & 0x7f TO SCNTL2
MOVE 1, NCR53c7xx_msg_abort, WHEN MSG_OUT
+ WAIT DISCONNECT
INT int_norm_aborted
;
diff --git a/drivers/scsi/53c8xx_d.h b/drivers/scsi/53c8xx_d.h
index 9a9c5c2db..e74804f6e 100644
--- a/drivers/scsi/53c8xx_d.h
+++ b/drivers/scsi/53c8xx_d.h
@@ -127,7 +127,7 @@ at 0x00000008 : */ 0x78380000,0x00000000,
/*
CALL scratch_to_dsa
-at 0x0000000a : */ 0x88080000,0x00000800,
+at 0x0000000a : */ 0x88080000,0x00000830,
/*
JUMP reselected_check_next
@@ -283,10 +283,10 @@ 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
+ABSOLUTE NCR53c7xx_msg_abort = 0 ; Pointer to abort message
+ABSOLUTE NCR53c7xx_msg_reject = 0 ; Pointer to reject message
+ABSOLUTE NCR53c7xx_zero = 0 ; long with zero in it, use for source
+ABSOLUTE NCR53c7xx_sink = 0 ; long to dump worthless data in
; Pointer to final bytes of multi-byte messages
ABSOLUTE msg_buf = 0
@@ -295,8 +295,8 @@ ABSOLUTE msg_buf = 0
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 allays want 16 bytes of
+; Request sense command pointer, it's a 6 byte command, should
+; be constant for all commands since we always want 16 bytes of
; sense and we don't 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
@@ -314,7 +314,7 @@ ABSOLUTE reselected_tag = 0
;
; MODIFIES : SCRATCH, reconnect_dsa_head
;
-; EXITS : allays passes control to schedule
+; EXITS : always passes control to schedule
ENTRY dsa_schedule
dsa_schedule:
@@ -325,7 +325,7 @@ dsa_schedule:
;
CALL dsa_to_scratch
-at 0x0000002d : */ 0x88080000,0x000007b8,
+at 0x0000002d : */ 0x88080000,0x000007e8,
/*
; XXX - we need to deal with the NCR53c710, which lacks an add with
; carry instruction, by moving around the DSA alignment to avoid
@@ -370,7 +370,7 @@ at 0x0000003e : */ 0xc0000004,0x00000000,0x00000000,
; And update the head pointer.
CALL dsa_to_scratch
-at 0x00000041 : */ 0x88080000,0x000007b8,
+at 0x00000041 : */ 0x88080000,0x000007e8,
/*
MOVE dmode_ncr_to_memory TO DMODE
@@ -422,7 +422,7 @@ at 0x00000051 : */ 0x78380000,0x00000000,
CALL scratch_to_dsa
-at 0x00000053 : */ 0x88080000,0x00000800,
+at 0x00000053 : */ 0x88080000,0x00000830,
/*
@@ -514,7 +514,7 @@ at 0x00000065 : */ 0x60000200,0x00000000,
SELECT ATN FROM dsa_select, select_failed
-at 0x00000067 : */ 0x4300003c,0x00000694,
+at 0x00000067 : */ 0x4300003c,0x000006a4,
/*
JUMP select_msgout, WHEN MSG_OUT
@@ -539,7 +539,7 @@ at 0x0000006b : */ 0x1e000000,0x00000040,
CALL dsa_to_scratch
-at 0x0000006d : */ 0x88080000,0x000007b8,
+at 0x0000006d : */ 0x88080000,0x000007e8,
/*
MOVE SCRATCH0 + dsa_next TO SCRATCH0
@@ -711,7 +711,7 @@ at 0x00000099 : */ 0x80080000,0x00000234,
do_dataout:
CALL dsa_to_scratch
-at 0x0000009b : */ 0x88080000,0x000007b8,
+at 0x0000009b : */ 0x88080000,0x000007e8,
/*
MOVE SCRATCH0 + dsa_dataout TO SCRATCH0
@@ -756,7 +756,7 @@ at 0x000000af : */ 0x80080000,0x00000000,
do_datain:
CALL dsa_to_scratch
-at 0x000000b1 : */ 0x88080000,0x000007b8,
+at 0x000000b1 : */ 0x88080000,0x000007e8,
/*
MOVE SCRATCH0 + dsa_datain TO SCRATCH0
@@ -1100,7 +1100,7 @@ at 0x00000130 : */ 0x60000040,0x00000000,
/*
MOVE 1, NCR53c7xx_msg_reject, WHEN MSG_OUT
-at 0x00000132 : */ 0x0e000001,((unsigned long)&NCR53c7xx_msg_reject),
+at 0x00000132 : */ 0x0e000001,0x00000000,
/*
RETURN
@@ -1295,7 +1295,7 @@ at 0x0000015a : */ 0x0f000001,0x00000000,
reselected_notag:
MOVE MEMORY 1, NCR53c7xx_zero, reselected_tag
-at 0x0000015c : */ 0xc0000001,((unsigned long)&NCR53c7xx_zero),0x00000000,
+at 0x0000015c : */ 0xc0000001,0x00000000,0x00000000,
/*
@@ -1317,7 +1317,7 @@ at 0x00000164 : */ 0x78380000,0x00000000,
/*
CALL scratch_to_dsa
-at 0x00000166 : */ 0x88080000,0x00000800,
+at 0x00000166 : */ 0x88080000,0x00000830,
/*
; Fix the update-next pointer so that the reconnect_dsa_head
@@ -1477,31 +1477,53 @@ wait_reselect_failed:
at 0x0000019f : */ 0x741a4000,0x00000000,
/*
- JUMP selected, IF NOT 0x40
+ JUMP schedule, IF 0x40
-at 0x000001a1 : */ 0x80040040,0x00000674,
+at 0x000001a1 : */ 0x800c0040,0x00000130,
/*
+ MOVE SIST0 & 0x20 TO SFBR
+
+at 0x000001a3 : */ 0x74422000,0x00000000,
+/*
+ JUMP selected, IF 0x20
+
+at 0x000001a5 : */ 0x800c0020,0x00000674,
+/*
+; FIXME : Something bogus happened, and we shouldn't fail silently.
JUMP schedule
-at 0x000001a3 : */ 0x80080000,0x00000130,
+at 0x000001a7 : */ 0x80080000,0x00000130,
/*
select_failed:
- MOVE ISTAT & 0x20 TO SFBR
+; If SIGP is set, the user just gave us another command, and
+; we should restart or return to the scheduler.
+; Reading CTEST2 clears the SIG_P bit in the ISTAT register.
+ MOVE CTEST2 & 0x40 TO SFBR
+
+at 0x000001a9 : */ 0x741a4000,0x00000000,
+/*
+ JUMP select, IF 0x40
-at 0x000001a5 : */ 0x74142000,0x00000000,
+at 0x000001ab : */ 0x800c0040,0x00000194,
/*
- JUMP reselected, IF NOT 0x20
+; Otherwise, mask the selected and reselected bits off SIST0
+ MOVE SIST0 & 0x30 TO SFBR
-at 0x000001a7 : */ 0x80040020,0x00000568,
+at 0x000001ad : */ 0x74423000,0x00000000,
/*
- MOVE ISTAT & 0xdf TO ISTAT
+ JUMP selected, IF 0x20
-at 0x000001a9 : */ 0x7c14df00,0x00000000,
+at 0x000001af : */ 0x800c0020,0x00000674,
/*
+ JUMP reselected, IF 0x10
+
+at 0x000001b1 : */ 0x800c0010,0x00000568,
+/*
+; FIXME : Something bogus happened, and we shouldn't fail silently.
JUMP schedule
-at 0x000001ab : */ 0x80080000,0x00000130,
+at 0x000001b3 : */ 0x80080000,0x00000130,
/*
;
@@ -1525,11 +1547,11 @@ ENTRY test_1
test_1:
MOVE MEMORY 4, test_src, test_dest
-at 0x000001ad : */ 0xc0000004,0x00000000,0x00000000,
+at 0x000001b5 : */ 0xc0000004,0x00000000,0x00000000,
/*
INT int_test_1
-at 0x000001b0 : */ 0x98080000,0x04000000,
+at 0x000001b8 : */ 0x98080000,0x04000000,
/*
;
@@ -1540,61 +1562,61 @@ ENTRY test_2
test_2:
CLEAR TARGET
-at 0x000001b2 : */ 0x60000200,0x00000000,
+at 0x000001ba : */ 0x60000200,0x00000000,
/*
SELECT ATN FROM 0, test_2_fail
-at 0x000001b4 : */ 0x43000000,0x00000720,
+at 0x000001bc : */ 0x43000000,0x00000740,
/*
JUMP test_2_msgout, WHEN MSG_OUT
-at 0x000001b6 : */ 0x860b0000,0x000006e0,
+at 0x000001be : */ 0x860b0000,0x00000700,
/*
ENTRY test_2_msgout
test_2_msgout:
MOVE FROM 8, WHEN MSG_OUT
-at 0x000001b8 : */ 0x1e000000,0x00000008,
+at 0x000001c0 : */ 0x1e000000,0x00000008,
/*
MOVE FROM 16, WHEN CMD
-at 0x000001ba : */ 0x1a000000,0x00000010,
+at 0x000001c2 : */ 0x1a000000,0x00000010,
/*
MOVE FROM 24, WHEN DATA_IN
-at 0x000001bc : */ 0x19000000,0x00000018,
+at 0x000001c4 : */ 0x19000000,0x00000018,
/*
MOVE FROM 32, WHEN STATUS
-at 0x000001be : */ 0x1b000000,0x00000020,
+at 0x000001c6 : */ 0x1b000000,0x00000020,
/*
MOVE FROM 40, WHEN MSG_IN
-at 0x000001c0 : */ 0x1f000000,0x00000028,
+at 0x000001c8 : */ 0x1f000000,0x00000028,
/*
MOVE SCNTL2 & 0x7f TO SCNTL2
-at 0x000001c2 : */ 0x7c027f00,0x00000000,
+at 0x000001ca : */ 0x7c027f00,0x00000000,
/*
CLEAR ACK
-at 0x000001c4 : */ 0x60000040,0x00000000,
+at 0x000001cc : */ 0x60000040,0x00000000,
/*
WAIT DISCONNECT
-at 0x000001c6 : */ 0x48000000,0x00000000,
+at 0x000001ce : */ 0x48000000,0x00000000,
/*
test_2_fail:
INT int_test_2
-at 0x000001c8 : */ 0x98080000,0x04010000,
+at 0x000001d0 : */ 0x98080000,0x04010000,
/*
ENTRY debug_break
debug_break:
INT int_debug_break
-at 0x000001ca : */ 0x98080000,0x03000000,
+at 0x000001d2 : */ 0x98080000,0x03000000,
/*
;
@@ -1610,26 +1632,26 @@ ENTRY target_abort
target_abort:
SET TARGET
-at 0x000001cc : */ 0x58000200,0x00000000,
+at 0x000001d4 : */ 0x58000200,0x00000000,
/*
DISCONNECT
-at 0x000001ce : */ 0x48000000,0x00000000,
+at 0x000001d6 : */ 0x48000000,0x00000000,
/*
CLEAR TARGET
-at 0x000001d0 : */ 0x60000200,0x00000000,
+at 0x000001d8 : */ 0x60000200,0x00000000,
/*
JUMP schedule
-at 0x000001d2 : */ 0x80080000,0x00000130,
+at 0x000001da : */ 0x80080000,0x00000130,
/*
ENTRY initiator_abort
initiator_abort:
SET ATN
-at 0x000001d4 : */ 0x58000008,0x00000000,
+at 0x000001dc : */ 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,
@@ -1637,60 +1659,69 @@ at 0x000001d4 : */ 0x58000008,0x00000000,
; false->true
JUMP no_eat_cmd, WHEN NOT CMD
-at 0x000001d6 : */ 0x82030000,0x00000768,
+at 0x000001de : */ 0x82030000,0x00000788,
/*
MOVE 1, NCR53c7xx_zero, WHEN CMD
-at 0x000001d8 : */ 0x0a000001,((unsigned long)&NCR53c7xx_zero),
+at 0x000001e0 : */ 0x0a000001,0x00000000,
/*
no_eat_cmd:
JUMP no_eat_msg, WHEN NOT MSG_IN
-at 0x000001da : */ 0x87030000,0x00000778,
+at 0x000001e2 : */ 0x87030000,0x00000798,
/*
MOVE 1, NCR53c7xx_sink, WHEN MSG_IN
-at 0x000001dc : */ 0x0f000001,((unsigned long)&NCR53c7xx_sink),
+at 0x000001e4 : */ 0x0f000001,0x00000000,
/*
no_eat_msg:
JUMP no_eat_data, WHEN NOT DATA_IN
-at 0x000001de : */ 0x81030000,0x00000788,
+at 0x000001e6 : */ 0x81030000,0x000007a8,
/*
MOVE 1, NCR53c7xx_sink, WHEN DATA_IN
-at 0x000001e0 : */ 0x09000001,((unsigned long)&NCR53c7xx_sink),
+at 0x000001e8 : */ 0x09000001,0x00000000,
/*
no_eat_data:
JUMP no_eat_status, WHEN NOT STATUS
-at 0x000001e2 : */ 0x83030000,0x00000798,
+at 0x000001ea : */ 0x83030000,0x000007b8,
/*
MOVE 1, NCR53c7xx_sink, WHEN STATUS
-at 0x000001e4 : */ 0x0b000001,((unsigned long)&NCR53c7xx_sink),
+at 0x000001ec : */ 0x0b000001,0x00000000,
/*
no_eat_status:
JUMP no_source_data, WHEN NOT DATA_OUT
-at 0x000001e6 : */ 0x80030000,0x000007a8,
+at 0x000001ee : */ 0x80030000,0x000007c8,
/*
MOVE 1, NCR53c7xx_zero, WHEN DATA_OUT
-at 0x000001e8 : */ 0x08000001,((unsigned long)&NCR53c7xx_zero),
+at 0x000001f0 : */ 0x08000001,0x00000000,
/*
no_source_data:
;
; If DSP points here, and a phase mismatch is encountered, we need to
; do a bus reset.
;
+
+ MOVE SCNTL2 & 0x7f TO SCNTL2
+
+at 0x000001f2 : */ 0x7c027f00,0x00000000,
+/*
MOVE 1, NCR53c7xx_msg_abort, WHEN MSG_OUT
-at 0x000001ea : */ 0x0e000001,((unsigned long)&NCR53c7xx_msg_abort),
+at 0x000001f4 : */ 0x0e000001,0x00000000,
+/*
+ WAIT DISCONNECT
+
+at 0x000001f6 : */ 0x48000000,0x00000000,
/*
INT int_norm_aborted
-at 0x000001ec : */ 0x98080000,0x02040000,
+at 0x000001f8 : */ 0x98080000,0x02040000,
/*
;
@@ -1711,77 +1742,101 @@ at 0x000001ec : */ 0x98080000,0x02040000,
dsa_to_scratch:
MOVE DSA0 TO SFBR
-at 0x000001ee : */ 0x72100000,0x00000000,
+at 0x000001fa : */ 0x72100000,0x00000000,
/*
MOVE SFBR TO SCRATCH0
-at 0x000001f0 : */ 0x6a340000,0x00000000,
+at 0x000001fc : */ 0x6a340000,0x00000000,
/*
MOVE DSA1 TO SFBR
-at 0x000001f2 : */ 0x72110000,0x00000000,
+at 0x000001fe : */ 0x72110000,0x00000000,
/*
MOVE SFBR TO SCRATCH1
-at 0x000001f4 : */ 0x6a350000,0x00000000,
+at 0x00000200 : */ 0x6a350000,0x00000000,
/*
MOVE DSA2 TO SFBR
-at 0x000001f6 : */ 0x72120000,0x00000000,
+at 0x00000202 : */ 0x72120000,0x00000000,
/*
MOVE SFBR TO SCRATCH2
-at 0x000001f8 : */ 0x6a360000,0x00000000,
+at 0x00000204 : */ 0x6a360000,0x00000000,
/*
MOVE DSA3 TO SFBR
-at 0x000001fa : */ 0x72130000,0x00000000,
+at 0x00000206 : */ 0x72130000,0x00000000,
/*
MOVE SFBR TO SCRATCH3
-at 0x000001fc : */ 0x6a370000,0x00000000,
+at 0x00000208 : */ 0x6a370000,0x00000000,
/*
RETURN
-at 0x000001fe : */ 0x90080000,0x00000000,
+at 0x0000020a : */ 0x90080000,0x00000000,
/*
scratch_to_dsa:
MOVE SCRATCH0 TO SFBR
-at 0x00000200 : */ 0x72340000,0x00000000,
+at 0x0000020c : */ 0x72340000,0x00000000,
/*
MOVE SFBR TO DSA0
-at 0x00000202 : */ 0x6a100000,0x00000000,
+at 0x0000020e : */ 0x6a100000,0x00000000,
/*
MOVE SCRATCH1 TO SFBR
-at 0x00000204 : */ 0x72350000,0x00000000,
+at 0x00000210 : */ 0x72350000,0x00000000,
/*
MOVE SFBR TO DSA1
-at 0x00000206 : */ 0x6a110000,0x00000000,
+at 0x00000212 : */ 0x6a110000,0x00000000,
/*
MOVE SCRATCH2 TO SFBR
-at 0x00000208 : */ 0x72360000,0x00000000,
+at 0x00000214 : */ 0x72360000,0x00000000,
/*
MOVE SFBR TO DSA2
-at 0x0000020a : */ 0x6a120000,0x00000000,
+at 0x00000216 : */ 0x6a120000,0x00000000,
/*
MOVE SCRATCH3 TO SFBR
-at 0x0000020c : */ 0x72370000,0x00000000,
+at 0x00000218 : */ 0x72370000,0x00000000,
/*
MOVE SFBR TO DSA3
-at 0x0000020e : */ 0x6a130000,0x00000000,
+at 0x0000021a : */ 0x6a130000,0x00000000,
/*
RETURN
-at 0x00000210 : */ 0x90080000,0x00000000,
+at 0x0000021c : */ 0x90080000,0x00000000,
+};
+
+#define A_NCR53c7xx_msg_abort 0x00000000
+unsigned long A_NCR53c7xx_msg_abort_used[] = {
+ 0x000001f5,
+};
+
+#define A_NCR53c7xx_msg_reject 0x00000000
+unsigned long A_NCR53c7xx_msg_reject_used[] = {
+ 0x00000133,
+};
+
+#define A_NCR53c7xx_sink 0x00000000
+unsigned long A_NCR53c7xx_sink_used[] = {
+ 0x000001e5,
+ 0x000001e9,
+ 0x000001ed,
+};
+
+#define A_NCR53c7xx_zero 0x00000000
+unsigned long A_NCR53c7xx_zero_used[] = {
+ 0x0000015d,
+ 0x000001e1,
+ 0x000001f1,
};
#define A_addr_scratch 0x00000000
@@ -1940,7 +1995,7 @@ unsigned long A_dsa_temp_target_used[] = {
#define A_int_debug_break 0x03000000
unsigned long A_int_debug_break_used[] = {
- 0x000001cb,
+ 0x000001d3,
};
#define A_int_debug_dsa_loaded 0x03030000
@@ -2013,7 +2068,7 @@ unsigned long A_int_msg_wdtr_used[] = {
#define A_int_norm_aborted 0x02040000
unsigned long A_int_norm_aborted_used[] = {
- 0x000001ed,
+ 0x000001f9,
};
#define A_int_norm_command_complete 0x02020000
@@ -2038,12 +2093,12 @@ unsigned long A_int_norm_select_complete_used[] = {
#define A_int_test_1 0x04000000
unsigned long A_int_test_1_used[] = {
- 0x000001b1,
+ 0x000001b9,
};
#define A_int_test_2 0x04010000
unsigned long A_int_test_2_used[] = {
- 0x000001c9,
+ 0x000001d1,
};
#define A_int_test_3 0x04020000
@@ -2087,26 +2142,26 @@ unsigned long A_reselected_tag_used[] = {
#define A_test_dest 0x00000000
unsigned long A_test_dest_used[] = {
- 0x000001af,
+ 0x000001b7,
};
#define A_test_src 0x00000000
unsigned long A_test_src_used[] = {
- 0x000001ae,
+ 0x000001b6,
};
#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_debug_break 0x00000748
#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_initiator_abort 0x00000770
#define Ent_msg_in 0x00000354
#define Ent_other_transfer 0x0000031c
#define Ent_reject_message 0x000004b8
@@ -2115,10 +2170,10 @@ unsigned long A_test_src_used[] = {
#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
+#define Ent_target_abort 0x00000750
+#define Ent_test_1 0x000006d4
+#define Ent_test_2 0x000006e8
+#define Ent_test_2_msgout 0x00000700
unsigned long LABELPATCHES[] = {
0x00000002,
0x0000000b,
@@ -2178,18 +2233,21 @@ unsigned long LABELPATCHES[] = {
0x0000017a,
0x00000191,
0x000001a2,
- 0x000001a4,
+ 0x000001a6,
0x000001a8,
0x000001ac,
- 0x000001b5,
- 0x000001b7,
- 0x000001d3,
- 0x000001d7,
+ 0x000001b0,
+ 0x000001b2,
+ 0x000001b4,
+ 0x000001bd,
+ 0x000001bf,
0x000001db,
0x000001df,
0x000001e3,
0x000001e7,
+ 0x000001eb,
+ 0x000001ef,
};
-unsigned long INSTRUCTIONS = 0x000000fe;
-unsigned long PATCHES = 0x00000045;
+unsigned long INSTRUCTIONS = 0x00000104;
+unsigned long PATCHES = 0x00000048;
diff --git a/drivers/scsi/53c8xx_u.h b/drivers/scsi/53c8xx_u.h
index a6645b3da..9f0bf58b0 100644
--- a/drivers/scsi/53c8xx_u.h
+++ b/drivers/scsi/53c8xx_u.h
@@ -1,3 +1,7 @@
+#undef A_NCR53c7xx_msg_abort
+#undef A_NCR53c7xx_msg_reject
+#undef A_NCR53c7xx_sink
+#undef A_NCR53c7xx_zero
#undef A_addr_scratch
#undef A_addr_sfbr
#undef A_addr_temp
diff --git a/drivers/scsi/ChangeLog b/drivers/scsi/ChangeLog
index 2d5e8e9d3..c3bf5af6d 100644
--- a/drivers/scsi/ChangeLog
+++ b/drivers/scsi/ChangeLog
@@ -1,3 +1,826 @@
+Wed Apr 12 15:25:52 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.2.5 released.
+
+ * buslogic.c: Update to version 1.15 (From Leonard N. Zubkoff).
+ Fixed interrupt routine to avoid races when handling multiple
+ complete commands per interrupt. Seems to come up with faster
+ cards.
+
+ * eata_dma.c: Modularize. Update to 2.3.5r.
+
+ * scsi.c: If we get a FMK, EOM, or ILI when attempting to scan
+ the bus, assume that it was just noise on the bus, and ignore
+ the device.
+
+ * scsi.h: Update and add a bunch of missing commands which we
+ were never using.
+
+ * sd.c: Use restore_flags in do_sd_request - this may result in
+ latency conditions, but it gets rid of races and crashes.
+ Do not save flags again when searching for a second command to
+ queue.
+
+ * st.c: Use bytes, not STP->buffer->buffer_size when reading
+ from tape.
+
+
+Tue Apr 4 09:42:08 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.2.4 released.
+
+ * st.c: Fix typo - restoring wrong flags.
+
+Wed Mar 29 06:55:12 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.2.3 released.
+
+ * st.c: Perform some waiting operations with interrupts off.
+ Is this correct???
+
+Wed Mar 22 10:34:26 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.2.2 released.
+
+ * aha152x.c: Modularize. Add support for PCMCIA.
+
+ * eata.c: Update to version 2.0. Fixed bug preventing media
+ detection. If scsi_register_host returns NULL, fail gracefully.
+
+ * scsi.c: Detect as NEC (for photo-cd purposes) for the 84
+ and 25 models as "NEC_OLDCDR".
+
+ * scsi.h: Add define for NEC_OLDCDR
+
+ * sr.c: Add handling for NEC_OLDCDR. Treat as unknown.
+
+ * u14-34f.c: Update to version 2.0. Fixed same bug as in
+ eata.c.
+
+
+Mon Mar 6 11:11:20 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.2.0 released. Yeah!!!
+
+ * Minor spelling/punctuation changes throughout. Nothing
+ substantive.
+
+Mon Feb 20 21:33:03 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.95 released.
+
+ * qlogic.c: Update to version 0.41.
+
+ * seagate.c: Change some message to be more descriptive about what
+ we detected.
+
+ * sr.c: spelling/whitespace changes.
+
+Mon Feb 20 21:33:03 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.94 released.
+
+Mon Feb 20 08:57:17 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.93 released.
+
+ * hosts.h: Change io_port to long int from short.
+
+ * 53c7,8xx.c: crash on AEN fixed, SCSI reset is no longer a NOP,
+ NULL pointer panic on odd UDCs fixed, two bugs in diagnostic output
+ fixed, should initialize correctly if left running, now loadable,
+ new memory allocation, extraneous diagnostic output suppressed,
+ splx() replaced with save/restore flags. [ Drew ]
+
+ * hosts.c, hosts.h, scsi_ioctl.c, sd.c, sd_ioctl.c, sg.c, sr.c,
+ sr_ioctl.c: Add special junk at end that Emacs will use for
+ formatting the file.
+
+ * qlogic.c: Update to v0.40a. Improve parity handling.
+
+ * scsi.c: Add Hitachi DK312C to blacklist. Change "};" to "}" in
+ many places. Use scsi_init_malloc to get command block - may
+ need this to be dma compatible for some host adapters.
+ Restore interrupts after unregistering a host.
+
+ * sd.c: Use sti instead of restore flags - causes latency problems.
+
+ * seagate.c: Use controller_type to determine string used when
+ registering irq.
+
+ * sr.c: More photo-cd hacks to make sure we get the xa stuff right.
+ * sr.h, sr.c: Change is_xa to xa_flags field.
+
+ * st.c: Disable retries for write operations.
+
+Wed Feb 15 10:52:56 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.92 released.
+
+ * eata.c: Update to 1.17.
+
+ * eata_dma.c: Add more support for /proc/scsi, add HBA_interpret flag.
+
+ * hosts.c: If we remove last host registered, reuse host number.
+ When freeing memory from host being deregistered, free extra_bytes
+ too.
+
+ * scsi.c (scan_scsis): memset(SDpnt, 0) and set SCmd.device to SDpnt.
+ Change memory allocation to work around bugs in __get_dma_pages.
+ Do not free host if usage count is not zero (for modules).
+
+ * sr_ioctl.c: Increase IOCTL_TIMEOUT to 3000.
+
+ * st.c: Allow for ST_EXTRA_DEVS in st data structures.
+
+ * u14-34f.c: Update to 1.17.
+
+Thu Feb 9 10:11:16 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.91 released.
+
+ * eata.c: Update to 1.16. Use wish_block instead of host->block.
+
+ * hosts.c: Initialize wish_block to 0.
+
+ * hosts.h: Add wish_block.
+
+ * scsi.c: Use wish_block as indicator that the host should be added
+ to block list.
+
+ * sg.c: Add SG_EXTRA_DEVS to number of slots.
+
+ * u14-34f.c: Use wish_block.
+
+Tue Feb 7 11:46:04 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.90 released.
+
+ * eata.c: Change naming from eata_* to eata2x_*. Now at vers 1.15.
+ Update interrupt handler to take pt_regs as arg. Allow blocking
+ even if loaded as module. Initialize target_time_out array.
+ Do not put sti(); in timing loop.
+
+ * hosts.c: Do not reuse host numbers.
+ Use scsi_make_blocked_list to generate blocking list.
+
+ * script_asm.pl: Beats me. Don't know perl. Something to do with
+ phase index.
+
+ * scsi.c (scsi_make_blocked_list): New function - code copied from
+ hosts.c.
+
+ * scsi.c: Update code to disable photo CD for Toshiba cdroms.
+ Use just manufacturer name, not model number.
+
+ * sr.c: Fix setting density for Toshiba drives.
+
+ * u14-34f.c: Clear target_time_out array during reset.
+
+Wed Feb 1 09:20:45 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.89 released.
+
+ * Makefile, u14-34f.c: Modularize.
+
+ * Makefile, eata.c: Modularize. Now version 1.14
+
+ * NCR5380.c: Update interrupt handler with new arglist. Minor
+ cleanups.
+
+ * eata_dma.c: Modularize. Add hooks for /proc/scsi.
+ New version 2.3.0a.
+
+ * hosts.c: Initialize ->dma_channel and ->io_port when registering
+ a new host.
+
+ * qlogic.c: Modularize and add PCMCIA support.
+
+ * scsi.c: Add Hitachi to blacklist.
+
+ * scsi.c: Change default to no lun scan (too many problem devices).
+
+ * scsi.h: Define QUEUE_FULL condition.
+
+ * sd.c: Do not check for non-existent partition until after
+ new media check.
+
+ * sg.c: Undo previous change which was wrong.
+
+ * sr_ioctl.c: Increase IOCTL_TIMEOUT to 2000.
+
+ * st.c: Patches from Kai - improve filemark handling.
+
+Tue Jan 31 17:32:12 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.88 released.
+
+ * Throughout - spelling/grammar fixups.
+
+ * scsi.c: Make sure that all buffers are 16 byte aligned - some
+ drivers (buslogic) need this.
+
+ * scsi.c (scan_scsis): Remove message printed.
+
+ * scsi.c (scsi_init): Move message here.
+
+Mon Jan 30 06:40:25 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.87 released.
+
+ * sr.c: Photo-cd related changes. (Gerd Knorr??).
+
+ * st.c: Changes from Kai related to EOM detection.
+
+Mon Jan 23 23:53:10 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.86 released.
+
+ * 53c7,8xx.h: Change SG size to 127.
+
+ * eata_dma: Update to version 0i.
+
+ * scsi.c: Test for Toshiba XM-3401TA and exclude from detection
+ as toshiba drive - photo cd does not work with this drive.
+
+ * sr.c: Update photocd code.
+
+Mon Jan 23 23:53:10 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.85 released.
+
+ * st.c, st_ioctl.c, sg.c, sd_ioctl.c, scsi_ioctl.c, hosts.c:
+ include linux/mm.h
+
+ * qlogic.c, buslogic.c, aha1542.c: Include linux/module.h.
+
+Sun Jan 22 22:08:46 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.84 released.
+
+ * Makefile: Support for loadable QLOGIC boards.
+
+ * aha152x.c: Update to version 1.8 from Juergen.
+
+ * eata_dma.c: Update from Michael Neuffer
+
+ * in2000.c: Fix biosparam to support large disks.
+
+ * qlogic.c: Minor changes (change sti -> restore_flags).
+
+Wed Jan 18 23:33:09 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.83 released.
+
+ * aha1542.c(aha1542_intr_handle): Use arguments handed down to find
+ which irq.
+
+ * buslogic.c: Likewise.
+
+ * eata_dma.c: Use min of 2 cmd_per_lun for OCS_enabled boards.
+
+ * scsi.c: Make RECOVERED_ERROR a SUGGEST_IS_OK.
+
+ * sd.c: Fail if we are opening a non-existent partition.
+
+ * sr.c: Bump SR_TIMEOUT to 15000.
+ Do not probe for media size at boot time(hard on changers).
+ Flag device as needing sector size instead.
+
+ * sr_ioctl.c: Remove CDROMMULTISESSION_SYS ioctl.
+
+ * ultrastor.c: Fix bug in call to ultrastor_interrupt (wrong #args).
+
+Mon Jan 16 07:18:23 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.82 released.
+
+ Throughout.
+ - Change all interrupt handlers to accept new calling convention.
+ In particular, we now receive the irq number as one of the arguments.
+
+ * More minor spelling corrections in some of the new files.
+
+ * aha1542.c, buslogic.c: Clean up interrupt handler a little now
+ that we receive the irq as an arg.
+
+ * aha274x.c: s/snarf_region/request_region/
+
+ * eata.c: Update to version 1.12. Fix some comments and display a
+ message if we cannot reserve the port addresses.
+
+ * u14-34f.c: Update to version 1.13. Fix some comments and display a
+ message if we cannot reserve the port addresses.
+
+ * eata_dma.c: Define get_board_data function (send INQUIRY command).
+ Use to improve detection of variants of different DPT boards. Change
+ version subnumber to "0g".
+
+ * fdomain.c: Update to version 5.26. Improve detection of some boards
+ repackaged by IBM.
+
+ * scsi.c (scsi_register_host): Change "name" to const char *.
+
+ * sr.c: Fix problem in set mode command for Toshiba drives.
+
+ * sr.c: Fix typo from patch 81.
+
+Fri Jan 13 12:54:46 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.81 released. Codefreeze for 1.2 release announced.
+
+ Big changes here.
+
+ * eata_dma.*: New files from Michael Neuffer.
+ (neuffer@goofy.zdv.uni-mainz.de). Should support
+ all eata/dpt cards.
+
+ * hosts.c, Makefile: Add eata_dma.
+
+ * README.st: Document MTEOM.
+
+ Patches from me (ERY) to finish support for low-level loadable scsi.
+ It now works, and is actually useful.
+
+ * Throughout - add new argument to scsi_init_malloc that takes an
+ additional parameter. This is used as a priority to kmalloc,
+ and you can specify the GFP_DMA flag if you need DMA-able memory.
+
+ * Makefile: For source files that are loadable, always add name
+ to SCSI_SRCS. Fill in modules: target.
+
+ * hosts.c: Change next_host to next_scsi_host, and make global.
+ Print hosts after we have identified all of them. Use info()
+ function if present, otherwise use name field.
+
+ * hosts.h: Change attach function to return int, not void.
+ Define number of device slots to allow for loadable devices.
+ Define tags to tell scsi module code what type of module we
+ are loading.
+
+ * scsi.c: Fix scan_scsis so that it can be run by a user process.
+ Do not use waiting loops - use up and down mechanism as long
+ as current != task[0].
+
+ * scsi.c(scan_scsis): Do not use stack variables for I/O - this
+ could be > 16Mb if we are loading a module at runtime (i.e. use
+ scsi_init_malloc to get some memory we know will be safe).
+
+ * scsi.c: Change dma freelist to be a set of pages. This allows
+ us to dynamically adjust the size of the list by adding more pages
+ to the pagelist. Fix scsi_malloc and scsi_free accordingly.
+
+ * scsi_module.c: Fix include.
+
+ * sd.c: Declare detach function. Increment/decrement module usage
+ count as required. Fix init functions to allow loaded devices.
+ Revalidate all new disks so we get the partition tables. Define
+ detach function.
+
+ * sr.c: Likewise.
+
+ * sg.c: Declare detach function. Allow attachment of devices on
+ loaded drivers.
+
+ * st.c: Declare detach function. Increment/decrement module usage
+ count as required.
+
+Tue Jan 10 10:09:58 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.79 released.
+
+ Patch from some undetermined individual who needs to get a life :-).
+
+ * sr.c: Attacked by spelling bee...
+
+ Patches from Gerd Knorr:
+
+ * sr.c: make printk messages for photoCD a little more informative.
+
+ * sr_ioctl.c: Fix CDROMMULTISESSION_SYS ioctl.
+
+Mon Jan 9 10:01:37 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.78 released.
+
+ * Makefile: Add empty modules: target.
+
+ * Wheee. Now change register_iomem to request_region.
+
+ * in2000.c: Bugfix - apparently this is the fix that we have
+ all been waiting for. It fixes a problem whereby the driver
+ is not stable under heavy load. Race condition and all that.
+ Patch from Peter Lu.
+
+Wed Jan 4 21:17:40 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.77 released.
+
+ * 53c7,8xx.c: Fix from Linus - emulate splx.
+
+ Throughout:
+
+ Change "snarf_region" with "register_iomem".
+
+ * scsi_module.c: New file. Contains support for low-level loadable
+ scsi drivers. [ERY].
+
+ * sd.c: More s/int/long/ changes.
+
+ * seagate.c: Explicitly include linux/config.h
+
+ * sg.c: Increment/decrement module usage count on open/close.
+
+ * sg.c: Be a bit more careful about the user not supplying enough
+ information for a valid command. Pass correct size down to
+ scsi_do_cmd.
+
+ * sr.c: More changes for Photo-CD. This apparently breaks NEC drives.
+
+ * sr_ioctl.c: Support CDROMMULTISESSION ioctl.
+
+
+Sun Jan 1 19:55:21 1995 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.76 released.
+
+ * constants.c: Add type cast in switch statement.
+
+ * scsi.c (scsi_free): Change datatype of "offset" to long.
+ (scsi_malloc): Change a few more variables to long. Who
+ did this and why was it important? 64 bit machines?
+
+
+ Lots of changes to use save_state/restore_state instead of cli/sti.
+ Files changed include:
+
+ * aha1542.c:
+ * aha1740.c:
+ * buslogic.c:
+ * in2000.c:
+ * scsi.c:
+ * scsi_debug.c:
+ * sd.c:
+ * sr.c:
+ * st.c:
+
+Wed Dec 28 16:38:29 1994 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.75 released.
+
+ * buslogic.c: Spelling fix.
+
+ * scsi.c: Add HP C1790A and C2500A scanjet to blacklist.
+
+ * scsi.c: Spelling fixup.
+
+ * sd.c: Add support for sd_hardsizes (hard sector sizes).
+
+ * ultrastor.c: Use save_flags/restore_flags instead of cli/sti.
+
+Fri Dec 23 13:36:25 1994 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.74 released.
+
+ * README.st: Update from Kai Makisara.
+
+ * eata.c: New version from Dario - version 1.11.
+ use scsicam bios_param routine. Add support for 2011
+ and 2021 boards.
+
+ * hosts.c: Add support for blocking. Linked list automatically
+ generated when shpnt->block is set.
+
+ * scsi.c: Add sankyo & HP scanjet to blacklist. Add support for
+ kicking things loose when we deadlock.
+
+ * scsi.c: Recognize scanners and processors in scan_scsis.
+
+ * scsi_ioctl.h: Increase timeout to 9 seconds.
+
+ * st.c: New version from Kai - add better support for backspace.
+
+ * u14-34f.c: New version from Dario. Supports blocking.
+
+Wed Dec 14 14:46:30 1994 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.73 released.
+
+ * buslogic.c: Update from Dave Gentzel. Version 1.14.
+ Add module related stuff. More fault tolerant if out of
+ DMA memory.
+
+ * fdomain.c: New version from Rik Faith - version 5.22. Add support
+ for ISA-200S SCSI adapter.
+
+ * hosts.c: Spelling.
+
+ * qlogic.c: Update to version 0.38a. Add more support for PCMCIA.
+
+ * scsi.c: Mask device type with 0x1f during scan_scsis.
+ Add support for deadlocking, err, make that getting out of
+ deadlock situations that are created when we allow the user
+ to limit requests to one host adapter at a time.
+
+ * scsi.c: Bugfix - pass pid, not SCpnt as second arg to
+ scsi_times_out.
+
+ * scsi.c: Restore interrupt state to previous value instead of using
+ cli/sti pairs.
+
+ * scsi.c: Add a bunch of module stuff (all commented out for now).
+
+ * scsi.c: Clean up scsi_dump_status.
+
+Tue Dec 6 12:34:20 1994 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.72 released.
+
+ * sg.c: Bugfix - always use sg_free, since we might have big buff.
+
+Fri Dec 2 11:24:53 1994 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.71 released.
+
+ * sg.c: Clear buff field when not in use. Only call scsi_free if
+ non-null.
+
+ * scsi.h: Call wake_up(&wait_for_request) when done with a
+ command.
+
+ * scsi.c (scsi_times_out): Pass pid down so that we can protect
+ against race conditions.
+
+ * scsi.c (scsi_abort): Zero timeout field if we get the
+ NOT_RUNNING message back from low-level driver.
+
+
+ * scsi.c (scsi_done): Restore cmd_len, use_sg here.
+
+ * scsi.c (request_sense): Not here.
+
+ * hosts.h: Add new forbidden_addr, forbidden_size fields. Who
+ added these and why????
+
+ * hosts.c (scsi_mem_init): Mark pages as reserved if they fall in
+ the forbidden regions. I am not sure - I think this is so that
+ we can deal with boards that do incomplete decoding of their
+ address lines for the bios chips, but I am not entirely sure.
+
+ * buslogic.c: Set forbidden_addr stuff if using a buggy board.
+
+ * aha1740.c: Test for NULL pointer in SCtmp. This should not
+ occur, but a nice message is better than a kernel segfault.
+
+ * 53c7,8xx.c: Add new PCI chip ID for 815.
+
+Fri Dec 2 11:24:53 1994 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.70 released.
+
+ * ChangeLog, st.c: Spelling.
+
+Tue Nov 29 18:48:42 1994 Eric Youngdale (eric@andante)
+
+ * Linux 1.1.69 released.
+
+ * u14-34f.h: Non-functional change. [Dario].
+
+ * u14-34f.c: Use block field in Scsi_Host to prevent commands from
+ being queued to more than one host at the same time (used when
+ motherboard does not deal with multiple bus-masters very well).
+ Only when SINGLE_HOST_OPERATIONS is defined.
+ Use new cmd_per_lun field. [Dario]
+
+ * eata.c: Likewise.
+
+ * st.c: More changes from Kai. Add ready flag to indicate drive
+ status.
+
+ * README.st: Document this.
+
+ * sr.c: Bugfix (do not subtract CD_BLOCK_OFFSET) for photo-cd
+ code.
+
+ * sg.c: Bugfix - fix problem where opcode is not correctly set up.
+
+ * seagate.[c,h]: Use #defines to set driver name.
+
+ * scsi_ioctl.c: Zero buffer before executing command.
+
+ * scsi.c: Use new cmd_per_lun field in Scsi_Hosts as appropriate.
+ Add Sony CDU55S to blacklist.
+
+ * hosts.h: Add new cmd_per_lun field to Scsi_Hosts.
+
+ * hosts.c: Initialize cmd_per_lun in Scsi_Hosts from template.
+
+ * buslogic.c: Use cmd_per_lun field - initialize to different
+ values depending upon bus type (i.e. use 1 if ISA, so we do not
+ hog memory). Use other patches which got lost from 1.1.68.
+
+ * aha1542.c: Spelling.
+
+Tue Nov 29 15:43:50 1994 Eric Youngdale (eric@andante.aib.com)
+
+ * Linux 1.1.68 released.
+
+ Add support for 12 byte vendor specific commands in scsi-generics,
+ more (i.e. the last mandatory) low-level changes to support
+ loadable modules, plus a few other changes people have requested
+ lately. Changes by me (ERY) unless otherwise noted. Spelling
+ changes appear from some unknown corner of the universe.
+
+ * Throughout: Change COMMAND_SIZE() to use SCpnt->cmd_len.
+
+ * Throughout: Change info() low level function to take a Scsi_Host
+ pointer. This way the info function can return specific
+ information about the host in question, if desired.
+
+ * All low-level drivers: Add NULL in initializer for the
+ usage_count field added to Scsi_Host_Template.
+
+ * aha152x.[c,h]: Remove redundant info() function.
+
+ * aha1542.[c,h]: Likewise.
+
+ * aha1740.[c,h]: Likewise.
+
+ * aha274x.[c,h]: Likewise.
+
+ * eata.[c,h]: Likewise.
+
+ * pas16.[c,h]: Likewise.
+
+ * scsi_debug.[c,h]: Likewise.
+
+ * t128.[c,h]: Likewise.
+
+ * u14-34f.[c,h]: Likewise.
+
+ * ultrastor.[c,h]: Likewise.
+
+ * wd7000.[c,h]: Likewise.
+
+ * aha1542.c: Add support for command line options with lilo to set
+ DMA parameters, I/O port. From Matt Aarnio.
+
+ * buslogic.[c,h]: New version (1.13) from Dave Gentzel.
+
+ * hosts.h: Add new field to Scsi_Hosts "block" to allow blocking
+ all I/O to certain other cards. Helps prevent problems with some
+ ISA motherboards.
+
+ * hosts.h: Add usage_count to Scsi_Host_Template.
+
+ * hosts.h: Add n_io_port to Scsi_Host (used when releasing module).
+
+ * hosts.c: Initialize block field.
+
+ * in2000.c: Remove "static" declarations from exported functions.
+
+ * in2000.h: Likewise.
+
+ * scsi.c: Correctly set cmd_len field as required. Save and
+ change setting when doing a request_sense, restore when done.
+ Move abort timeout message. Fix panic in request_queueable to
+ print correct function name.
+
+ * scsi.c: When incrementing usage count, walk block linked list
+ for host, and or in SCSI_HOST_BLOCK bit. When decrementing usage
+ count to 0, clear this bit to allow usage to continue, wake up
+ processes waiting.
+
+
+ * scsi_ioctl.c: If we have an info() function, call it, otherwise
+ if we have a "name" field, use it, else do nothing.
+
+ * sd.c, sr.c: Clear cmd_len field prior to each command we
+ generate.
+
+ * sd.h: Add "has_part_table" bit to rscsi_disks.
+
+ * sg.[c,h]: Add support for vendor specific 12 byte commands (i.e.
+ override command length in COMMAND_SIZE).
+
+ * sr.c: Bugfix from Gerd in photocd code.
+
+ * sr.c: Bugfix in get_sectorsize - always use scsi_malloc buffer -
+ we cannot guarantee that the stack is < 16Mb.
+
+Tue Nov 22 15:40:46 1994 Eric Youngdale (eric@andante.aib.com)
+
+ * Linux 1.1.67 released.
+
+ * sr.c: Change spelling of manufactor to manufacturer.
+
+ * scsi.h: Likewise.
+
+ * scsi.c: Likewise.
+
+ * qlogic.c: Spelling corrections.
+
+ * in2000.h: Spelling corrections.
+
+ * in2000.c: Update from Bill Earnest, change from
+ jshiffle@netcom.com. Support new bios versions.
+
+ * README.qlogic: Spelling correction.
+
+Tue Nov 22 15:40:46 1994 Eric Youngdale (eric@andante.aib.com)
+
+ * Linux 1.1.66 released.
+
+ * u14-34f.c: Spelling corrections.
+
+ * sr.[h,c]: Add support for multi-session CDs from Gerd Knorr.
+
+ * scsi.h: Add manufactor field for keeping track of device
+ manufacturer.
+
+ * scsi.c: More spelling corrections.
+
+ * qlogic.h, qlogic.c, README.qlogic: New driver from Tom Zerucha.
+
+ * in2000.c, in2000.h: New driver from Brad McLean/Bill Earnest.
+
+ * fdomain.c: Spelling correction.
+
+ * eata.c: Spelling correction.
+
+Fri Nov 18 15:22:44 1994 Eric Youngdale (eric@andante.aib.com)
+
+ * Linux 1.1.65 released.
+
+ * eata.h: Update version string to 1.08.00.
+
+ * eata.c: Set sg_tablesize correctly for DPT PM2012 boards.
+
+ * aha274x.seq: Spell checking.
+
+ * README.st: Likewise.
+
+ * README.aha274x: Likewise.
+
+ * ChangeLog: Likewise.
+
+Tue Nov 15 15:35:08 1994 Eric Youngdale (eric@andante.aib.com)
+
+ * Linux 1.1.64 released.
+
+ * u14-34f.h: Update version number to 1.10.01.
+
+ * u14-34f.c: Use Scsi_Host can_queue variable instead of one from template.
+
+ * eata.[c,h]: New driver for DPT boards from Dario Ballabio.
+
+ * buslogic.c: Use can_queue field.
+
+Wed Nov 30 12:09:09 1994 Eric Youngdale (eric@andante.aib.com)
+
+ * Linux 1.1.63 released.
+
+ * sd.c: Give I/O error if we attempt 512 byte I/O to a disk with
+ 1024 byte sectors.
+
+ * scsicam.c: Make sure we do read from whole disk (mask off
+ partition).
+
+ * scsi.c: Use can_queue in Scsi_Host structure.
+ Fix panic message about invalid host.
+
+ * hosts.c: Initialize can_queue from template.
+
+ * hosts.h: Add can_queue to Scsi_Host structure.
+
+ * aha1740.c: Print out warning about NULL ecbptr.
+
+Fri Nov 4 12:40:30 1994 Eric Youngdale (eric@andante.aib.com)
+
+ * Linux 1.1.62 released.
+
+ * fdomain.c: Update to version 5.20. (From Rik Faith). Support
+ BIOS version 3.5.
+
+ * st.h: Add ST_EOD symbol.
+
+ * st.c: Patches from Kai Makisara - support additional densities,
+ add support for MTFSS, MTBSS, MTWSM commands.
+
+ * README.st: Update to document new commands.
+
+ * scsi.c: Add Mediavision CDR-H93MV to blacklist.
+
+Sat Oct 29 20:57:36 1994 Eric Youngdale (eric@andante.aib.com)
+
+ * Linux 1.1.60 released.
+
+ * u14-34f.[c,h]: New driver from Dario Ballabio.
+
+ * aic7770.c, aha274x_seq.h, aha274x.seq, aha274x.h, aha274x.c,
+ README.aha274x: New files, new driver from John Aycock.
+
+
Tue Oct 11 08:47:39 1994 Eric Youngdale (eric@andante)
* Linux 1.1.54 released.
@@ -6,6 +829,13 @@ Tue Oct 11 08:47:39 1994 Eric Youngdale (eric@andante)
* buslogic.c: Set BUSLOGIC_CMDLUN back to 1 [Eric].
+ * ultrastor.c: Fix asm directives for new GCC.
+
+ * sr.c, sd.c: Use new end_scsi_request function.
+
+ * scsi.h(end_scsi_request): Return pointer to block if still
+ active, else return NULL if inactive. Fixes race condition.
+
Sun Oct 9 20:23:14 1994 Eric Youngdale (eric@andante)
* Linux 1.1.53 released.
@@ -141,6 +971,140 @@ Thu Aug 4 08:47:27 1994 Eric Youngdale (eric@andante)
* st.c: Print correct number for device.
+Tue Aug 2 11:29:14 1994 Eric Youngdale (eric@esp22)
+
+ * Linux 1.1.38 released.
+
+ Lots of changes in 1.1.38. All from Drew unless otherwise noted.
+
+ * 53c7,8xx.c: New file from Drew. PCI driver.
+
+ * 53c7,8xx.h: Likewise.
+
+ * 53c7,8xx.scr: Likewise.
+
+ * 53c8xx_d.h, 53c8xx_u.h, script_asm.pl: Likewise.
+
+ * scsicam.c: New file from Drew. Read block 0 on the disk and
+ read the partition table. Attempt to deduce the geometry from
+ the partition table if possible. Only used by 53c[7,8]xx right
+ now, but could be used by any device for which we have no way
+ of identifying the geometry.
+
+ * sd.c: Use device letters instead of sd%d in a lot of messages.
+
+ * seagate.c: Fix bug that resulted in lockups with some devices.
+
+ * sr.c (sr_open): Return -EROFS, not -EACCES if we attempt to open
+ device for write.
+
+ * hosts.c, Makefile: Update for new driver.
+
+ * NCR5380.c, NCR5380.h, g_NCR5380.h: Update from Drew to support
+ 53C400 chip.
+
+ * constants.c: Define CONST_CMND and CONST_MSG. Other minor
+ cleanups along the way. Improve handling of CONST_MSG.
+
+ * fdomain.c, fdomain.h: New version from Rik Faith. Update to
+ 5.18. Should now support TMC-3260 PCI card with 18C30 chip.
+
+ * pas16.c: Update with new irq initialization.
+
+ * t128.c: Update with minor cleanups.
+
+ * scsi.c (scsi_pid): New variable - gives each command a unique
+ id. Add Quantum LPS5235S to blacklist. Change in_scan to
+ in_scan_scsis and make global.
+
+ * scsi.h: Add some defines for extended message handling,
+ INITIATE/RELEASE_RECOVERY. Add a few new fields to support sync
+ transfers.
+
+ * scsi_ioctl.h: Add ioctl to request synchronous transfers.
+
+
+Tue Jul 26 21:36:58 1994 Eric Youngdale (eric@esp22)
+
+ * Linux 1.1.37 released.
+
+ * aha1542.c: Always call aha1542_mbenable, use new udelay
+ mechanism so we do not wait a long time if the board does not
+ implement this command.
+
+ * g_NCR5380.c: Remove #include <linux/config.h> and #if
+ defined(CONFIG_SCSI_*).
+
+ * seagate.c: Likewise.
+
+ Next round of changes to support loadable modules. Getting closer
+ now, still not possible to do anything remotely usable.
+
+ hosts.c: Create a linked list of detected high level devices.
+ (scsi_register_device): New function to insert into this list.
+ (scsi_init): Call scsi_register_device for each of the known high
+ level drivers.
+
+ hosts.h: Add prototype for linked list header. Add structure
+ definition for device template structure which defines the linked
+ list.
+
+ scsi.c: (scan_scsis): Use linked list instead of knowledge about
+ existing high level device drivers.
+ (scsi_dev_init): Use init functions for drivers on linked list
+ instead of explicit list to initialize and attach devices to high
+ level drivers.
+
+ scsi.h: Add new field "attached" to scsi_device - count of number
+ of high level devices attached.
+
+ sd.c, sr.c, sg.c, st.c: Adjust init/attach functions to use new
+ scheme.
+
+Sat Jul 23 13:03:17 1994 Eric Youngdale (eric@esp22)
+
+ * Linux 1.1.35 released.
+
+ * ultrastor.c: Change constraint on asm() operand so that it works
+ with gcc 2.6.0.
+
+Thu Jul 21 10:37:39 1994 Eric Youngdale (eric@esp22)
+
+ * Linux 1.1.33 released.
+
+ * sr.c(sr_open): Do not allow opens with write access.
+
+Mon Jul 18 09:51:22 1994 1994 Eric Youngdale (eric@esp22)
+
+ * Linux 1.1.31 released.
+
+ * sd.c: Increase SD_TIMEOUT from 300 to 600.
+
+ * sr.c: Remove stray task_struct* variable that was no longer
+ used.
+
+ * sr_ioctl.c: Fix typo in up() call.
+
+Sun Jul 17 16:25:29 1994 Eric Youngdale (eric@esp22)
+
+ * Linux 1.1.30 released.
+
+ * scsi.c (scan_scsis): Fix detection of some Toshiba CDROM drives
+ that report themselves as disk drives.
+
+ * (Throughout): Use request.sem instead of request.waiting.
+ Should fix swap problem with fdomain.
+
+Thu Jul 14 10:51:42 1994 Eric Youngdale (eric@esp22)
+
+ * Linux 1.1.29 released.
+
+ * scsi.c (scan_scsis): Add new devices to end of linked list, not
+ to the beginning.
+
+ * scsi.h (SCSI_SLEEP): Remove brain dead hack to try and save
+ the task state before sleeping.
+
Sat Jul 9 15:01:03 1994 Eric Youngdale (eric@esp22)
More changes to eventually support loadable modules. Mainly
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index d4500bee3..860ac40dc 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -2,14 +2,18 @@
# Makefile for kernel/blk_drv/scsi
#
# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DONT put your own dependencies here
-# unless its something special (ie not a .c file).
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
#
-#AHA152X = -DDEBUG -DAUTOCONF -DIRQ=11 -DSCSI_ID=7 -DRECONNECT=0 \
-# -DPORTBASE=0x340 -DSKIP_BIOSTEST -DDONT_SNARF
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+.s.o:
+ $(AS) -c -o $*.o $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
-AHA152X = -DDEBUG_AHA152X -DAUTOCONF
+AHA152X = -DDEBUG_AHA152X -DAUTOCONF -DSKIP_BIOSTEST -DIRQ=11
ifeq (${CFLAGS},)
CFLAGS = -D__KERNEL__=1 \
@@ -21,6 +25,7 @@ endif
SCSI_OBJS =
SCSI_SRCS =
+SCSI_MODULE_OBJS =
ifdef CONFIG_SCSI
@@ -47,9 +52,11 @@ SCSI_OBJS := $(SCSI_OBJS) sg.o
SCSI_SRCS := $(SCSI_SRCS) sg.c
endif
+SCSI_SRCS := $(SCSI_SRCS) qlogic.c
ifdef CONFIG_SCSI_QLOGIC
SCSI_OBJS := $(SCSI_OBJS) qlogic.o
-SCSI_SRCS := $(SCSI_SRCS) qlogic.c
+else
+SCSI_MODULE_OBJS := $(SCSI_MODULE_OBJS) qlogic.o
endif
ifdef CONFIG_SCSI_AHA152X
@@ -58,8 +65,10 @@ SCSI_SRCS := $(SCSI_SRCS) aha152x.c
endif
ifdef CONFIG_SCSI_AHA1542
-SCSI_OBJS := $(SCSI_OBJS) aha1542.o
SCSI_SRCS := $(SCSI_SRCS) aha1542.c
+SCSI_OBJS := $(SCSI_OBJS) aha1542.o
+else
+SCSI_MODULE_OBJS := $(SCSI_MODULE_OBJS) aha1542.o
endif
ifdef CONFIG_SCSI_AHA1740
@@ -77,14 +86,25 @@ SCSI_OBJS := $(SCSI_OBJS) buslogic.o
SCSI_SRCS := $(SCSI_SRCS) buslogic.c
endif
+SCSI_SRCS := $(SCSI_SRCS) eata_dma.c
+ifdef CONFIG_SCSI_EATA_DMA
+SCSI_OBJS := $(SCSI_OBJS) eata_dma.o
+else
+SCSI_MODULE_OBJS := $(SCSI_MODULE_OBJS) eata_dma.o
+endif
+
ifdef CONFIG_SCSI_U14_34F
SCSI_OBJS := $(SCSI_OBJS) u14-34f.o
SCSI_SRCS := $(SCSI_SRCS) u14-34f.c
+else
+SCSI_MODULE_OBJS := $(SCSI_MODULE_OBJS) u14-34f.o
endif
ifdef CONFIG_SCSI_DEBUG
-SCSI_OBJS := $(SCSI_OBJS) scsi_debug.o
SCSI_SRCS := $(SCSI_SRCS) scsi_debug.c
+SCSI_OBJS := $(SCSI_OBJS) scsi_debug.o
+else
+SCSI_MODULE_OBJS := $(SCSI_MODULE_OBJS) scsi_debug.o
endif
ifdef CONFIG_SCSI_FUTURE_DOMAIN
@@ -93,8 +113,10 @@ SCSI_SRCS := $(SCSI_SRCS) fdomain.c
endif
ifdef CONFIG_SCSI_IN2000
-SCSI_OBJS := $(SCSI_OBJS) in2000.o
SCSI_SRCS := $(SCSI_SRCS) in2000.c
+SCSI_OBJS := $(SCSI_OBJS) in2000.o
+else
+SCSI_MODULE_OBJS := $(SCSI_MODULE_OBJS) in2000.o
endif
ifdef CONFIG_SCSI_GENERIC_NCR5380
@@ -105,6 +127,8 @@ endif
ifdef CONFIG_SCSI_NCR53C7xx
SCSI_OBJS := $(SCSI_OBJS) 53c7,8xx.o
SCSI_SRCS := $(SCSI_SRCS) 53c7,8xx.c
+else
+SCSI_MODULE_OBJS := $(SCSI_MODULE_OBJS) 53c7,8xx.o
endif
ifdef CONFIG_SCSI_PAS16
@@ -140,6 +164,8 @@ endif
ifdef CONFIG_SCSI_EATA
SCSI_OBJS := $(SCSI_OBJS) eata.o
SCSI_SRCS := $(SCSI_SRCS) eata.c
+else
+SCSI_MODULE_OBJS := $(SCSI_MODULE_OBJS) eata.o
endif
@@ -166,13 +192,18 @@ seagate.o: seagate.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
+ $(CPP) -traditional -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
+modules: $(SCSI_MODULE_OBJS)
+ echo $(SCSI_MODULE_OBJS) > ../../modules/SCSI_MODULES
+ (cd ../../modules;for i in $(SCSI_MODULE_OBJS); do ln -sf ../drivers/scsi/$$i .; done)
+
dep:
$(CPP) -M $(AHA152X) $(SCSI_SRCS) > .depend
+ $(CPP) -M -DMODULE $(SCSI_MODULE_OBJS:.o=.c) >> .depend
else
@@ -185,7 +216,6 @@ dep:
endif
-
#
# include a dependency file if one exists
#
diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c
index f99f5875e..c2881457b 100644
--- a/drivers/scsi/NCR5380.c
+++ b/drivers/scsi/NCR5380.c
@@ -538,7 +538,7 @@ static int NCR5380_set_timer (struct Scsi_Host *instance) {
*prev = instance;
timer_table[NCR5380_TIMER].expires = expires_first->time_expires;
timer_active |= 1 << NCR5380_TIMER;
- sti;
+ sti();
return 0;
}
@@ -600,8 +600,8 @@ static void NCR5380_all_init (void) {
static int probe_irq;
-static void probe_intr (int sig) {
- probe_irq = sig;
+static void probe_intr (int irq, struct pt_regs * regs) {
+ probe_irq = irq;
};
static int NCR5380_probe_irq (struct Scsi_Host *instance, int possible) {
@@ -636,7 +636,8 @@ static int NCR5380_probe_irq (struct Scsi_Host *instance, int possible) {
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA |
ICR_ASSERT_SEL);
- while (probe_irq == IRQ_NONE && jiffies < timeout);
+ while (probe_irq == IRQ_NONE && jiffies < timeout)
+ barrier();
NCR5380_write(SELECT_ENABLE_REG, 0);
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
@@ -1030,7 +1031,7 @@ static void NCR5380_main (void) {
*
*/
-static void NCR5380_intr (int irq) {
+static void NCR5380_intr (int irq, struct pt_regs * regs) {
NCR5380_local_declare();
struct Scsi_Host *instance;
int done;
@@ -1912,7 +1913,10 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) {
instance->hostdata;
unsigned char msgout = NOP;
int sink = 0;
- int len, transfersize;
+ int len;
+#if defined(PSEUDO_DMA) || defined(REAL_DMA_POLL)
+ int transfersize;
+#endif
unsigned char *data;
unsigned char phase, tmp, extended_msg[10], old_phase=0xff;
Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
@@ -2014,7 +2018,7 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) {
} else
cmd->SCp.this_residual -= transfersize - len;
} else
-#endif /* defined(REAL_DMA) || defined(REAL_DMA_POLL) */
+#endif /* defined(PSEUDO_DMA) || defined(REAL_DMA_POLL) */
NCR5380_transfer_pio(instance, &phase,
(int *) &cmd->SCp.this_residual, (unsigned char **)
&cmd->SCp.ptr);
@@ -2144,8 +2148,8 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) {
*/
NCR5380_write(TARGET_COMMAND_REG, 0);
- while ((NCR5380_read(STATUS_REG) & SR_BSY) &&
- !hostdata->connected);
+ while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
+ barrier();
return;
case MESSAGE_REJECT:
/* Accept message by clearing ACK */
@@ -2184,8 +2188,8 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) {
/* Enable reselect interrupts */
NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
/* Wait for bus free to avoid nasty timeouts */
- while ((NCR5380_read(STATUS_REG) & SR_BSY) &&
- !hostdata->connected);
+ while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
+ barrier();
return;
/*
* The SCSI data pointer is *IMPLICITLY* saved on a disconnect
diff --git a/drivers/scsi/NCR5380.h b/drivers/scsi/NCR5380.h
index e0a21dc33..7d80697fe 100644
--- a/drivers/scsi/NCR5380.h
+++ b/drivers/scsi/NCR5380.h
@@ -272,7 +272,7 @@ static int NCR5380_probe_irq (struct Scsi_Host *instance, int possible);
#endif
static void NCR5380_init (struct Scsi_Host *instance, int flags);
static void NCR5380_information_transfer (struct Scsi_Host *instance);
-static void NCR5380_intr (int irq);
+static void NCR5380_intr (int irq, struct pt_regs * regs);
static void NCR5380_main (void);
static void NCR5380_print_options (struct Scsi_Host *instance);
#ifndef NCR5380_abort
diff --git a/drivers/scsi/README.st b/drivers/scsi/README.st
index 3d14837ae..cce8d031f 100644
--- a/drivers/scsi/README.st
+++ b/drivers/scsi/README.st
@@ -1,5 +1,5 @@
This file contains brief information about the SCSI tape driver.
-Last modified: Sun Oct 16 19:20:03 1994 by root@kai.home
+Last modified: Tue Jan 10 21:32:35 1995 by root@kai.home
BASICS
@@ -11,8 +11,9 @@ variable block size (within buffer limits). Both the auto-rewind
device number) are implemented.
By default the driver writes one filemark when the device is closed after
-writing. Two filemarks can be optionally written. In both cases end
-of data is signified by returning zero bytes for two consecutive reads.
+writing and the last operation has been a write. Two filemarks can be
+optionally written. In both cases end of data is signified by
+returning zero bytes for two consecutive reads.
BUFFERING
@@ -106,7 +107,8 @@ MTSETDRVBUFFER
Sets the buffering options. The bits are the new states
(enabled/disabled) of the write buffering (MT_ST_BUFFER_WRITES),
asynchronous writes (MT_ST_ASYNC_WRITES), read ahead
- (MT_ST_READ_AHEAD), writing of two filemark (ST_TWO_FM), and
+ (MT_ST_READ_AHEAD), writing of two filemark (ST_TWO_FM),
+ using the SCSI spacing to EOD (MT_ST_FAST_EOM), and
debugging (MT_ST_DEBUGGING ; debugging must be compiled into the
driver).
MT_ST_WRITE_THRESHOLD
@@ -128,8 +130,9 @@ MTIOCGET Returns some status information.
The current block size and the density code are stored in the field
mt_dsreg (shifts for the subfields are MT_ST_BLKSIZE_SHIFT and
MT_ST_DENSITY_SHIFT).
- The WR_PROT, BOT, EOF, EOT, EOD, and D_[800,1600,6250]_BPI
- status bits reflect the tape status. The other bits are not used.
+ The GMT_xxx status bits reflect the drive status. GMT_DR_OPEN
+ is set if there is no tape in the drive. GMT_EOD means either
+ end of recorded data or end of tape. GMT_EOT means end of tape.
MISCELLANEOUS COMPILE OPTIONS
@@ -140,6 +143,12 @@ is defined.
Immediate return from tape positioning SCSI commands can be enabled by
defining ST_NOWAIT.
+The MTEOM command is by default implemented as spacing over 32767
+filemarks. With this method the file number in the status is
+correct. The user can request using direct spacing to EOD by setting
+ST_FAST_EOM 1 (or using the MT_ST_OPTIONS ioctl). In this case the file
+number will be invalid.
+
When using read ahead or buffered writes the position within the file
may not be correct after the file is closed (correct position may
require backspacing over more than one record). The correct position
diff --git a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c
index 7ae1ee4a0..21b33fe2f 100644
--- a/drivers/scsi/aha152x.c
+++ b/drivers/scsi/aha152x.c
@@ -20,10 +20,22 @@
* General Public License for more details.
*
- * $Id: aha152x.c,v 1.6 1994/11/24 20:35:27 root Exp $
+ * $Id: aha152x.c,v 1.9 1995/03/18 09:20:24 root Exp root $
*
* $Log: aha152x.c,v $
+ * Revision 1.9 1995/03/18 09:20:24 root
+ * - patches for PCMCIA and modules
+ *
+ * Revision 1.8 1995/01/21 22:07:19 root
+ * - snarf_region => request_region
+ * - aha152x_intr interface change
+ *
+ * Revision 1.7 1995/01/02 23:19:36 root
+ * - updated COMMAND_SIZE to cmd_len
+ * - changed sti() to restore_flags()
+ * - fixed some #ifdef which generated warnings
+ *
* Revision 1.6 1994/11/24 20:35:27 root
* - problem with odd number of bytes in fifo fixed
*
@@ -188,6 +200,11 @@
**************************************************************************/
+#ifdef MODULE
+#include <linux/config.h>
+#include <linux/module.h>
+#endif
+
#include <linux/sched.h>
#include <asm/io.h>
#include "../block/blk.h"
@@ -205,6 +222,10 @@
/* DEFINES */
+/* For PCMCIA cards, always use AUTOCONF */
+#if defined(PCMCIA) || defined(MODULE)
+#define AUTOCONF
+#endif
/* If auto configuration is disabled, IRQ, SCSI_ID and RECONNECT have to
be predefined */
@@ -252,16 +273,14 @@
#if 0
#endif
-#define DEBUG_QUEUE
#define DEBUG_PHASES
-
-#endif
-
-#define DEBUG_RESET /* resets should be rare */
-#define DEBUG_ABORT /* aborts too */
+#define DEBUG_RESET
+#define DEBUG_ABORT
#define DEBUG_DEFAULT (debug_reset|debug_abort)
+#endif
+
/* END OF DEFINES */
/* some additional "phases" for getphase() */
@@ -313,7 +332,7 @@ static Scsi_Cmnd *disconnected_SC = NULL;
static int aborting=0, abortion_complete=0, abort_result;
-void aha152x_intr( int irqno );
+void aha152x_intr( int irq, struct pt_regs * );
void aha152x_done( int error );
void aha152x_setup( char *str, int *ints );
@@ -330,6 +349,18 @@ static void enter_driver(const char *);
static void leave_driver(const char *);
#endif
+/* possible i/o addresses for the AIC-6260 */
+static unsigned short ports[] =
+{
+ 0x340, /* default first */
+ 0x140
+};
+#define PORT_COUNT (sizeof( ports ) / sizeof( unsigned short ))
+
+/* possible interrupt channels */
+static unsigned short irqs[] = { 9, 10, 11, 12, 0 };
+
+#if !defined(SKIP_BIOSTEST)
/* possible locations for the Adaptec BIOS */
static void *addresses[] =
{
@@ -345,17 +376,6 @@ static void *addresses[] =
};
#define ADDRESS_COUNT (sizeof( addresses ) / sizeof( void * ))
-/* possible i/o addresses for the AIC-6260 */
-static unsigned short ports[] =
-{
- 0x340, /* default first */
- 0x140
-};
-#define PORT_COUNT (sizeof( ports ) / sizeof( unsigned short ))
-
-/* possible interrupt channels */
-static unsigned short ints[] = { 9, 10, 11, 12 };
-
/* signatures for various AIC-6[23]60 based controllers.
The point in detecting signatures is to avoid useless
and maybe harmful probes on ports. I'm not sure that
@@ -378,6 +398,7 @@ static struct signature {
{ "GA-400 LOCAL BUS SCSI BIOS", 0x102e, 26 }, /* Gigabyte Local-Bus-SCSI */
};
#define SIGNATURE_COUNT (sizeof( signatures ) / sizeof( struct signature ))
+#endif
static void do_pause( unsigned amount ) /* Pause for amount*10 milliseconds */
@@ -521,7 +542,7 @@ static int aha152x_porttest(int port_base)
{
int i;
- if(check_region(port_base, TEST-SCSISEQ))
+ if(check_region(port_base, 0x20))
return 0;
SETPORT( DMACNTRL1, 0 ); /* reset stack pointer */
@@ -537,9 +558,12 @@ static int aha152x_porttest(int port_base)
int aha152x_detect(Scsi_Host_Template * tpnt)
{
- int i, j, ok;
+ int i, ok;
+#if defined(AUTOCONF)
aha152x_config conf;
+#endif
int interrupt_level;
+ struct Scsi_Host *hreg;
if(setup_called)
{
@@ -569,6 +593,7 @@ int aha152x_detect(Scsi_Host_Template * tpnt)
aha152x_debug = setup_debug;
#endif
+#ifndef PCMCIA
for( i=0; i<PORT_COUNT && (port_base != ports[i]); i++)
;
@@ -577,22 +602,29 @@ int aha152x_detect(Scsi_Host_Template * tpnt)
printk("unknown portbase 0x%03x\n", port_base);
panic("aha152x panics in line %d", __LINE__);
}
+#endif
if(!aha152x_porttest(port_base))
{
printk("portbase 0x%03x fails probe\n", port_base);
+#ifdef PCMCIA
+ return 0;
+#else
panic("aha152x panics in line %d", __LINE__);
+#endif
}
+#ifndef PCMCIA
i=0;
- while(ints[i] && (interrupt_level!=ints[i]))
+ while(irqs[i] && (interrupt_level!=irqs[i]))
i++;
- if(!ints[i])
+ if(!irqs[i])
{
printk("illegal IRQ %d\n", interrupt_level);
panic("aha152x panics in line %d", __LINE__);
}
-
+#endif
+
if( (this_host < 0) || (this_host > 7) )
{
printk("illegal SCSI ID %d\n", this_host);
@@ -615,6 +647,8 @@ int aha152x_detect(Scsi_Host_Template * tpnt)
else
{
#if !defined(SKIP_BIOSTEST)
+ int j;
+
ok=0;
for( i=0; i < ADDRESS_COUNT && !ok; i++)
for( j=0; (j < SIGNATURE_COUNT) && !ok; j++)
@@ -651,7 +685,7 @@ int aha152x_detect(Scsi_Host_Template * tpnt)
conf.cf_port = (GETPORT(PORTA)<<8) + GETPORT(PORTB);
- interrupt_level = ints[conf.cf_irq];
+ interrupt_level = irqs[conf.cf_irq];
this_host = conf.cf_id;
can_disconnect = conf.cf_tardisc;
can_doparity = !conf.cf_parity;
@@ -722,7 +756,12 @@ int aha152x_detect(Scsi_Host_Template * tpnt)
can_disconnect ? "enabled" : "disabled",
can_doparity ? "enabled" : "disabled");
- snarf_region(port_base, TEST-SCSISEQ); /* Register */
+ request_region(port_base, 0x20, "aha152x"); /* Register */
+
+ hreg = scsi_register(tpnt, 0);
+ hreg->io_port = port_base;
+ hreg->n_io_port = 0x20;
+ hreg->irq = interrupt_level;
/* not expecting any interrupts */
SETPORT(SIMODE0, 0);
@@ -737,6 +776,8 @@ int aha152x_detect(Scsi_Host_Template * tpnt)
*/
int aha152x_queue( Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
{
+ unsigned long flags;
+
#if defined(DEBUG_RACE)
enter_driver("queue");
#else
@@ -749,13 +790,10 @@ int aha152x_queue( Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
#if defined(DEBUG_QUEUE)
if(aha152x_debug & debug_queue)
{
- printk( "SCpnt (target = %d lun = %d cmnd = ",
- SCpnt->target,
- SCpnt->lun);
+ printk( "SCpnt (target = %d lun = %d cmnd = ", SCpnt->target, SCpnt->lun);
print_command(SCpnt->cmnd);
- printk( ", pieces = %d size = %u), ",
- SCpnt->use_sg,
- SCpnt->request_bufflen );
+ printk( ", cmd_len=%d, pieces = %d size = %u), ",
+ SCpnt->cmd_len, SCpnt->use_sg, SCpnt->request_bufflen );
disp_ports();
}
#endif
@@ -790,6 +828,7 @@ int aha152x_queue( Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
SCpnt->SCp.sent_command = 0;
/* Turn led on, when this is the first command. */
+ save_flags(flags);
cli();
commands++;
if(commands==1)
@@ -807,7 +846,7 @@ int aha152x_queue( Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
SETPORT(SIMODE0, disconnected_SC ? ENSELDI : 0 );
SETPORT(SIMODE1, issue_SC ? ENBUSFREE : 0);
}
- sti();
+ restore_flags(flags);
#if defined(DEBUG_RACE)
leave_driver("queue");
@@ -831,8 +870,10 @@ int aha152x_command( Scsi_Cmnd *SCpnt )
*/
int aha152x_abort( Scsi_Cmnd *SCpnt)
{
+ unsigned long flags;
Scsi_Cmnd *ptr, *prev;
+ save_flags(flags);
cli();
#if defined(DEBUG_ABORT)
@@ -857,7 +898,7 @@ int aha152x_abort( Scsi_Cmnd *SCpnt)
prev->host_scribble = ptr->host_scribble;
else
issue_SC = (Scsi_Cmnd *) ptr->host_scribble;
- sti();
+ restore_flags(flags);
ptr->host_scribble = NULL;
ptr->result = DID_ABORT << 16;
@@ -874,7 +915,7 @@ int aha152x_abort( Scsi_Cmnd *SCpnt)
if(!current_SC)
printk("bus busy w/o current command, ");
- sti();
+ restore_flags(flags);
return SCSI_ABORT_BUSY;
}
@@ -883,7 +924,7 @@ int aha152x_abort( Scsi_Cmnd *SCpnt)
if(current_SC)
{
/* target entered bus free before COMMAND COMPLETE, nothing to abort */
- sti();
+ restore_flags(flags);
current_SC->result = DID_ERROR << 16;
current_SC->done(current_SC);
current_SC = (Scsi_Cmnd *) NULL;
@@ -922,24 +963,25 @@ int aha152x_abort( Scsi_Cmnd *SCpnt)
abort_result=SCSI_ABORT_SUCCESS;
aborting++;
abortion_complete=0;
- sti();
+
+ sti(); /* Hi Eric, guess what ;-) */
/* sleep until the abortion is complete */
while(!abortion_complete)
- ;
+ barrier();
aborting=0;
return abort_result;
}
else
{
/* we're already aborting a command */
- sti();
- return( SCSI_ABORT_BUSY );
+ restore_flags(flags);
+ return SCSI_ABORT_BUSY;
}
/* command wasn't found */
printk("command not found\n");
- sti();
+ restore_flags(flags);
return SCSI_ABORT_NOT_RUNNING;
}
@@ -983,6 +1025,7 @@ static void aha152x_reset_ports(void)
*/
int aha152x_reset(Scsi_Cmnd * __unused)
{
+ unsigned long flags;
Scsi_Cmnd *ptr, *prev, *next;
aha152x_reset_ports();
@@ -1008,6 +1051,7 @@ int aha152x_reset(Scsi_Cmnd * __unused)
current_SC=NULL;
}
+ save_flags(flags);
cli();
prev=NULL; ptr=disconnected_SC;
while(ptr)
@@ -1033,7 +1077,7 @@ int aha152x_reset(Scsi_Cmnd * __unused)
ptr = (Scsi_Cmnd *) ptr->host_scribble;
}
}
- sti();
+ restore_flags(flags);
#if defined( DEBUG_RESET )
if(aha152x_debug & debug_reset)
@@ -1093,6 +1137,7 @@ int aha152x_biosparam(Scsi_Disk * disk, int dev, int *info_array )
*/
void aha152x_done( int error )
{
+ unsigned long flags;
Scsi_Cmnd *done_SC;
#if defined(DEBUG_DONE)
@@ -1110,6 +1155,7 @@ void aha152x_done( int error )
printk("done(%x), ", error);
#endif
+ save_flags(flags);
cli();
done_SC = current_SC;
@@ -1124,7 +1170,7 @@ void aha152x_done( int error )
if(aha152x_debug & debug_queues)
printk("ok (%d), ", commands);
#endif
- sti();
+ restore_flags(flags);
SETPORT(SIMODE0, disconnected_SC ? ENSELDI : 0 );
SETPORT(SIMODE1, issue_SC ? ENBUSFREE : 0);
@@ -1163,8 +1209,9 @@ void aha152x_done( int error )
/*
* Interrupts handler (main routine of the driver)
*/
-void aha152x_intr( int irqno )
+void aha152x_intr( int irqno, struct pt_regs * regs )
{
+ unsigned int flags;
int done=0, phase;
#if defined(DEBUG_RACE)
@@ -1181,7 +1228,7 @@ void aha152x_intr( int irqno )
intr(). To avoid race conditions we have to return
immediately afterwards. */
CLRBITS( DMACNTRL0, INTEN);
- sti();
+ sti(); /* Yes, sti() really needs to be here */
/* disconnected target is trying to reconnect.
Only possible, if we have disconnected nexuses and
@@ -1202,10 +1249,11 @@ void aha152x_intr( int irqno )
if(aha152x_debug & debug_queues)
printk("i+, ");
#endif
+ save_flags(flags);
cli();
append_SC( &issue_SC, current_SC);
current_SC=NULL;
- sti();
+ restore_flags(flags);
}
/* disable sequences */
@@ -1265,7 +1313,9 @@ void aha152x_intr( int irqno )
printk("identify=%02x, lun=%d, ", identify_msg, identify_msg & 0x3f );
#endif
+ save_flags(flags);
cli();
+
#if defined(DEBUG_QUEUES)
if(aha152x_debug & debug_queues)
printk("d-, ");
@@ -1281,7 +1331,7 @@ void aha152x_intr( int irqno )
}
current_SC->SCp.phase &= ~disconnected;
- sti();
+ restore_flags(flags);
SETPORT( SIMODE0, 0 );
SETPORT( SIMODE1, ENPHASEMIS|ENBUSFREE );
@@ -1298,13 +1348,14 @@ void aha152x_intr( int irqno )
/* bus is free to issue a queued command */
if(TESTHI( SSTAT1, BUSFREE) && issue_SC)
{
+ save_flags(flags);
cli();
#if defined(DEBUG_QUEUES)
if(aha152x_debug & debug_queues)
printk("i-, ");
#endif
current_SC = remove_first_SC( &issue_SC );
- sti();
+ restore_flags(flags);
#if defined(DEBUG_INTR) || defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
if(aha152x_debug & (debug_intr|debug_selection|debug_phases))
@@ -1579,15 +1630,13 @@ void aha152x_intr( int irqno )
#if defined(DEBUG_CMD)
if(aha152x_debug & debug_cmd)
{
- printk("DFIFOEMP, outsw (%d words), ",
- current_SC->cmd_len >>1 );
+ printk("DFIFOEMP, outsw (%d bytes, %d words), ",
+ current_SC->cmd_len, current_SC->cmd_len >> 1 );
disp_ports();
}
#endif
- outsw( DATAPORT,
- &current_SC->cmnd,
- current_SC->cmd_len >>1 );
+ outsw( DATAPORT, &current_SC->cmnd, current_SC->cmd_len >> 1 );
#if defined(DEBUG_CMD)
if(aha152x_debug & debug_cmd)
@@ -1597,16 +1646,31 @@ void aha152x_intr( int irqno )
}
#endif
+#if defined(DEBUG_CMD)
+ if(aha152x_debug & debug_cmd)
+ printk("waiting for SEMPTY, ");
+#endif
+
/* wait for SCSI FIFO to get empty.
very important to send complete commands. */
while( TESTLO ( SSTAT2, SEMPTY ) )
;
+#if defined(DEBUG_CMD)
+ if(aha152x_debug & debug_cmd)
+ printk("SEMPTY, ");
+#endif
+
CLRBITS(SXFRCTL0, SCSIEN|DMAEN);
/* transfer can be considered ended, when SCSIEN reads back zero */
while( TESTHI( SXFRCTL0, SCSIEN ) )
;
+#if defined(DEBUG_CMD)
+ if(aha152x_debug & debug_cmd)
+ printk("!SEMPTY, ");
+#endif
+
CLRBITS(DMACNTRL0, ENDMA);
#if defined(DEBUG_CMD) || defined(DEBUG_INTR)
@@ -1774,6 +1838,7 @@ void aha152x_intr( int irqno )
if(current_SC->SCp.phase & disconnected)
{
+ save_flags(flags);
cli();
#if defined(DEBUG_QUEUES)
if(aha152x_debug & debug_queues)
@@ -1781,7 +1846,7 @@ void aha152x_intr( int irqno )
#endif
append_SC( &disconnected_SC, current_SC);
current_SC = NULL;
- sti();
+ restore_flags(flags);
SETBITS( SCSISEQ, ENRESELI );
@@ -2223,8 +2288,10 @@ static void disp_ports(void)
#ifdef DEBUG_AHA152X
int s;
+#ifdef SKIP_PORTS
if(aha152x_debug & debug_skipports)
return;
+#endif
printk("\n%s: ", current_SC ? "on bus" : "waiting");
@@ -2436,6 +2503,9 @@ static int in_driver=0;
*/
static void enter_driver(const char *func)
{
+ unsigned long flags;
+
+ save_flags(flags);
cli();
printk("aha152x: entering %s() (%x)\n", func, jiffies);
if(in_driver)
@@ -2446,11 +2516,14 @@ static void enter_driver(const char *func)
in_driver++;
should_leave=func;
- sti();
+ restore_flags(flags);
}
static void leave_driver(const char *func)
{
+ unsigned long flags;
+
+ save_flags(flags);
cli();
printk("\naha152x: leaving %s() (%x)\n", func, jiffies);
if(!in_driver)
@@ -2461,7 +2534,7 @@ static void leave_driver(const char *func)
in_driver--;
should_leave=func;
- sti();
+ restore_flags(flags);
}
#endif
@@ -2522,8 +2595,10 @@ static void show_command(Scsi_Cmnd *ptr)
*/
static void show_queues(void)
{
+ unsigned long flags;
Scsi_Cmnd *ptr;
+ save_flags(flags);
cli();
printk("QUEUE STATUS:\nissue_SC:\n");
for(ptr=issue_SC; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble )
@@ -2541,5 +2616,5 @@ static void show_queues(void)
disp_ports();
disp_enintr();
- sti();
+ restore_flags(flags);
}
diff --git a/drivers/scsi/aha152x.h b/drivers/scsi/aha152x.h
index b8c9ae9bd..7b7a81b72 100644
--- a/drivers/scsi/aha152x.h
+++ b/drivers/scsi/aha152x.h
@@ -2,7 +2,7 @@
#define _AHA152X_H
/*
- * $Id: aha152x.h,v 1.6 1994/11/24 21:35:38 root Exp root $
+ * $Id: aha152x.h,v 1.9 1995/03/18 09:21:04 root Exp root $
*/
#if defined(__KERNEL__)
@@ -22,7 +22,7 @@ int aha152x_biosparam(Disk *, int, int*);
(unless we support more than 1 cmd_per_lun this should do) */
#define AHA152X_MAXQUEUE 7
-#define AHA152X_REVID "Adaptec 152x SCSI driver; $Revision: 1.6 $"
+#define AHA152X_REVID "Adaptec 152x SCSI driver; $Revision: 1.9 $"
/* Initial value of Scsi_Host entry */
#define AHA152X { /* next */ NULL, \
diff --git a/drivers/scsi/aha1542.c b/drivers/scsi/aha1542.c
index 9011814f6..0d8c73c40 100644
--- a/drivers/scsi/aha1542.c
+++ b/drivers/scsi/aha1542.c
@@ -2,7 +2,7 @@
* linux/kernel/aha1542.c
*
* Copyright (C) 1992 Tommy Thorn
- * Copyright (C) 1993, 1994 Eric Youngdale
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
*
* Modified by Eric Youngdale
* Use request_irq and request_dma to help prevent unexpected conflicts
@@ -14,6 +14,10 @@
* Accept parameters from LILO cmd-line. -- 1-Oct-94
*/
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
#include <linux/kernel.h>
#include <linux/head.h>
#include <linux/types.h>
@@ -45,7 +49,7 @@ static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/aha
/* The adaptec can be configured for quite a number of addresses, but
I generally do not want the card poking around at random. We allow
two addresses - this allows people to use the Adaptec with a Midi
-card, which also used 0x330 -- can be overriden with LILO! */
+card, which also used 0x330 -- can be overridden with LILO! */
#define MAXBOARDS 2 /* Increase this and the sizes of the
arrays below, if you need more.. */
@@ -149,27 +153,31 @@ static void aha1542_stat(void)
are ever sent. */
static int aha1542_out(unsigned int base, unchar *cmdp, int len)
{
+ unsigned long flags = 0;
+
if(len == 1) {
while(1==1){
WAIT(STATUS(base), CDF, 0, CDF);
+ save_flags(flags);
cli();
- if(inb(STATUS(base)) & CDF) {sti(); continue;}
+ if(inb(STATUS(base)) & CDF) {restore_flags(flags); continue;}
outb(*cmdp, DATA(base));
- sti();
+ restore_flags(flags);
return 0;
}
} else {
+ save_flags(flags);
cli();
while (len--)
{
WAIT(STATUS(base), CDF, 0, CDF);
outb(*cmdp++, DATA(base));
}
- sti();
+ restore_flags(flags);
}
return 0;
fail:
- sti();
+ restore_flags(flags);
printk("aha1542_out failed(%d): ", len+1); aha1542_stat();
return 1;
}
@@ -178,16 +186,19 @@ static int aha1542_out(unsigned int base, unchar *cmdp, int len)
here */
static int aha1542_in(unsigned int base, unchar *cmdp, int len)
{
+ unsigned long flags;
+
+ save_flags(flags);
cli();
while (len--)
{
WAIT(STATUS(base), DF, DF, 0);
*cmdp++ = inb(DATA(base));
}
- sti();
+ restore_flags(flags);
return 0;
fail:
- sti();
+ restore_flags(flags);
printk("aha1542_in failed(%d): ", len+1); aha1542_stat();
return 1;
}
@@ -197,16 +208,19 @@ static int aha1542_in(unsigned int base, unchar *cmdp, int len)
if the board will respond the the command we are about to send or not */
static int aha1542_in1(unsigned int base, unchar *cmdp, int len)
{
+ unsigned long flags;
+
+ save_flags(flags);
cli();
while (len--)
{
WAITd(STATUS(base), DF, DF, 0, 100);
*cmdp++ = inb(DATA(base));
}
- sti();
+ restore_flags(flags);
return 0;
fail:
- sti();
+ restore_flags(flags);
return 1;
}
@@ -335,23 +349,20 @@ static int aha1542_test_port(int bse, struct Scsi_Host * shpnt)
}
/* A "high" level interrupt handler */
-static void aha1542_intr_handle(int foo)
+static void aha1542_intr_handle(int irq, struct pt_regs *regs)
{
void (*my_done)(Scsi_Cmnd *) = NULL;
int errstatus, mbi, mbo, mbistatus;
int number_serviced;
+ unsigned int flags;
struct Scsi_Host * shost;
Scsi_Cmnd * SCtmp;
- int irqno, * irqp, flag;
+ int flag;
int needs_restart;
struct mailbox * mb;
struct ccb *ccb;
- irqp = (int *) foo;
- irqp -= 2; /* Magic - this is only required for slow interrupt handlers */
- irqno = *irqp;
-
- shost = aha_host[irqno - 9];
+ shost = aha_host[irq - 9];
if(!shost) panic("Splunge!");
mb = HOSTDATA(shost)->mb;
@@ -390,6 +401,7 @@ static void aha1542_intr_handle(int foo)
aha1542_intr_reset(shost->io_port);
+ save_flags(flags);
cli();
mbi = HOSTDATA(shost)->aha1542_last_mbi_used + 1;
if (mbi >= 2*AHA1542_MAILBOXES) mbi = AHA1542_MAILBOXES;
@@ -401,7 +413,7 @@ static void aha1542_intr_handle(int foo)
} while (mbi != HOSTDATA(shost)->aha1542_last_mbi_used);
if(mb[mbi].status == 0){
- sti();
+ restore_flags(flags);
/* Hmm, no mail. Must have read it the last time around */
if (!number_serviced && !needs_restart)
printk("aha1542.c: interrupt received, but no mail.\n");
@@ -415,7 +427,7 @@ static void aha1542_intr_handle(int foo)
mbistatus = mb[mbi].status;
mb[mbi].status = 0;
HOSTDATA(shost)->aha1542_last_mbi_used = mbi;
- sti();
+ restore_flags(flags);
#ifdef DEBUG
{
@@ -498,6 +510,7 @@ int aha1542_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
unchar *cmd = (unchar *) SCpnt->cmnd;
unchar target = SCpnt->target;
unchar lun = SCpnt->lun;
+ unsigned long flags;
void *buff = SCpnt->request_buffer;
int bufflen = SCpnt->request_bufflen;
int mbo;
@@ -545,6 +558,7 @@ int aha1542_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
/* Use the outgoing mailboxes in a round-robin fashion, because this
is how the host adapter will scan for them */
+ save_flags(flags);
cli();
mbo = HOSTDATA(SCpnt->host)->aha1542_last_mbo_used + 1;
if (mbo >= AHA1542_MAILBOXES) mbo = 0;
@@ -563,7 +577,7 @@ int aha1542_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
screwing with this cdb. */
HOSTDATA(SCpnt->host)->aha1542_last_mbo_used = mbo;
- sti();
+ restore_flags(flags);
#ifdef DEBUG
printk("Sending command (%d %x)...",mbo, done);
@@ -667,7 +681,8 @@ int aha1542_command(Scsi_Cmnd * SCpnt)
aha1542_queuecommand(SCpnt, internal_done);
SCpnt->SCp.Status = 0;
- while (!SCpnt->SCp.Status);
+ while (!SCpnt->SCp.Status)
+ barrier();
return SCpnt->result;
}
@@ -903,6 +918,7 @@ int aha1542_detect(Scsi_Host_Template * tpnt)
{
unsigned char dma_chan;
unsigned char irq_level;
+ unsigned long flags;
unsigned int base_io;
int trans;
struct Scsi_Host * shpnt = NULL;
@@ -974,6 +990,7 @@ int aha1542_detect(Scsi_Host_Template * tpnt)
DEB(aha1542_stat());
DEB(printk("aha1542_detect: enable interrupt channel %d\n", irq_level));
+ save_flags(flags);
cli();
if (request_irq(irq_level,aha1542_intr_handle, 0, "aha1542")) {
printk("Unable to allocate IRQ for adaptec controller.\n");
@@ -1003,7 +1020,7 @@ int aha1542_detect(Scsi_Host_Template * tpnt)
HOSTDATA(shpnt)->aha1542_last_mbi_used = (2*AHA1542_MAILBOXES - 1);
HOSTDATA(shpnt)->aha1542_last_mbo_used = (AHA1542_MAILBOXES - 1);
memset(HOSTDATA(shpnt)->SCint, 0, sizeof(HOSTDATA(shpnt)->SCint));
- sti();
+ restore_flags(flags);
#if 0
DEB(printk(" *** READ CAPACITY ***\n"));
@@ -1037,7 +1054,7 @@ int aha1542_detect(Scsi_Host_Template * tpnt)
aha1542_command(0, cmd, buffer, 512);
}
#endif
- snarf_region(bases[indx], 4); /* Register the IO ports that we use */
+ request_region(bases[indx], 4,"aha1542"); /* Register the IO ports that we use */
count++;
continue;
unregister:
@@ -1081,8 +1098,8 @@ static int aha1542_restart(struct Scsi_Host * shost)
int aha1542_abort(Scsi_Cmnd * SCpnt)
{
#if 0
- int intval[3];
unchar ahacmd = CMD_START_SCSI;
+ unsigned long flags;
struct mailbox * mb;
int mbi, mbo, i;
@@ -1090,6 +1107,7 @@ int aha1542_abort(Scsi_Cmnd * SCpnt)
inb(STATUS(SCpnt->host->io_port)),
inb(INTRFLAGS(SCpnt->host->io_port)));
+ save_flags(flags);
cli();
mb = HOSTDATA(SCpnt->host)->mb;
mbi = HOSTDATA(SCpnt->host)->aha1542_last_mbi_used + 1;
@@ -1100,13 +1118,12 @@ int aha1542_abort(Scsi_Cmnd * SCpnt)
mbi++;
if (mbi >= 2*AHA1542_MAILBOXES) mbi = AHA1542_MAILBOXES;
} while (mbi != HOSTDATA(SCpnt->host)->aha1542_last_mbi_used);
- sti();
+ restore_flags(flags);
if(mb[mbi].status) {
printk("Lost interrupt discovered on irq %d - attempting to recover\n",
SCpnt->host->irq);
- intval[0] = SCpnt->host->irq;
- aha1542_intr_handle((int) &intval[2]);
+ aha1542_intr_handle(SCpnt->host->irq, NULL);
return 0;
}
@@ -1130,12 +1147,13 @@ int aha1542_abort(Scsi_Cmnd * SCpnt)
DEB(printk("aha1542_abort\n"));
#if 0
+ save_flags(flags);
cli();
for(mbo = 0; mbo < AHA1542_MAILBOXES; mbo++)
if (SCpnt == HOSTDATA(SCpnt->host)->SCint[mbo]){
mb[mbo].status = 2; /* Abort command */
aha1542_out(SCpnt->host->io_port, &ahacmd, 1); /* start scsi command */
- sti();
+ restore_flags(flags);
break;
};
#endif
diff --git a/drivers/scsi/aha1740.c b/drivers/scsi/aha1740.c
index baf310de4..557cb2292 100644
--- a/drivers/scsi/aha1740.c
+++ b/drivers/scsi/aha1740.c
@@ -120,7 +120,7 @@ sense[0],sense[1],sense[2],sense[3]);
}
else if ( status[0]&0x60 )
{
- retval = DID_ERROR; /* Didn't found a better error */
+ retval = DID_ERROR; /* Didn't find a better error */
}
/* In any other case return DID_OK so for example
CONDITION_CHECKS make it through to the appropriate
@@ -164,7 +164,7 @@ int aha1740_test_port(void)
}
/* A "high" level interrupt handler */
-void aha1740_intr_handle(int foo)
+void aha1740_intr_handle(int irq, struct pt_regs * regs)
{
void (*my_done)(Scsi_Cmnd *);
int errstatus, adapstat;
@@ -178,6 +178,7 @@ void aha1740_intr_handle(int foo)
{
DEB(printk("aha1740_intr top of loop.\n"));
adapstat = inb(G2INTST);
+ ecbptr = (struct ecb *) bus_to_virt(inl(MBOXIN0));
outb(G2CNTRL_IRST,G2CNTRL); /* interrupt reset */
switch ( adapstat & G2INTST_MASK )
@@ -185,10 +186,6 @@ void aha1740_intr_handle(int foo)
case G2INTST_CCBRETRY:
case G2INTST_CCBERROR:
case G2INTST_CCBGOOD:
- ecbptr = (struct ecb *) ( ((ulong) inb(MBOXIN0)) +
- ((ulong) inb(MBOXIN1) <<8) +
- ((ulong) inb(MBOXIN2) <<16) +
- ((ulong) inb(MBOXIN3) <<24) );
outb(G2CNTRL_HRDY,G2CNTRL); /* Host Ready -> Mailbox in complete */
if (!ecbptr)
{
@@ -197,6 +194,12 @@ void aha1740_intr_handle(int foo)
continue;
}
SCtmp = ecbptr->SCpnt;
+ if (!SCtmp)
+ {
+ printk("Aha1740 null SCtmp in interrupt (%x,%x,%x,%d)\n",
+ inb(G2STAT),adapstat,inb(G2INTST),number_serviced++);
+ continue;
+ }
if (SCtmp->host_scribble)
scsi_free(SCtmp->host_scribble, 512);
/* Fetch the sense data, and tuck it away, in the required slot. The
@@ -233,7 +236,7 @@ void aha1740_intr_handle(int foo)
break;
}
number_serviced++;
- };
+ }
}
int aha1740_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
@@ -241,6 +244,7 @@ int aha1740_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
unchar direction;
unchar *cmd = (unchar *) SCpnt->cmnd;
unchar target = SCpnt->target;
+ unsigned long flags;
void *buff = SCpnt->request_buffer;
int bufflen = SCpnt->request_bufflen;
int ecbno;
@@ -273,6 +277,7 @@ int aha1740_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
/* locate an available ecb */
+ save_flags(flags);
cli();
ecbno = aha1740_last_ecb_used + 1; /* An optimization */
if (ecbno >= AHA1740_ECBS) ecbno = 0;
@@ -290,7 +295,7 @@ int aha1740_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
ecb[ecbno].cmdw = AHA1740CMD_INIT; /* SCSI Initiator Command doubles as reserved flag */
aha1740_last_ecb_used = ecbno;
- sti();
+ restore_flags(flags);
#ifdef DEBUG
printk("Sending command (%d %x)...",ecbno, done);
@@ -366,21 +371,18 @@ int aha1740_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
thing else is in the driver was broken, like _makecode(), or
when a scsi device hung the scsi bus. Even under these conditions,
The loop actually only cycled < 3 times (we instrumented it). */
- ulong adrs;
DEB(printk("aha1740[%d] critical section\n",ecbno));
+ save_flags(flags);
cli();
if ( ! (inb(G2STAT) & G2STAT_MBXOUT) )
{
printk("aha1740[%d]_mbxout wait!\n",ecbno);
cli(); /* printk may have done a sti()! */
}
+ mb();
while ( ! (inb(G2STAT) & G2STAT_MBXOUT) ); /* Oh Well. */
- adrs = (ulong) &(ecb[ecbno]); /* Spit the command */
- outb((char) (adrs&0xff), MBOXOUT0); /* out, note this set */
- outb((char) ((adrs>>8)&0xff), MBOXOUT1); /* of outb's must be */
- outb((char) ((adrs>>16)&0xff), MBOXOUT2); /* atomic */
- outb((char) ((adrs>>24)&0xff), MBOXOUT3);
+ outl(virt_to_bus(ecb+ecbno), MBOXOUT0);
if ( inb(G2STAT) & G2STAT_BUSY )
{
printk("aha1740[%d]_attn wait!\n",ecbno);
@@ -388,7 +390,7 @@ int aha1740_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
}
while ( inb(G2STAT) & G2STAT_BUSY ); /* And Again! */
outb(ATTN_START | (target & 7), ATTN); /* Start it up */
- sti();
+ restore_flags(flags);
DEB(printk("aha1740[%d] request queued.\n",ecbno));
}
else
@@ -423,6 +425,7 @@ void aha1740_getconfig(void)
static int intab[] = { 9,10,11,12,0,14,15,0 };
irq_level = intab [ inb(INTDEF)&0x7 ];
+ outb(inb(INTDEF) | 0x10, INTDEF);
}
int aha1740_detect(Scsi_Host_Template * tpnt)
@@ -462,7 +465,7 @@ int aha1740_detect(Scsi_Host_Template * tpnt)
printk("Unable to allocate IRQ for adaptec controller.\n");
return 0;
}
- snarf_region(base, 0x5c); /* Reserve the space that we need to use */
+ request_region(base, 0x5c,"aha1740"); /* Reserve the space that we need to use */
return 1;
}
diff --git a/drivers/scsi/aha1740.h b/drivers/scsi/aha1740.h
index dae31cc1f..d216979f8 100644
--- a/drivers/scsi/aha1740.h
+++ b/drivers/scsi/aha1740.h
@@ -81,8 +81,8 @@
/* This is used with scatter-gather */
struct aha1740_chain {
- ulong dataptr; /* Location of data */
- ulong datalen; /* Size of this part of chain */
+ u32 dataptr; /* Location of data */
+ u32 datalen; /* Size of this part of chain */
};
/* These belong in scsi.h */
@@ -107,16 +107,16 @@ struct aha1740_chain {
#define MAX_STATUS 32
struct ecb { /* Enhanced Control Block 6.1 */
- ushort cmdw; /* Command Word */
- /* Flag Word 1 */
- ushort cne:1, /* Control Block Chaining */
+ u16 cmdw; /* Command Word */
+ /* Flag Word 1 */
+ u16 cne:1, /* Control Block Chaining */
:6, di:1, /* Disable Interrupt */
:2, ses:1, /* Suppress Underrun error */
:1, sg:1, /* Scatter/Gather */
:1, dsb:1, /* Disable Status Block */
ars:1; /* Automatic Request Sense */
- /* Flag Word 2 */
- ushort lun:3, /* Logical Unit */
+ /* Flag Word 2 */
+ u16 lun:3, /* Logical Unit */
tag:1, /* Tagged Queuing */
tt:2, /* Tag Type */
nd:1, /* No Disconnect */
@@ -125,20 +125,20 @@ struct ecb { /* Enhanced Control Block 6.1 */
st:1, /* Suppress Transfer */
chk:1, /* Calculate Checksum */
:2, rec:1, :1; /* Error Recovery */
- ushort nil0; /* nothing */
- ulong dataptr; /* Data or Scatter List ptr */
- ulong datalen; /* Data or Scatter List len */
- ulong statusptr; /* Status Block ptr */
- ulong linkptr; /* Chain Address */
- ulong nil1; /* nothing */
- ulong senseptr; /* Sense Info Pointer */
- unchar senselen; /* Sense Length */
- unchar cdblen; /* CDB Length */
- ushort datacheck; /* Data checksum */
- unchar cdb[MAX_CDB]; /* CDB area */
- /* Hardware defined portion ends here, rest is driver defined */
- unchar sense[MAX_SENSE]; /* Sense area */
- unchar status[MAX_STATUS]; /* Status area */
+ u16 nil0; /* nothing */
+ u32 dataptr; /* Data or Scatter List ptr */
+ u32 datalen; /* Data or Scatter List len */
+ u32 statusptr; /* Status Block ptr */
+ u32 linkptr; /* Chain Address */
+ u32 nil1; /* nothing */
+ u32 senseptr; /* Sense Info Pointer */
+ u8 senselen; /* Sense Length */
+ u8 cdblen; /* CDB Length */
+ u16 datacheck; /* Data checksum */
+ u8 cdb[MAX_CDB]; /* CDB area */
+/* Hardware defined portion ends here, rest is driver defined */
+ u8 sense[MAX_SENSE]; /* Sense area */
+ u8 status[MAX_STATUS]; /* Status area */
Scsi_Cmnd *SCpnt; /* Link to the SCSI Command Block */
void (*done)(Scsi_Cmnd *); /* Completion Function */
};
diff --git a/drivers/scsi/aha274x.c b/drivers/scsi/aha274x.c
index abadeab39..a2317a864 100644
--- a/drivers/scsi/aha274x.c
+++ b/drivers/scsi/aha274x.c
@@ -188,14 +188,14 @@ static struct {
short rate;
char *english;
} aha274x_synctab[] = {
- 100, 0, "10.0",
- 125, 1, "8.0",
- 150, 2, "6.67",
- 175, 3, "5.7",
- 200, 4, "5.0",
- 225, 5, "4.4",
- 250, 6, "4.0",
- 275, 7, "3.6"
+ {100, 0, "10.0"},
+ {125, 1, "8.0"},
+ {150, 2, "6.67"},
+ {175, 3, "5.7"},
+ {200, 4, "5.0"},
+ {225, 5, "4.4"},
+ {250, 6, "4.0"},
+ {275, 7, "3.6"}
};
static int aha274x_synctab_max =
@@ -322,8 +322,8 @@ void aha274x_setup(char *s, int *dummy)
char *name;
int *flag;
} options[] = {
- "extended", &aha274x_extended,
- NULL
+ {"extended", &aha274x_extended},
+ {NULL, NULL }
};
for (p = strtok(s, ","); p; p = strtok(NULL, ",")) {
@@ -456,7 +456,7 @@ void aha274x_to_scsirate(unsigned char *rate,
* be disabled all through this function unless we say otherwise.
*/
static
-void aha274x_isr(int irq)
+void aha274x_isr(int irq, struct pt_regs * regs)
{
int base, intstat;
struct aha274x_host *p;
@@ -745,9 +745,9 @@ enum aha_type aha274x_probe(int slot, int s_base)
unsigned char signature[sizeof(buf)];
enum aha_type type;
} S[] = {
- 4, { 0x04, 0x90, 0x77, 0x71 }, T_274X, /* host adapter 274x */
- 4, { 0x04, 0x90, 0x77, 0x70 }, T_274X, /* motherboard 274x */
- 4, { 0x04, 0x90, 0x77, 0x56 }, T_284X, /* 284x, BIOS enabled */
+ {4, { 0x04, 0x90, 0x77, 0x71 }, T_274X}, /* host adapter 274x */
+ {4, { 0x04, 0x90, 0x77, 0x70 }, T_274X}, /* motherboard 274x */
+ {4, { 0x04, 0x90, 0x77, 0x56 }, T_284X}, /* 284x, BIOS enabled */
};
for (i = 0; i < sizeof(buf); i++) {
@@ -887,7 +887,7 @@ int aha274x_register(Scsi_Host_Template *template,
/*
* Lock out other contenders for our i/o space.
*/
- snarf_region(O_MINREG(base), O_MAXREG(base)-O_MINREG(base));
+ request_region(O_MINREG(base), O_MAXREG(base)-O_MINREG(base), "aha27x");
/*
* Any card-type-specific adjustments before we register
diff --git a/drivers/scsi/buslogic.c b/drivers/scsi/buslogic.c
index f3e49c8b9..a5824b04b 100644
--- a/drivers/scsi/buslogic.c
+++ b/drivers/scsi/buslogic.c
@@ -1,13 +1,17 @@
/*
- * buslogic.c (C) 1993, 1994 David B. Gentzel
+ * buslogic.c Copyright (C) 1993, 1994 David B. Gentzel
* Low-level scsi driver for BusLogic adapters
* by David B. Gentzel, Whitfield Software Services, Carnegie, PA
* (gentzel@nova.enet.dec.com)
* Thanks to BusLogic for providing the necessary documentation
*
- * The original version of this driver was derived from aha1542.[ch] which
- * is Copyright (C) 1992 Tommy Thorn. Much has been reworked, but most of
- * basic structure and substantial chunks of code still remain.
+ * The original version of this driver was derived from aha1542.[ch],
+ * which is Copyright (C) 1992 Tommy Thorn. Much has been reworked, but
+ * most of basic structure and substantial chunks of code still remain.
+ *
+ * Furthermore, many subsequent fixes and improvements to the aha1542
+ * driver have been folded back into this driver. These changes to
+ * aha1542.[ch] are Copyright (C) 1993, 1994 Eric Youngdale.
*
* Thanks to the following individuals who have made contributions (of
* (code, information, support, or testing) to this driver:
@@ -24,7 +28,6 @@
* 5. Allow multiple boards to share an IRQ if the bus allows (EISA, MCA,
* and PCI).
* 6. Avoid using the 445S workaround for board revs >= D.
- * 7. Get cmd_per_lun put in the Scsi_Host structure.
*/
/*
@@ -53,16 +56,18 @@
* BT-747D - 747S + differential termination.
* BT-757S - 747S + WIDE SCSI.
* BT-757D - 747D + WIDE SCSI.
- * BT-445S - VESA bus-master FAST SCSI with active termination and floppy
- * support.
+ * BT-445S - VESA bus-master FAST SCSI with active termination
+ * and floppy support.
* BT-445C - 445S + enhanced BIOS & firmware options.
- * BT-946C - PCI bus-master FAST SCSI. (??? Nothing else known.)
+ * BT-946C - PCI bus-master FAST SCSI.
+ * BT-956C - PCI bus-master FAST/WIDE SCSI.
*
* ??? I believe other boards besides the 445 now have a "C" model, but I
* have no facts on them.
*
* This driver SHOULD support all of these boards. It has only been tested
- * with a 747S and 445S.
+ * with a 747S, 445S, 946C, and 956C; there is no PCI-specific support as
+ * yet.
*
* Should you require further information on any of these boards, BusLogic
* can be reached at (408)492-9090. Their BBS # is (408)492-1984 (maybe BBS
@@ -72,6 +77,10 @@
* unfinished, questionable, or wrong.
*/
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -79,6 +88,7 @@
#include <linux/types.h>
#include <linux/ioport.h>
#include <linux/delay.h>
+#include <linux/config.h>
#include <asm/io.h>
#include <asm/system.h>
@@ -95,6 +105,9 @@
# define BUSLOGIC_DEBUG 0
#endif
+/* ??? Until kmalloc actually implements GFP_DMA, we can't depend on it... */
+#undef GFP_DMA
+
/* If different port addresses are needed (e.g. to install more than two
cards), you must define BUSLOGIC_PORT_OVERRIDE to be a comma-separated list
of the addresses which will be checked. This can also be used to resolve a
@@ -107,7 +120,7 @@
The test is believed to fail on at least some AMI BusLogic clones. */
/* #define BIOS_TRANSLATION_OVERRIDE BIOS_TRANSLATION_BIG */
-#define BUSLOGIC_VERSION "1.13"
+#define BUSLOGIC_VERSION "1.15"
/* Not a random value - if this is too large, the system hangs for a long time
waiting for something to happen if a board is not installed. */
@@ -126,22 +139,28 @@
/* Since the SG list is malloced, we have to limit the length. */
#define BUSLOGIC_MAX_SG (BUSLOGIC_SG_MALLOC / sizeof (struct chain))
-/* ??? Arbitrary. If we can dynamically allocate the mailbox arrays, I may
- bump up this number. */
-#define BUSLOGIC_MAILBOXES 16
+/* Since the host adapters have room to buffer 32 commands internally, there
+ is some virtue in setting BUSLOGIC_MAILBOXES to 32. The maximum value
+ appears to be 255, since the Count parameter to the Initialize Extended
+ Mailbox command is limited to one byte. */
+#define BUSLOGIC_MAILBOXES 32
-#define BUSLOGIC_NONISA_CMDLUN 4 /* ??? Arbitrary (> 1) */
+#define BUSLOGIC_CMDLUN 4 /* Arbitrary, but seems to work well. */
/* BusLogic boards can be configured for quite a number of port addresses (six
to be exact), but I generally do not want the driver poking around at
random. We allow two port addresses - this allows people to use a BusLogic
- with a MIDI card, which frequently also uses 0x330. */
-static const unsigned int bases[] = {
+ with a MIDI card, which frequently also uses 0x330.
+
+ This can also be overridden on the command line to the kernel, via LILO or
+ LOADLIN. */
+static unsigned short bases[7] = {
#ifdef BUSLOGIC_PORT_OVERRIDE
- BUSLOGIC_PORT_OVERRIDE
+ BUSLOGIC_PORT_OVERRIDE,
#else
- 0x330, 0x334, /* 0x130, 0x134, 0x230, 0x234 */
+ 0x330, 0x334, /* 0x130, 0x134, 0x230, 0x234, */
#endif
+ 0
};
#define BIOS_TRANSLATION_DEFAULT 0 /* Default case */
@@ -150,8 +169,8 @@ static const unsigned int bases[] = {
struct hostdata {
unsigned int bus_type;
unsigned int bios_translation: 1; /* BIOS mapping (for compatibility) */
- size_t last_mbi_used;
- size_t last_mbo_used;
+ int last_mbi_used;
+ int last_mbo_used;
char model[7];
char firmware_rev[6];
Scsi_Cmnd *sc[BUSLOGIC_MAILBOXES];
@@ -169,7 +188,14 @@ static int restart(struct Scsi_Host *shpnt);
#define INTR_RESET(base) outb(RINT, CONTROL(base))
-#define buslogic_printk buslogic_prefix(),printk
+#define buslogic_printk buslogic_prefix(__PRETTY_FUNCTION__),printk
+
+#if defined(MODULE) && !defined(GFP_DMA)
+# define CHECK_DMA_ADDR(isa, addr, badstmt) \
+ do { if ((isa) && (addr) > (void *)ISA_DMA_THRESHOLD) badstmt; } while (0)
+#else
+# define CHECK_DMA_ADDR(isa, addr, badstmt)
+#endif
#define CHECK(cond) if (cond) ; else goto fail
@@ -204,9 +230,9 @@ static __inline__ int wait(unsigned short port,
}
}
-static void buslogic_prefix(void)
+static void buslogic_prefix(const char *func)
{
- printk("BusLogic SCSI: ");
+ printk("BusLogic SCSI: %s: ", func);
}
static void buslogic_stat(unsigned int base)
@@ -223,29 +249,33 @@ static void buslogic_stat(unsigned int base)
static int buslogic_out(unsigned int base, const unsigned char *cmdp,
size_t len)
{
+ unsigned long flags = 0;
+
if (len == 1) {
for (;;) {
WAIT_WHILE(STATUS(base), CPRBSY);
+ save_flags(flags);
cli();
if (!(inb(STATUS(base)) & CPRBSY)) {
outb(*cmdp, COMMAND_PARAMETER(base));
- sti();
+ restore_flags(flags);
return FALSE;
}
- sti();
+ restore_flags(flags);
}
} else {
+ save_flags(flags);
cli();
while (len--) {
WAIT_WHILE(STATUS(base), CPRBSY);
outb(*cmdp++, COMMAND_PARAMETER(base));
}
- sti();
+ restore_flags(flags);
}
return FALSE;
fail:
- sti();
- buslogic_printk("buslogic_out failed(%u): ", len + 1);
+ restore_flags(flags);
+ buslogic_printk("failed(%u): ", len + 1);
buslogic_stat(base);
return TRUE;
}
@@ -255,17 +285,20 @@ static int buslogic_out(unsigned int base, const unsigned char *cmdp,
sure whether the board will respond to the command we just sent. */
static int buslogic_in(unsigned int base, unsigned char *cmdp, size_t len)
{
+ unsigned long flags;
+
+ save_flags(flags);
cli();
while (len--) {
WAIT_UNTIL_FAST(STATUS(base), DIRRDY);
*cmdp++ = inb(DATA_IN(base));
}
- sti();
+ restore_flags(flags);
return FALSE;
fail:
- sti();
+ restore_flags(flags);
#if (BUSLOGIC_DEBUG & BD_IO)
- buslogic_printk("buslogic_in failed(%u): ", len + 1);
+ buslogic_printk("failed(%u): ", len + 1);
buslogic_stat(base);
#endif
return TRUE;
@@ -386,184 +419,151 @@ static unsigned int makecode(unsigned int haerr, unsigned int scsierr)
# endif
#endif
if (errstr != NULL)
- buslogic_printk("makecode: %s (%02X)\n", errstr, haerr);
+ buslogic_printk("%s (%02X)\n", errstr, haerr);
return (hosterr << 16) | scsierr;
}
-const char *buslogic_info(void)
+/* ??? this should really be "const struct Scsi_Host *" */
+const char *buslogic_info(struct Scsi_Host *shpnt)
{
- return "BusLogic SCSI driver version " BUSLOGIC_VERSION;
+ return "BusLogic SCSI driver " BUSLOGIC_VERSION;
}
-/* A "high" level interrupt handler. */
-static void buslogic_interrupt(int junk)
+/*
+ This is a major rewrite of the interrupt handler to support the newer
+ and faster PCI cards. While the previous interrupt handler was supposed
+ to handle multiple incoming becoming available mailboxes during the same
+ interrupt, my testing showed that in practice only a single mailbox was
+ ever made available. With the 946C and 956C, multiple incoming mailboxes
+ being ready for processing during a single interrupt occurs much more
+ frequently, and so care must be taken to avoid race conditions managing
+ the Host Adapter Interrupt Register, which can lead to lost interrupts.
+
+ Leonard N. Zubkoff, 23-Mar-95
+*/
+
+static void buslogic_interrupt(int irq, struct pt_regs * regs)
{
- void (*my_done)(Scsi_Cmnd *) = NULL;
- int errstatus, mbistatus = MBX_NOT_IN_USE, number_serviced, found;
- size_t mbi, mbo = 0;
+ int mbi, saved_mbo[BUSLOGIC_MAILBOXES];
+ int base, interrupt_flags, found, i;
struct Scsi_Host *shpnt;
Scsi_Cmnd *sctmp;
- int irqno, base, flag;
- int needs_restart;
struct mailbox *mb;
struct ccb *ccb;
- /* Magic - this -2 is only required for slow interrupt handlers */
- irqno = ((int *)junk)[-2];
-
- shpnt = host[irqno - 9];
- if (!shpnt)
- panic("buslogic.c: NULL SCSI host entry");
+ shpnt = host[irq - 9];
+ if (shpnt == NULL)
+ panic("buslogic_interrupt: NULL SCSI host entry");
mb = HOSTDATA(shpnt)->mb;
ccb = HOSTDATA(shpnt)->ccbs;
base = shpnt->io_port;
-#if (BUSLOGIC_DEBUG & BD_INTERRUPT)
- flag = inb(INTERRUPT(base));
-
- buslogic_printk("buslogic_interrupt: ");
- if (!(flag & INTV))
- printk("no interrupt? ");
- if (flag & IMBL)
- printk("IMBL ");
- if (flag & MBOR)
- printk("MBOR ");
- if (flag & CMDC)
- printk("CMDC ");
- if (flag & RSTS)
- printk("RSTS ");
- printk("status %02X\n", inb(STATUS(base)));
-#endif
+ /*
+ This interrupt handler is now specified to use the SA_INTERRUPT
+ protocol, so interrupts are inhibited on entry until explicitly
+ allowed again. Read the Host Adapter Interrupt Register, and
+ complain if there is no pending interrupt being signaled.
+ */
- number_serviced = 0;
- needs_restart = 0;
+ interrupt_flags = inb(INTERRUPT(base));
- for (;;) {
- flag = inb(INTERRUPT(base));
-
- /* Check for unusual interrupts. If any of these happen, we should
- probably do something special, but for now just printing a message
- is sufficient. A SCSI reset detected is something that we really
- need to deal with in some way. */
- if (flag & (MBOR | CMDC | RSTS)) {
- buslogic_printk("Unusual flag:");
- if (flag & MBOR)
- printk(" MBOR");
- if (flag & CMDC)
- printk(" CMDC");
- if (flag & RSTS) {
- needs_restart = 1;
- printk(" RSTS");
- }
- printk("\n");
- }
+ if (!(interrupt_flags & INTV))
+ buslogic_printk("interrupt received, but INTV not set\n");
- INTR_RESET(base);
+ /*
+ Reset the Host Adapter Interrupt Register. It appears to be
+ important that this is only done once per interrupt to avoid
+ losing interrupts under heavy loads.
+ */
- cli();
+ INTR_RESET(base);
- mbi = HOSTDATA(shpnt)->last_mbi_used + 1;
- if (mbi >= 2 * BUSLOGIC_MAILBOXES)
- mbi = BUSLOGIC_MAILBOXES;
+ if (interrupt_flags & RSTS)
+ {
+ restart(shpnt);
+ return;
+ }
- /* I use the "found" variable as I like to keep cli/sti pairs at the
- same block level. Debugging dropped sti's is no fun... */
+ /*
+ With interrupts still inhibited, scan through the incoming mailboxes
+ in strict round robin fashion saving the status information and
+ then freeing the mailbox. A second pass over the completed commands
+ will be made separately to complete their processing.
+ */
- found = FALSE;
- do {
- if (mb[mbi].status != MBX_NOT_IN_USE) {
- found = TRUE;
- break;
- }
- mbi++;
- if (mbi >= 2 * BUSLOGIC_MAILBOXES)
- mbi = BUSLOGIC_MAILBOXES;
- } while (mbi != HOSTDATA(shpnt)->last_mbi_used);
-
- if (found) {
- mbo = (struct ccb *)mb[mbi].ccbptr - ccb;
- mbistatus = mb[mbi].status;
- mb[mbi].status = MBX_NOT_IN_USE;
- HOSTDATA(shpnt)->last_mbi_used = mbi;
- }
+ mbi = HOSTDATA(shpnt)->last_mbi_used + 1;
+ if (mbi >= 2*BUSLOGIC_MAILBOXES)
+ mbi = BUSLOGIC_MAILBOXES;
- sti();
+ found = 0;
- if (!found) {
- /* Hmm, no mail. Must have read it the last time around. */
- if (!number_serviced && !needs_restart)
- buslogic_printk("interrupt received, but no mail.\n");
- /* We detected a reset. Restart all pending commands for devices
- that use the hard reset option. */
- if (needs_restart)
- restart(shpnt);
- return;
- }
+ while (mb[mbi].status != MBX_NOT_IN_USE && found < BUSLOGIC_MAILBOXES)
+ {
+ int mbo = (struct ccb *)mb[mbi].ccbptr - ccb;
-#if (BUSLOGIC_DEBUG & BD_INTERRUPT)
- if (ccb[mbo].tarstat || ccb[mbo].hastat)
- buslogic_printk("buslogic_interrupt: returning %08X (status %d)\n",
- ((int)ccb[mbo].hastat << 16) | ccb[mbo].tarstat,
- mb[mbi].status);
-#endif
+ sctmp = HOSTDATA(shpnt)->sc[mbo];
- if (mbistatus == MBX_COMPLETION_NOT_FOUND)
- continue;
+ /*
+ If sctmp has become NULL, higher level code must have aborted
+ this operation and called the necessary completion routine.
+ */
-#if (BUSLOGIC_DEBUG & BD_INTERRUPT)
- buslogic_printk("...done %u %u\n", mbo, mbi);
-#endif
+ if (sctmp != NULL && mb[mbi].status != MBX_COMPLETION_NOT_FOUND)
+ {
+ int result = 0;
- sctmp = HOSTDATA(shpnt)->sc[mbo];
+ saved_mbo[found++] = mbo;
- if (!sctmp || !sctmp->scsi_done) {
- buslogic_printk("buslogic_interrupt: Unexpected interrupt\n");
- buslogic_printk("tarstat=%02X, hastat=%02X id=%d lun=%d ccb#=%u\n",
- ccb[mbo].tarstat, ccb[mbo].hastat,
- ccb[mbo].id, ccb[mbo].lun, mbo);
- return;
- }
+ if (mb[mbi].status != MBX_COMPLETION_OK)
+ result = makecode(ccb[mbo].hastat, ccb[mbo].tarstat);
- my_done = sctmp->scsi_done;
- if (sctmp->host_scribble)
- scsi_free(sctmp->host_scribble, BUSLOGIC_SG_MALLOC);
-
- /* ??? more error checking left out here */
- if (mbistatus != MBX_COMPLETION_OK) {
- /* ??? This is surely wrong, but I don't know what's right. */
- errstatus = makecode(ccb[mbo].hastat, ccb[mbo].tarstat);
- } else
- errstatus = 0;
-
-#if (BUSLOGIC_DEBUG & BD_INTERRUPT)
- if (errstatus)
- buslogic_printk("error: %04X %04X\n",
- ccb[mbo].hastat, ccb[mbo].tarstat);
-
- if (status_byte(ccb[mbo].tarstat) == CHECK_CONDITION) {
- size_t i;
-
- buslogic_printk("buslogic_interrupt: sense:");
- for (i = 0; i < sizeof sctmp->sense_buffer; i++)
- printk(" %02X", sctmp->sense_buffer[i]);
- printk("\n");
- }
+ sctmp->result = result;
- if (errstatus)
- buslogic_printk("buslogic_interrupt: returning %08X\n", errstatus);
-#endif
+ mb[mbi].status = MBX_NOT_IN_USE;
+ }
- sctmp->result = errstatus;
- HOSTDATA(shpnt)->sc[mbo] = NULL; /* This effectively frees up
- the mailbox slot, as far as
- queuecommand is
- concerned. */
- my_done(sctmp);
- number_serviced++;
- }
+ HOSTDATA(shpnt)->last_mbi_used = mbi;
+
+ if (++mbi >= 2*BUSLOGIC_MAILBOXES)
+ mbi = BUSLOGIC_MAILBOXES;
+ }
+
+ /*
+ With interrupts no longer inhibited, iterate over the completed
+ commands freeing resources and calling the completion routines.
+ Since we exit upon completion of this loop, there is no need to
+ inhibit interrupts before exit, as this will be handled by the
+ fast interrupt assembly code we return to.
+ */
+
+ sti();
+
+ for (i = 0; i < found; i++)
+ {
+ int mbo = saved_mbo[i];
+ sctmp = HOSTDATA(shpnt)->sc[mbo];
+ if (sctmp == NULL) continue;
+ /*
+ First, free any storage allocated for a scatter/gather
+ data segment list.
+ */
+ if (sctmp->host_scribble)
+ scsi_free(sctmp->host_scribble, BUSLOGIC_SG_MALLOC);
+ /*
+ Next, mark the SCSI Command as completed so it may be reused
+ for another command by buslogic_queuecommand. This also signals
+ to buslogic_reset that the command is no longer active.
+ */
+ HOSTDATA(shpnt)->sc[mbo] = NULL;
+ /*
+ Finally, call the SCSI command completion handler.
+ */
+ sctmp->scsi_done(sctmp);
+ }
}
+
/* ??? Why does queuecommand return a value? scsi.c never looks at it... */
int buslogic_queuecommand(Scsi_Cmnd *scpnt, void (*done)(Scsi_Cmnd *))
{
@@ -575,9 +575,12 @@ int buslogic_queuecommand(Scsi_Cmnd *scpnt, void (*done)(Scsi_Cmnd *))
void *buff = scpnt->request_buffer;
int bufflen = scpnt->request_bufflen;
int mbo;
- struct mailbox *mb;
+ unsigned long flags;
+ struct Scsi_Host *shpnt = scpnt->host;
+ struct mailbox *mb = HOSTDATA(shpnt)->mb;
struct ccb *ccb;
+
#if (BUSLOGIC_DEBUG & BD_COMMAND)
if (target > 1) {
scpnt->result = DID_TIME_OUT << 16;
@@ -589,8 +592,8 @@ int buslogic_queuecommand(Scsi_Cmnd *scpnt, void (*done)(Scsi_Cmnd *))
if (*cmd == REQUEST_SENSE) {
#if (BUSLOGIC_DEBUG & (BD_COMMAND | BD_ERRORS))
if (bufflen != sizeof scpnt->sense_buffer) {
- buslogic_printk("Wrong buffer length supplied for request sense"
- " (%d)\n",
+ buslogic_printk("wrong buffer length supplied for request sense"
+ " (%d).\n",
bufflen);
}
#endif
@@ -608,12 +611,11 @@ int buslogic_queuecommand(Scsi_Cmnd *scpnt, void (*done)(Scsi_Cmnd *))
i = *(int *)(cmd + 2);
else
i = -1;
- buslogic_printk("buslogic_queuecommand:"
- " dev %d cmd %02X pos %d len %d ",
+ buslogic_printk("dev %d cmd %02X pos %d len %d ",
target, *cmd, i, bufflen);
- buslogic_stat(scpnt->host->io_port);
- buslogic_printk("buslogic_queuecommand: dumping scsi cmd:");
- for (i = 0; i < (COMMAND_SIZE(*cmd)); i++)
+ buslogic_stat(shpnt->io_port);
+ buslogic_printk("dumping scsi cmd:");
+ for (i = 0; i < scpnt->cmd_len; i++)
printk(" %02X", cmd[i]);
printk("\n");
if (*cmd == WRITE_10 || *cmd == WRITE_6)
@@ -621,51 +623,53 @@ int buslogic_queuecommand(Scsi_Cmnd *scpnt, void (*done)(Scsi_Cmnd *))
}
#endif
- mb = HOSTDATA(scpnt->host)->mb;
- ccb = HOSTDATA(scpnt->host)->ccbs;
-
/* Use the outgoing mailboxes in a round-robin fashion, because this
is how the host adapter will scan for them. */
+ save_flags(flags);
cli();
- mbo = HOSTDATA(scpnt->host)->last_mbo_used + 1;
+ mbo = HOSTDATA(shpnt)->last_mbo_used + 1;
if (mbo >= BUSLOGIC_MAILBOXES)
mbo = 0;
do {
if (mb[mbo].status == MBX_NOT_IN_USE
- && HOSTDATA(scpnt->host)->sc[mbo] == NULL)
+ && HOSTDATA(shpnt)->sc[mbo] == NULL)
break;
mbo++;
if (mbo >= BUSLOGIC_MAILBOXES)
mbo = 0;
- } while (mbo != HOSTDATA(scpnt->host)->last_mbo_used);
+ } while (mbo != HOSTDATA(shpnt)->last_mbo_used);
- if (mb[mbo].status != MBX_NOT_IN_USE || HOSTDATA(scpnt->host)->sc[mbo]) {
- /* ??? Instead of panicing, should we enable OMBR interrupts and
- sleep until we get one? */
- panic("buslogic.c: unable to find empty mailbox");
+ if (mb[mbo].status != MBX_NOT_IN_USE || HOSTDATA(shpnt)->sc[mbo]) {
+ /* ??? Instead of failing, should we enable OMBR interrupts and sleep
+ until we get one? */
+ restore_flags(flags);
+ buslogic_printk("unable to find empty mailbox.\n");
+ goto fail;
}
- HOSTDATA(scpnt->host)->sc[mbo] = scpnt; /* This will effectively
+ HOSTDATA(shpnt)->sc[mbo] = scpnt; /* This will effectively
prevent someone else from
screwing with this cdb. */
- HOSTDATA(scpnt->host)->last_mbo_used = mbo;
+ HOSTDATA(shpnt)->last_mbo_used = mbo;
- sti();
+ restore_flags(flags);
#if (BUSLOGIC_DEBUG & BD_COMMAND)
buslogic_printk("sending command (%d %08X)...", mbo, done);
#endif
+ ccb = &HOSTDATA(shpnt)->ccbs[mbo];
+
/* This gets trashed for some reason */
- mb[mbo].ccbptr = &ccb[mbo];
+ mb[mbo].ccbptr = ccb;
- memset(&ccb[mbo], 0, sizeof (struct ccb));
+ memset(ccb, 0, sizeof (struct ccb));
- ccb[mbo].cdblen = COMMAND_SIZE(*cmd); /* SCSI Command Descriptor
+ ccb->cdblen = scpnt->cmd_len; /* SCSI Command Descriptor
Block Length */
direction = 0;
@@ -674,33 +678,36 @@ int buslogic_queuecommand(Scsi_Cmnd *scpnt, void (*done)(Scsi_Cmnd *))
else if (*cmd == WRITE_10 || *cmd == WRITE_6)
direction = 16;
- memcpy(ccb[mbo].cdb, cmd, ccb[mbo].cdblen);
+ memcpy(ccb->cdb, cmd, ccb->cdblen);
if (scpnt->use_sg) {
struct scatterlist *sgpnt;
struct chain *cptr;
size_t i;
- ccb[mbo].op = CCB_OP_INIT_SG; /* SCSI Initiator Command
+ ccb->op = CCB_OP_INIT_SG; /* SCSI Initiator Command
w/scatter-gather */
scpnt->host_scribble
= (unsigned char *)scsi_malloc(BUSLOGIC_SG_MALLOC);
- if (scpnt->host_scribble == NULL)
- panic("buslogic.c: unable to allocate DMA memory");
+ if (scpnt->host_scribble == NULL) {
+ buslogic_printk("unable to allocate DMA memory.\n");
+ goto fail;
+ }
sgpnt = (struct scatterlist *)scpnt->request_buffer;
cptr = (struct chain *)scpnt->host_scribble;
- if (scpnt->use_sg > scpnt->host->sg_tablesize) {
- buslogic_printk("buslogic_queuecommand: bad segment list,"
- " %d > %d\n",
- scpnt->use_sg, scpnt->host->sg_tablesize);
- panic("buslogic.c: bad segment list");
+ if (scpnt->use_sg > shpnt->sg_tablesize) {
+ buslogic_printk("bad segment list, %d > %d.\n",
+ scpnt->use_sg, shpnt->sg_tablesize);
+ goto fail;
}
for (i = 0; i < scpnt->use_sg; i++) {
+ CHECK_DMA_ADDR(shpnt->unchecked_isa_dma, sgpnt[i].address,
+ goto baddma);
cptr[i].dataptr = sgpnt[i].address;
cptr[i].datalen = sgpnt[i].length;
}
- ccb[mbo].datalen = scpnt->use_sg * sizeof (struct chain);
- ccb[mbo].dataptr = cptr;
+ ccb->datalen = scpnt->use_sg * sizeof (struct chain);
+ ccb->dataptr = cptr;
#if (BUSLOGIC_DEBUG & BD_COMMAND)
{
unsigned char *ptr;
@@ -713,44 +720,54 @@ int buslogic_queuecommand(Scsi_Cmnd *scpnt, void (*done)(Scsi_Cmnd *))
}
#endif
} else {
- ccb[mbo].op = CCB_OP_INIT; /* SCSI Initiator Command */
+ ccb->op = CCB_OP_INIT; /* SCSI Initiator Command */
scpnt->host_scribble = NULL;
- ccb[mbo].datalen = bufflen;
- ccb[mbo].dataptr = buff;
+ CHECK_DMA_ADDR(shpnt->unchecked_isa_dma, buff, goto baddma);
+ ccb->datalen = bufflen;
+ ccb->dataptr = buff;
}
- ccb[mbo].id = target;
- ccb[mbo].lun = lun;
- ccb[mbo].dir = direction;
- ccb[mbo].rsalen = sizeof scpnt->sense_buffer;
- ccb[mbo].senseptr = scpnt->sense_buffer;
- ccb[mbo].linkptr = NULL;
- ccb[mbo].commlinkid = 0;
+ ccb->id = target;
+ ccb->lun = lun;
+ ccb->dir = direction;
+ ccb->rsalen = sizeof scpnt->sense_buffer;
+ ccb->senseptr = scpnt->sense_buffer;
+ /* ccbcontrol, commlinkid, and linkptr are 0 due to above memset. */
#if (BUSLOGIC_DEBUG & BD_COMMAND)
{
size_t i;
- buslogic_printk("buslogic_queuecommand: sending...");
- for (i = 0; i < sizeof ccb[mbo] - 10; i++)
- printk(" %02X", ((unsigned char *)&ccb[mbo])[i]);
+ buslogic_printk("sending...");
+ for (i = 0; i < sizeof(struct ccb) - 10; i++)
+ printk(" %02X", ((unsigned char *)ccb)[i]);
printk("\n");
}
#endif
if (done) {
#if (BUSLOGIC_DEBUG & BD_COMMAND)
- buslogic_printk("buslogic_queuecommand: now waiting for interrupt: ");
- buslogic_stat(scpnt->host->io_port);
+ buslogic_printk("now waiting for interrupt: ");
+ buslogic_stat(shpnt->io_port);
#endif
scpnt->scsi_done = done;
mb[mbo].status = MBX_ACTION_START;
/* start scsi command */
- buslogic_out(scpnt->host->io_port, buscmd, sizeof buscmd);
+ buslogic_out(shpnt->io_port, buscmd, sizeof buscmd);
#if (BUSLOGIC_DEBUG & BD_COMMAND)
- buslogic_stat(scpnt->host->io_port);
+ buslogic_stat(shpnt->io_port);
#endif
} else
- buslogic_printk("buslogic_queuecommand: done can't be NULL\n");
+ buslogic_printk("done can't be NULL.\n");
+
+ while (0) {
+#if defined(MODULE) && !defined(GFP_DMA)
+ baddma:
+ buslogic_printk("address > 16MB used for ISA HA.\n");
+#endif
+ fail:
+ scpnt->result = DID_ERROR << 16;
+ done(scpnt);
+ }
return 0;
}
@@ -764,14 +781,14 @@ static void internal_done(Scsi_Cmnd *scpnt)
int buslogic_command(Scsi_Cmnd *scpnt)
{
#if (BUSLOGIC_DEBUG & BD_COMMAND)
- buslogic_printk("buslogic_command: calling buslogic_queuecommand\n");
+ buslogic_printk("calling buslogic_queuecommand.\n");
#endif
buslogic_queuecommand(scpnt, internal_done);
scpnt->SCp.Status = 0;
while (!scpnt->SCp.Status)
- continue;
+ barrier();
return scpnt->result;
}
#endif
@@ -802,7 +819,7 @@ static int setup_mailboxes(unsigned int base, struct Scsi_Host *shpnt)
while (0) {
fail:
- buslogic_printk("buslogic_detect: failed setting up mailboxes\n");
+ buslogic_printk("failed setting up mailboxes.\n");
}
INTR_RESET(base);
@@ -820,7 +837,7 @@ static int getconfig(unsigned int base, unsigned char *irq,
int i;
#if (BUSLOGIC_DEBUG & BD_DETECT)
- buslogic_printk("getconfig: called\n");
+ buslogic_printk("called\n");
#endif
i = inb(STATUS(base));
@@ -854,8 +871,8 @@ static int getconfig(unsigned int base, unsigned char *irq,
*irq = 15;
break;
default:
- buslogic_printk("Unable to determine BusLogic IRQ level."
- " Disabling board.\n");
+ buslogic_printk("unable to determine BusLogic IRQ level, "
+ " disabling board.\n");
goto fail;
}
*id = inquiry_result[2] & 0x7;
@@ -898,8 +915,8 @@ static int getconfig(unsigned int base, unsigned char *irq,
*dma = 7;
break;
default:
- buslogic_printk("Unable to determine BusLogic DMA channel."
- " Disabling board.\n");
+ buslogic_printk("unable to determine BusLogic DMA channel,"
+ " disabling board.\n");
goto fail;
}
else
@@ -908,7 +925,7 @@ static int getconfig(unsigned int base, unsigned char *irq,
while (0) {
fail:
#if (BUSLOGIC_DEBUG & BD_DETECT)
- buslogic_printk("buslogic_detect: query board settings\n");
+ buslogic_printk("query board settings\n");
#endif
return TRUE;
}
@@ -930,7 +947,7 @@ static int buslogic_query(unsigned int base, unsigned char *trans,
unsigned int i;
#if (BUSLOGIC_DEBUG & BD_DETECT)
- buslogic_printk("buslogic_query: called\n");
+ buslogic_printk("called\n");
#endif
/* Quick and dirty test for presence of the card. */
@@ -983,7 +1000,7 @@ static int buslogic_query(unsigned int base, unsigned char *trans,
firmware_rev[2] = inquiry_result[3];
firmware_rev[3] = '\0';
#if 0
- buslogic_printk("Inquiry Bytes: %02X(%c) %02X(%c)\n",
+ buslogic_printk("inquiry bytes: %02X(%c) %02X(%c)\n",
inquiry_result[0], inquiry_result[0],
inquiry_result[1], inquiry_result[1]);
#endif
@@ -1050,13 +1067,15 @@ static int buslogic_query(unsigned int base, unsigned char *trans,
/* bus_type from getconfig doesn't differentiate between EISA/VESA. We
override using the model number here. */
- /* ??? What bus_type gets returned for PCI? */
switch (*bus_type) {
case 'E':
switch (model[0]) {
case '4':
*bus_type = 'V';
break;
+ case '9':
+ *bus_type = 'P';
+ break;
case '7':
break;
default:
@@ -1071,7 +1090,7 @@ static int buslogic_query(unsigned int base, unsigned char *trans,
while (0) {
fail:
#if (BUSLOGIC_DEBUG & BD_DETECT)
- buslogic_printk("buslogic_query: query board settings\n");
+ buslogic_printk("query board settings\n");
#endif
return TRUE;
}
@@ -1089,19 +1108,21 @@ int buslogic_detect(Scsi_Host_Template *tpnt)
char bus_type;
unsigned short max_sg;
unsigned char bios_translation;
+ unsigned long flags;
const unsigned char *bios;
char *model;
char *firmware_rev;
struct Scsi_Host *shpnt;
size_t indx;
+ int unchecked_isa_dma;
int count = 0;
#if (BUSLOGIC_DEBUG & BD_DETECT)
- buslogic_printk("buslogic_detect:\n");
+ buslogic_printk("called\n");
#endif
tpnt->can_queue = BUSLOGIC_MAILBOXES;
- for (indx = 0; indx < ARRAY_SIZE(bases); indx++)
+ for (indx = 0; bases[indx] != 0; indx++)
if (!check_region(bases[indx], 4)) {
shpnt = scsi_register(tpnt, sizeof (struct hostdata));
@@ -1117,13 +1138,45 @@ int buslogic_detect(Scsi_Host_Template *tpnt)
buslogic_stat(base);
#endif
+ /* Only type 'A' (AT/ISA) bus adapters use unchecked DMA. */
+ unchecked_isa_dma = (bus_type == 'A');
+#ifndef CONFIG_NO_BUGGY_BUSLOGIC
+ /* There is a hardware bug in the BT-445S prior to revision D.
+ When the BIOS is enabled and you have more than 16MB of memory,
+ the card mishandles memory transfers over 16MB which (if viewed
+ as a 24-bit address) overlap with the BIOS address space. For
+ example if you have the BIOS located at physical address
+ 0xDC000 and a DMA transfer from the card to RAM starts at
+ physical address 0x10DC000 then the transfer is messed up. To
+ be more precise every fourth byte of the transfer is messed up.
+ (This analysis courtesy of Tomas Hurka, author of the NeXTSTEP
+ BusLogic driver.) */
+
+ if (bus_type == 'V' /* 445 */
+ && firmware_rev[0] <= '3' /* S */
+ && bios != NULL) { /* BIOS enabled */
+#if 1
+ /* Now that LNZ's forbidden_addr stuff is in the higher level
+ scsi code, we can use this instead. */
+ /* Avoid addresses which "mirror" the BIOS for DMA. */
+ shpnt->forbidden_addr = (unsigned long)bios;
+ shpnt->forbidden_size = 16 * 1024;
+#else
+ /* Use double-buffering. */
+ unchecked_isa_dma = TRUE;
+#endif
+ }
+#endif
+
+ CHECK_DMA_ADDR(unchecked_isa_dma, shpnt, goto unregister);
+
if (setup_mailboxes(base, shpnt))
goto unregister;
/* Set the Bus on/off-times as not to ruin floppy performance.
CMD_BUSOFF_TIME is a noop for EISA boards (and possibly
others???). */
- if (bus_type != 'E') {
+ if (bus_type != 'E' && bus_type != 'P') {
/* The default ON/OFF times for BusLogic adapters is 7/4. */
static const unsigned char oncmd[] = { CMD_BUSON_TIME, 7 };
static const unsigned char offcmd[] = { CMD_BUSOFF_TIME, 5 };
@@ -1136,19 +1189,18 @@ int buslogic_detect(Scsi_Host_Template *tpnt)
WAIT_UNTIL(INTERRUPT(base), CMDC);
while (0) {
fail:
- buslogic_printk("buslogic_detect:"
- " setting bus on/off-time failed\n");
+ buslogic_printk("setting bus on/off-time failed.\n");
}
INTR_RESET(base);
}
- buslogic_printk("Configuring %s HA at port 0x%03X, IRQ %u",
+ buslogic_printk("configuring %s HA at port 0x%03X, IRQ %u",
(bus_type == 'A' ? "ISA"
: (bus_type == 'E' ? "EISA"
: (bus_type == 'M' ? "MCA"
: (bus_type == 'P' ? "PCI"
: (bus_type == 'V' ? "VESA"
- : (bus_type == 'X' ? "EISA/VESA"
+ : (bus_type == 'X' ? "EISA/VESA/PCI"
: "Unknown")))))),
base, irq);
if (bios != NULL)
@@ -1161,31 +1213,32 @@ int buslogic_detect(Scsi_Host_Template *tpnt)
if (model[0])
printk(" (revision %d)", model[6]);
printk("\n");
- buslogic_printk("Firmware revision: %s\n", firmware_rev);
+ buslogic_printk("firmware revision: %s\n", firmware_rev);
#if (BUSLOGIC_DEBUG & BD_DETECT)
buslogic_stat(base);
#endif
#if (BUSLOGIC_DEBUG & BD_DETECT)
- buslogic_printk("buslogic_detect: enable interrupt channel %d\n",
- irq);
+ buslogic_printk("enable interrupt channel %d.\n", irq);
#endif
+ save_flags(flags);
cli();
- if (request_irq(irq, buslogic_interrupt, 0, "buslogic")) {
- buslogic_printk("Unable to allocate IRQ for "
+ if (request_irq(irq, buslogic_interrupt,
+ SA_INTERRUPT, "buslogic")) {
+ buslogic_printk("unable to allocate IRQ for "
"BusLogic controller.\n");
- sti();
+ restore_flags(flags);
goto unregister;
}
if (dma) {
if (request_dma(dma, "buslogic")) {
- buslogic_printk("Unable to allocate DMA channel for "
+ buslogic_printk("unable to allocate DMA channel for "
"BusLogic controller.\n");
free_irq(irq);
- sti();
+ restore_flags(flags);
goto unregister;
}
@@ -1198,59 +1251,26 @@ int buslogic_detect(Scsi_Host_Template *tpnt)
host[irq - 9] = shpnt;
shpnt->this_id = id;
- /* Only type 'A' (AT/ISA) bus adapters use unchecked DMA. */
- shpnt->unchecked_isa_dma = (bus_type == 'A');
-#ifndef CONFIG_NO_BUGGY_BUSLOGIC
- /* There is a hardware bug in the BT-445S prior to revision D.
- When the BIOS is enabled and you have more than 16MB of memory,
- the card mishandles memory transfers over 16MB which (if viewed
- as a 24-bit address) overlap with the BIOS address space. For
- example if you have the BIOS located at physical address
- 0xDC000 and a DMA transfer from the card to RAM starts at
- physical address 0x10DC000 then the transfer is messed up. To
- be more precise every fourth byte of the transfer is messed up.
- (This analysis courtesy of Tomas Hurka, author of the NeXTSTEP
- BusLogic driver.) */
-
- if (bus_type == 'V' /* 445 */
- && firmware_rev[0] <= '3' /* S */
- && bios != NULL) { /* BIOS enabled */
-#if 0
- /* ??? Once LNZ's forbidden_addr stuff makes it into the higher
- level scsi code, we can use this instead. */
- /* Avoid addresses which "mirror" the BIOS for DMA. */
- shpnt->forbidden_addr = bios;
- shpnt->forbidden_size = 16 * 1024;
-#else
- /* Use double-buffering. */
- shpnt->unchecked_isa_dma = TRUE;
-#endif
- }
-#endif
+ shpnt->unchecked_isa_dma = unchecked_isa_dma;
/* Have to keep cmd_per_lun at 1 for ISA machines otherwise lots
of memory gets sucked up for bounce buffers. */
- /* ??? Unfortunately, cmd_per_lun is only in the
- Scsi_Host_Template structure, not the Scsi_Host structure.
- Therefore, this could cause high memory consumption if a system
- has multiple BusLogic adapters which are a mix of ISA and
- non-ISA. */
- if (!shpnt->unchecked_isa_dma)
- shpnt->hostt->cmd_per_lun = BUSLOGIC_NONISA_CMDLUN;
+ shpnt->cmd_per_lun = (unchecked_isa_dma ? 1 : BUSLOGIC_CMDLUN);
shpnt->sg_tablesize = max_sg;
if (shpnt->sg_tablesize > BUSLOGIC_MAX_SG)
shpnt->sg_tablesize = BUSLOGIC_MAX_SG;
/* ??? shpnt->base should really be "const unsigned char *"... */
shpnt->base = (unsigned char *)bios;
shpnt->io_port = base;
+ shpnt->n_io_port = 4; /* Number of bytes of I/O space used */
shpnt->dma_channel = dma;
shpnt->irq = irq;
HOSTDATA(shpnt)->bios_translation = bios_translation;
if (bios_translation == BIOS_TRANSLATION_BIG)
- buslogic_printk("Using extended bios translation.\n");
+ buslogic_printk("using extended bios translation.\n");
HOSTDATA(shpnt)->last_mbi_used = 2 * BUSLOGIC_MAILBOXES - 1;
HOSTDATA(shpnt)->last_mbo_used = BUSLOGIC_MAILBOXES - 1;
memset(HOSTDATA(shpnt)->sc, 0, sizeof HOSTDATA(shpnt)->sc);
- sti();
+ restore_flags(flags);
#if 0
{
@@ -1266,8 +1286,7 @@ int buslogic_detect(Scsi_Host_Template *tpnt)
buf[i] = 0x87;
for (i = 0; i < 2; i++)
if (!buslogic_command(i, cmd, buf, sizeof buf)) {
- buslogic_printk("buslogic_detect: LU %u "
- "sector_size %d device_size %d\n",
+ buslogic_printk("LU %u sector_size %d device_size %d\n",
i, *(int *)(buf + 4), *(int *)buf);
}
@@ -1289,8 +1308,8 @@ int buslogic_detect(Scsi_Host_Template *tpnt)
}
#endif
- snarf_region(bases[indx], 4); /* Register the IO ports that
- we use */
+ request_region(bases[indx], 4,"buslogic");
+ /* Register the IO ports that we use */
count++;
continue;
unregister:
@@ -1317,7 +1336,7 @@ static int restart(struct Scsi_Host *shpnt)
count++;
}
- buslogic_printk("Potential to restart %d stalled commands...\n", count);
+ buslogic_printk("potential to restart %d stalled commands...\n", count);
#if 0
/* start scsi command */
if (count)
@@ -1335,65 +1354,67 @@ int buslogic_abort(Scsi_Cmnd *scpnt)
#if 1
static const unsigned char buscmd[] = { CMD_START_SCSI };
struct mailbox *mb;
- size_t mbi, mbo;
+ int mbi, mbo, last_mbi;
+ unsigned long flags;
unsigned int i;
- buslogic_printk("buslogic_abort: %X %X\n",
+ buslogic_printk("%X %X\n",
inb(STATUS(scpnt->host->io_port)),
inb(INTERRUPT(scpnt->host->io_port)));
+ save_flags(flags);
cli();
mb = HOSTDATA(scpnt->host)->mb;
- mbi = HOSTDATA(scpnt->host)->last_mbi_used + 1;
+ last_mbi = HOSTDATA(scpnt->host)->last_mbi_used;
+ mbi = last_mbi + 1;
if (mbi >= 2 * BUSLOGIC_MAILBOXES)
mbi = BUSLOGIC_MAILBOXES;
do {
if (mb[mbi].status != MBX_NOT_IN_USE)
break;
+ last_mbi = mbi;
mbi++;
if (mbi >= 2 * BUSLOGIC_MAILBOXES)
mbi = BUSLOGIC_MAILBOXES;
} while (mbi != HOSTDATA(scpnt->host)->last_mbi_used);
- sti();
if (mb[mbi].status != MBX_NOT_IN_USE) {
- buslogic_printk("Lost interrupt discovered on irq %d"
- " - attempting to recover\n",
+ buslogic_printk("lost interrupt discovered on irq %d, "
+ " - attempting to recover...\n",
scpnt->host->irq);
- {
- int intval[3];
-
- intval[0] = scpnt->host->irq;
- buslogic_interrupt((int)&intval[2]);
- return SCSI_ABORT_SUCCESS;
- }
+ HOSTDATA(scpnt->host)->last_mbi_used = last_mbi;
+ buslogic_interrupt(scpnt->host->irq, NULL);
+ restore_flags(flags);
+ return SCSI_ABORT_SUCCESS;
}
+ restore_flags(flags);
/* OK, no lost interrupt. Try looking to see how many pending commands we
think we have. */
for (i = 0; i < BUSLOGIC_MAILBOXES; i++)
if (HOSTDATA(scpnt->host)->sc[i]) {
if (HOSTDATA(scpnt->host)->sc[i] == scpnt) {
- buslogic_printk("Timed out command pending for %4.4X\n",
+ buslogic_printk("timed out command pending for %4.4X.\n",
scpnt->request.dev);
if (HOSTDATA(scpnt->host)->mb[i].status != MBX_NOT_IN_USE) {
- buslogic_printk("OGMB still full - restarting\n");
+ buslogic_printk("OGMB still full - restarting...\n");
buslogic_out(scpnt->host->io_port, buscmd, sizeof buscmd);
}
} else
- buslogic_printk("Other pending command %4.4X\n",
+ buslogic_printk("other pending command: %4.4X\n",
scpnt->request.dev);
}
#endif
#if (BUSLOGIC_DEBUG & BD_ABORT)
- buslogic_printk("buslogic_abort\n");
+ buslogic_printk("called\n");
#endif
#if 1
/* This section of code should be used carefully - some devices cannot
abort a command, and this merely makes it worse. */
+ save_flags(flags);
cli();
for (mbo = 0; mbo < BUSLOGIC_MAILBOXES; mbo++)
if (scpnt == HOSTDATA(scpnt->host)->sc[mbo]) {
@@ -1401,7 +1422,7 @@ int buslogic_abort(Scsi_Cmnd *scpnt)
buslogic_out(scpnt->host->io_port, buscmd, sizeof buscmd);
break;
}
- sti();
+ restore_flags(flags);
#endif
return SCSI_ABORT_SNOOZE;
@@ -1418,7 +1439,7 @@ int buslogic_reset(Scsi_Cmnd *scpnt)
unsigned int i;
#if (BUSLOGIC_DEBUG & BD_RESET)
- buslogic_printk("buslogic_reset\n");
+ buslogic_printk("called\n");
#endif
#if 0
/* This does a scsi reset for all devices on the bus. */
@@ -1438,7 +1459,7 @@ int buslogic_reset(Scsi_Cmnd *scpnt)
interrupt for the commands that we aborted with the specified
target, or do we generate this on our own? Try it without first
and see what happens. */
- buslogic_printk("Sent BUS DEVICE RESET to target %d\n",
+ buslogic_printk("sent BUS DEVICE RESET to target %d.\n",
scpnt->target);
/* If the first does not work, then try the second. I think the
@@ -1453,7 +1474,7 @@ int buslogic_reset(Scsi_Cmnd *scpnt)
sctmp->result = DID_RESET << 16;
if (sctmp->host_scribble)
scsi_free(sctmp->host_scribble, BUSLOGIC_SG_MALLOC);
- buslogic_printk("Sending DID_RESET for target %d\n",
+ buslogic_printk("sending DID_RESET for target %d.\n",
scpnt->target);
sctmp->scsi_done(scpnt);
@@ -1510,3 +1531,36 @@ int buslogic_biosparam(Disk *disk, int dev, int *ip)
ip[2] = 1024; */
return 0;
}
+
+/* called from init/main.c */
+void buslogic_setup(char *str, int *ints)
+{
+ static const unsigned short valid_bases[]
+ = { 0x130, 0x134, 0x230, 0x234, 0x330, 0x334 };
+ static size_t setup_idx = 0;
+ size_t i;
+
+ if (setup_idx >= ARRAY_SIZE(bases) - 1) {
+ buslogic_printk("called too many times. Bad LILO params?\n");
+ return;
+ }
+ if (ints[0] != 1) {
+ buslogic_printk("malformed command line.\n");
+ buslogic_printk("usage: buslogic=<portbase>\n");
+ return;
+ }
+ for (i = 0; i < ARRAY_SIZE(valid_bases); i++)
+ if (valid_bases[i] == ints[1]) {
+ bases[setup_idx++] = ints[1];
+ bases[setup_idx] = 0;
+ return;
+ }
+ buslogic_printk("invalid base 0x%X specified.\n", ints[1]);
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but that's later... */
+Scsi_Host_Template driver_template = BUSLOGIC;
+
+# include "scsi_module.c"
+#endif
diff --git a/drivers/scsi/buslogic.h b/drivers/scsi/buslogic.h
index 04ca8b01c..4a1e4059c 100644
--- a/drivers/scsi/buslogic.h
+++ b/drivers/scsi/buslogic.h
@@ -1,13 +1,6 @@
/*
- * buslogic.h (C) 1993 David B. Gentzel
- * Low-level scsi driver for BusLogic adapters
- * by David B. Gentzel, Whitfield Software Services, Carnegie, PA
- * (gentzel@nova.enet.dec.com)
- * Thanks to BusLogic for providing the necessary documentation
- *
- * The original version of this driver was derived from aha1542.[ch] which
- * is Copyright (C) 1992 Tommy Thorn. Much has been reworked, but most of
- * basic structure and substantial chunks of code still remain.
+ * buslogic.h Copyright (C) 1993, 1994 David B. Gentzel
+ * See buslogic.c for more information.
*/
#ifndef _BUSLOGIC_H
@@ -15,22 +8,14 @@
int buslogic_detect(Scsi_Host_Template *);
int buslogic_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
int buslogic_abort(Scsi_Cmnd *);
-const char *buslogic_info(void);
+const char *buslogic_info(struct Scsi_Host *);
int buslogic_reset(Scsi_Cmnd *);
int buslogic_biosparam(Disk *, int, int *);
-#define BUSLOGIC_CMDLUN 1 /* Do not set this too high. It sucks
- up lots of memory on ISA machines
- with > 16MB because of the huge number of
- bounce buffers that need to be allocated.
- For boards that use non-ISA bus, we can
- bump this in the board detect routine.
- 10/8/94 ERY */
-
-#define BUSLOGIC { NULL, \
+#define BUSLOGIC { NULL, NULL, \
"BusLogic", \
buslogic_detect, \
- NULL, \
+ 0, /* no release func */ \
buslogic_info, \
0, /* no command func */ \
buslogic_queuecommand, \
@@ -41,7 +26,7 @@ int buslogic_biosparam(Disk *, int, int *);
0, /* set by driver */ \
0, /* set by driver */ \
0, /* set by driver */ \
- BUSLOGIC_CMDLUN, \
+ 0, /* set by driver */ \
0, \
0, /* set by driver */ \
ENABLE_CLUSTERING \
diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c
index b7e44fda5..793cfc805 100644
--- a/drivers/scsi/constants.c
+++ b/drivers/scsi/constants.c
@@ -78,7 +78,7 @@ static const char vendor[] = "VENDOR SPECIFIC";
static void print_opcode(int opcode) {
const char **table = commands[ group(opcode) ];
- switch ((int) table) {
+ switch ((unsigned long) table) {
case RESERVED_GROUP:
printk("%s(0x%02x) ", reserved, opcode);
break;
diff --git a/drivers/scsi/eata.c b/drivers/scsi/eata.c
index b6976e1bc..8f95f2c59 100644
--- a/drivers/scsi/eata.c
+++ b/drivers/scsi/eata.c
@@ -1,23 +1,87 @@
/*
- * eata.c - Low-level SCSI driver for EISA EATA SCSI controllers.
+ * eata.c - Low-level driver for EATA/DMA SCSI host adapters.
+ *
+ * 11 Mar 1995 rev. 2.00 for linux 1.2.0
+ * Fixed a bug which prevented media change detection for removable
+ * disk drives.
+ *
+ * 23 Feb 1995 rev. 1.18 for linux 1.1.94
+ * Added a check for scsi_register returning NULL.
+ *
+ * 11 Feb 1995 rev. 1.17 for linux 1.1.91
+ * Now DEBUG_RESET is disabled by default.
+ * Register a board even if it does not assert DMA protocol support
+ * (DPT SK2011B does not report correctly the dmasup bit).
+ *
+ * 9 Feb 1995 rev. 1.16 for linux 1.1.90
+ * Use host->wish_block instead of host->block.
+ * New list of Data Out SCSI commands.
+ *
+ * 8 Feb 1995 rev. 1.15 for linux 1.1.89
+ * Cleared target_time_out counter while performing a reset.
+ * All external symbols renamed to avoid possible name conflicts.
+ *
+ * 28 Jan 1995 rev. 1.14 for linux 1.1.86
+ * Added module support.
+ * Log and do a retry when a disk drive returns a target status
+ * different from zero on a recovered error.
+ *
+ * 24 Jan 1995 rev. 1.13 for linux 1.1.85
+ * Use optimized board configuration, with a measured performance
+ * increase in the range 10%-20% on i/o throughput.
+ *
+ * 16 Jan 1995 rev. 1.12 for linux 1.1.81
+ * Fix mscp structure comments (no functional change).
+ * Display a message if check_region detects a port address
+ * already in use.
+ *
+ * 17 Dec 1994 rev. 1.11 for linux 1.1.74
+ * Use the scsicam_bios_param routine. This allows an easy
+ * migration path from disk partition tables created using
+ * different SCSI drivers and non optimal disk geometry.
+ *
+ * 15 Dec 1994 rev. 1.10 for linux 1.1.74
+ * Added support for ISA EATA boards (DPT PM2011, DPT PM2021).
+ * The host->block flag is set for all the detected ISA boards.
+ * The detect routine no longer enforces LEVEL triggering
+ * for EISA boards, it just prints a warning message.
+ *
+ * 30 Nov 1994 rev. 1.09 for linux 1.1.68
+ * Redo i/o on target status CHECK_CONDITION for TYPE_DISK only.
+ * Added optional support for using a single board at a time.
*
* 18 Nov 1994 rev. 1.08 for linux 1.1.64
- * Forces sg_tablesize = 64 and can_queue = 64 if these
- * values are not correctly detected (DPT PM2012).
+ * Forces sg_tablesize = 64 and can_queue = 64 if these
+ * values are not correctly detected (DPT PM2012).
*
* 14 Nov 1994 rev. 1.07 for linux 1.1.63 Final BETA release.
* 04 Aug 1994 rev. 1.00 for linux 1.1.39 First BETA release.
*
*
* This driver is based on the CAM (Common Access Method Committee)
- * EATA (Enhanced AT Bus Attachment) rev. 2.0A.
+ * EATA (Enhanced AT Bus Attachment) rev. 2.0A, using DMA protocol.
*
- * Released by Dario Ballabio (Dario_Ballabio@milano.europe.dg.com)
+ * Copyright (C) 1994, 1995 Dario Ballabio (dario@milano.europe.dg.com)
*
*/
/*
*
+ * Here is a brief description of the DPT SCSI host adapters.
+ * All these boards provide an EATA/DMA compatible programming interface
+ * and are fully supported by this driver:
+ *
+ * PM2011B/9X - Entry Level ISA
+ * PM2021A/9X - High Performance ISA
+ * PM2012A Old EISA
+ * PM2012B Old EISA
+ * PM2022A/9X - Entry Level EISA
+ * PM2122A/9X - High Performance EISA
+ * PM2322A/9X - Extra High Performance EISA
+ *
+ * The DPT PM2001 provides only the EATA/PIO interface and hence is not
+ * supported by this driver.
+ *
* This code has been tested with up to 3 Distributed Processing Technology
* PM2122A/9X (DPT SCSI BIOS v002.D1, firmware v05E.0) eisa controllers,
* no on board cache and no RAID option.
@@ -27,9 +91,11 @@
* All boards should be configured at the same IRQ level.
* Multiple IRQ configurations are supported too.
* Boards can be located in any eisa slot (1-15) and are named EATA0,
- * EATA1,... in increasing eisa slot number.
- * In order to detect the boards, the IRQ must be _level_ triggered
- * (not _edge_ triggered).
+ * EATA1,... in increasing eisa slot number. ISA boards are detected
+ * after the eisa slot probes.
+ *
+ * The IRQ for EISA boards should be _level_ triggered (not _edge_ triggered).
+ * This is a requirement in order to support multiple boards on the same IRQ.
*
* Other eisa configuration parameters are:
*
@@ -37,8 +103,15 @@
* COMMAND TIMEOUT : ENABLED
* CACHE : DISABLED
*
+ * In order to support multiple ISA boards in a reliable way,
+ * the driver sets host->wish_block = TRUE for all ISA boards.
*/
+#if defined(MODULE)
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -49,17 +122,25 @@
#include "scsi.h"
#include "hosts.h"
#include "sd.h"
+#include <asm/dma.h>
#include <asm/irq.h>
#include "linux/in.h"
#include "eata.h"
-#define NO_DEBUG_DETECT
-#define NO_DEBUG_INTERRUPT
-#define NO_DEBUG_STATISTICS
+/* Subversion values */
+#define ISA 0
+#define ESA 1
+
+#undef FORCE_CONFIG
+
+#undef DEBUG_DETECT
+#undef DEBUG_INTERRUPT
+#undef DEBUG_STATISTICS
+#undef DEBUG_RESET
#define MAX_TARGET 8
#define MAX_IRQ 16
-#define MAX_BOARDS 15
+#define MAX_BOARDS 18
#define MAX_MAILBOXES 64
#define MAX_SGLIST 64
#define MAX_CMD_PER_LUN 2
@@ -70,8 +151,10 @@
#define IN_USE 1
#define LOCKED 2
#define IN_RESET 3
+#define IGNORE 4
#define NO_IRQ 0xff
-#define MAXLOOP 20000
+#define NO_DMA 0xff
+#define MAXLOOP 200000
#define REG_CMD 7
#define REG_STATUS 7
@@ -83,31 +166,39 @@
#define REG_LM 3
#define REG_MID 4
#define REG_MSB 5
+#define REGION_SIZE 9
+#define EISA_RANGE 0xf000
#define BSY_ASSERTED 0x80
#define DRQ_ASSERTED 0x08
#define ABSY_ASSERTED 0x01
#define IRQ_ASSERTED 0x02
-#define READ_CONFIG_PIO 0xF0
-#define SET_CONFIG_PIO 0xF1
-#define SEND_CP_PIO 0xF2
-#define RECEIVE_SP_PIO 0xF3
-#define TRUNCATE_XFR_PIO 0xF4
-#define RESET_PIO 0xF9
-#define READ_CONFIG_DMA 0xFD
-#define SET_CONFIG_DMA 0xFE
-#define SEND_CP_DMA 0xFF
+#define READ_CONFIG_PIO 0xf0
+#define SET_CONFIG_PIO 0xf1
+#define SEND_CP_PIO 0xf2
+#define RECEIVE_SP_PIO 0xf3
+#define TRUNCATE_XFR_PIO 0xf4
+#define RESET_PIO 0xf9
+#define READ_CONFIG_DMA 0xfd
+#define SET_CONFIG_DMA 0xfe
+#define SEND_CP_DMA 0xff
#define ASOK 0x00
#define ASST 0x01
+#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0])
+
/* "EATA", in Big Endian format */
#define EATA_SIGNATURE 0x41544145
+/* Number of valid bytes in the board config structure for EATA 2.0x */
+#define EATA_2_0A_SIZE 28
+#define EATA_2_0B_SIZE 30
+
/* Board info structure */
struct eata_info {
- ulong data_len; /* Number of valid bytes after this field (30) */
+ ulong data_len; /* Number of valid bytes after this field */
ulong sign; /* ASCII "EATA" signature */
unchar :4, /* unused low nibble */
- version:4; /* EATA version */
+ version:4; /* EATA version, should be 0x1 */
unchar ocsena:1, /* Overlap Command Support Enabled */
tarsup:1, /* Target Mode Supported */
:2,
@@ -116,7 +207,8 @@ struct eata_info {
ata:1, /* This is an ATA device */
haaval:1; /* Host Adapter Address Valid */
ushort cp_pad_len; /* Number of pad bytes after cp_len */
- ulong host_addr; /* Host Adapter SCSI ID */
+ unchar host_addr[3]; /* Host Adapter SCSI ID for channels 2, 1, 0 */
+ unchar reserved;
ulong cp_len; /* Number of valid bytes in cp */
ulong sp_len; /* Number of valid bytes in sp */
ushort queue_size; /* Max number of cp that can be queued */
@@ -125,9 +217,17 @@ struct eata_info {
unchar irq:4, /* Interrupt Request assigned to this controller */
irq_tr:1, /* 0 for edge triggered, 1 for level triggered */
second:1, /* 1 if this is a secondary (not primary) controller */
- drqx:2; /* DRQ Index (0=DRQ0, 1=DRQ7, 2=DRQ6, 3=DRQ5) */
+ drqx:2; /* DRQ Index (0=DMA0, 1=DMA7, 2=DMA6, 3=DMA5) */
unchar sync; /* 1 if scsi target id 7...0 is running sync scsi */
- ushort ipad[250];
+
+ /* Structure extension defined in EATA 2.0B */
+ unchar isaena:1, /* ISA i/o addressing is disabled/enabled */
+ forcaddr:1, /* Port address has been forced */
+ :6;
+ unchar max_id:5, /* Max number of SCSI target IDs */
+ max_chan:3; /* Max SCSI channel number on this board */
+
+ ushort ipad[249];
};
/* Board config structure */
@@ -152,14 +252,14 @@ struct mssp {
char mess[12];
};
-/* Command packet structure */
+/* MailBox SCSI Command Packet */
struct mscp {
unchar sreset:1, /* SCSI Bus Reset Signal should be asserted */
- interp:1, /* The controller interprets cp, not the target */
+ init:1, /* Re-initialize controller and self test */
reqsen:1, /* Transfer Request Sense Data to addr using DMA */
sg:1, /* Use Scatter/Gather */
:1,
- init:1, /* Re-initialize controller and self test */
+ interp:1, /* The controller interprets cp, not the target */
dout:1, /* Direction of Transfer is Out (Host to Target) */
din:1; /* Direction of Transfer is In (Target to Host) */
unchar sense_len; /* Request Sense Length */
@@ -196,9 +296,12 @@ struct hostdata {
unsigned int multicount; /* Total ... in second ihdlr loop */
int board_number; /* Number of this board */
char board_name[16]; /* Name of this board */
+ char board_id[256]; /* data from INQUIRY on this board */
int in_reset; /* True if board is doing a reset */
int target_time_out[MAX_TARGET]; /* N. of timeout errors on target */
int target_reset[MAX_TARGET]; /* If TRUE redo operation on target */
+ unsigned char subversion; /* Bus type, either ISA or ESA */
+ unsigned char protocol_rev; /* EATA 2.0 rev., 'A' or 'B' or 'C' */
struct mssp sp[MAX_MAILBOXES]; /* Returned status for this board */
};
@@ -209,7 +312,7 @@ static unsigned int irqlist[MAX_IRQ], calls[MAX_IRQ];
#define HD(board) ((struct hostdata *) &sh[board]->hostdata)
#define BN(board) (HD(board)->board_name)
-static void eata_interrupt_handler(int);
+static void eata2x_interrupt_handler(int, struct pt_regs *);
static int do_trace = FALSE;
static inline unchar wait_on_busy(ushort iobase) {
@@ -252,40 +355,105 @@ static inline unchar read_pio (ushort iobase, ushort *start, ushort *end) {
return FALSE;
}
-static inline int port_detect (ushort *port_base, unsigned int j,
- Scsi_Host_Template * tpnt) {
+static inline int port_detect(ushort *port_base, unsigned int j,
+ Scsi_Host_Template * tpnt) {
+ unsigned char irq, dma_channel, subversion;
+ unsigned char protocol_rev;
struct eata_info info;
- struct eata_config config;
+ char *board_status;
+
+ /* Allowed DMA channels for ISA (0 indicates reserved) */
+ unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
+
char name[16];
sprintf(name, "%s%d", driver_name, j);
+ if(check_region(*port_base, REGION_SIZE)) {
+ printk("%s: address 0x%03x in use, skipping probe.\n",
+ name, *port_base);
+ return FALSE;
+ }
+
if (do_dma(*port_base, 0, READ_CONFIG_PIO)) return FALSE;
/* Read the info structure */
if (read_pio(*port_base, (ushort *)&info, (ushort *)&info.ipad[0]))
return FALSE;
- /* check the controller "EATA" signature */
+ /* Check the controller "EATA" signature */
if (info.sign != EATA_SIGNATURE) return FALSE;
- if (!info.haaval || info.ata || info.drqvld || !info.dmasup) {
- printk("%s: unusable board found, detaching.\n", name);
+ if (ntohl(info.data_len) < EATA_2_0A_SIZE) {
+ printk("%s: config structure size (%ld bytes) too short, detaching.\n",
+ name, ntohl(info.data_len));
return FALSE;
}
+ else if (ntohl(info.data_len) == EATA_2_0A_SIZE)
+ protocol_rev = 'A';
+ else if (ntohl(info.data_len) == EATA_2_0B_SIZE)
+ protocol_rev = 'B';
+ else
+ protocol_rev = 'C';
+
+ if (protocol_rev != 'A' && info.max_chan > 0)
+ printk("%s: warning, only scsi channel 0 is supported.\n", name);
+
+ irq = info.irq;
+
+ if (*port_base & EISA_RANGE) {
+
+ if (!info.haaval || info.ata || info.drqvld) {
+ printk("%s: unusable EISA board found (%d%d%d), detaching.\n",
+ name, info.haaval, info.ata, info.drqvld);
+ return FALSE;
+ }
+
+ subversion = ESA;
+ dma_channel = NO_DMA;
+ }
+ else {
+
+ if (!info.haaval || info.ata || !info.drqvld) {
+ printk("%s: unusable ISA board found (%d%d%d), detaching.\n",
+ name, info.haaval, info.ata, info.drqvld);
+ return FALSE;
+ }
+
+ subversion = ISA;
+ dma_channel = dma_channel_table[3 - info.drqx];
+ }
+
+ if (!info.dmasup)
+ printk("%s: warning, DMA protocol support not asserted.\n", name);
- if (!info.irq_tr) {
- printk("%s: IRQ must be level triggered, detaching.\n", name);
+ if (subversion == ESA && !info.irq_tr)
+ printk("%s: warning, LEVEL triggering is suggested for IRQ %u.\n",
+ name, irq);
+
+ if (info.second)
+ board_status = "Sec.";
+ else
+ board_status = "Prim.";
+
+ /* Board detected, allocate its IRQ if not already done */
+ if ((irq >= MAX_IRQ) || ((irqlist[irq] == NO_IRQ) && request_irq
+ (irq, eata2x_interrupt_handler, SA_INTERRUPT, driver_name))) {
+ printk("%s: unable to allocate IRQ %u, detaching.\n", name, irq);
return FALSE;
}
- /* EATA board detected, allocate its IRQ if not already done */
- if ((info.irq >= MAX_IRQ) || ((irqlist[info.irq] == NO_IRQ) && request_irq
- (info.irq, eata_interrupt_handler, SA_INTERRUPT, driver_name))) {
- printk("%s: unable to allocate IRQ %u, detaching.\n", name, info.irq);
+ if (subversion == ISA && request_dma(dma_channel, driver_name)) {
+ printk("%s: unable to allocate DMA channel %u, detaching.\n",
+ name, dma_channel);
+ free_irq(irq);
return FALSE;
}
+#if defined (FORCE_CONFIG)
+ {
+ struct eata_config config;
+
/* Set board configuration */
memset((char *)&config, 0, sizeof(struct eata_config));
config.len = (ushort) htons((ushort)510);
@@ -295,25 +463,56 @@ static inline int port_detect (ushort *port_base, unsigned int j,
printk("%s: busy timeout sending configuration, detaching.\n", name);
return FALSE;
}
+ }
+#endif
sh[j] = scsi_register(tpnt, sizeof(struct hostdata));
+
+ if (sh[j] == NULL) {
+ printk("%s: unable to register host, detaching.\n", name);
+
+ if (irqlist[irq] == NO_IRQ) free_irq(irq);
+
+ if (subversion == ISA) free_dma(dma_channel);
+
+ return FALSE;
+ }
+
sh[j]->io_port = *port_base;
- sh[j]->dma_channel = 0;
- sh[j]->irq = info.irq;
+ sh[j]->n_io_port = REGION_SIZE;
+ sh[j]->dma_channel = dma_channel;
+ sh[j]->irq = irq;
sh[j]->sg_tablesize = (ushort) ntohs(info.scatt_size);
- sh[j]->this_id = (ushort) ntohl(info.host_addr);
+ sh[j]->this_id = (ushort) info.host_addr[3];
sh[j]->can_queue = (ushort) ntohs(info.queue_size);
- sh[j]->hostt->cmd_per_lun = MAX_CMD_PER_LUN;
- sh[j]->unchecked_isa_dma = FALSE;
+ sh[j]->cmd_per_lun = MAX_CMD_PER_LUN;
+
+ /* Register the I/O space that we use */
+ request_region(sh[j]->io_port, REGION_SIZE, driver_name);
+
memset(HD(j), 0, sizeof(struct hostdata));
+ HD(j)->subversion = subversion;
+ HD(j)->protocol_rev = protocol_rev;
HD(j)->board_number = j;
- irqlist[info.irq] = j;
+ irqlist[irq] = j;
+
+ if (HD(j)->subversion == ESA)
+ sh[j]->unchecked_isa_dma = FALSE;
+ else {
+ sh[j]->wish_block = TRUE;
+ sh[j]->unchecked_isa_dma = TRUE;
+ disable_dma(dma_channel);
+ clear_dma_ff(dma_channel);
+ set_dma_mode(dma_channel, DMA_MODE_CASCADE);
+ enable_dma(dma_channel);
+ }
+
strcpy(BN(j), name);
- printk("%s: SCSI ID %d, PORT 0x%03x, IRQ %u, SG %d, "\
- "Mbox %d, CmdLun %d.\n", BN(j), sh[j]->this_id,
- sh[j]->io_port, sh[j]->irq, sh[j]->sg_tablesize,
- sh[j]->can_queue, sh[j]->hostt->cmd_per_lun);
+ printk("%s: 2.0%c, %s, ID %d, PORT 0x%03x, IRQ %u, DMA %u, SG %d, "\
+ "Mbox %d, CmdLun %d.\n", BN(j), HD(j)->protocol_rev, board_status,
+ sh[j]->this_id, sh[j]->io_port, sh[j]->irq, sh[j]->dma_channel,
+ sh[j]->sg_tablesize, sh[j]->can_queue, sh[j]->cmd_per_lun);
/* DPT PM2012 does not allow to detect sg_tablesize correctly */
if (sh[j]->sg_tablesize > MAX_SGLIST || sh[j]->sg_tablesize < 2) {
@@ -328,6 +527,11 @@ static inline int port_detect (ushort *port_base, unsigned int j,
}
#if defined (DEBUG_DETECT)
+ if (protocol_rev != 'A')
+ printk("%s: EATA 2.0%c, isaena %u, forcaddr %u, max_id %u,"\
+ " max_chan %u.\n", name, protocol_rev, info.isaena,
+ info.forcaddr, info.max_id, info.max_chan);
+
printk("%s: Version 0x%x, SYNC 0x%x, infol %ld, cpl %ld spl %ld.\n",
name, info.version, info.sync, ntohl(info.data_len),
ntohl(info.cp_len), ntohl(info.sp_len));
@@ -336,15 +540,16 @@ static inline int port_detect (ushort *port_base, unsigned int j,
return TRUE;
}
-int eata_detect (Scsi_Host_Template * tpnt) {
+int eata2x_detect (Scsi_Host_Template * tpnt) {
unsigned int j = 0, k, flags;
- ushort eisa_io_port[] = {
+ ushort io_port[] = {
0x1c88, 0x2c88, 0x3c88, 0x4c88, 0x5c88, 0x6c88, 0x7c88, 0x8c88,
- 0x9c88, 0xac88, 0xbc88, 0xcc88, 0xdc88, 0xec88, 0xfc88, 0x0
+ 0x9c88, 0xac88, 0xbc88, 0xcc88, 0xdc88, 0xec88, 0xfc88,
+ 0x1f0, 0x170, 0x330, 0x230, 0x0
};
- ushort *port_base = eisa_io_port;
+ ushort *port_base = io_port;
save_flags(flags);
cli();
@@ -358,11 +563,14 @@ int eata_detect (Scsi_Host_Template * tpnt) {
while (*port_base) {
- if(j < MAX_BOARDS && port_detect(port_base, j, tpnt)) j++;
+ if (j < MAX_BOARDS && port_detect(port_base, j, tpnt)) j++;
port_base++;
}
+ if (j > 0)
+ printk("EATA/DMA 2.0x: Copyright (C) 1994, 1995 Dario Ballabio.\n");
+
restore_flags(flags);
return j;
}
@@ -373,7 +581,7 @@ static inline void build_sg_list(struct mscp *cpp, Scsi_Cmnd *SCpnt) {
sgpnt = (struct scatterlist *) SCpnt->request_buffer;
- for(k = 0; k < SCpnt->use_sg; k++) {
+ for (k = 0; k < SCpnt->use_sg; k++) {
cpp->sglist[k].address = htonl((unsigned int) sgpnt[k].address);
cpp->sglist[k].num_bytes = htonl((unsigned int) sgpnt[k].length);
}
@@ -382,11 +590,17 @@ static inline void build_sg_list(struct mscp *cpp, Scsi_Cmnd *SCpnt) {
cpp->data_len = htonl((SCpnt->use_sg * sizeof(struct sg_list)));
}
-int eata_queuecommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
+int eata2x_queuecommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
unsigned int i, j, k, flags;
struct mscp *cpp;
struct mssp *spp;
+ static const unsigned char data_out_cmds[] = {
+ 0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x0b, 0x10, 0x16, 0x18, 0x1d,
+ 0x24, 0x2b, 0x2e, 0x30, 0x31, 0x32, 0x38, 0x39, 0x3a, 0x3b, 0x3d,
+ 0x3f, 0x40, 0x41, 0x4c, 0xaa, 0xae, 0xb0, 0xb1, 0xb2, 0xb6, 0xea
+ };
+
save_flags(flags);
cli();
/* j is the board number */
@@ -398,7 +612,7 @@ int eata_queuecommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
starting from last_cp_used */
i = HD(j)->last_cp_used + 1;
- for(k = 0; k < sh[j]->can_queue; k++, i++) {
+ for (k = 0; k < sh[j]->can_queue; k++, i++) {
if (i >= sh[j]->can_queue) i = 0;
@@ -413,7 +627,7 @@ int eata_queuecommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
if (HD(j)->in_reset)
printk("%s: qcomm, already in reset.\n", BN(j));
- else if (eata_reset(SCpnt) == SCSI_RESET_SUCCESS)
+ else if (eata2x_reset(SCpnt) == SCSI_RESET_SUCCESS)
panic("%s: qcomm, SCSI_RESET_SUCCESS.\n", BN(j));
SCpnt->result = DID_BUS_BUSY << 16;
@@ -444,11 +658,13 @@ int eata_queuecommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
if (do_trace) printk("%s: qcomm, mbox %d, target %d, pid %ld.\n",
BN(j), i, SCpnt->target, SCpnt->pid);
- if (SCpnt->cmnd[0] == WRITE_10 || SCpnt->cmnd[0] == WRITE_6)
- cpp->dout = TRUE;
- else
- cpp->din = TRUE;
+ for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++)
+ if (SCpnt->cmnd[0] == data_out_cmds[k]) {
+ cpp->dout = TRUE;
+ break;
+ }
+ cpp->din = !cpp->dout;
cpp->reqsen = TRUE;
cpp->dispri = TRUE;
cpp->one = TRUE;
@@ -485,7 +701,7 @@ int eata_queuecommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
return 0;
}
-int eata_abort (Scsi_Cmnd *SCarg) {
+int eata2x_abort (Scsi_Cmnd *SCarg) {
unsigned int i, j, flags;
save_flags(flags);
@@ -543,7 +759,7 @@ int eata_abort (Scsi_Cmnd *SCarg) {
panic("%s: abort, mbox %d, invalid cp_stat.\n", BN(j), i);
}
-int eata_reset (Scsi_Cmnd *SCarg) {
+int eata2x_reset (Scsi_Cmnd *SCarg) {
unsigned int i, j, flags, time, k, limit = 0;
int arg_done = FALSE;
Scsi_Cmnd *SCpnt;
@@ -571,6 +787,8 @@ int eata_reset (Scsi_Cmnd *SCarg) {
for (k = 0; k < MAX_TARGET; k++) HD(j)->target_reset[k] = TRUE;
+ for (k = 0; k < MAX_TARGET; k++) HD(j)->target_time_out[k] = 0;
+
for (i = 0; i < sh[j]->can_queue; i++) {
if (HD(j)->cp_stat[i] == FREE) continue;
@@ -608,11 +826,15 @@ int eata_reset (Scsi_Cmnd *SCarg) {
}
printk("%s: reset, board reset done, enabling interrupts.\n", BN(j));
+
+#if defined (DEBUG_RESET)
do_trace = TRUE;
+#endif
+
HD(j)->in_reset = TRUE;
sti();
time = jiffies;
- while (jiffies < (time + 200) && limit++ < 100000000) sti();
+ while (jiffies < (time + 100) && limit++ < 100000000);
cli();
printk("%s: reset, interrupts disabled, loops %d.\n", BN(j), limit);
@@ -649,33 +871,9 @@ int eata_reset (Scsi_Cmnd *SCarg) {
}
}
-int eata_bios_param (Disk * disk, int dev, int *ip) {
- int size = disk->capacity;
-
- if(size < 0x200000) { /* < 1Gbyte */
- ip[0]=64;
- ip[1]=32;
- }
- else if (size < 0x400000) { /* < 2Gbyte */
- ip[0]=65;
- ip[1]=63;
- }
- else if(size < 0x800000) { /* < 4Gbyte */
- ip[0]=128;
- ip[1]=63;
- }
- else { /* ok up to 8Gbyte */
- ip[0]=255;
- ip[1]=63;
- }
-
- ip[2] = size / (ip[0] * ip[1]);
- return 0;
-}
-
-static void eata_interrupt_handler(int irq) {
+static void eata2x_interrupt_handler(int irq, struct pt_regs * regs) {
Scsi_Cmnd *SCpnt;
- unsigned int i, j, k, flags, status, loops, total_loops = 0;
+ unsigned int i, j, k, flags, status, tstatus, loops, total_loops = 0;
struct mssp *spp;
struct mscp *cpp;
@@ -710,7 +908,7 @@ static void eata_interrupt_handler(int irq) {
inb(sh[j]->io_port + REG_STATUS);
/* Service all mailboxes of this board */
- for(i = 0; i < sh[j]->can_queue; i++) {
+ for (i = 0; i < sh[j]->can_queue; i++) {
spp = &HD(j)->sp[i];
/* Check if this mailbox has completed the operation */
@@ -718,7 +916,11 @@ static void eata_interrupt_handler(int irq) {
spp->eoc = FALSE;
- if (HD(j)->cp_stat[i] == LOCKED) {
+ if (HD(j)->cp_stat[i] == IGNORE) {
+ HD(j)->cp_stat[i] = FREE;
+ continue;
+ }
+ else if (HD(j)->cp_stat[i] == LOCKED) {
HD(j)->cp_stat[i] = FREE;
printk("%s: ihdlr, mbox %d unlocked, count %d.\n",
BN(j), i, HD(j)->iocount);
@@ -754,30 +956,44 @@ static void eata_interrupt_handler(int irq) {
" irq %d.\n", BN(j), i, SCpnt->pid,
*(unsigned int *)SCpnt->host_scribble, irq);
+ tstatus = status_byte(spp->target_status);
+
switch (spp->adapter_status) {
- case ASOK: /* status OK */
+ case ASOK: /* status OK */
- /* Fix a "READ CAPACITY failed" error on some disk drives */
- if (spp->target_status == INTERMEDIATE_GOOD
- && SCpnt->device->type != TYPE_TAPE)
+ /* Forces a reset if a disk drive keeps returning BUSY */
+ if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE)
status = DID_ERROR << 16;
/* If there was a bus reset, redo operation on each target */
- else if (spp->target_status == CONDITION_GOOD
- && SCpnt->device->type != TYPE_TAPE
- && HD(j)->target_reset[SCpnt->target])
+ else if (tstatus != GOOD
+ && SCpnt->device->type == TYPE_DISK
+ && HD(j)->target_reset[SCpnt->target])
+ status = DID_BUS_BUSY << 16;
+
+ /* Works around a flaw in scsi.c */
+ else if (tstatus == CHECK_CONDITION
+ && SCpnt->device->type == TYPE_DISK
+ && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
status = DID_BUS_BUSY << 16;
+
else
status = DID_OK << 16;
- if (spp->target_status == 0)
+ if (tstatus == GOOD)
HD(j)->target_reset[SCpnt->target] = FALSE;
+ if (spp->target_status && SCpnt->device->type == TYPE_DISK)
+ printk("%s: ihdlr, target %d:%d, pid %ld, target_status "\
+ "0x%x, sense key 0x%x.\n", BN(j),
+ SCpnt->target, SCpnt->lun, SCpnt->pid,
+ spp->target_status, SCpnt->sense_buffer[2]);
+
HD(j)->target_time_out[SCpnt->target] = 0;
break;
- case ASST: /* Selection Time Out */
- case 0x02: /* Command Time Out */
+ case ASST: /* Selection Time Out */
+ case 0x02: /* Command Time Out */
if (HD(j)->target_time_out[SCpnt->target] > 1)
status = DID_ERROR << 16;
@@ -787,7 +1003,8 @@ static void eata_interrupt_handler(int irq) {
}
break;
- case 0x03: /* SCSI Bus Reset Received */
+ case 0x03: /* SCSI Bus Reset Received */
+ case 0x04: /* Initial Controller Power-up */
if (SCpnt->device->type != TYPE_TAPE)
status = DID_BUS_BUSY << 16;
@@ -798,15 +1015,14 @@ static void eata_interrupt_handler(int irq) {
HD(j)->target_reset[k] = TRUE;
break;
- case 0x07: /* Bus Parity Error */
- case 0x0c: /* Controller Ram Parity */
- case 0x04: /* Initial Controller Power-up */
- case 0x05: /* Unexpected Bus Phase */
- case 0x06: /* Unexpected Bus Free */
- case 0x08: /* SCSI Hung */
- case 0x09: /* Unexpected Message Reject */
- case 0x0a: /* SCSI Bus Reset Stuck */
- case 0x0b: /* Auto Request-Sense Failed */
+ case 0x07: /* Bus Parity Error */
+ case 0x0c: /* Controller Ram Parity */
+ case 0x05: /* Unexpected Bus Phase */
+ case 0x06: /* Unexpected Bus Free */
+ case 0x08: /* SCSI Hung */
+ case 0x09: /* Unexpected Message Reject */
+ case 0x0a: /* SCSI Bus Reset Stuck */
+ case 0x0b: /* Auto Request-Sense Failed */
default:
status = DID_ERROR << 16;
break;
@@ -862,3 +1078,9 @@ static void eata_interrupt_handler(int irq) {
restore_flags(flags);
return;
}
+
+#if defined(MODULE)
+Scsi_Host_Template driver_template = EATA;
+
+#include "scsi_module.c"
+#endif
diff --git a/drivers/scsi/eata.h b/drivers/scsi/eata.h
index 27b86f345..7c61df22c 100644
--- a/drivers/scsi/eata.h
+++ b/drivers/scsi/eata.h
@@ -1,37 +1,38 @@
/*
- * eata.h - used by low-level scsi driver for EISA EATA controllers.
+ * eata.h - used by the low-level driver for EATA/DMA SCSI host adapters.
*
*/
-#ifndef _EISA_EATA_H
-#define _EISA_EATA_H
+#ifndef _EATA_H
+#define _EATA_H
-#define EATA_VERSION "1.08.00"
+#include <linux/scsicam.h>
-int eata_detect(Scsi_Host_Template *);
-int eata_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-int eata_abort(Scsi_Cmnd *);
-int eata_reset(Scsi_Cmnd *);
-int eata_bios_param(Disk *, int, int*);
+#define EATA_VERSION "2.00.00"
-#define EATA { NULL, /* Ptr for modules */ \
- NULL, /* usage count for modules */ \
- "EISA EATA 2.0A rev. " EATA_VERSION " by " \
- "Dario_Ballabio@milano.europe.dg.com.", \
- eata_detect, \
+int eata2x_detect(Scsi_Host_Template *);
+int eata2x_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int eata2x_abort(Scsi_Cmnd *);
+int eata2x_reset(Scsi_Cmnd *);
+
+#define EATA { \
+ NULL, /* Ptr for modules */ \
+ NULL, /* usage count for modules */ \
+ "EATA/DMA 2.0x rev. " EATA_VERSION " ", \
+ eata2x_detect, \
NULL, /* Release */ \
NULL, \
NULL, \
- eata_queuecommand, \
- eata_abort, \
- eata_reset, \
+ eata2x_queuecommand, \
+ eata2x_abort, \
+ eata2x_reset, \
NULL, \
- eata_bios_param, \
+ scsicam_bios_param, \
0, /* can_queue, reset by detect */ \
7, /* this_id, reset by detect */ \
0, /* sg_tablesize, reset by detect */ \
0, /* cmd_per_lun, reset by detect */ \
0, /* number of boards present */ \
- 0, /* unchecked isa dma */ \
+ 1, /* unchecked isa dma, reset by detect */ \
ENABLE_CLUSTERING \
}
#endif
diff --git a/drivers/scsi/eata_dma.c b/drivers/scsi/eata_dma.c
new file mode 100644
index 000000000..e103bc9f4
--- /dev/null
+++ b/drivers/scsi/eata_dma.c
@@ -0,0 +1,1185 @@
+/************************************************************
+ * *
+ * Linux EATA SCSI driver *
+ * *
+ * based on the CAM document CAM/89-004 rev. 2.0c, *
+ * DPT's driver kit, some internal documents and source, *
+ * and several other Linux scsi drivers and kernel docs. *
+ * *
+ * The driver currently: *
+ * -supports all ISA based EATA-DMA boards *
+ * -supports all EISA based EATA-DMA boards *
+ * -supports all PCI based EATA-DMA boards *
+ * -supports multiple HBAs with & without IRQ sharing *
+ * -supports all SCSI channels on multi channel boards *
+ * -displays (more or less useful) infos in /proc/scsi *
+ * -can be loaded as module *
+ * *
+ * (c)1993,94,95 Michael Neuffer *
+ * neuffer@goofy.zdv.uni-mainz.de *
+ * *
+ * 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 kernel; if not, write to *
+ * the Free Software Foundation, Inc., 675 Mass Ave, *
+ * Cambridge, MA 02139, USA. *
+ * *
+ * I have to thank DPT for their excellent support. I took *
+ * me almost a year and a stopover at their HQ, on my first *
+ * trip to the USA, to get it, but since then they've been *
+ * very helpful and tried to give me all the infos and *
+ * support I need. *
+ * *
+ * Thanks also to Greg Hosler who did a lot of testing and *
+ * found quite a number of bugs during the development. *
+ ************************************************************
+ * last change: 95/04/10 OS: Linux 1.2.00 or higher *
+ ************************************************************/
+
+/* Look in eata_dma.h for configuration and revision information */
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/in.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <asm/types.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include "../block/blk.h"
+#include "scsi.h"
+#include "sd.h"
+#include "hosts.h"
+#include <linux/scsicam.h>
+#include "eata_dma.h"
+
+#if EATA_DMA_PROC
+#include "eata_dma_proc.h" /* If you're interested send me a mail */
+ulong reads[13]; /* /proc/scsi probably won't get */
+ulong writes[13]; /* into the kernel before pl. 1.3 */
+#endif
+
+static uint ISAbases[] =
+{0x1F0, 0x170, 0x330, 0x230};
+static unchar EISAbases[] =
+{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
+static uint registered_HBAs = 0;
+static struct Scsi_Host *last_HBA = NULL;
+static struct Scsi_Host *first_HBA = NULL;
+static unchar reg_IRQ[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static unchar reg_IRQL[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static struct eata_sp *status = 0; /* Statuspacket array */
+static void *dma_scratch = 0;
+
+static uint internal_command_finished = TRUE;
+static unchar HBA_interpret = FALSE;
+static u32 fake_int_base;
+static u32 fake_int_result;
+static struct geom_emul geometry; /* Drive 1 & 2 geometry */
+
+static ulong int_counter = 0;
+static ulong queue_counter = 0;
+
+void eata_scsi_done (Scsi_Cmnd * SCpnt)
+{
+ return;
+}
+
+void eata_fake_int_handler(s32 irq, struct pt_regs * regs)
+{
+ fake_int_result = inb(fake_int_base + HA_RSTATUS);
+ DBG(DBG_INTR3, printk("eata_fake_int_handler called irq%ld base %#lx res %#lx\n",
+ irq, fake_int_base, fake_int_result));
+ return;
+}
+
+#if EATA_DMA_PROC
+#include "eata_dma_proc.c"
+#endif
+
+int eata_release(struct Scsi_Host *sh)
+{
+ if (sh->irq && reg_IRQ[sh->irq] == 1) free_irq(sh->irq);
+ else reg_IRQ[sh->irq]--;
+
+ scsi_init_free((void *)status, 512);
+
+ if (SD(sh)->channel == 0) {
+ if (sh->dma_channel != 0xff) free_dma(sh->dma_channel);
+ if (sh->io_port && sh->n_io_port)
+ release_region(sh->io_port, sh->n_io_port);
+ }
+ return(TRUE);
+}
+
+const char *eata_info(struct Scsi_Host *host)
+{
+ static char *information = "EATA SCSI HBA Driver";
+ return information;
+}
+
+void eata_int_handler(int irq, struct pt_regs * regs)
+{
+ uint i, result = 0;
+ uint hba_stat, scsi_stat, eata_stat;
+ Scsi_Cmnd *cmd;
+ struct eata_ccb *cp;
+ struct eata_sp *sp;
+ uint base;
+ ulong flags;
+ uint x;
+ struct Scsi_Host *sh;
+
+ save_flags(flags);
+ cli();
+
+ for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->prev) {
+ if (sh->irq != irq)
+ continue;
+ if (!(inb((uint)sh->base + HA_RAUXSTAT) & HA_AIRQ))
+ continue;
+
+ int_counter++;
+
+ sp=&SD(sh)->sp;
+
+ cp = sp->ccb;
+ cmd = cp->cmd;
+ base = (uint) cmd->host->base;
+
+ hba_stat = sp->hba_stat;
+
+ scsi_stat = (sp->scsi_stat >> 1) && 0x1f;
+
+ if (sp->EOC == FALSE) {
+ eata_stat = inb(base + HA_RSTATUS);
+ printk("eata_dma: int_handler, board: %x cmd %lx returned "
+ "unfinished.\nEATA: %x HBA: %x SCSI: %x spadr %lx spadrirq "
+ "%lx, irq%d\n", base, (long)cp, eata_stat, hba_stat,
+ scsi_stat,(long)&status, (long)&status[irq], irq);
+ DBG(DBG_DELAY,DEL2(800));
+ restore_flags(flags);
+ return;
+ }
+
+ if (cp->status == LOCKED) {
+ cp->status = FREE;
+ eata_stat = inb(base + HA_RSTATUS);
+ printk("eata_dma: int_handler, freeing locked queueslot\n");
+ DBG(DBG_INTR&&DBG_DELAY,DEL2(800));
+ restore_flags(flags);
+ return;
+ }
+
+ eata_stat = inb(base + HA_RSTATUS);
+ DBG(DBG_INTR, printk("IRQ %d received, base %#.4x, pid %ld, target: "
+ "%x, lun: %x, ea_s: %#.2x, hba_s: %#.2x \n",
+ irq, base, cmd->pid, cmd->target, cmd->lun,
+ eata_stat, hba_stat));
+
+ switch (hba_stat) {
+ case HA_NO_ERROR: /* NO Error */
+ if (scsi_stat == CONDITION_GOOD
+ && cmd->device->type == TYPE_DISK
+ && (HD(cmd)->t_state[cmd->target] == RESET))
+ result = DID_BUS_BUSY << 16;
+ else if (scsi_stat == GOOD)
+ HD(cmd)->t_state[cmd->target] = FALSE;
+ else if (scsi_stat == CHECK_CONDITION
+ && cmd->device->type == TYPE_DISK
+ && (cmd->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
+ result = DID_BUS_BUSY << 16;
+ else
+ result = DID_OK << 16;
+ HD(cmd)->t_timeout[cmd->target] = FALSE;
+ break;
+ case HA_ERR_SEL_TO: /* Selection Timeout */
+ result = DID_BAD_TARGET << 16;
+ break;
+ case HA_ERR_CMD_TO: /* Command Timeout */
+ if (HD(cmd)->t_timeout[cmd->target] > 1)
+ result = DID_ERROR << 16;
+ else {
+ result = DID_TIME_OUT << 16;
+ HD(cmd)->t_timeout[cmd->target]++;
+ }
+ break;
+ case HA_ERR_RESET: /* SCSI Bus Reset Received */
+ case HA_INIT_POWERUP: /* Initial Controller Power-up */
+ if (cmd->device->type != TYPE_TAPE)
+ result = DID_BUS_BUSY << 16;
+ else
+ result = DID_ERROR << 16;
+
+ for (i = 0; i < MAXTARGET; i++)
+ HD(cmd)->t_state[i] = RESET;
+ break;
+ case HA_UNX_BUSPHASE: /* Unexpected Bus Phase */
+ case HA_UNX_BUS_FREE: /* Unexpected Bus Free */
+ case HA_BUS_PARITY: /* Bus Parity Error */
+ case HA_SCSI_HUNG: /* SCSI Hung */
+ case HA_UNX_MSGRJCT: /* Unexpected Message Reject */
+ case HA_RESET_STUCK: /* SCSI Bus Reset Stuck */
+ case HA_RSENSE_FAIL: /* Auto Request-Sense Failed */
+ case HA_PARITY_ERR: /* Controller Ram Parity */
+ default:
+ result = DID_ERROR << 16;
+ break;
+ }
+ cmd->result = result | (scsi_stat << 1);
+
+#if DBG_INTR2
+ if (scsi_stat || result || hba_stat || eata_stat != 0x50)
+ printk("eata_stat: %#x hba_stat: %#.2x,scsi_stat: %#.2x, "
+ "sense_key: %#x, result: %#.8x\n", eata_stat, hba_stat,
+ scsi_stat,cmd->sense_buffer[2] & 0xf, cmd->result);
+ DBG(DBG_INTR&&DBG_DELAY,DEL2(800));
+#endif
+
+ cp->status = FREE; /* now we can release the slot */
+
+ restore_flags(flags);
+ if(cmd->scsi_done != eata_scsi_done) cmd->scsi_done(cmd);
+ else {
+ internal_command_finished = TRUE;
+ HBA_interpret = FALSE;
+ }
+ save_flags(flags);
+ cli();
+ }
+ restore_flags(flags);
+
+ return;
+}
+
+inline uint eata_send_command(ulong addr, uint base, unchar command)
+{
+ uint loop = R_LIMIT;
+
+ while (inb(base + HA_RAUXSTAT) & HA_ABUSY)
+ if (--loop == 0)
+ return(FALSE);
+
+ outb(addr & 0x000000ff, base + HA_WDMAADDR);
+ outb((addr & 0x0000ff00) >> 8, base + HA_WDMAADDR + 1);
+ outb((addr & 0x00ff0000) >> 16, base + HA_WDMAADDR + 2);
+ outb((addr & 0xff000000) >> 24, base + HA_WDMAADDR + 3);
+ outb(command, base + HA_WCOMMAND);
+ return(TRUE);
+}
+
+int eata_queue(Scsi_Cmnd * cmd, void *(done) (Scsi_Cmnd *))
+{
+ uint i, x, y;
+ long flags;
+
+ hostdata *hd;
+ struct Scsi_Host *sh;
+ struct eata_ccb *cp;
+ struct scatterlist *sl;
+
+ save_flags(flags);
+ cli();
+
+ queue_counter++;
+
+ if (done == (void *)eata_scsi_done) {
+ if (internal_command_finished == TRUE)
+ internal_command_finished = FALSE;
+ else
+ cmd->result = (DID_ERROR << 16) + QUEUE_FULL;
+ }
+
+ hd = HD(cmd);
+ sh = cmd->host;
+
+ /* check for free slot */
+ for (y = hd->last_ccb + 1, x = 0; x < sh->can_queue; x++, y++) {
+ if (y >= sh->can_queue)
+ y = 0;
+ if (hd->ccb[y].status == FREE)
+ break;
+ }
+
+ hd->last_ccb = y;
+
+ if (x == sh->can_queue) {
+
+ DBG(DBG_QUEUE, printk("can_queue %d, x %d, y %d\n",sh->can_queue,x,y));
+#if DEBUG_EATA
+ panic("eata_dma: run out of queue slots cmdno:%ld intrno: %ld\n",
+ queue_counter, int_counter);
+#else
+ panic("eata_dma: run out of queue slots....\n");
+#endif
+ }
+
+ cp = &hd->ccb[y];
+
+ memset(cp, 0, sizeof(struct eata_ccb));
+
+ cp->status = USED; /* claim free slot */
+
+ DBG(DBG_QUEUE, printk("eata_queue pid %ld, target: %x, lun: %x, y %d\n",
+ cmd->pid, cmd->target, cmd->lun, y));
+ DBG(DBG_QUEUE && DBG_DELAY, DEL2(250));
+
+ cmd->scsi_done = (void *)done;
+
+ switch (cmd->cmnd[0]) {
+ case CHANGE_DEFINITION: case COMPARE: case COPY:
+ case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT:
+ case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER:
+ case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE:
+ case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW:
+ case WRITE_6: case WRITE_10: case WRITE_VERIFY:
+ case UPDATE_BLOCK: case WRITE_LONG: case WRITE_SAME:
+ case SEARCH_HIGH_12: case SEARCH_EQUAL_12: case SEARCH_LOW_12:
+ case WRITE_12: case WRITE_VERIFY_12: case SET_WINDOW:
+ case MEDIUM_SCAN: case SEND_VOLUME_TAG:
+ case 0xea: /* alternate number for WRITE LONG */
+ cp->DataOut = TRUE; /* Output mode */
+ break;
+ case 0x00:
+ default:
+ cp->DataIn = TRUE; /* Input mode */
+ }
+
+ if (done == (void *) eata_scsi_done && HBA_interpret == TRUE)
+ cp->Interpret = TRUE; /* Interpret command */
+
+ if (cmd->use_sg) {
+ cp->scatter = TRUE; /* SG mode */
+ cp->cp_dataDMA = htonl((long)&cp->sg_list);
+ cp->cp_datalen = htonl(cmd->use_sg*8);
+ sl=(struct scatterlist *)cmd->request_buffer;
+
+ for(i = 0; i < cmd->use_sg; i++, sl++){
+ cp->sg_list[i].data = htonl((ulong) sl->address);
+ cp->sg_list[i].len = htonl((ulong) sl->length);
+ }
+ } else {
+ cp->scatter = FALSE;
+ cp->cp_datalen = htonl(cmd->request_bufflen);
+ cp->cp_dataDMA = htonl((ulong)cmd->request_buffer);
+ }
+
+ cp->Auto_Req_Sen = TRUE;
+ cp->cp_reqDMA = htonl((ulong) cmd->sense_buffer);
+ cp->reqlen = sizeof(cmd->sense_buffer);
+
+ cp->cp_id = cmd->target;
+ cp->cp_lun = cmd->lun;
+ cp->cp_dispri = TRUE;
+ cp->cp_identify = TRUE;
+ memcpy(cp->cp_cdb, cmd->cmnd, cmd->cmd_len);
+
+ cp->cp_statDMA = htonl((ulong) &(hd->sp));
+
+ cp->cp_viraddr = cp;
+ cp->cmd = cmd;
+ cmd->host_scribble = (char *)&hd->ccb[y];
+
+ if(eata_send_command((ulong) cp, (uint) sh->base, EATA_CMD_DMA_SEND_CP) == FALSE) {
+ cmd->result = DID_ERROR << 16;
+ printk("eata_queue target %d, pid %ld, HBA busy, returning DID_ERROR, done.\n",
+ cmd->target, cmd->pid);
+ restore_flags(flags);
+ if(done != (void *)eata_scsi_done) done(cmd);
+ return (0);
+ }
+ DBG(DBG_QUEUE,printk("Queued base %#.4lx pid: %ld target: %x lun: %x slot %d irq %d\n",
+ (long)sh->base, cmd->pid, cmd->target, cmd->lun, y, sh->irq));
+ DBG(DBG_QUEUE && DBG_DELAY, DEL2(200));
+ restore_flags(flags);
+ return (0);
+}
+
+static volatile int internal_done_flag = 0;
+static volatile int internal_done_errcode = 0;
+
+static void internal_done(Scsi_Cmnd * cmd)
+{
+ internal_done_errcode = cmd->result;
+ ++internal_done_flag;
+}
+
+int eata_command(Scsi_Cmnd * cmd)
+{
+
+ DBG(DBG_COM, printk("eata_command: calling eata_queue\n"));
+
+ eata_queue(cmd, (void *)internal_done);
+
+ while (!internal_done_flag);
+ internal_done_flag = 0;
+ return (internal_done_errcode);
+}
+
+int eata_abort(Scsi_Cmnd * cmd)
+{
+ ulong flags;
+ uint loop = R_LIMIT;
+
+ save_flags(flags);
+ cli();
+
+ DBG(DBG_ABNORM, printk("eata_abort called pid: %ld target: %x lun: %x reason %x\n",
+ cmd->pid, cmd->target, cmd->lun, cmd->abort_reason));
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+
+
+ while (inb((uint)(cmd->host->base) + HA_RAUXSTAT) & HA_ABUSY)
+ if (--loop == 0) {
+ printk("eata_dma: abort, timeout error.\n");
+ restore_flags(flags);
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ return (SCSI_ABORT_ERROR);
+ }
+ if (CD(cmd)->status == FREE) {
+ DBG(DBG_ABNORM, printk("Returning: SCSI_ABORT_NOT_RUNNING\n"));
+ restore_flags(flags);
+ return (SCSI_ABORT_NOT_RUNNING);
+ }
+ if (CD(cmd)->status == USED) {
+ DBG(DBG_ABNORM, printk("Returning: SCSI_ABORT_BUSY\n"));
+ restore_flags(flags);
+ return (SCSI_ABORT_BUSY); /* SNOOZE */
+ }
+ if (CD(cmd)->status == RESET) {
+ restore_flags(flags);
+ printk("eata_dma: abort, command reset error.\n");
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ return (SCSI_ABORT_ERROR);
+ }
+ if (CD(cmd)->status == LOCKED) {
+ restore_flags(flags);
+ DBG(DBG_ABNORM, printk("eata_dma: abort, queue slot locked.\n"));
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ return (SCSI_ABORT_NOT_RUNNING);
+ } else
+ panic("eata_dma: abort: invalid slot status\n");
+}
+
+int eata_reset(Scsi_Cmnd * cmd)
+{
+ uint x, z, time, limit = 0;
+ uint loop = R_LIMIT;
+ ulong flags;
+ unchar success = FALSE;
+ Scsi_Cmnd *sp;
+
+ save_flags(flags);
+ cli();
+
+ DBG(DBG_ABNORM, printk("eata_reset called pid:%ld target: %x lun: %x reason %x\n",
+ cmd->pid, cmd->target, cmd->lun, cmd->abort_reason));
+
+
+ if (HD(cmd)->state == RESET) {
+ printk("eata_reset: exit, already in reset.\n");
+ restore_flags(flags);
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ return (SCSI_RESET_ERROR);
+ }
+
+ while (inb((uint)(cmd->host->base) + HA_RAUXSTAT) & HA_ABUSY)
+ if (--loop == 0) {
+ printk("eata_reset: exit, timeout error.\n");
+ restore_flags(flags);
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ return (SCSI_RESET_ERROR);
+ }
+ for (z = 0; z < MAXTARGET; z++) {
+ HD(cmd)->t_state[z] = RESET;
+ HD(cmd)->t_timeout[z] = FALSE;
+ }
+
+ for (x = 0; x < cmd->host->can_queue; x++) {
+
+ if (HD(cmd)->ccb[x].status == FREE)
+ continue;
+
+ if (HD(cmd)->ccb[x].status == LOCKED) {
+ HD(cmd)->ccb[x].status = FREE;
+ printk("eata_reset: locked slot %d forced free.\n", x);
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ continue;
+ }
+ sp = HD(cmd)->ccb[x].cmd;
+ HD(cmd)->ccb[x].status = RESET;
+ printk("eata_reset: slot %d in reset, pid %ld.\n", x, sp->pid);
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+
+ if (sp == NULL)
+ panic("eata_reset: slot %d, sp==NULL.\n", x);
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+
+ if (sp == cmd)
+ success = TRUE;
+ }
+
+ /* hard reset the HBA */
+ inb((uint) (cmd->host->base) + HA_RSTATUS); /* This might cause trouble */
+ eata_send_command(0, (uint) cmd->host->base, EATA_CMD_RESET);
+
+ DBG(DBG_ABNORM, printk("eata_reset: board reset done, enabling interrupts.\n"));
+ HD(cmd)->state = RESET;
+
+ restore_flags(flags);
+
+ time = jiffies;
+ while (jiffies < (time + 300) && limit++ < 10000000);
+
+ save_flags(flags);
+ cli();
+
+ DBG(DBG_ABNORM, printk("eata_reset: interrupts disabled, loops %d.\n", limit));
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+
+ for (x = 0; x < cmd->host->can_queue; x++) {
+
+ /* Skip slots already set free by interrupt */
+ if (HD(cmd)->ccb[x].status != RESET)
+ continue;
+
+ sp = HD(cmd)->ccb[x].cmd;
+ sp->result = DID_RESET << 16;
+
+ /* This mailbox is still waiting for its interrupt */
+ HD(cmd)->ccb[x].status = LOCKED;
+
+ printk("eata_reset: slot %d locked, DID_RESET, pid %ld done.\n",
+ x, sp->pid);
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ restore_flags(flags);
+ sp->scsi_done(sp);
+ cli();
+ }
+
+ HD(cmd)->state = FALSE;
+ restore_flags(flags);
+
+ if (success) {
+ DBG(DBG_ABNORM, printk("eata_reset: exit, success.\n"));
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ return (SCSI_RESET_SUCCESS);
+ } else {
+ DBG(DBG_ABNORM, printk("eata_reset: exit, wakeup.\n"));
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ return (SCSI_RESET_PUNT);
+ }
+}
+
+char * get_board_data(ulong base, uint irq, uint id)
+{
+ struct eata_ccb cp;
+ struct eata_sp sp;
+ static char *buff;
+ u32 i;
+
+ buff = dma_scratch;
+
+ memset(&cp, 0, sizeof(struct eata_ccb));
+ memset(&sp, 0, sizeof(struct eata_sp));
+ memset(buff, 0, 256);
+
+ cp.DataIn = TRUE;
+ cp.Interpret = TRUE; /* Interpret command */
+
+ cp.cp_datalen = htonl(255);
+ cp.cp_dataDMA = htonl((s32)buff);
+ cp.cp_viraddr = &cp;
+
+ cp.cp_id = id;
+ cp.cp_lun = 0;
+
+ cp.cp_cdb[0] = INQUIRY;
+ cp.cp_cdb[1] = 0;
+ cp.cp_cdb[2] = 0;
+ cp.cp_cdb[3] = 0;
+ cp.cp_cdb[4] = 255;
+ cp.cp_cdb[5] = 0;
+
+ cp.cp_statDMA = htonl((ulong) &sp);
+
+ fake_int_base = base;
+ fake_int_result = 0;
+
+ eata_send_command((u32) &cp, (u32) base, EATA_CMD_DMA_SEND_CP);
+
+ i = jiffies + 300;
+ while (!fake_int_result && jiffies <= i)
+ /* nothing */;
+
+ DBG(DBG_INTR3, printk("fake_int_result: %#lx hbastat %#lx scsistat %#lx,"
+ " buff %p sp %p\n",
+ fake_int_result, (u32) (sp.hba_stat & 0x7f),
+ (u32) sp.scsi_stat, buff, &sp));
+
+ if (jiffies > i || (fake_int_result & 1))
+ return (NULL);
+ else
+ return (buff);
+}
+
+int check_blink_state(long base)
+{
+ uint loops = 10;
+ ulong blinkindicator = 0x42445054;
+ ulong state = 0x12345678;
+ ulong oldstate = 0;
+
+ while ((loops--) && (state != oldstate)) {
+ oldstate = state;
+ state = inl((uint) base + 1);
+ }
+
+ DBG(DBG_BLINK, printk("Did Blink check. Status: %d\n",
+ (state == oldstate) && (state == blinkindicator)));
+
+ if ((state == oldstate) && (state == blinkindicator))
+ return(TRUE);
+ else
+ return (FALSE);
+}
+
+int get_conf_PIO(struct eata_register *base, struct get_conf *buf)
+{
+ ulong loop = R_LIMIT;
+ ushort *p;
+
+ u8 warning = FALSE;
+
+ if(check_region((int) base, 9)) {
+ if ((int)base == 0x1f0 || (int)base == 0x170) {
+ warning = 1;
+ } else
+ return (FALSE);
+ }
+
+ memset(buf, 0, sizeof(struct get_conf));
+
+ while (inb((uint) base + HA_RSTATUS) & HA_SBUSY)
+ if (--loop == 0)
+ return (FALSE);
+
+ DBG(DBG_PIO && DBG_PROBE,printk("Issuing PIO READ CONFIG to HBA at %lx\n",
+ (long)base));
+ eata_send_command(0, (uint) base, EATA_CMD_PIO_READ_CONFIG);
+ loop = R_LIMIT;
+ for (p = (ushort *) buf;
+ (long)p <= ((long)buf + (sizeof(struct get_conf)/ 2)); p++) {
+ while (!(inb((uint) base + HA_RSTATUS) & HA_SDRQ))
+ if (--loop == 0)
+ return (FALSE);
+ loop = R_LIMIT;
+ *p = inw((uint) base + HA_RDATA);
+ }
+ if (!(inb((uint) base + HA_RSTATUS) & HA_SERROR)) { /* Error ? */
+ DBG(DBG_PIO&&DBG_PROBE, printk("\nSignature: %c%c%c%c\n",
+ (char)buf->sig[0], (char)buf->sig[1],
+ (char)buf->sig[2], (char)buf->sig[3]));
+
+ if ((buf->sig[0] == 'E') && (buf->sig[1] == 'A')
+ && (buf->sig[2] == 'T') && (buf->sig[3] == 'A')) {
+ DBG(DBG_PIO&&DBG_PROBE, printk("EATA Controller found at %x "
+ "EATA Level: %x\n", (uint) base, (uint) (buf->version)));
+
+ while (inb((uint) base + HA_RSTATUS) & HA_SDRQ)
+ inw((uint) base + HA_RDATA);
+ if (warning == TRUE)
+ printk("Warning: HBA with IO on 0x%p detected,\n"
+ " this IO space is already allocated, probably by the IDE driver.\n"
+ " This might lead to problems.", base);
+ return (TRUE);
+ }
+ } else {
+ DBG(DBG_PROBE, printk("eata_dma: get_conf_PIO, error during transfer "
+ "for HBA at %lx\n", (long)base));
+ }
+ return (FALSE);
+}
+
+void print_config(struct get_conf *gc)
+{
+ printk("Please check values: (read config data)\n");
+ printk("LEN: %d ver:%d OCS:%d TAR:%d TRNXFR:%d MORES:%d DMAS:%d\n",
+ (uint) ntohl(gc->len), gc->version,
+ gc->OCS_enabled, gc->TAR_support, gc->TRNXFR, gc->MORE_support,
+ gc->DMA_support);
+ printk("DMAV:%d HAAV:%d SCSIID0:%d ID1:%d ID2:%d QUEUE:%d SG:%d SEC:%d\n",
+ gc->DMA_valid, gc->HAA_valid, gc->scsi_id[3], gc->scsi_id[2],
+ gc->scsi_id[1], ntohs(gc->queuesiz), ntohs(gc->SGsiz), gc->SECOND);
+ printk("IRQ:%d IRQT:%d DMAC:%d FORCADR:%d MCH:%d RIDQ:%d PCI:%d EISA:%d\n",
+ gc->IRQ, gc->IRQ_TR, (8 - gc->DMA_channel) & 7, gc->FORCADR,
+ gc->MAX_CHAN, gc->ID_qest, gc->is_PCI, gc->is_EISA);
+ DBG(DPT_DEBUG, DELAY(1400));
+}
+
+int register_HBA(long base, struct get_conf *gc, Scsi_Host_Template * tpnt)
+{
+ ulong size = 0;
+ unchar dma_channel = 0;
+ char *buff;
+ uint i;
+ struct Scsi_Host *sh;
+ hostdata *hd;
+
+ DBG(DBG_REGISTER, print_config(gc));
+
+ if(gc->HAA_valid == FALSE || ntohl(gc->len) < 0x22)
+ gc->MAX_CHAN = 0;
+
+ if (!reg_IRQ[gc->IRQ]) { /* Interrupt already registered ? */
+ if (!request_irq(gc->IRQ, (void *) eata_fake_int_handler, SA_INTERRUPT, "eata_dma")){
+ reg_IRQ[gc->IRQ] += (gc->MAX_CHAN+1);
+ if (!gc->IRQ_TR)
+ reg_IRQL[gc->IRQ] = TRUE; /* IRQ is edge triggered */
+ } else {
+ printk("Couldn't allocate IRQ %d, Sorry.", gc->IRQ);
+ return (FALSE);
+ }
+ } else { /* More than one HBA on this IRQ */
+ if (reg_IRQL[gc->IRQ] == TRUE) {
+ printk("Can't support more than one HBA on this IRQ,\n"
+ " if the IRQ is edge triggered. Sorry.\n");
+ return (FALSE);
+ } else
+ reg_IRQ[gc->IRQ] += (gc->MAX_CHAN+1);
+ }
+
+ /* if gc->DMA_valid it must be an ISA HBA and we have to register it */
+ dma_channel = 0xff;
+ if (gc->DMA_valid) {
+ if (request_dma(dma_channel = (8 - gc->DMA_channel) & 7, "eata_dma")) {
+ printk("Unable to allocate DMA channel %d for ISA HBA at %#.4lx.\n",
+ dma_channel, base);
+ reg_IRQ[gc->IRQ] -= (gc->MAX_CHAN+1);
+ if (reg_IRQ[gc->IRQ] == 0)
+ free_irq(gc->IRQ);
+ if (!gc->IRQ_TR)
+ reg_IRQL[gc->IRQ] = FALSE;
+ return (FALSE);
+ }
+ }
+
+ buff = get_board_data(base, gc->IRQ, gc->scsi_id[3]);
+
+ if (buff == NULL) {
+ if (gc->DMA_support == FALSE)
+ printk("HBA at %#.4lx doesn't support DMA. Sorry\n", base);
+ else
+ printk("HBA at %#.4lx didn't react on INQUIRY. Sorry.\n", base);
+ if (gc->DMA_valid)
+ free_dma(dma_channel);
+ reg_IRQ[gc->IRQ] -= (gc->MAX_CHAN+1);
+ if (reg_IRQ[gc->IRQ] == 0)
+ free_irq(gc->IRQ);
+ if (!gc->IRQ_TR)
+ reg_IRQL[gc->IRQ] = FALSE;
+ return (FALSE);
+ }
+
+ if (gc->DMA_support == FALSE && buff != NULL)
+ printk("HBA %.12sat %#.4lx doesn't set the DMA_support flag correctly.\n",
+ &buff[16], base);
+
+ request_region(base, 9, "eata_dma"); /* We already checked the
+ * availability, so this could
+ * only fail if we're on
+ * 0x1f0 or 0x170.
+ */
+
+ if(ntohs(gc->queuesiz) == 0) {
+ gc->queuesiz = ntohs(64);
+ printk("Warning: Queue size had to be corrected.\n"
+ "This might be a PM2012 with a defective Firmware\n");
+ }
+
+ size = sizeof(hostdata) + ((sizeof(struct eata_ccb) * ntohs(gc->queuesiz))/
+ (gc->MAX_CHAN + 1));
+
+ if (gc->MAX_CHAN) {
+ printk("This is a multichannel HBA. Linux doesn't support them,\n");
+ printk("so we'll try to register every channel as a virtual HBA.\n");
+ }
+
+ for (i = 0; i <= gc->MAX_CHAN; i++) {
+
+ sh = scsi_register(tpnt, size);
+
+ if(sh == NULL) {
+ if (gc->DMA_valid)
+ free_dma(dma_channel);
+ reg_IRQ[gc->IRQ] -= 1;
+ if (reg_IRQ[gc->IRQ] == 0)
+ free_irq(gc->IRQ);
+ if (!gc->IRQ_TR)
+ reg_IRQL[gc->IRQ] = FALSE;
+ return (FALSE);
+ }
+
+ hd = SD(sh);
+
+ memset(hd->ccb, 0, (sizeof(struct eata_ccb) * ntohs(gc->queuesiz)) /
+ (gc->MAX_CHAN + 1));
+ memset(hd->reads, 0, sizeof(ulong) * 26);
+
+ strncpy(SD(sh)->vendor, &buff[8], 8);
+ SD(sh)->vendor[8] = 0;
+ strncpy(SD(sh)->name, &buff[16], 17);
+ SD(sh)->name[17] = 0;
+ SD(sh)->revision[0] = buff[32];
+ SD(sh)->revision[1] = buff[33];
+ SD(sh)->revision[2] = buff[34];
+ SD(sh)->revision[3] = '.';
+ SD(sh)->revision[4] = buff[35];
+ SD(sh)->revision[5] = 0;
+ switch (ntohl(gc->len)) {
+ case 0x1c:
+ SD(sh)->EATA_revision = 'a';
+ break;
+ case 0x1e:
+ SD(sh)->EATA_revision = 'b';
+ break;
+ case 0x22:
+ SD(sh)->EATA_revision = 'c';
+ break;
+ default:
+ SD(sh)->EATA_revision = '?';
+ }
+ sh->base = (char *) base;
+ sh->io_port = (ushort) base;
+ sh->n_io_port = 9;
+ sh->irq = gc->IRQ;
+ sh->dma_channel = dma_channel;
+ sh->this_id = gc->scsi_id[3 - i];
+ sh->can_queue = ntohs(gc->queuesiz) / (gc->MAX_CHAN + 1);
+
+ if (gc->OCS_enabled == TRUE) {
+ sh->cmd_per_lun = sh->can_queue/C_P_L_DIV;
+#if 0 /* The memory management seems to be more stable now */
+ if (sh->cmd_per_lun > C_P_L_CURRENT_MAX)
+ sh->cmd_per_lun = C_P_L_CURRENT_MAX;
+#endif
+ } else {
+ sh->cmd_per_lun = 1;
+ }
+ sh->sg_tablesize = ntohs(gc->SGsiz);
+ if (sh->sg_tablesize > SG_SIZE || sh->sg_tablesize == 0) {
+ sh->sg_tablesize = SG_SIZE;
+ if (ntohs(gc->SGsiz) == 0)
+ printk("Warning: SG size had to be corrected.\n"
+ "This might be a PM2012 with a defective Firmware\n");
+ }
+
+ hd->channel = i;
+
+ if (buff[21] == '4')
+ hd->bustype = 'P';
+ else if (buff[21] == '2')
+ hd->bustype = 'E';
+ else
+ hd->bustype = 'I';
+
+ if (gc->SECOND)
+ hd->primary = FALSE;
+ else
+ hd->primary = TRUE;
+
+ if (hd->bustype != 'I') {
+ sh->unchecked_isa_dma = FALSE;
+ sh->wish_block = FALSE;
+ }
+ else {
+ sh->unchecked_isa_dma = TRUE; /* We're doing ISA DMA */
+ sh->wish_block = TRUE; /* This will reduce performance */
+ }
+ if((hd->primary == TRUE) && (i == 0) && HARDCODED){
+ geometry.drv[0].heads = HEADS0;
+ geometry.drv[0].sectors = SECTORS0;
+ geometry.drv[0].cylinder = CYLINDER0;
+ geometry.drv[0].id = ID0;
+ geometry.drv[0].trans = TRUE;
+ geometry.drv[1].heads = HEADS1;
+ geometry.drv[1].sectors = SECTORS1;
+ geometry.drv[1].cylinder = CYLINDER1;
+ geometry.drv[1].id = ID1;
+ geometry.drv[1].trans = TRUE;
+ } else {
+ geometry.drv[0].id=-1;
+ geometry.drv[1].id=-1;
+ }
+
+ hd->next = NULL; /* build a linked list of all HBAs */
+ hd->prev = last_HBA;
+ if(hd->prev != NULL)
+ SD(hd->prev)->next = sh;
+ last_HBA = sh;
+ if (first_HBA == NULL)
+ first_HBA = sh;
+ registered_HBAs++;
+ }
+ return (1);
+}
+
+
+long find_EISA(struct get_conf *buf)
+{
+ struct eata_register *base;
+ int i;
+
+#if CHECKPAL
+ unsigned char pal1, pal2, pal3, *p;
+#endif
+
+ for (i = 0; i < MAXEISA; i++) {
+ if (EISAbases[i] == TRUE) { /* Still a possibility ? */
+
+ base = (void *)0x1c88 + (i * 0x1000);
+#if CHECKPAL
+ p = (char *)base;
+ pal1 = *(p - 8);
+ pal2 = *(p - 7);
+ pal3 = *(p - 6);
+
+ if (((pal1 == 0x12) && (pal2 == 0x14)) ||
+ ((pal1 == 0x38) && (pal2 == 0xa3) && (pal3 == 0x82)) ||
+ ((pal1 == 0x06) && (pal2 == 0x94) && (pal3 == 0x24))) {
+ DBG(DBG_PROBE, printk("EISA EATA id tags found: %x %x %x \n",
+ (int)pal1, (int)pal2, (int)pal3));
+#endif
+ if (get_conf_PIO(base, buf) == TRUE) {
+ DBG(DBG_PROBE&&DBG_EISA,print_config(buf));
+ if (buf->IRQ) { /* We'll check the
+ * primary/secondary stuff
+ * later
+ */
+ EISAbases[i] = 0;
+ return ((ulong)base);
+ }
+ printk("No valid IRQ. HBA removed from list\n");
+ }
+ /* Nothing found here so we take it from the list */
+ EISAbases[i] = 0;
+#if CHECKPAL
+ }
+#endif
+ }
+ }
+ return (0l); /* Nothing found :-( */
+}
+
+long find_ISA(struct get_conf *buf)
+{
+ int l;
+ long ret;
+
+ for (l = 0; l < MAXISA; l++) {
+ if (ISAbases[l]) {
+ if (get_conf_PIO((struct eata_register *)ISAbases[l],buf) == TRUE){
+ ret = ISAbases[l];
+ ISAbases[l] = 0;
+ return (ret);
+ } else
+ ISAbases[l] = 0;
+ }
+ }
+ return ((long)NULL);
+}
+
+void find_PCI(struct get_conf *buf, Scsi_Host_Template * tpnt)
+{
+
+#ifndef CONFIG_PCI
+ printk("Kernel PCI support not enabled. Skipping scan for PCI HBAs.\n");
+#else
+
+ unchar pci_bus, pci_device_fn;
+ static short pci_index = 0; /* Device index to PCI BIOS calls */
+ ulong base = 0;
+ ushort com_adr;
+ ushort rev_device;
+ uint error, i, x;
+
+ if (pcibios_present()) {
+ for (i = 0; i <= MAXPCI; ++i, ++pci_index) {
+
+ if (pcibios_find_device(PCI_VENDOR_ID_DPT, PCI_DEVICE_ID_DPT,
+ pci_index, &pci_bus, &pci_device_fn))
+ break;
+ DBG(DBG_PROBE && DBG_PCI, printk("eata_dma: HBA at bus %d, device %d,"
+ " function %d, index %d\n", (int)pci_bus,
+ (int)((pci_device_fn & 0xf8) >> 3),
+ (int)(pci_device_fn & 7), pci_index));
+
+ if (!(error = pcibios_read_config_word(pci_bus, pci_device_fn,
+ PCI_CLASS_DEVICE, &rev_device))) {
+ if (rev_device == PCI_CLASS_STORAGE_SCSI) {
+ if (!(error = pcibios_read_config_word(pci_bus,
+ pci_device_fn, PCI_COMMAND,
+ (ushort *) & com_adr))) {
+ if (!((com_adr & PCI_COMMAND_IO) &&
+ (com_adr & PCI_COMMAND_MASTER))) {
+ printk("HBA has IO or BUSMASTER mode disabled\n");
+ continue;
+ }
+ } else
+ printk("error %x while reading PCI_COMMAND\n", error);
+ } else
+ printk("DEVICECLASSID %x didn't match\n", rev_device);
+ } else {
+ printk("error %x while reading PCI_CLASS_BASE\n", error);
+ continue;
+ }
+
+ if (!(error = pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &base))){
+
+ /* Check if the address is valid */
+ if (base & 0x01) {
+ base &= 0xfffffffe;
+ /* EISA tag there ? */
+ if ((inb(base) == 0x12) && (inb(base + 1) == 0x14))
+ continue; /* Jep, it's forced, so move on */
+ base += 0x10; /* Now, THIS is the real address */
+ if (base != 0x1f8) {
+ /* We didn't find it in the primary search */
+ if (get_conf_PIO((struct eata_register *)base, buf)) {
+ if (buf->FORCADR) /* If the address is forced */
+ continue; /* we'll find it later */
+
+ /* OK. We made it till here, so we can go now
+ * and register it. We only have to check and
+ * eventually remove it from the EISA and ISA list
+ */
+
+ register_HBA(base, buf, tpnt);
+
+ if (base < 0x1000) {
+ for (x = 0; x < MAXISA; ++x) {
+ if (ISAbases[x] == base) {
+ ISAbases[x] = 0;
+ break;
+ }
+ }
+ } else if ((base & 0x0fff) == 0x0c88) {
+ x = (base >> 12) & 0x0f;
+ EISAbases[x] = 0;
+ }
+ continue; /*break;*/
+ } else if (check_blink_state(base)) {
+ printk("HBA is in BLINK state. Consult your HBAs "
+ " Manual to correct this.\n");
+ }
+ }
+ }
+ } else
+ printk("error %x while reading PCI_BASE_ADDRESS_0\n", error);
+ }
+ } else
+ printk("No BIOS32 extensions present. This release still depends on it."
+ " Sorry.\n");
+#endif /* #ifndef CONFIG_PCI */
+ return;
+}
+
+int eata_detect(Scsi_Host_Template * tpnt)
+{
+ struct Scsi_Host *HBA_ptr;
+ struct get_conf gc;
+ ulong base = 0;
+ int i;
+
+ geometry.drv[0].trans = geometry.drv[1].trans = 0;
+
+ DBG((DBG_PROBE && DBG_DELAY)|| DPT_DEBUG,
+ printk("Using lots of delays to let you read the debugging output\n"));
+
+ status = scsi_init_malloc(512, GFP_ATOMIC | GFP_DMA);
+ dma_scratch = scsi_init_malloc(512, GFP_ATOMIC | GFP_DMA);
+
+ find_PCI(&gc, tpnt);
+
+ for (i = 0; i < MAXEISA; i++) {
+ base = find_EISA(&gc);
+ if (base)
+ register_HBA(base, &gc, tpnt);
+ }
+
+ for (i = 0; i <= MAXISA; i++) {
+ base = find_ISA(&gc);
+ if (base)
+ register_HBA(base, &gc, tpnt);
+ }
+
+ for (i = 0; i <= MAXIRQ; i++)
+ if (reg_IRQ[i]){
+ free_irq(i);
+ request_irq(i, eata_int_handler, SA_INTERRUPT, "EATA-DMA");
+ }
+
+ HBA_ptr = first_HBA;
+
+ if (registered_HBAs != 0) {
+ printk("EATA (Extended Attachment) driver version: %d.%d%s\n"
+ "developed in co-operation with DPT\n"
+ "(c) 1993-95 Michael Neuffer neuffer@goofy.zdv.uni-mainz.de\n",
+ VER_MAJOR, VER_MINOR, VER_SUB);
+ printk("Registered HBAs:\n");
+ printk("HBA no. Boardtype: Revis: EATA: Bus: BaseIO: IRQ: DMA: Ch: ID: Pr: QS: SG: CPL:\n");
+ for (i = 1; i <= registered_HBAs; i++) {
+ printk("scsi%-2d: %.10s v%s 2.0%c %s %#.4lx %2d",
+ HBA_ptr->host_no, SD(HBA_ptr)->name, SD(HBA_ptr)->revision,
+ SD(HBA_ptr)->EATA_revision, (SD(HBA_ptr)->bustype == 'P')?
+ "PCI ":(SD(HBA_ptr)->bustype == 'E')?"EISA":"ISA ",
+ (u32) HBA_ptr->base, HBA_ptr->irq);
+ if(HBA_ptr->dma_channel != 0xff)
+ printk(" %2x ", HBA_ptr->dma_channel);
+ else
+ printk(" %s", "BMST");
+ printk(" %d %d %c %2d %2d %2d\n", SD(HBA_ptr)->channel,
+ HBA_ptr->this_id, (SD(HBA_ptr)->primary == TRUE)?'Y':'N',
+ HBA_ptr->can_queue, HBA_ptr->sg_tablesize, HBA_ptr->cmd_per_lun);
+ HBA_ptr = SD(HBA_ptr)->next;
+ }
+ } else
+ scsi_init_free((void *)status, 512);
+
+ scsi_init_free((void *)dma_scratch, 512);
+
+ DBG(DPT_DEBUG,DELAY(1200));
+
+ return (registered_HBAs);
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = EATA_DMA;
+
+#include "scsi_module.c"
+#endif
+
+
diff --git a/drivers/scsi/eata_dma.h b/drivers/scsi/eata_dma.h
new file mode 100644
index 000000000..025dcc790
--- /dev/null
+++ b/drivers/scsi/eata_dma.h
@@ -0,0 +1,408 @@
+/********************************************************
+* Header file for eata_dma.c Linux EATA-DMA SCSI driver *
+* (c) 1993,94,95 Michael Neuffer *
+*********************************************************
+* last change: 95/04/10 *
+********************************************************/
+
+
+#ifndef _EATA_DMA_H
+#define _EATA_DMA_H
+
+#define VER_MAJOR 2
+#define VER_MINOR 3
+#define VER_SUB "5r"
+
+/************************************************************************
+ * Here you can configure your drives that are using a non-standard *
+ * geometry. *
+ * To enable this set HARDCODED to 1 *
+ * If you have only one drive that need reconfiguration, set ID1 to -1 *
+ ************************************************************************/
+#define HARDCODED 0 /* Here are drives running in emu. mode */
+
+#define ID0 0 /* SCSI ID of "IDE" drive mapped to C:
+ * If you're not sure check your config
+ * utility that came with your controller
+ */
+#define HEADS0 13 /* Number of emulated heads of this drive */
+#define SECTORS0 38 /* Number of emulated sectors */
+#define CYLINDER0 719 /* Number of emulated cylinders */
+
+#define ID1 1 /* SCSI ID of "IDE" drive mapped to D: */
+#define HEADS1 16 /* Number of emulated heads of this drive */
+#define SECTORS1 62 /* Number of emulated sectors */
+#define CYLINDER1 1024 /* Number of emulated cylinders */
+
+/************************************************************************
+ * Here you can switch parts of the code on and of *
+ ************************************************************************/
+
+#define CHECKPAL 0 /* EISA pal checking on/off */
+#define EATA_DMA_PROC 0 /* proc-fs support */
+
+/************************************************************************
+ * Debug options. *
+ * Enable DEBUG and whichever options you require. *
+ ************************************************************************/
+#define DEBUG_EATA 1 /* Enable debug code. */
+#define DPT_DEBUG 0 /* Bobs special */
+#define DBG_DELAY 0 /* Build in delays so debug messages can be
+ * be read before they vanish of the top of
+ * the screen!
+ */
+#define DBG_PROBE 0 /* Debug probe routines. */
+#define DBG_PCI 0 /* Trace PCI routines */
+#define DBG_EISA 0 /* Trace EISA routines */
+#define DBG_ISA 0 /* Trace ISA routines */
+#define DBG_BLINK 0 /* Trace Blink check */
+#define DBG_PIO 0 /* Trace get_config_PIO */
+#define DBG_COM 0 /* Trace command call */
+#define DBG_QUEUE 0 /* Trace command queueing. */
+#define DBG_INTR 0 /* Trace interrupt service routine. */
+#define DBG_INTR2 0 /* Trace interrupt service routine. */
+#define DBG_INTR3 0 /* Trace interrupt service routine. */
+#define DBG_PROC 0 /* Debug proc-fs related statistics */
+#define DBG_REGISTER 0 /* */
+#define DBG_ABNORM 1 /* Debug abnormal actions (reset, abort)*/
+
+#if DEBUG_EATA
+#define DBG(x, y) if ((x)) {y;}
+#else
+#define DBG(x, y)
+#endif
+
+
+#define EATA_DMA { \
+ NULL, NULL, \
+ "EATA (Extended Attachment) driver", \
+ eata_detect, \
+ eata_release, \
+ eata_info, \
+ eata_command, \
+ eata_queue, \
+ eata_abort, \
+ eata_reset, \
+ NULL, /* Slave attach */ \
+ scsicam_bios_param, \
+ 0, /* Canqueue */ \
+ 0, /* this_id */ \
+ 0, /* sg_tablesize */ \
+ 0, /* cmd_per_lun */ \
+ 0, /* present */ \
+ 1, /* True if ISA */ \
+ ENABLE_CLUSTERING }
+
+int eata_detect(Scsi_Host_Template *);
+const char *eata_info(struct Scsi_Host *);
+int eata_command(Scsi_Cmnd *);
+int eata_queue(Scsi_Cmnd *, void *(done)(Scsi_Cmnd *));
+int eata_abort(Scsi_Cmnd *);
+int eata_reset(Scsi_Cmnd *);
+int eata_release(struct Scsi_Host *);
+
+/*********************************************
+ * Misc. definitions *
+ *********************************************/
+
+#ifndef TRUE
+# define TRUE 1
+#endif
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+#define R_LIMIT 0x20000
+
+#define MAXISA 4
+#define MAXEISA 16
+#define MAXPCI 16
+#define MAXIRQ 16
+#define MAXTARGET 8
+
+#define MAX_PCI_DEVICES 32 /* Maximum # Of Devices Per Bus */
+#define MAX_METHOD_2 16 /* Max Devices For Method 2 */
+#define MAX_PCI_BUS 16 /* Maximum # Of Busses Allowed */
+
+#define SG_SIZE 64
+
+#define C_P_L_CURRENT_MAX 16 /* Until this limit in the mm is removed
+ * Kernels < 1.1.86 died horrible deaths
+ * if you used values >2. The memory management
+ * since pl1.1.86 seems to cope with up to 10
+ * queued commands per device.
+ * Since 1.2.0 the memory management seems to
+ * have no more problems......
+ */
+#define C_P_L_DIV 3 /* 1 <= C_P_L_DIV <= 8
+ * You can use this parameter to fine-tune
+ * the driver. Depending on the number of
+ * devices and their speed and ability to queue
+ * commands, you will get the best results with a
+ * value
+ * ~= numdevices-(devices_unable_to_queue_commands/2)
+ * The reason for this is that the disk driver
+ * tends to flood the queue, so that other
+ * drivers have problems to queue commands
+ * themselves. This can for example result in
+ * the effect that the tape stops during disk
+ * accesses.
+ */
+
+#define FREE 0
+#define USED 1
+#define TIMEOUT 2
+#define RESET 4
+#define LOCKED 8
+
+#define HD(cmd) ((hostdata *)&(cmd->host->hostdata))
+#define CD(cmd) ((struct eata_ccb *)(cmd->host_scribble))
+#define SD(host) ((hostdata *)&(host->hostdata))
+
+#define DELAY(x) { int i; i = jiffies + x; while (jiffies < i); }
+#define DEL2(x) { ulong i; for (i = 0; i < 0xffff*x; i++); }
+
+/***********************************************
+ * EATA Command & Register definitions *
+ ***********************************************/
+#define PCI_REG_DPTconfig 0x40
+#define PCI_REG_PumpModeAddress 0x44
+#define PCI_REG_PumpModeData 0x48
+#define PCI_REG_ConfigParam1 0x50
+#define PCI_REG_ConfigParam2 0x54
+
+
+#define EATA_CMD_PIO_READ_CONFIG 0xf0
+#define EATA_CMD_PIO_SET_CONFIG 0xf1
+#define EATA_CMD_PIO_SEND_CP 0xf2
+#define EATA_CMD_PIO_RECEIVE_SP 0xf3
+#define EATA_CMD_PIO_TRUNC 0xf4
+
+#define EATA_CMD_RESET 0xf9
+
+#define EATA_CMD_DMA_READ_CONFIG 0xfd
+#define EATA_CMD_DMA_SET_CONFIG 0xfe
+#define EATA_CMD_DMA_SEND_CP 0xff
+
+#define ECS_EMULATE_SENSE 0xd4
+
+#define HA_WCOMMAND 0x07 /* command register offset */
+#define HA_WDMAADDR 0x02 /* DMA address LSB offset */
+#define HA_RAUXSTAT 0x08 /* aux status register offset*/
+#define HA_RSTATUS 0x07 /* status register offset */
+#define HA_RDATA 0x00 /* data register (16bit) */
+
+#define HA_ABUSY 0x01 /* aux busy bit */
+#define HA_AIRQ 0x02 /* aux IRQ pending bit */
+#define HA_SERROR 0x01 /* pr. command ended in error*/
+#define HA_SMORE 0x02 /* more data soon to come */
+#define HA_SCORR 0x04 /* data corrected */
+#define HA_SDRQ 0x08 /* data request active */
+#define HA_SSC 0x10 /* seek complete */
+#define HA_SFAULT 0x20 /* write fault */
+#define HA_SREADY 0x40 /* drive ready */
+#define HA_SBUSY 0x80 /* drive busy */
+#define HA_SDRDY HA_SSC+HA_SREADY+HA_SDRQ
+
+#define HA_NO_ERROR 0x00
+#define HA_ERR_SEL_TO 0x01
+#define HA_ERR_CMD_TO 0x02
+#define HA_ERR_RESET 0x03
+#define HA_INIT_POWERUP 0x04
+#define HA_UNX_BUSPHASE 0x05
+#define HA_UNX_BUS_FREE 0x06
+#define HA_BUS_PARITY 0x07
+#define HA_SCSI_HUNG 0x08
+#define HA_UNX_MSGRJCT 0x09
+#define HA_RESET_STUCK 0x0a
+#define HA_RSENSE_FAIL 0x0b
+#define HA_PARITY_ERR 0x0c
+#define HA_CP_ABORT_NA 0x0d
+#define HA_CP_ABORTED 0x0e
+#define HA_CP_RESET_NA 0x0f
+#define HA_CP_RESET 0x10
+
+/**********************************************
+ * Message definitions *
+ **********************************************/
+
+struct reg_bit { /* reading this one will clear the interrupt */
+ unchar error:1; /* previous command ended in an error */
+ unchar more:1; /* more DATA coming soon, poll BSY & DRQ (PIO) */
+ unchar corr:1; /* data read was successfully corrected with ECC*/
+ unchar drq:1; /* data request active */
+ unchar sc:1; /* seek complete */
+ unchar fault:1; /* write fault */
+ unchar ready:1; /* drive ready */
+ unchar busy:1; /* controller busy */
+};
+
+struct reg_abit { /* reading this won't clear the interrupt */
+ unchar abusy:1; /* auxiliary busy */
+ unchar irq:1; /* set when drive interrupt is asserted */
+ unchar dummy:6;
+};
+
+struct eata_register { /* EATA register set */
+ unchar data_reg[2]; /* R, couldn't figure this one out */
+ unchar cp_addr[4]; /* W, CP address register */
+ union {
+ unchar command; /* W, command code: [read|set] conf, send CP*/
+ struct reg_bit status; /* R, see register_bit1 */
+ unchar statusunchar;
+ } ovr;
+ struct reg_abit aux_stat; /* R, see register_bit2 */
+};
+
+/**********************************************
+ * Other definitions *
+ **********************************************/
+
+struct eata_sg_list
+{
+ ulong data;
+ ulong len;
+};
+
+struct get_conf { /* Read Configuration Array */
+ ulong len; /* Should return 0x22 */
+ unchar sig[4]; /* Signature MUST be "EATA" */
+ unchar version2:4,
+ version:4; /* EATA Version level */
+ unchar OCS_enabled:1, /* Overlap Command Support enabled */
+ TAR_support:1, /* SCSI Target Mode supported */
+ TRNXFR:1, /* Truncate Transfer Cmd not necessary */
+ /* Only used in PIO Mode */
+ MORE_support:1, /* MORE supported (only PIO Mode) */
+ DMA_support:1, /* DMA supported Driver uses only */
+ /* this mode */
+ DMA_valid:1, /* DRQ value in Byte 30 is valid */
+ ATA:1, /* ATA device connected (not supported) */
+ HAA_valid:1; /* Hostadapter Address is valid */
+
+ ushort cppadlen; /* Number of pad unchars send after CD data */
+ /* set to zero for DMA commands */
+ unchar scsi_id[4]; /* SCSI ID of controller 2-0 Byte 0 res. */
+ /* if not, zero is returned */
+ ulong cplen; /* CP length: number of valid cp unchars */
+ ulong splen; /* Number of unchars returned after */
+ /* Receive SP command */
+ ushort queuesiz; /* max number of queueable CPs */
+ ushort dummy;
+ ushort SGsiz; /* max number of SG table entries */
+ unchar IRQ:4, /* IRQ used this HA */
+ IRQ_TR:1, /* IRQ Trigger: 0=edge, 1=level */
+ SECOND:1, /* This is a secondary controller */
+ DMA_channel:2; /* DRQ index, DRQ is 2comp of DRQX */
+ unchar sync; /* device at ID 7 tru 0 is running in */
+ /* synchronous mode, this will disappear */
+ unchar DSBLE:1, /* ISA i/o addressing is disabled */
+ FORCADR:1, /* i/o address has been forced */
+ :6;
+ unchar MAX_ID:5, /* Max number of SCSI target IDs */
+ MAX_CHAN:3; /* Number of SCSI busses on HBA */
+ unchar MAX_LUN; /* Max number of LUNs */
+ unchar :5,
+ ID_qest:1, /* Raidnum ID is questionable */
+ is_PCI:1, /* HBA is PCI */
+ is_EISA:1; /* HBA is EISA */
+ unchar unused[478];
+};
+
+struct eata_ccb { /* Send Command Packet structure */
+
+ unchar SCSI_Reset:1, /* Cause a SCSI Bus reset on the cmd */
+ HBA_Init:1, /* Cause Controller to reinitialize */
+ Auto_Req_Sen:1, /* Do Auto Request Sense on errors */
+ scatter:1, /* Data Ptr points to a SG Packet */
+ Resrvd:1, /* RFU */
+ Interpret:1, /* Interpret the SCSI cdb of own use */
+ DataOut:1, /* Data Out phase with command */
+ DataIn:1; /* Data In phase with command */
+ unchar reqlen; /* Request Sense Length */
+ /* Valid if Auto_Req_Sen=1 */
+ unchar unused[3];
+ unchar FWNEST:1, /* send cmd to phys RAID component*/
+ unused2:7;
+ unchar Phsunit:1, /* physical unit on mirrored pair */
+ I_AT:1, /* inhibit address translation */
+ I_HBA_C:1, /* HBA Inhibit caching */
+ unused3:5;
+
+ unchar cp_id; /* SCSI Device ID of target */
+ unchar cp_lun:3,
+ :2,
+ cp_luntar:1, /* CP is for target ROUTINE */
+ cp_dispri:1, /* Grant disconnect privilege */
+ cp_identify:1; /* Always TRUE */
+ unchar cp_msg1; /* Message bytes 0-3 */
+ unchar cp_msg2;
+ unchar cp_msg3;
+ unchar cp_cdb[12]; /* Command Descriptor Block */
+ ulong cp_datalen; /* Data Transfer Length */
+ /* If scatter=1 len of sg package */
+ void *cp_viraddr; /* address of this ccb */
+ ulong cp_dataDMA; /* Data Address, if scatter=1 */
+ /* address of scatter packet */
+ ulong cp_statDMA; /* address for Status Packet */
+ ulong cp_reqDMA; /* Request Sense Address, used if */
+ /* CP command ends with error */
+
+ ulong timeout;
+ unchar retries;
+ unchar status; /* status of this queueslot */
+ struct eata_sg_list sg_list[SG_SIZE];
+ Scsi_Cmnd *cmd; /* address of cmd */
+};
+
+
+struct eata_sp {
+ unchar hba_stat:7, /* HBA status */
+ EOC:1; /* True if command finished */
+ unchar scsi_stat; /* Target SCSI status */
+ unchar reserved[2];
+ ulong residue_len; /* Number of unchars not transferred */
+ struct eata_ccb *ccb; /* Address set in COMMAND PACKET */
+ unchar msg[12];
+};
+
+typedef struct hstd {
+ char vendor[9];
+ char name[18];
+ char revision[6];
+ char EATA_revision;
+ unchar bustype; /* bustype of HBA */
+ unchar channel; /* no. of scsi channel */
+ unchar state; /* state of HBA */
+ unchar primary; /* true if primary */
+ ulong reads[13];
+ ulong writes[13];
+ unchar t_state[MAXTARGET]; /* state of Target (RESET,..) */
+ uint t_timeout[MAXTARGET]; /* timeouts on target */
+ uint last_ccb; /* Last used ccb */
+ struct Scsi_Host *next;
+ struct Scsi_Host *prev;
+ struct eata_sp sp; /* status packet */
+ struct eata_ccb ccb[0]; /* ccb array begins here */
+}hostdata;
+
+
+
+/* structure for max. 2 emulated drives */
+struct drive_geom_emul {
+ unchar trans; /* translation flag 1=transl */
+ unchar channel; /* SCSI channel number */
+ unchar HBA; /* HBA number (prim/sec) */
+ unchar id; /* drive id */
+ unchar lun; /* drive lun */
+ uint heads; /* number of heads */
+ uint sectors; /* number of sectors */
+ uint cylinder; /* number of cylinders */
+};
+
+struct geom_emul {
+ int bios_drives; /* number of emulated drives */
+ struct drive_geom_emul drv[2]; /* drive structures */
+};
+
+#endif /* _EATA_H */
diff --git a/drivers/scsi/fdomain.c b/drivers/scsi/fdomain.c
index 6a3159187..806dc0151 100644
--- a/drivers/scsi/fdomain.c
+++ b/drivers/scsi/fdomain.c
@@ -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: Wed Nov 2 16:37:58 1994 by faith@cs.unc.edu
+ * Revised: Mon Jun 5 09:21:54 1995 by faith@cs.unc.edu
* Author: Rickard E. Faith, faith@cs.unc.edu
- * Copyright 1992, 1993, 1994 Rickard E. Faith
+ * Copyright 1992, 1993, 1994, 1995 Rickard E. Faith
*
- * $Id: fdomain.c,v 5.20 1994/11/02 21:38:33 root Exp $
+ * $Id: fdomain.c,v 5.28 1995/06/05 13:21:57 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
@@ -40,7 +40,8 @@
The following BIOS versions are supported: 2.0, 3.0, 3.2, 3.4, and 3.5.
The following chips are supported: TMC-1800, TMC-18C50, TMC-18C30.
- Reports suggest that the driver will also work with the 36C70 chip.
+ Reports suggest that the driver will also work with the 36C70 chip and
+ with the Quantum ISA-200S and ISA-250MG SCSI adapters.
Please note that the drive ordering that Future Domain implemented in BIOS
versions 3.4 and 3.5 is the opposite of the order (currently) used by the
@@ -133,6 +134,17 @@
Thanks to Eric Kasten (tigger@petroglyph.cl.msu.edu) for providing the
patch for the version 3.5 BIOS.
+ Thanks for Stephen Henson (shenson@nyx10.cs.du.edu) for providing the
+ patch for the Quantum ISA-200S SCSI adapter.
+
+ Thanks to Adam Bowen for the signature to the 1610M/MER/MEX scsi cards,
+ and to Martin Andrews (andrewm@ccfadm.eeg.ccf.org) for the signature to
+ some random TMC-1680 repackaged by IBM.
+
+ Thanks for Mark Singer (elf@netcom.com) and Richard Simpson
+ (rsimpson@ewrcsdra.demon.co.uk) for more Quantum signatures and detective
+ work on the Quantum RAM layout.
+
All of the alpha testers deserve much thanks.
@@ -182,7 +194,7 @@
#include <linux/string.h>
#include <linux/ioport.h>
-#define VERSION "$Revision: 5.20 $"
+#define VERSION "$Revision: 5.28 $"
/* START OF USER DEFINABLE OPTIONS */
@@ -273,6 +285,7 @@ static void *bios_base = NULL;
static int bios_major = 0;
static int bios_minor = 0;
static int PCI_bus = 0;
+static int Quantum = 0; /* Quantum board variant */
static int interrupt_level = 0;
static volatile int in_command = 0;
static Scsi_Cmnd *current_SC = NULL;
@@ -299,7 +312,7 @@ static int Write_SCSI_Data_port;
static int FIFO_Size = 0x2000; /* 8k FIFO for
pre-tmc18c30 chips */
-extern void fdomain_16x0_intr( int unused );
+extern void fdomain_16x0_intr( int irq, struct pt_regs * regs );
static void *addresses[] = {
(void *)0xc8000,
@@ -307,6 +320,7 @@ static void *addresses[] = {
(void *)0xce000,
(void *)0xde000,
(void *)0xd0000, /* Extra addresses for PCI boards */
+ (void *)0xe0000,
};
#define ADDRESS_COUNT (sizeof( addresses ) / sizeof( unsigned ))
@@ -347,17 +361,24 @@ struct signature {
int sig_length;
int major_bios_version;
int minor_bios_version;
- int PCI_bus;
+ int flag; /* 1 == PCI_bus, 2 == ISA_200S, 3 == ISA_250MG, 4 == ISA_200S */
} signatures[] = {
/* 1 2 3 4 5 6 */
/* 123456789012345678901234567890123456789012345678901234567890 */
{ "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) 1986-1990 1800-V2.07/28/89", 72, 50, 2, 0, 2 },
+ { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.0", 73, 43, 2, 0, 3 },
+ { "FUTURE DOMAIN CORP. (C) 1991 1800-V2.0.", 72, 39, 2, 0, 4 },
{ "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 },
+ { "IBM F1 P2 BIOS v1.0104/29/93", 5, 28, 3, -1, 0 },
{ "Future Domain Corp. V1.0008/18/93", 5, 33, 3, 4, 0 },
- { "FUTURE DOMAIN CORP. V3.5008/18/93", 5, 34, 3, 5, 0 },
{ "Future Domain Corp. V1.0008/18/93", 26, 33, 3, 4, 1 },
+ /* This next signature may not be a 3.5 bios */
+ { "Future Domain Corp. V2.0108/18/93", 5, 33, 3, 5, 0 },
+ { "FUTURE DOMAIN CORP. V3.5008/18/93", 5, 34, 3, 5, 0 },
+ { "FUTURE DOMAIN 18c30/18c50/1800 (C) 1994 V3.5", 5, 44, 3, 5, 0 },
{ "FUTURE DOMAIN TMC-18XX", 5, 22, -1, -1, 0 },
/* READ NOTICE ABOVE *BEFORE* YOU WASTE YOUR TIME ADDING A SIGNATURE
@@ -370,27 +391,38 @@ struct signature {
geometry information in the on-board RAM area for each of the first
three BIOS's, it is still important to enter a fully qualified
signature in the table for any new BIOS's (after the host SCSI ID and
- geometry location are verified.) */
+ geometry location are verified). */
};
#define SIGNATURE_COUNT (sizeof( signatures ) / sizeof( struct signature ))
-static void print_banner( struct Scsi_Host * shpnt )
+static void print_banner( struct Scsi_Host *shpnt )
{
- printk( "%s", fdomain_16x0_info(shpnt) );
- printk( "Future Domain: BIOS version %d.%d, %s\n",
- bios_major, bios_minor,
+ if (!shpnt) return; /* This won't ever happen */
+
+ printk( "scsi%d <fdomain>: BIOS version ", shpnt->host_no );
+
+ if (bios_major >= 0) printk( "%d.", bios_major );
+ else printk( "?." );
+
+ if (bios_minor >= 0) printk( "%d", bios_minor );
+ else printk( "?." );
+
+ printk( " at 0x%x using scsi id %d\n",
+ (unsigned)bios_base, shpnt->this_id );
+
+ printk( "scsi%d <fdomain>: %s chip at 0x%x irq ",
+ shpnt->host_no,
chip == tmc1800 ? "TMC-1800"
: (chip == tmc18c50 ? "TMC-18C50"
- : (chip == tmc18c30 ? "TMC-18C30" : "Unknown")) );
-
- if (interrupt_level) {
- printk( "Future Domain: BIOS at %x; port base at %x; using IRQ %d\n",
- (unsigned)bios_base, port_base, interrupt_level );
- } else {
- printk( "Future Domain: BIOS at %x; port base at %x; *NO* IRQ\n",
- (unsigned)bios_base, port_base );
- }
+ : (chip == tmc18c30 ? "TMC-18C30" : "Unknown")),
+ port_base );
+
+ if (interrupt_level) printk( "%d", interrupt_level );
+ else printk( "<none>" );
+
+ if (PCI_bus) printk( " (PCI bus)" );
+ printk( "\n" );
}
static void do_pause( unsigned amount ) /* Pause for amount*10 milliseconds */
@@ -509,6 +541,7 @@ int fdomain_16x0_detect( Scsi_Host_Template *tpnt )
int i, j;
int flag = 0;
int retcode;
+ struct Scsi_Host *shpnt;
#if DO_DETECT
const int buflen = 255;
Scsi_Cmnd SCinit;
@@ -532,7 +565,8 @@ int fdomain_16x0_detect( Scsi_Host_Template *tpnt )
signatures[j].signature, signatures[j].sig_length )) {
bios_major = signatures[j].major_bios_version;
bios_minor = signatures[j].minor_bios_version;
- PCI_bus = signatures[j].PCI_bus;
+ PCI_bus = (signatures[j].flag == 1);
+ Quantum = (signatures[j].flag > 1) ? signatures[j].flag : 0;
bios_base = addresses[i];
}
}
@@ -554,8 +588,21 @@ int fdomain_16x0_detect( Scsi_Host_Template *tpnt )
DOS (this geometry has nothing to do with physical geometry).
*/
- port_base = *((char *)bios_base + 0x1fcc)
- + (*((char *)bios_base + 0x1fcd) << 8);
+ switch (Quantum) {
+ case 2: /* ISA_200S */
+ case 3: /* ISA_250MG */
+ port_base = *((char *)bios_base + 0x1fa2)
+ + (*((char *)bios_base + 0x1fa3) << 8);
+ break;
+ case 4: /* ISA_200S (another one) */
+ port_base = *((char *)bios_base + 0x1fa3)
+ + (*((char *)bios_base + 0x1fa4) << 8);
+ break;
+ default:
+ port_base = *((char *)bios_base + 0x1fcc)
+ + (*((char *)bios_base + 0x1fcd) << 8);
+ break;
+ }
#if DEBUG_DETECT
printk( " %x,", port_base );
@@ -617,7 +664,7 @@ int fdomain_16x0_detect( Scsi_Host_Template *tpnt )
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) {
+ for (i = 0xfff8; !flag && i > 0xe000; i -= 8) {
port_base = i;
if (check_region( port_base, 0x10 )) {
#if DEBUG_DETECT
@@ -637,8 +684,6 @@ int fdomain_16x0_detect( Scsi_Host_Template *tpnt )
return 0; /* Cannot find valid set of ports */
}
- print_banner(NULL);
-
SCSI_Mode_Cntl_port = port_base + SCSI_Mode_Cntl;
FIFO_Data_Count_port = port_base + FIFO_Data_Count;
Interrupt_Cntl_port = port_base + Interrupt_Cntl;
@@ -657,48 +702,51 @@ int fdomain_16x0_detect( Scsi_Host_Template *tpnt )
if (fdomain_test_loopback()) {
#if DEBUG_DETECT
- printk( "Future Domain: LOOPBACK TEST FAILED, FAILING DETECT!\n" );
+ printk( "fdomain: LOOPBACK TEST FAILED, FAILING DETECT!\n" );
#endif
return 0;
- } /* Log IRQ with kernel */
-
+ }
+
+ if ((bios_major == 3 && bios_minor >= 2) || bios_major < 0) {
+ adapter_mask = 0x80;
+ tpnt->this_id = 7;
+ }
+
+ /* Print out a banner here in case we can't
+ get resources. */
+
+ shpnt = scsi_register( tpnt, 0 );
+ print_banner( shpnt );
+
+ /* Log IRQ with kernel */
if (!interrupt_level) {
- panic( "Future Domain: *NO* interrupt level selected!\n" );
+ panic( "fdomain: *NO* interrupt level selected!\n" );
} else {
/* Register the IRQ with the kernel */
- retcode = request_irq( interrupt_level, fdomain_16x0_intr, SA_INTERRUPT, "FDomain");
+ retcode = request_irq( interrupt_level,
+ fdomain_16x0_intr, SA_INTERRUPT, "fdomain" );
if (retcode < 0) {
if (retcode == -EINVAL) {
- printk( "Future Domain: IRQ %d is bad!\n", interrupt_level );
- printk( " This shouldn't happen!\n" );
- printk( " Send mail to faith@cs.unc.edu\n" );
+ printk( "fdomain: IRQ %d is bad!\n", interrupt_level );
+ printk( " This shouldn't happen!\n" );
+ printk( " Send mail to faith@cs.unc.edu\n" );
} else if (retcode == -EBUSY) {
- printk( "Future Domain: IRQ %d is already in use!\n",
- interrupt_level );
- printk( " Please use another IRQ!\n" );
+ printk( "fdomain: IRQ %d is already in use!\n", interrupt_level );
+ printk( " Please use another IRQ!\n" );
} else {
- printk( "Future Domain: Error getting IRQ %d\n", interrupt_level );
- printk( " This shouldn't happen!\n" );
- printk( " Send mail to faith@cs.unc.edu\n" );
+ printk( "fdomain: Error getting IRQ %d\n", interrupt_level );
+ printk( " This shouldn't happen!\n" );
+ printk( " Send mail to faith@cs.unc.edu\n" );
}
- panic( "Future Domain: Driver requires interruptions\n" );
- } else {
- printk( "Future Domain: IRQ %d requested from kernel\n",
- interrupt_level );
+ panic( "fdomain: Driver requires interruptions\n" );
}
}
/* Log I/O ports with kernel */
+ request_region( port_base, 0x10, "fdomain" );
- snarf_region( port_base, 0x10 );
-
- if ((bios_major == 3 && bios_minor >= 2) || bios_major < 0) {
- adapter_mask = 0x80;
- tpnt->this_id = 7;
- }
-
#if DO_DETECT
/* These routines are here because of the way the SCSI bus behaves after
@@ -713,7 +761,7 @@ int fdomain_16x0_detect( Scsi_Host_Template *tpnt )
SCinit.use_sg = 0;
SCinit.lun = 0;
- printk( "Future Domain detection routine scanning for devices:\n" );
+ printk( "fdomain: detection routine scanning for devices:\n" );
for (i = 0; i < 8; i++) {
SCinit.target = i;
if (i == tpnt->this_id) /* Skip host adapter */
@@ -752,15 +800,15 @@ int fdomain_16x0_detect( Scsi_Host_Template *tpnt )
}
#endif
- return 1;
+ return 1; /* Maximum of one adapter will be detected. */
}
-const char *fdomain_16x0_info(struct Scsi_Host * shpnt)
+const char *fdomain_16x0_info( struct Scsi_Host *ignore )
{
static char buffer[80];
char *pt;
- strcpy( buffer, "Future Domain: TMC-16x0 SCSI driver, version" );
+ strcpy( buffer, "Future Domain TMC-16x0 SCSI driver, version" );
if (strchr( VERSION, ':')) { /* Assume VERSION is an RCS Revision string */
strcat( buffer, strchr( VERSION, ':' ) + 1 );
pt = strrchr( buffer, '$') - 1;
@@ -768,10 +816,9 @@ const char *fdomain_16x0_info(struct Scsi_Host * shpnt)
pt = buffer + strlen( buffer ) - 1;
if (*pt != ' ')
++pt;
- *pt++ = '\n';
*pt = '\0';
} else { /* Assume VERSION is a number */
- strcat( buffer, " " VERSION "\n" );
+ strcat( buffer, " " VERSION );
}
return buffer;
@@ -805,7 +852,7 @@ static int fdomain_arbitrate( void )
printk( "Arbitration failed, status = %x\n", status );
#endif
#if ERRORS_ONLY
- printk( "Future Domain: Arbitration failed, status = %x\n", status );
+ printk( "fdomain: Arbitration failed, status = %x\n", status );
#endif
return 1;
}
@@ -845,7 +892,7 @@ static int fdomain_select( int target )
if (chip == tmc18c30 && !flag) /* Skip first failure for 18C30 chips. */
++flag;
else
- printk( "Future Domain: Selection failed\n" );
+ printk( "fdomain: Selection failed\n" );
}
#endif
return 1;
@@ -860,22 +907,28 @@ void my_done( int error )
current_SC->result = error;
if (current_SC->scsi_done)
current_SC->scsi_done( current_SC );
- else panic( "Future Domain: current_SC->scsi_done() == NULL" );
+ else panic( "fdomain: current_SC->scsi_done() == NULL" );
} else {
- panic( "Future Domain: my_done() called outside of command\n" );
+ panic( "fdomain: my_done() called outside of command\n" );
}
#if DEBUG_RACE
in_interrupt_flag = 0;
#endif
}
-void fdomain_16x0_intr( int unused )
+void fdomain_16x0_intr( int irq, struct pt_regs * regs )
{
int status;
int done = 0;
unsigned data_count;
- sti();
+ /* The fdomain_16x0_intr is only called via
+ the interrupt handler. The goal of the
+ sti() here is to allow other
+ interruptions while this routine is
+ running. */
+
+ sti(); /* Yes, we really want sti() here */
outb( 0x00, Interrupt_Cntl_port );
@@ -985,9 +1038,9 @@ void fdomain_16x0_intr( int unused )
#endif
#if ERRORS_ONLY
if (current_SC->SCp.Status && current_SC->SCp.Status != 2) {
- printk( "Future Domain: target = %d, command = %x, "
- "Status = %x\n",
- current_SC->target, current_SC->cmnd[0],
+ printk( "fdomain: target = %d, command = %x, status = %x\n",
+ current_SC->target,
+ current_SC->cmnd[0],
current_SC->SCp.Status );
}
#endif
@@ -1003,8 +1056,7 @@ void fdomain_16x0_intr( int unused )
if (!current_SC->SCp.Message) ++done;
#if DEBUG_MESSAGES || EVERY_ACCESS
if (current_SC->SCp.Message) {
- printk( "Future Domain: Message = %x\n",
- current_SC->SCp.Message );
+ printk( "fdomain: message = %x\n", current_SC->SCp.Message );
}
#endif
break;
@@ -1265,7 +1317,7 @@ void fdomain_16x0_intr( int unused )
|| code == 0x24
|| !code)))
- printk( "Future Domain: REQUEST SENSE "
+ printk( "fdomain: REQUEST SENSE "
"Key = %x, Code = %x, Qualifier = %x\n",
key, code, qualifier );
}
@@ -1297,7 +1349,7 @@ void fdomain_16x0_intr( int unused )
int fdomain_16x0_queue( Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
{
if (in_command) {
- panic( "Future Domain: fdomain_16x0_queue() NOT REENTRANT!\n" );
+ panic( "fdomain: fdomain_16x0_queue() NOT REENTRANT!\n" );
}
#if EVERY_ACCESS
printk( "queue: target = %d cmnd = 0x%02x pieces = %d size = %u\n",
@@ -1375,8 +1427,13 @@ void print_info( Scsi_Cmnd *SCpnt )
unsigned int imr;
unsigned int irr;
unsigned int isr;
+
+ if (!SCpnt || !SCpnt->host) {
+ printk( "fdomain: cannot provide detailed information\n" );
+ }
- print_banner(SCpnt->host);
+ printk( "%s\n", fdomain_16x0_info( SCpnt->host ) );
+ print_banner( SCpnt->host );
switch (SCpnt->SCp.phase) {
case in_arbitration: printk( "arbitration " ); break;
case in_selection: printk( "selection " ); break;
@@ -1436,16 +1493,18 @@ void print_info( Scsi_Cmnd *SCpnt )
int fdomain_16x0_abort( Scsi_Cmnd *SCpnt)
{
+ unsigned long flags;
#if EVERY_ACCESS || ERRORS_ONLY || DEBUG_ABORT
- printk( "Future Domain: Abort " );
+ printk( "fdomain: abort " );
#endif
+ save_flags( flags );
cli();
if (!in_command) {
#if EVERY_ACCESS || ERRORS_ONLY
printk( " (not in command)\n" );
#endif
- sti();
+ restore_flags( flags );
return SCSI_ABORT_NOT_RUNNING;
}
@@ -1459,7 +1518,7 @@ int fdomain_16x0_abort( Scsi_Cmnd *SCpnt)
current_SC->result = DID_ABORT << 16;
- sti();
+ restore_flags( flags );
/* Aborts are not done well. . . */
my_done( DID_ABORT << 16 );
@@ -1474,7 +1533,7 @@ int fdomain_16x0_reset( Scsi_Cmnd *SCpnt )
#endif
#if ERRORS_ONLY
- printk( "Future Domain: SCSI Bus Reset\n" );
+ if (SCpnt) printk( "fdomain: SCSI Bus Reset\n" );
#endif
#if DEBUG_RESET
@@ -1540,6 +1599,17 @@ int fdomain_16x0_biosparam( Scsi_Disk *disk, int dev, int *info_array )
The table at 0x1fcc are I/O ports addresses for the various
operations. I calculate these by hand in this driver code.
+
+
+ For the ISA-200S version of BIOS Version 2.0:
+
+ The drive parameter table starts at 0x1f33.
+
+ WARNING: Assume that the table entry is 25 bytes long. Someone needs
+ to check this for the Quantum ISA-200S card.
+
+
+
For BIOS Version 3.2:
The drive parameter table starts at 0x1f70. Each entry is
@@ -1549,11 +1619,28 @@ int fdomain_16x0_biosparam( Scsi_Disk *disk, int dev, int *info_array )
drive = MINOR(dev) / 16;
if (bios_major == 2) {
- i = (struct drive_info *)( (char *)bios_base + 0x1f31 + drive * 25 );
+ switch (Quantum) {
+ case 2: /* ISA_200S */
+ /* The value of 25 has never been verified.
+ It should probably be 15. */
+ i = (struct drive_info *)( (char *)bios_base + 0x1f33 + drive * 25 );
+ break;
+ case 3: /* ISA_250MG */
+ i = (struct drive_info *)( (char *)bios_base + 0x1f36 + drive * 15 );
+ break;
+ case 4: /* ISA_200S (another one) */
+ i = (struct drive_info *)( (char *)bios_base + 0x1f34 + drive * 15 );
+ break;
+ default:
+ i = (struct drive_info *)( (char *)bios_base + 0x1f31 + drive * 25 );
+ break;
+ }
info_array[0] = i->heads;
info_array[1] = i->sectors;
info_array[2] = i->cylinders;
- } else if (bios_major == 3 && bios_minor < 4) { /* 3.0 and 3.2 BIOS */
+ } else if (bios_major == 3
+ && bios_minor >= 0
+ && bios_minor < 4) { /* 3.0 and 3.2 BIOS */
i = (struct drive_info *)( (char *)bios_base + 0x1f71 + drive * 10 );
info_array[0] = i->heads + 1;
info_array[1] = i->sectors;
diff --git a/drivers/scsi/fdomain.h b/drivers/scsi/fdomain.h
index f82963d4e..97f35da53 100644
--- a/drivers/scsi/fdomain.h
+++ b/drivers/scsi/fdomain.h
@@ -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 Jul 30 20:20:31 1994 by faith@cs.unc.edu
+ * Revised: Sat Jan 14 20:56:52 1995 by faith@cs.unc.edu
* Author: Rickard E. Faith, faith@cs.unc.edu
- * Copyright 1992, 1993, 1994 Rickard E. Faith
+ * Copyright 1992, 1993, 1994, 1995 Rickard E. Faith
*
- * $Id: fdomain.h,v 5.7 1994/07/31 03:09:15 faith Exp $
+ * $Id: fdomain.h,v 5.10 1995/01/15 01:56:56 root 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
@@ -33,7 +33,8 @@ int fdomain_16x0_reset( Scsi_Cmnd * );
int fdomain_16x0_queue( Scsi_Cmnd *, void (*done)(Scsi_Cmnd *) );
int fdomain_16x0_biosparam( Disk *, int, int * );
-#define FDOMAIN_16X0 { NULL, NULL, \
+#define FDOMAIN_16X0 { NULL, \
+ NULL, \
NULL, \
fdomain_16x0_detect, \
NULL, \
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index 9488c1f19..eb7ba3789 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -1,7 +1,10 @@
/*
- * hosts.c Copyright (C) 1992 Drew Eckhardt
- * mid to lowlevel SCSI driver interface by
- * Drew Eckhardt
+ * hosts.c Copyright (C) 1992 Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ * mid to lowlevel SCSI driver interface
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
*
* <drew@colorado.edu>
*/
@@ -10,16 +13,18 @@
/*
* This file contains the medium level SCSI
* host interface initialization, as well as the scsi_hosts array of SCSI
- * hosts currently present in the system.
+ * hosts currently present in the system.
*/
#include <linux/config.h>
#include "../block/blk.h"
#include <linux/kernel.h>
#include <linux/string.h>
+#include <linux/mm.h>
+
#include "scsi.h"
-#ifndef NULL
+#ifndef NULL
#define NULL 0L
#endif
@@ -47,10 +52,14 @@
#include "buslogic.h"
#endif
+#ifdef CONFIG_SCSI_EATA_DMA
+#include "eata_dma.h"
+#endif
+
#ifdef CONFIG_SCSI_U14_34F
#include "u14-34f.h"
#endif
-
+
#ifdef CONFIG_SCSI_FUTURE_DOMAIN
#include "fdomain.h"
#endif
@@ -94,7 +103,7 @@
#ifdef CONFIG_SCSI_EATA
#include "eata.h"
#endif
-
+
#ifdef CONFIG_SCSI_DEBUG
#include "scsi_debug.h"
#endif
@@ -104,9 +113,9 @@ static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/hos
*/
/*
- * The scsi host entries should be in the order you wish the
+ * The scsi host entries should be in the order you wish the
* cards to be detected. A driver may appear more than once IFF
- * it can deal with being detected (and therefore initialized)
+ * it can deal with being detected (and therefore initialized)
* with more than one simultaneous host number, can handle being
* reentrant, etc.
*
@@ -122,9 +131,9 @@ static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/hos
NULL, NULL, 0, 0, 0, 0, 0, 0}
/*
- * When figure is run, we don't want to link to any object code. Since
- * the macro for each host will contain function pointers, we cannot
- * use it and instead must use a "blank" that does no such
+ * When figure is run, we don't want to link to any object code. Since
+ * the macro for each host will contain function pointers, we cannot
+ * use it and instead must use a "blank" that does no such
* idiocy.
*/
@@ -161,7 +170,7 @@ static Scsi_Host_Template builtin_scsi_hosts[] =
IN2000,
#endif
#ifdef CONFIG_SCSI_GENERIC_NCR5380
- GENERIC_NCR5380,
+ GENERIC_NCR5380,
#endif
#ifdef CONFIG_SCSI_QLOGIC
QLOGIC,
@@ -173,11 +182,14 @@ static Scsi_Host_Template builtin_scsi_hosts[] =
SEAGATE_ST0X,
#endif
#ifdef CONFIG_SCSI_T128
- TRANTOR_T128,
+ TRANTOR_T128,
#endif
#ifdef CONFIG_SCSI_NCR53C7xx
NCR53c7xx,
#endif
+#ifdef CONFIG_SCSI_EATA_DMA
+ EATA_DMA,
+#endif
#ifdef CONFIG_SCSI_7000FASST
WD7000,
#endif
@@ -192,31 +204,34 @@ static Scsi_Host_Template builtin_scsi_hosts[] =
#define MAX_SCSI_HOSTS (sizeof(builtin_scsi_hosts) / sizeof(Scsi_Host_Template))
/*
- * Our semaphores and timeout counters, where size depends on MAX_SCSI_HOSTS here.
+ * Our semaphores and timeout counters, where size depends on MAX_SCSI_HOSTS here.
*/
struct Scsi_Host * scsi_hostlist = NULL;
struct Scsi_Device_Template * scsi_devicelist;
int max_scsi_hosts = 0;
-static int next_host = 0;
+int next_scsi_host = 0;
void
scsi_unregister(struct Scsi_Host * sh){
struct Scsi_Host * shpnt;
- int j;
-
- j = sh->extra_bytes;
if(scsi_hostlist == sh)
- scsi_hostlist = NULL;
+ scsi_hostlist = sh->next;
else {
shpnt = scsi_hostlist;
while(shpnt->next != sh) shpnt = shpnt->next;
shpnt->next = shpnt->next->next;
};
- next_host--;
- scsi_init_free((char *) sh, sizeof(struct Scsi_Host) + j);
+
+ /* If we are removing the last host registered, it is safe to reuse
+ its host number (this avoids "holes" at boot time) (DB) */
+ if (max_scsi_hosts == next_scsi_host && !scsi_loadable_module_flag)
+ max_scsi_hosts--;
+
+ next_scsi_host--;
+ scsi_init_free((char *) sh, sizeof(struct Scsi_Host) + sh->extra_bytes);
}
/* We call this when we come across a new host adapter. We only do this
@@ -225,21 +240,28 @@ scsi_unregister(struct Scsi_Host * sh){
struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j){
struct Scsi_Host * retval, *shpnt;
- retval = (struct Scsi_Host *)scsi_init_malloc(sizeof(struct Scsi_Host) + j);
+ retval = (struct Scsi_Host *)scsi_init_malloc(sizeof(struct Scsi_Host) + j,
+ (tpnt->unchecked_isa_dma && j ? GFP_DMA : 0) | GFP_ATOMIC);
retval->host_busy = 0;
retval->block = NULL;
+ retval->wish_block = 0;
if(j > 0xffff) panic("Too many extra bytes requested\n");
retval->extra_bytes = j;
retval->loaded_as_module = scsi_loadable_module_flag;
- retval->host_no = next_host++;
- retval->host_queue = NULL;
- retval->host_wait = NULL;
- retval->last_reset = 0;
+ retval->host_no = max_scsi_hosts++; /* never reuse host_no (DB) */
+ next_scsi_host++;
+ retval->host_queue = NULL;
+ retval->host_wait = NULL;
+ retval->last_reset = 0;
retval->irq = 0;
- retval->hostt = tpnt;
+ retval->dma_channel = 0xff;
+ retval->io_port = 0;
+ retval->forbidden_addr = 0;
+ retval->forbidden_size = 0;
+ retval->hostt = tpnt;
retval->next = NULL;
#ifdef DEBUG
- printk("Register %x %x: %d\n", retval, retval->hostt, j);
+ printk("Register %x %x: %d\n", (int)retval, (int)retval->hostt, j);
#endif
/* The next four are the default values which can be overridden
@@ -247,6 +269,7 @@ struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j){
retval->this_id = tpnt->this_id;
retval->can_queue = tpnt->can_queue;
retval->sg_tablesize = tpnt->sg_tablesize;
+ retval->cmd_per_lun = tpnt->cmd_per_lun;
retval->unchecked_isa_dma = tpnt->unchecked_isa_dma;
if(!scsi_hostlist)
@@ -273,43 +296,56 @@ scsi_register_device(struct Scsi_Device_Template * sdpnt)
unsigned int scsi_init()
{
static int called = 0;
- int i, j, count, pcount;
+ int i, pcount;
Scsi_Host_Template * tpnt;
- count = 0;
+ struct Scsi_Host * shpnt;
+ const char * name;
if(called) return 0;
- called = 1;
+ called = 1;
for (tpnt = &builtin_scsi_hosts[0], i = 0; i < MAX_SCSI_HOSTS; ++i, tpnt++)
{
/*
- * Initialize our semaphores. -1 is interpreted to mean
+ * Initialize our semaphores. -1 is interpreted to mean
* "inactive" - where as 0 will indicate a time out condition.
- */
-
- pcount = next_host;
- if ((tpnt->detect) &&
- (tpnt->present =
+ */
+
+ pcount = next_scsi_host;
+ if ((tpnt->detect) &&
+ (tpnt->present =
tpnt->detect(tpnt)))
- {
+ {
/* The only time this should come up is when people use
some kind of patched driver of some kind or another. */
- if(pcount == next_host) {
+ if(pcount == next_scsi_host) {
if(tpnt->present > 1)
panic("Failure to register low-level scsi driver");
/* The low-level driver failed to register a driver. We
- can do this now. */
+ can do this now. */
scsi_register(tpnt,0);
};
tpnt->next = scsi_hosts;
scsi_hosts = tpnt;
- for(j = 0; j < tpnt->present; j++)
- printk ("scsi%d : %s\n",
- count++, tpnt->name);
}
}
- printk ("scsi : %d hosts.\n", count);
-
+
+
+ for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+ {
+ if(shpnt->hostt->info)
+ name = shpnt->hostt->info(shpnt);
+ else
+ name = shpnt->hostt->name;
+ printk ("scsi%d : %s\n", /* And print a little message */
+ shpnt->host_no, name);
+ }
+
+ printk ("scsi : %d host%s.\n", next_scsi_host,
+ (next_scsi_host == 1) ? "" : "s");
+
+ scsi_make_blocked_list();
+
/* Now attach the high level drivers */
#ifdef CONFIG_BLK_DEV_SD
scsi_register_device(&sd_template);
@@ -324,6 +360,47 @@ unsigned int scsi_init()
scsi_register_device(&sg_template);
#endif
- max_scsi_hosts = count;
+#if 0
+ max_scsi_hosts = next_scsi_host;
+#endif
return 0;
}
+
+
+void scsi_mem_init(unsigned long memory_end)
+{
+ struct Scsi_Host *Host;
+ long High8, Low24;
+ for (Host = scsi_hostlist; Host != NULL; Host = Host->next) {
+ if (Host->forbidden_addr > 0 && Host->forbidden_size > 0) {
+ for (High8 = 1<<24; High8 < memory_end; High8 += 1<<24) {
+ for (Low24 = Host->forbidden_addr;
+ Low24 < Host->forbidden_addr + Host->forbidden_size;
+ Low24 += PAGE_SIZE) {
+ unsigned long ForbiddenAddress = High8 + Low24;
+ if (ForbiddenAddress >= memory_end) goto next_host;
+ mem_map[MAP_NR(ForbiddenAddress)] = MAP_PAGE_RESERVED;
+ }
+ }
+ }
+ next_host:
+ continue;
+ }
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h
index 26b4e1c9f..01b5f29c1 100644
--- a/drivers/scsi/hosts.h
+++ b/drivers/scsi/hosts.h
@@ -228,8 +228,6 @@ typedef struct SHT
be two Scsi_Host entries, but only 1 Scsi_Host_Template entries.
*/
-#define SCSI_HOST_BLOCK 0x800
-
struct Scsi_Host
{
struct Scsi_Host * next;
@@ -245,25 +243,36 @@ struct Scsi_Host
that should be locked out of performing I/O while we have an active
command on this host. */
struct Scsi_Host * block;
+ unsigned wish_block:1;
/* These parameters should be set by the detect routine */
unsigned char *base;
- short unsigned int io_port;
+ unsigned int io_port;
unsigned char n_io_port;
unsigned char irq;
unsigned char dma_channel;
+
+ /*
+ Set these if there are conflicts between memory
+ in the < 1mb region and regions at 16mb multiples.
+ The address must be on a page boundary.
+ */
+ unsigned long forbidden_addr;
+ unsigned long forbidden_size;
+
/*
The rest can be copied from the template, or specifically
initialized, as required.
- */
+ */
int this_id;
int can_queue;
+ short cmd_per_lun;
short unsigned int sg_tablesize;
unsigned unchecked_isa_dma:1;
/*
True if this host was loaded as a loadable module
- */
+ */
unsigned loaded_as_module:1;
int hostdata[0]; /* Used for storage of host specific stuff */
@@ -284,14 +293,18 @@ extern Scsi_Host_Template * scsi_hosts;
looks normal. Also, it makes it possible to use the same code for a
loadable module. */
-extern void * scsi_init_malloc(unsigned int size);
+extern void * scsi_init_malloc(unsigned int size, int priority);
extern void scsi_init_free(char * ptr, unsigned int size);
+void scan_scsis (struct Scsi_Host * shpnt);
+
+extern int next_scsi_host;
extern int scsi_loadable_module_flag;
unsigned int scsi_init(void);
extern struct Scsi_Host * scsi_register(Scsi_Host_Template *, int j);
extern void scsi_unregister(struct Scsi_Host * i);
+extern int scsicam_bios_param (Disk *, int, int *);
#define BLANK_HOST {"", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
@@ -309,7 +322,7 @@ struct Scsi_Device_Template
int (*detect)(Scsi_Device *); /* Returns 1 if we can attach this device */
void (*init)(void); /* Sizes arrays based upon number of devices detected */
void (*finish)(void); /* Perform initialization after attachment */
- void (*attach)(Scsi_Device *); /* Attach devices to arrays */
+ int (*attach)(Scsi_Device *); /* Attach devices to arrays */
void (*detach)(Scsi_Device *);
};
@@ -320,4 +333,42 @@ extern struct Scsi_Device_Template sg_template;
int scsi_register_device(struct Scsi_Device_Template * sdpnt);
+/* These are used by loadable modules */
+extern int scsi_register_module(int, void *);
+extern void scsi_unregister_module(int, void *);
+
+/* The different types of modules that we can load and unload */
+#define MODULE_SCSI_HA 1
+#define MODULE_SCSI_CONST 2
+#define MODULE_SCSI_IOCTL 3
+#define MODULE_SCSI_DEV 4
+
+
+/*
+ * This is an ugly hack. If we expect to be able to load devices at run time, we need
+ * to leave extra room in some of the data structures. Doing a realloc to enlarge
+ * the structures would be riddled with race conditions, so until a better solution
+ * is discovered, we use this crude approach
+ */
+#define SD_EXTRA_DEVS 2
+#define ST_EXTRA_DEVS 2
+#define SR_EXTRA_DEVS 2
+#define SG_EXTRA_DEVS (SD_EXTRA_DEVS + SR_EXTRA_DEVS + ST_EXTRA_DEVS)
+
#endif
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/drivers/scsi/in2000.c b/drivers/scsi/in2000.c
index 7d9db4acb..1669732fe 100644
--- a/drivers/scsi/in2000.c
+++ b/drivers/scsi/in2000.c
@@ -1,7 +1,7 @@
/*
* This file is in2000.c, written and
* Copyright (C) 1993 Brad McLean
- * Last edit 08/25/94 WDE
+ * Last edit 1/19/95 TZ
* Disclaimer:
* Note: This is ugly. I know it, I wrote it, but my whole
* focus was on getting the damn thing up and out quickly.
@@ -46,6 +46,12 @@
*/
/* Changes for 1.1.43+ kernels made 8/25/94, code added to check for
* new BIOS version, derived by jshiffle@netcom.com. (WDE)
+ *
+ * 1/7/95 Fix from Peter Lu (swift@world.std.com) for datalen vs. dataptr
+ * logic, much more stable under load.
+ *
+ * 1/19/95 (zerucha@shell.portal.com) Added module and biosparam support for
+ * larger SCSI hard drives (untested).
*/
#include <linux/kernel.h>
@@ -195,10 +201,9 @@ static void in2000_fifo_out(void) /* uses FIFOCNTR */
#endif
} while((in2000_datalen > 0) && ((infcnt = (inb(INFCNT)) & 0xfe) >= 0x20) );
/* If scatter-gather, go on to next segment */
- if( !in2000_datalen && in2000_current_segment < in2000_nsegment)
+ if( !in2000_datalen && ++in2000_current_segment < in2000_nsegment)
{
in2000_scatter++;
- in2000_current_segment++;
in2000_datalen = in2000_scatter->length;
in2000_dataptr = (unsigned short*)in2000_scatter->address;
}
@@ -253,10 +258,9 @@ DEB(printk("FIr:%d %02x %08x %08x\n", in2000_datalen,fic,count2,(unsigned int)in
DEB(printk("FIer:%d %02x %08x\n", in2000_datalen,fic,(unsigned int )in2000_dataptr));
/* while ( count-- )
inw(INFIFO);*/ /* Throw away some extra stuff */
- if( !in2000_datalen && in2000_current_segment < in2000_nsegment)
+ if( !in2000_datalen && ++in2000_current_segment < in2000_nsegment)
{
in2000_scatter++;
- in2000_current_segment++;
in2000_datalen = in2000_scatter->length;
in2000_dataptr = (unsigned short*)in2000_scatter->address;
}
@@ -265,7 +269,7 @@ DEB(printk("FIer:%d %02x %08x\n", in2000_datalen,fic,(unsigned int )in2000_datap
ficmsk = 0;}
}
-static void in2000_intr_handle(int foo)
+static void in2000_intr_handle(int irq, struct pt_regs *regs)
{
int result=0;
unsigned int count,auxstatus,scsistatus,cmdphase,scsibyte;
@@ -288,13 +292,16 @@ static void in2000_intr_handle(int foo)
scsistatus,cmdphase,scsibyte));
/* Why do we assume that we need to send more data here??? ERY */
- if ( in2000_datalen && in2000_dataptr ) /* data xfer pending */
+ if ( in2000_datalen ) /* data xfer pending */
{
- if ( in2000_datawrite )
+ if ( in2000_dataptr == NULL )
+ printk("int2000: dataptr=NULL datalen=%d\n",
+ in2000_datalen);
+ else if ( in2000_datawrite )
in2000_fifo_out();
else
in2000_fifo_in();
- } else ficmsk = 0;
+ }
if ( (auxstatus & 0x8c) == 0x80 )
{ /* There is a WD Chip interrupt & register read good */
outb(2,ININTR); /* Disable fifo interrupts */
@@ -405,6 +412,7 @@ int in2000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
unchar *cmd = (unchar *) SCpnt->cmnd;
unchar target = SCpnt->target;
void *buff = SCpnt->request_buffer;
+ unsigned long flags;
int bufflen = SCpnt->request_bufflen;
int timeout, size, loop;
int i;
@@ -420,7 +428,7 @@ int in2000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
bufflen = 0;
/*
- * What it looks like. Boy did I get tired of reading it's output.
+ * What it looks like. Boy did I get tired of reading its output.
*/
if (*cmd == READ_10 || *cmd == WRITE_10) {
i = xscsi2int((cmd+1));
@@ -454,7 +462,8 @@ int in2000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
if ( in2000_SCptr )
{
printk("in2000_queue_command waiting for free command block!\n");
- while ( in2000_SCptr );
+ while ( in2000_SCptr )
+ barrier();
}
for ( timeout = jiffies + 5; timeout > jiffies; )
{
@@ -511,6 +520,7 @@ int in2000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
/*
* Set up the FIFO
*/
+ save_flags(flags);
cli(); /* so FIFO init waits till WD set */
outb(0,INFRST);
if ( direction == 1 )
@@ -538,7 +548,7 @@ int in2000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
outb(COMMAND,INSTAT);
outb(0,INNLED);
outb(8,INDATA); /* Select w/ATN & Transfer */
- sti(); /* let the intrpt rip */
+ restore_flags(flags); /* let the intrpt rip */
return 0;
}
@@ -628,7 +638,7 @@ int in2000_detect(Scsi_Host_Template * tpnt)
shpnt->io_port = base;
shpnt->n_io_port = 12;
shpnt->irq = irq_level;
- snarf_region(base, 12); /* Prevent other drivers from using this space */
+ request_region(base, 12,"in2000"); /* Prevent other drivers from using this space */
return 1;
}
@@ -678,5 +688,33 @@ int in2000_biosparam(Disk * disk, int dev, int* iinfo)
iinfo[0] = 64;
iinfo[1] = 32;
iinfo[2] = size >> 11;
+/* This should approximate the large drive handling that the DOS ASPI manager
+ uses. Drives very near the boundaries may not be handled correctly (i.e.
+ near 2.0 Gb and 4.0 Gb) */
+ if (iinfo[2] > 1024) {
+ iinfo[0] = 64;
+ iinfo[1] = 63;
+ iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]);
+ }
+ if (iinfo[2] > 1024) {
+ iinfo[0] = 128;
+ iinfo[1] = 63;
+ iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]);
+ }
+ if (iinfo[2] > 1024) {
+ iinfo[0] = 255;
+ iinfo[1] = 63;
+ iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]);
+ if (iinfo[2] > 1023)
+ iinfo[2] = 1023;
+ }
return 0;
}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = IN2000;
+
+#include "scsi_module.c"
+#endif
+
diff --git a/drivers/scsi/qlogic.c b/drivers/scsi/qlogic.c
index 826315a6b..e1b82addf 100644
--- a/drivers/scsi/qlogic.c
+++ b/drivers/scsi/qlogic.c
@@ -4,49 +4,94 @@
Use at your own risk. Support Tort Reform so you won't have to read all
these silly disclaimers.
- Copyright 1994, Tom Zerucha.
+ Copyright 1994, Tom Zerucha.
zerucha@shell.portal.com
Additional Code, and much appreciated help by
Michael A. Griffith
grif@cs.ucr.edu
+ Thanks to Eric Youngdale and Dave Hinds for loadable module and PCMCIA
+ help respectively, and for suffering through my foolishness during the
+ debugging process.
+
Reference Qlogic FAS408 Technical Manual, 53408-510-00A, May 10, 1994
(you can reference it, but it is incomplete and inaccurate in places)
- Version 0.37
+ Version 0.43 4/6/95 - kernel 1.2.0+, pcmcia 2.5.4+
+
+ Functions as standalone, loadable, and PCMCIA driver, the latter from
+ Dave Hind's PCMCIA package.
+
Redistributable under terms of the GNU Public License
*/
/*----------------------------------------------------------------*/
/* Configuration */
+/* Set the following to 2 to use normal interrupt (active high/totempole-
+ tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open
+ drain */
+#define QL_INT_ACTIVE_HIGH 2
+
/* Set the following to 1 to enable the use of interrupts. Note that 0 tends
to be more stable, but slower (or ties up the system more) */
#define QL_USE_IRQ 1
/* Set the following to max out the speed of the PIO PseudoDMA transfers,
- again, 0 tends to be slower, but more stable */
+ again, 0 tends to be slower, but more stable. */
#define QL_TURBO_PDMA 1
+/* This should be 1 to enable parity detection */
+#define QL_ENABLE_PARITY 1
+
/* This will reset all devices when the driver is initialized (during bootup).
The other linux drivers don't do this, but the DOS drivers do, and after
- using DOS or some kind of crash or lockup this will bring things back */
-#define QL_RESET_AT_START 1
-
-/* This will set fast (10Mhz) synchronous timing, FASTCLK must also be 1*/
-#define FASTSCSI 0
-
-/* This will set a faster sync transfer rate */
-#define FASTCLK 0
-
-/* This bit needs to be set to 1 if your cabling is long or noisy */
-#define SLOWCABLE 0
-
-/* This is the sync transfer divisor, 40Mhz/X will be the data rate
- The power on default is 5, the maximum normal value is 5 */
-#define SYNCXFRPD 4
-
+ using DOS or some kind of crash or lockup this will bring things back
+ without requiring a cold boot. It does take some time to recover from a
+ reset, so it is slower, and I have seen timeouts so that devices weren't
+ recognized when this was set. */
+#define QL_RESET_AT_START 0
+
+/* crystal frequency in megahertz (for offset 5 and 9)
+ Please set this for your card. Most Qlogic cards are 40 Mhz. The
+ Control Concepts ISA (not VLB) is 24 Mhz */
+#define XTALFREQ 40
+
+/**********/
+/* DANGER! modify these at your own risk */
+/* SLOWCABLE can usually be reset to zero if you have a clean setup and
+ proper termination. The rest are for synchronous transfers and other
+ advanced features if your device can transfer faster than 5Mb/sec.
+ If you are really curious, email me for a quick howto until I have
+ something official */
+/**********/
+
+/*****/
+/* config register 1 (offset 8) options */
+/* This needs to be set to 1 if your cabling is long or noisy */
+#define SLOWCABLE 1
+
+/*****/
+/* offset 0xc */
+/* This will set fast (10Mhz) synchronous timing when set to 1
+ For this to have an effect, FASTCLK must also be 1 */
+#define FASTSCSI 0
+
+/* This when set to 1 will set a faster sync transfer rate */
+#define FASTCLK 0
+/*(XTALFREQ>25?1:0)*/
+
+/*****/
+/* offset 6 */
+/* This is the sync transfer divisor, XTALFREQ/X will be the maximum
+ achievable data rate (assuming the rest of the system is capable
+ and set properly) */
+#define SYNCXFRPD 5
+/*(XTALFREQ/5)*/
+
+/*****/
+/* offset 7 */
/* This is the count of how many synchronous transfers can take place
i.e. how many reqs can occur before an ack is given.
The maximum value for this is 15, the upper bits can modify
@@ -59,12 +104,27 @@
the assertion delay, also in 1/2 clocks (FASTCLK is ignored here). */
/*----------------------------------------------------------------*/
+#ifdef PCMCIA
+#undef QL_INT_ACTIVE_HIGH
+#define QL_INT_ACTIVE_HIGH 0
+#define MODULE
+#endif
+#if defined(MODULE)
#include <linux/config.h>
+#include <linux/module.h>
+#endif
+
+#ifdef PCMCIA
+#undef MODULE
+#endif
+
#include "../block/blk.h" /* to get disk capacity */
#include <linux/kernel.h>
#include <linux/string.h>
-#include <unistd.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/unistd.h>
#include <asm/io.h>
#include <asm/irq.h>
#include "sd.h"
@@ -73,19 +133,26 @@
/*----------------------------------------------------------------*/
/* driver state info, local to driver */
-static int qbase; /* Port */
+static int qbase = 0; /* Port */
static int qinitid; /* initiator ID */
static int qabort; /* Flag to cause an abort */
-static int qlirq; /* IRQ being used */
+static int qlirq = -1; /* IRQ being used */
static char qinfo[80]; /* description */
static Scsi_Cmnd *qlcmd; /* current command being processed */
-/*----------------------------------------------------------------*/
+static int qlcfg5 = ( XTALFREQ << 5 ); /* 15625/512 */
+static int qlcfg6 = SYNCXFRPD;
+static int qlcfg7 = SYNCOFFST;
+static int qlcfg8 = ( SLOWCABLE << 7 ) | ( QL_ENABLE_PARITY << 4 );
+static int qlcfg9 = ( ( XTALFREQ + 4 ) / 5 );
+static int qlcfgc = ( FASTCLK << 3 ) | ( FASTSCSI << 4 );
+/*----------------------------------------------------------------*/
+/* The qlogic card uses two register maps - These macros select which one */
#define REG0 ( outb( inb( qbase + 0xd ) & 0x7f , qbase + 0xd ), outb( 4 , qbase + 0xd ))
-#define REG1 ( outb( inb( qbase + 0xd ) | 0x80 , qbase + 0xd ), outb( 0xb6 , qbase + 0xd ))
+#define REG1 ( outb( inb( qbase + 0xd ) | 0x80 , qbase + 0xd ), outb( 0xb4 | QL_INT_ACTIVE_HIGH , qbase + 0xd ))
-/* following is watchdog timeout - 0 is longest possible */
+/* following is watchdog timeout in microseconds */
#define WATCHDOG 5000000
/*----------------------------------------------------------------*/
@@ -106,6 +173,8 @@ static void ql_zap(void);
void ql_zap()
{
int x;
+unsigned long flags;
+ save_flags( flags );
cli();
x = inb(qbase + 0xd);
REG0;
@@ -113,7 +182,7 @@ int x;
outb(2, qbase + 3); /* reset chip */
if (x & 0x80)
REG1;
- sti();
+ restore_flags( flags );
}
/*----------------------------------------------------------------*/
@@ -124,6 +193,7 @@ int j;
j = 0;
if (phase & 1) { /* in */
#if QL_TURBO_PDMA
+rtrc(4)
/* empty fifo in large chunks */
if( reqlen >= 128 && (inb( qbase + 8 ) & 2) ) { /* full */
insl( qbase + 4, request, 32 );
@@ -143,6 +213,7 @@ int j;
}
#endif
/* until both empty and int (or until reclen is 0) */
+rtrc(7)
j = 0;
while( reqlen && !( (j & 0x10) && (j & 0xc0) ) ) {
/* while bytes to receive and not empty */
@@ -158,6 +229,7 @@ int j;
}
else { /* out */
#if QL_TURBO_PDMA
+rtrc(4)
if( reqlen >= 128 && inb( qbase + 8 ) & 0x10 ) { /* empty */
outsl(qbase + 4, request, 32 );
reqlen -= 128;
@@ -176,6 +248,7 @@ int j;
}
#endif
/* until full and int (or until reclen is 0) */
+rtrc(7)
j = 0;
while( reqlen && !( (j & 2) && (j & 0xc0) ) ) {
/* while bytes to send and not full */
@@ -196,9 +269,11 @@ int j;
static int ql_wai(void)
{
int i,k;
- i = WATCHDOG;
- while (--i && !qabort && !((k = inb(qbase + 4)) & 0xe0));
- if (!i)
+ k = 0;
+ i = jiffies + WATCHDOG;
+ while ( i > jiffies && !qabort && !((k = inb(qbase + 4)) & 0xe0))
+ barrier();
+ if (i <= jiffies)
return (DID_TIME_OUT);
if (qabort)
return (qabort == 1 ? DID_ABORT : DID_RESET);
@@ -216,8 +291,11 @@ int i,k;
static void ql_icmd(Scsi_Cmnd * cmd)
{
unsigned int i;
+unsigned long flags;
+
qabort = 0;
+ save_flags( flags );
cli();
REG0;
/* clearing of interrupts and the fifo is needed */
@@ -235,34 +313,22 @@ unsigned int i;
outb(0x40, qbase + 0xb); /* enable features */
/* configurables */
-#if FASTSCSI
-#if FASTCLK
- outb(0x18, qbase + 0xc);
-#else
- outb(0x10, qbase + 0xc);
-#endif
-#else
-#if FASTCLK
- outb(8, qbase + 0xc);
-#endif
-#endif
-
-#if SLOWCABLE
- outb(0xd0 | qinitid, qbase + 8); /* (initiator) bus id */
-#else
- outb(0x50 | qinitid, qbase + 8); /* (initiator) bus id */
-#endif
- outb( SYNCOFFST , qbase + 7 );
- outb( SYNCXFRPD , qbase + 6 );
+ outb( qlcfgc , qbase + 0xc);
+/* config: no reset interrupt, (initiator) bus id */
+ outb( 0x40 | qlcfg8 | qinitid, qbase + 8);
+ outb( qlcfg7 , qbase + 7 );
+ outb( qlcfg6 , qbase + 6 );
/**/
- outb(0x99, qbase + 5); /* timer */
+ outb(qlcfg5, qbase + 5); /* select timer */
+ outb(qlcfg9 & 7, qbase + 9); /* prescaler */
+/* outb(0x99, qbase + 5); */
outb(cmd->target, qbase + 4);
for (i = 0; i < cmd->cmd_len; i++)
outb(cmd->cmnd[i], qbase + 2);
qlcmd = cmd;
outb(0x41, qbase + 3); /* select and send command */
- sti();
+ restore_flags( flags );
}
/*----------------------------------------------------------------*/
/* process scsi command - usually after interrupt */
@@ -277,6 +343,7 @@ unsigned int reqlen; /* total length of transfer */
struct scatterlist *sglist; /* scatter-gather list pointer */
unsigned int sgcount; /* sg counter */
+rtrc(1)
j = inb(qbase + 6);
i = inb(qbase + 5);
if (i == 0x20) {
@@ -291,7 +358,9 @@ unsigned int sgcount; /* sg counter */
j &= 7; /* j = inb( qbase + 7 ) >> 5; */
/* correct status is supposed to be step 4 */
/* it sometimes returns step 3 but with 0 bytes left to send */
- if (j != 4 && (j != 3 || inb(qbase + 7) & 0x1f)) {
+/* We can try stuffing the FIFO with the max each time, but we will get a
+ sequence of 3 if any bytes are left (but we do flush the FIFO anyway */
+ if(j != 3 && j != 4) {
printk("Ql:Bad sequence for command %d, int %02X, cmdleft = %d\n", j, i, inb( qbase+7 ) & 0x1f );
ql_zap();
return (DID_ERROR << 16);
@@ -303,7 +372,7 @@ unsigned int sgcount; /* sg counter */
reqlen = cmd->request_bufflen;
/* note that it won't work if transfers > 16M are requested */
if (reqlen && !((phase = inb(qbase + 4)) & 6)) { /* data phase */
-rtrc(1)
+rtrc(2)
outb(reqlen, qbase); /* low-mid xfer cnt */
outb(reqlen >> 8, qbase+1); /* low-mid xfer cnt */
outb(reqlen >> 16, qbase + 0xe); /* high xfer cnt */
@@ -333,10 +402,9 @@ rtrc(2)
k = inb(qbase + 5); /* should be 0x10, bus service */
}
/*** Enter Status (and Message In) Phase ***/
- k = WATCHDOG;
-rtrc(4)
- while (--k && !qabort && !(inb(qbase + 4) & 6)); /* wait for status phase */
- if (!k) {
+ k = jiffies + WATCHDOG;
+ while ( k > jiffies && !qabort && !(inb(qbase + 4) & 6)); /* wait for status phase */
+ if ( k <= jiffies ) {
ql_zap();
return (DID_TIME_OUT << 16);
}
@@ -356,13 +424,15 @@ rtrc(4)
result = DID_ERROR;
}
outb(0x12, qbase + 3); /* done, disconnect */
-rtrc(3)
+rtrc(1)
if ((k = ql_wai()))
return (k << 16);
/* should get bus service interrupt and disconnect interrupt */
i = inb(qbase + 5); /* should be bus service */
- while (!qabort && ((i & 0x20) != 0x20))
+ while (!qabort && ((i & 0x20) != 0x20)) {
+ barrier();
i |= inb(qbase + 5);
+ }
rtrc(0)
if (qabort)
return ((qabort == 1 ? DID_ABORT : DID_RESET) << 16);
@@ -372,14 +442,16 @@ rtrc(0)
#if QL_USE_IRQ
/*----------------------------------------------------------------*/
/* interrupt handler */
-static void ql_ihandl(int irq)
+static void ql_ihandl(int irq, struct pt_regs * regs)
{
Scsi_Cmnd *icmd;
REG0;
if (!(inb(qbase + 4) & 0x80)) /* false alarm? */
return;
if (qlcmd == NULL) { /* no command to process? */
- while (inb(qbase + 5)); /* maybe also ql_zap() */
+ int i;
+ i = 16;
+ while (i-- && inb(qbase + 5)); /* maybe also ql_zap() */
return;
}
icmd = qlcmd;
@@ -432,7 +504,8 @@ int qlogic_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
cmd->scsi_done = done;
/* wait for the last command's interrupt to finish */
- while (qlcmd != NULL);
+ while (qlcmd != NULL)
+ barrier();
ql_icmd(cmd);
return 0;
}
@@ -443,6 +516,17 @@ int qlogic_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
}
#endif
+#ifdef PCMCIA
+/*----------------------------------------------------------------*/
+/* allow PCMCIA code to preset the port */
+/* port should be 0 and irq to -1 respectively for autoprobing */
+void qlogic_preset(int port, int irq)
+{
+ qbase=port;
+ qlirq=irq;
+}
+#endif
+
/*----------------------------------------------------------------*/
/* look for qlogic card and init if found */
int qlogic_detect(Scsi_Host_Template * host)
@@ -450,28 +534,30 @@ int qlogic_detect(Scsi_Host_Template * host)
int i, j; /* these are only used by IRQ detect */
int qltyp; /* type of chip */
struct Scsi_Host *hreg; /* registered host structure */
+unsigned long flags;
/* Qlogic Cards only exist at 0x230 or 0x330 (the chip itself decodes the
address - I check 230 first since MIDI cards are typically at 330
- Note that this will not work for 2 Qlogic cards in 1 system. The
- easiest way to do that is to create 2 versions of this file, one for
- 230 and one for 330.
-
- Alternately, the Scsi_Host structure now stores the i/o port and can
- be used to set the port (go through and replace qbase with
- (struct Scsi_Cmnd *) cmd->host->io_port, or for efficiency, set a local
- copy of qbase. There will also need to be something similar within the
- IRQ handlers to sort out which board it came from and thus which port.
+
+ Theoretically, two Qlogic cards can coexist in the same system. This
+ should work by simply using this as a loadable module for the second
+ card, but I haven't tested this.
*/
- for (qbase = 0x230; qbase < 0x430; qbase += 0x100) {
- REG1;
- if ( ( (inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7 )
- && ( (inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7 ) )
- break;
+ if( !qbase ) {
+ for (qbase = 0x230; qbase < 0x430; qbase += 0x100) {
+ if( check_region( qbase , 0x10 ) )
+ continue;
+ REG1;
+ if ( ( (inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7 )
+ && ( (inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7 ) )
+ break;
+ }
+ if (qbase == 0x430)
+ return 0;
}
- if (qbase == 0x430)
- return 0;
+ else
+ printk( "Ql: Using preset base address of %03x\n", qbase );
qltyp = inb(qbase + 0xe) & 0xf8;
qinitid = host->this_id;
@@ -479,9 +565,9 @@ struct Scsi_Host *hreg; /* registered host structure */
qinitid = 7; /* if no ID, use 7 */
outb(1, qbase + 8); /* set for PIO pseudo DMA */
REG0;
- outb(0xd0 | qinitid, qbase + 8); /* (ini) bus id, disable scsi rst */
- outb(0x99, qbase + 5); /* select timer */
- qlirq = -1;
+ outb(0x40 | qlcfg8 | qinitid, qbase + 8); /* (ini) bus id, disable scsi rst */
+ outb(qlcfg5, qbase + 5); /* select timer */
+ outb(qlcfg9, qbase + 9); /* prescaler */
#if QL_RESET_AT_START
outb( 3 , qbase + 3 );
REG1;
@@ -490,33 +576,48 @@ struct Scsi_Host *hreg; /* registered host structure */
#endif
#if QL_USE_IRQ
/* IRQ probe - toggle pin and check request pending */
- cli();
- i = 0xffff;
- j = 3;
- outb(0x90, qbase + 3); /* illegal command - cause interrupt */
- REG1;
- outb(10, 0x20); /* access pending interrupt map */
- outb(10, 0xa0);
- while (j--) {
- outb(0xb2, qbase + 0xd); /* int pin off */
- i &= ~(inb(0x20) | (inb(0xa0) << 8)); /* find IRQ off */
- outb(0xb6, qbase + 0xd); /* int pin on */
- i &= inb(0x20) | (inb(0xa0) << 8); /* find IRQ on */
+
+ if( qlirq == -1 ) {
+ save_flags( flags );
+ cli();
+ i = 0xffff;
+ j = 3;
+ outb(0x90, qbase + 3); /* illegal command - cause interrupt */
+ REG1;
+ outb(10, 0x20); /* access pending interrupt map */
+ outb(10, 0xa0);
+ while (j--) {
+ outb(0xb0 | QL_INT_ACTIVE_HIGH , qbase + 0xd); /* int pin off */
+ i &= ~(inb(0x20) | (inb(0xa0) << 8)); /* find IRQ off */
+ outb(0xb4 | QL_INT_ACTIVE_HIGH , qbase + 0xd); /* int pin on */
+ i &= inb(0x20) | (inb(0xa0) << 8); /* find IRQ on */
+ }
+ REG0;
+ while (inb(qbase + 5)); /* purge int */
+ j = -1;
+ while (i) /* find on bit */
+ i >>= 1, j++; /* should check for exactly 1 on */
+ qlirq = j;
+ restore_flags( flags );
}
- REG0;
- while (inb(qbase + 5)); /* purge int */
- while (i) /* find on bit */
- i >>= 1, qlirq++; /* should check for exactly 1 on */
+ else
+ printk( "Ql: Using preset IRQ %d\n", qlirq );
+
if (qlirq >= 0 && !request_irq(qlirq, ql_ihandl, 0, "qlogic"))
host->can_queue = 1;
- sti();
#endif
+ request_region( qbase , 0x10 ,"qlogic");
hreg = scsi_register( host , 0 ); /* no host data */
hreg->io_port = qbase;
- hreg->irq = qlirq;
+ hreg->n_io_port = 16;
+ hreg->dma_channel = -1;
+ if( qlirq != -1 )
+ hreg->irq = qlirq;
- sprintf(qinfo, "Qlogic Driver version 0.36, chip %02X at %03X, IRQ %d", qltyp, qbase, qlirq);
+ sprintf(qinfo, "Qlogic Driver version 0.43, chip %02X at %03X, IRQ %d, TPdma:%d",
+ qltyp, qbase, qlirq, QL_TURBO_PDMA );
host->name = qinfo;
+
return 1;
}
@@ -562,3 +663,10 @@ const char *qlogic_info(struct Scsi_Host * host)
{
return qinfo;
}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = QLOGIC;
+
+#include "scsi_module.c"
+#endif
diff --git a/drivers/scsi/script_asm.pl b/drivers/scsi/script_asm.pl
index 37c120a55..f1e739d98 100644
--- a/drivers/scsi/script_asm.pl
+++ b/drivers/scsi/script_asm.pl
@@ -280,11 +280,11 @@ sub parse_conditional {
$conditional = $1;
print STDERR "$0 : parsed ATN\n" if ($debug);
} elsif ($conditional =~ /^($phase)\s*(.*)/i) {
- $1 = "\U$1\E";
- $p = $scsi_phases{$1};
+ $phase_index = "\U$1\E";
+ $p = $scsi_phases{$phase_index};
$code[$address] |= $p | 0x00_02_00_00;
$conditional = $2;
- print STDERR "$0 : parsed phase $1\n" if ($debug);
+ print STDERR "$0 : parsed phase $phase_index\n" if ($debug);
} else {
$other = '';
$need_data = 1;
@@ -378,16 +378,17 @@ while (<STDIN>) {
$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};
+ local ($id, $cnst) = ($1, $2);
+ if ($symbol_values{$id} eq undef) {
+ $symbol_values{$id} = eval $cnst;
+ delete $forward{$id};
if ($is_absolute =~ /ABSOLUTE/i) {
- push (@absolute , $1);
+ push (@absolute , $id);
} else {
- push (@relative, $1);
+ push (@relative, $id);
}
} else {
- die "$0 : redefinition of symbol $1 in line $lineno : $_\n";
+ die "$0 : redefinition of symbol $id in line $lineno : $_\n";
}
} else {
die
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index 96dd21932..999377142 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -1,17 +1,18 @@
/*
- * scsi.c Copyright (C) 1992 Drew Eckhardt
- * Copyright (C) 1993, 1994 Eric Youngdale
+ * scsi.c Copyright (C) 1992 Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
*
- * generic mid-level SCSI driver by
- * Drew Eckhardt
+ * generic mid-level SCSI driver
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
*
* <drew@colorado.edu>
*
- * Bug correction thanks go to :
+ * Bug correction thanks go to :
* Rik Faith <faith@cs.unc.edu>
* Tommy Thorn <tthorn>
* Thomas Wuensche <tw@fgb1.fgb.mw.tu-muenchen.de>
- *
+ *
* Modified by Eric Youngdale ericy@cais.com to
* add scatter-gather, multiple outstanding request, and other
* enhancements.
@@ -23,12 +24,16 @@
#include <linux/string.h>
#include <linux/malloc.h>
#include <asm/irq.h>
+#include <asm/dma.h>
+#include <linux/ioport.h>
#include "../block/blk.h"
#include "scsi.h"
#include "hosts.h"
#include "constants.h"
+#undef USE_STATIC_SCSI_MEMORY
+
/*
static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/scsi.c,v 1.5 1993/09/24 12:45:18 drew Exp drew $";
*/
@@ -41,10 +46,13 @@ const unsigned char scsi_command_size[8] = { 6, 10, 10, 12, 12, 12, 10, 10 };
static void scsi_done (Scsi_Cmnd *SCpnt);
static int update_timeout (Scsi_Cmnd *, int);
static void print_inquiry(unsigned char *data);
-static void scsi_times_out (Scsi_Cmnd * SCpnt);
+static void scsi_times_out (Scsi_Cmnd * SCpnt, int pid);
static int time_start;
static int time_elapsed;
+static volatile struct Scsi_Host * host_active = NULL;
+#define SCSI_BLOCK(HOST) ((HOST->block && host_active && HOST != host_active) \
+ || (HOST->can_queue && HOST->host_busy >= HOST->can_queue))
#define MAX_SCSI_DEVICE_CODE 10
const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] =
@@ -63,8 +71,8 @@ const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] =
/*
- global variables :
- scsi_devices an array of these specifying the address for each
+ global variables :
+ scsi_devices an array of these specifying the address for each
(host, id, LUN)
*/
@@ -79,21 +87,21 @@ static unsigned char generic_sense[6] = {REQUEST_SENSE, 0,0,0, 255, 0};
Scsi_Cmnd * last_cmnd = NULL;
/*
- * As the scsi do command functions are intelligent, and may need to
- * redo a command, we need to keep track of the last command
+ * As the scsi do command functions are intelligent, and may need to
+ * redo a command, we need to keep track of the last command
* executed on each one.
*/
-#define WAS_RESET 0x01
-#define WAS_TIMEDOUT 0x02
+#define WAS_RESET 0x01
+#define WAS_TIMEDOUT 0x02
#define WAS_SENSE 0x04
#define IS_RESETTING 0x08
#define IS_ABORTING 0x10
#define ASKED_FOR_SENSE 0x20
/*
- * This is the number of clock ticks we should wait before we time out
- * and abort the command. This is for where the scsi.c module generates
+ * This is the number of clock ticks we should wait before we time out
+ * and abort the command. This is for where the scsi.c module generates
* the command, not where it originates from a higher level, in which
* case the timeout is specified there.
*
@@ -107,9 +115,9 @@ static void scsi_dump_status(void);
#ifdef DEBUG
- #define SCSI_TIMEOUT 500
+ #define SCSI_TIMEOUT (5*HZ)
#else
- #define SCSI_TIMEOUT 100
+ #define SCSI_TIMEOUT (1*HZ)
#endif
#ifdef DEBUG
@@ -117,12 +125,16 @@ static void scsi_dump_status(void);
#define ABORT_TIMEOUT SCSI_TIMEOUT
#define RESET_TIMEOUT SCSI_TIMEOUT
#else
- #define SENSE_TIMEOUT 50
- #define RESET_TIMEOUT 50
- #define ABORT_TIMEOUT 50
- #define MIN_RESET_DELAY 100
+ #define SENSE_TIMEOUT (5*HZ/10)
+ #define RESET_TIMEOUT (5*HZ/10)
+ #define ABORT_TIMEOUT (5*HZ/10)
#endif
+#define MIN_RESET_DELAY (1*HZ)
+
+/* Do not call reset on error if we just did a reset within 10 sec. */
+#define MIN_RESET_PERIOD (10*HZ)
+
/* The following devices are known not to tolerate a lun != 0 scan for
one reason or another. Some will respond to all luns, others will
lock up. */
@@ -133,11 +145,13 @@ static void scsi_dump_status(void);
char * revision; /* Latest revision known to be bad. Not used yet */
};
-static struct blist blacklist[] =
+static struct blist blacklist[] =
{
{"CHINON","CD-ROM CDS-431","H42"}, /* Locks up if polled for lun != 0 */
{"CHINON","CD-ROM CDS-535","Q14"}, /* Lockup if polled for lun != 0 */
{"DENON","DRD-25X","V"}, /* A cdrom that locks up when probed at lun != 0 */
+ {"HITACHI","DK312C","CM81"}, /* Responds to all lun - dtg */
+ {"HITACHI","DK314C","CR21" }, /* responds to all lun */
{"IMS", "CDD521/10","2.06"}, /* Locks-up when LUN>0 polled. */
{"MAXTOR","XT-3280","PR02"}, /* Locks-up when LUN>0 polled. */
{"MAXTOR","XT-4380S","B3C"}, /* Locks-up when LUN>0 polled. */
@@ -150,6 +164,7 @@ static struct blist blacklist[] =
* controller, which causes SCSI code to reset bus.*/
{"SEAGATE", "ST296","921"}, /* Responds to all lun */
{"SONY","CD-ROM CDU-541","4.3d"},
+ {"SONY","CD-ROM CDU-55S","1.0i"},
{"TANDBERG","TDC 3600","U07"}, /* Locks up if polled for lun != 0 */
{"TEAC","CD-ROM","1.06"}, /* causes failed REQUEST SENSE on lun 1 for seagate
* controller, which causes SCSI code to reset bus.*/
@@ -158,7 +173,11 @@ static struct blist blacklist[] =
{"QUANTUM","LPS525S","3110"},/* Locks sometimes if polled for lun != 0 */
{"QUANTUM","PD1225S","3110"},/* Locks sometimes if polled for lun != 0 */
{"MEDIAVIS","CDR-H93MV","1.31"}, /* Locks up if polled for lun != 0 */
- {NULL, NULL, NULL}};
+ {"SANKYO", "CP525","6.64"}, /* causes failed REQ SENSE, extra reset */
+ {"HP", "C1750A", "3226"}, /* scanjet iic */
+ {"HP", "C1790A", ""}, /* scanjet iip */
+ {"HP", "C2500A", ""}, /* scanjet iicx */
+ {NULL, NULL, NULL}};
static int blacklisted(unsigned char * response_data){
int i = 0;
@@ -174,172 +193,258 @@ static int blacklisted(unsigned char * response_data){
if(memcmp(blacklist[i].model, pnt,
strlen(blacklist[i].model))) continue;
return 1;
- };
-};
+ }
+}
/*
- * As the actual SCSI command runs in the background, we must set up a
- * flag that tells scan_scsis() when the result it has is valid.
- * scan_scsis can set the_result to -1, and watch for it to become the
- * actual return code for that call. the scan_scsis_done function() is
- * our user specified completion function that is passed on to the
+ * As the actual SCSI command runs in the background, we must set up a
+ * flag that tells scan_scsis() when the result it has is valid.
+ * scan_scsis can set the_result to -1, and watch for it to become the
+ * actual return code for that call. the scan_scsis_done function() is
+ * our user specified completion function that is passed on to the
* scsi_do_cmd() function.
*/
volatile int in_scan_scsis = 0;
static int the_result;
+
+void scsi_make_blocked_list(void) {
+ int block_count = 0, index;
+ unsigned int flags;
+ struct Scsi_Host * sh[128], * shpnt;
+
+ /*
+ * Create a circular linked list from the scsi hosts which have
+ * the "wish_block" field in the Scsi_Host structure set.
+ * The blocked list should include all the scsi hosts using ISA DMA.
+ * In some systems, using two dma channels simultaneously causes
+ * unpredictable results.
+ * Among the scsi hosts in the blocked list, only one host at a time
+ * is allowed to have active commands queued. The transition from
+ * one active host to the next one is allowed only when host_busy == 0
+ * for the active host (which implies host_busy == 0 for all the hosts
+ * in the list). Moreover for block devices the transition to a new
+ * active host is allowed only when a request is completed, since a
+ * block device request can be divided into multiple scsi commands
+ * (when there are few sg lists or clustering is disabled).
+ *
+ * (DB, 4 Feb 1995)
+ */
+
+ save_flags(flags);
+ cli();
+ host_active = NULL;
+
+ for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next) {
+
+#if 0
+ /*
+ * Is this is a candidate for the blocked list?
+ * Useful to put into the blocked list all the hosts whose driver
+ * does not know about the host->block feature.
+ */
+ if (shpnt->unchecked_isa_dma) shpnt->wish_block = 1;
+#endif
+
+ if (shpnt->wish_block) sh[block_count++] = shpnt;
+ }
+
+ if (block_count == 1) sh[0]->block = NULL;
+
+ else if (block_count > 1) {
+
+ for(index = 0; index < block_count - 1; index++) {
+ sh[index]->block = sh[index + 1];
+ printk("scsi%d : added to blocked host list.\n",
+ sh[index]->host_no);
+ }
+
+ sh[block_count - 1]->block = sh[0];
+ printk("scsi%d : added to blocked host list.\n",
+ sh[index]->host_no);
+ }
+
+ restore_flags(flags);
+ }
+
static void scan_scsis_done (Scsi_Cmnd * SCpnt)
{
-
+
#ifdef DEBUG
- printk ("scan_scsis_done(%d, %06x)\n", SCpnt->host, SCpnt->result);
-#endif
+ printk ("scan_scsis_done(%p, %06x)\n", SCpnt->host, SCpnt->result);
+#endif
SCpnt->request.dev = 0xfffe;
+
+ if (SCpnt->request.sem != NULL)
+ up(SCpnt->request.sem);
}
-#ifdef NO_MULTI_LUN
-static int max_scsi_luns = 1;
-#else
+#ifdef CONFIG_SCSI_MULTI_LUN
static int max_scsi_luns = 8;
+#else
+static int max_scsi_luns = 1;
#endif
void scsi_luns_setup(char *str, int *ints) {
- if (ints[0] != 1)
+ if (ints[0] != 1)
printk("scsi_luns_setup : usage max_scsi_luns=n (n should be between 1 and 8)\n");
else
- max_scsi_luns = ints[1];
+ max_scsi_luns = ints[1];
}
/*
- * Detecting SCSI devices :
- * We scan all present host adapter's busses, from ID 0 to ID 6.
- * We use the INQUIRY command, determine device type, and pass the ID /
- * lun address of all sequential devices to the tape driver, all random
+ * Detecting SCSI devices :
+ * We scan all present host adapter's busses, from ID 0 to ID 6.
+ * We use the INQUIRY command, determine device type, and pass the ID /
+ * lun address of all sequential devices to the tape driver, all random
* devices to the disk driver.
*/
-static void scan_scsis (struct Scsi_Host * shpnt)
+void scan_scsis (struct Scsi_Host * shpnt)
{
int dev, lun, type;
unsigned char scsi_cmd [12];
- unsigned char scsi_result [256];
+ unsigned char scsi_result0 [256];
+ unsigned char * scsi_result;
Scsi_Device * SDpnt, *SDtail;
struct Scsi_Device_Template * sdtpnt;
- Scsi_Cmnd SCmd;
-
+ Scsi_Cmnd *SCpnt;
+
++in_scan_scsis;
lun = 0;
type = -1;
- SCmd.next = NULL;
- SCmd.prev = NULL;
- SDpnt = (Scsi_Device *) scsi_init_malloc(sizeof (Scsi_Device));
+ SCpnt = (Scsi_Cmnd *) scsi_init_malloc(sizeof(Scsi_Cmnd), GFP_ATOMIC|GFP_DMA);
+ SDpnt = (Scsi_Device *) scsi_init_malloc(sizeof (Scsi_Device), GFP_ATOMIC);
SDtail = scsi_devices;
- if(scsi_devices) {
- while(SDtail->next) SDtail = SDtail->next;
- }
- shpnt->host_queue = &SCmd; /* We need this so that
- commands can time out */
+ if(scsi_devices) while(SDtail->next) SDtail = SDtail->next;
+
+ /* Make sure we have something that is valid for DMA purposes */
+ scsi_result = ((current == task[0] || !shpnt->unchecked_isa_dma)
+ ? &scsi_result0[0] : scsi_malloc(512));
+
+
+ shpnt->host_queue = SCpnt; /* We need this so that commands can time out */
+
for (dev = 0; dev < 8; ++dev)
if (shpnt->this_id != dev)
+
/*
* We need the for so our continue, etc. work fine.
*/
-
for (lun = 0; lun < max_scsi_luns; ++lun)
{
+ memset(SDpnt, 0, sizeof(Scsi_Device));
SDpnt->host = shpnt;
SDpnt->id = dev;
SDpnt->lun = lun;
- SDpnt->device_wait = NULL;
- SDpnt->next = NULL;
- SDpnt->attached = 0;
+
+ /* Some low level driver could use device->type (DB) */
+ SDpnt->type = -1;
/*
- * Assume that the device will have handshaking problems, and then
+ * Assume that the device will have handshaking problems, and then
* fix this field later if it turns out it doesn't.
*/
SDpnt->borken = 1;
-
+
scsi_cmd[0] = TEST_UNIT_READY;
scsi_cmd[1] = lun << 5;
- scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
- scsi_cmd[4] = 0;
-
- SCmd.host = shpnt;
- SCmd.target = dev;
- SCmd.lun = lun;
-
- SCmd.request.sem = NULL; /* Used for mutex if loading devices after boot */
- SCmd.request.dev = 0xffff; /* Mark not busy */
- SCmd.use_sg = 0;
- SCmd.cmd_len = 0;
- SCmd.old_use_sg = 0;
- SCmd.transfersize = 0;
- SCmd.underflow = 0;
-
- scsi_do_cmd (&SCmd,
- (void *) scsi_cmd, (void *)
- scsi_result, 256, scan_scsis_done,
- SCSI_TIMEOUT + 400, 5);
-
- while (SCmd.request.dev != 0xfffe);
+ scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[4] = scsi_cmd[5] = 0;
+
+ memset(SCpnt, 0, sizeof(Scsi_Cmnd));
+ SCpnt->host = SDpnt->host;
+ SCpnt->device = SDpnt;
+ SCpnt->target = SDpnt->id;
+ SCpnt->lun = SDpnt->lun;
+
+ /* Used for mutex if loading devices after boot */
+ SCpnt->request.sem = NULL;
+
+ SCpnt->request.dev = 0xffff; /* Mark not busy */
+
+ scsi_do_cmd (SCpnt, (void *) scsi_cmd, (void *) scsi_result,
+ 256, scan_scsis_done, SCSI_TIMEOUT + 4 * HZ, 5);
+
+ /* Wait for command to finish. Use simple wait if we are booting, else
+ do it right and use a mutex */
+
+ if (current == task[0])
+ while (SCpnt->request.dev != 0xfffe) barrier();
+ else if (SCpnt->request.dev != 0xfffe) {
+ struct semaphore sem = MUTEX_LOCKED;
+
+ SCpnt->request.sem = &sem;
+ down(&sem);
+
+ /* Hmm.. Have to ask about this one */
+ while (SCpnt->request.dev != 0xfffe) schedule();
+ }
+
#if defined(DEBUG) || defined(DEBUG_INIT)
printk("scsi: scan SCSIS id %d lun %d\n", dev, lun);
- printk("scsi: return code %08x\n", SCmd.result);
+ printk("scsi: return code %08x\n", SCpnt->result);
#endif
-
-
- if(SCmd.result) {
- if ((driver_byte(SCmd.result) & DRIVER_SENSE) &&
- ((SCmd.sense_buffer[0] & 0x70) >> 4) == 7) {
- if (SCmd.sense_buffer[2] &0xe0)
+
+
+ if(SCpnt->result) {
+ if (((driver_byte(SCpnt->result) & DRIVER_SENSE) ||
+ (status_byte(SCpnt->result) & CHECK_CONDITION)) &&
+ ((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) {
+ if (SCpnt->sense_buffer[2] &0xe0)
continue; /* No devices here... */
- if(((SCmd.sense_buffer[2] & 0xf) != NOT_READY) &&
- ((SCmd.sense_buffer[2] & 0xf) != UNIT_ATTENTION))
+ if(((SCpnt->sense_buffer[2] & 0xf) != NOT_READY) &&
+ ((SCpnt->sense_buffer[2] & 0xf) != UNIT_ATTENTION))
continue;
}
else
break;
- };
-
+ }
+
#if defined (DEBUG) || defined(DEBUG_INIT)
printk("scsi: performing INQUIRY\n");
#endif
-
+
/*
- * Build an INQUIRY command block.
+ * Build an INQUIRY command block.
*/
-
scsi_cmd[0] = INQUIRY;
scsi_cmd[1] = (lun << 5) & 0xe0;
scsi_cmd[2] = 0;
scsi_cmd[3] = 0;
scsi_cmd[4] = 255;
scsi_cmd[5] = 0;
-
- SCmd.request.dev = 0xffff; /* Mark not busy */
- SCmd.cmd_len = 0;
-
- scsi_do_cmd (&SCmd,
- (void *) scsi_cmd, (void *)
- scsi_result, 256, scan_scsis_done,
- SCSI_TIMEOUT, 3);
-
- while (SCmd.request.dev != 0xfffe);
-
- the_result = SCmd.result;
-
+
+ SCpnt->request.dev = 0xffff; /* Mark not busy */
+ SCpnt->cmd_len = 0;
+
+ scsi_do_cmd (SCpnt, (void *) scsi_cmd, (void *) scsi_result,
+ 256, scan_scsis_done, SCSI_TIMEOUT, 3);
+
+ if (current == task[0])
+ while (SCpnt->request.dev != 0xfffe) barrier();
+ else if (SCpnt->request.dev != 0xfffe) {
+ struct semaphore sem = MUTEX_LOCKED;
+
+ SCpnt->request.sem = &sem;
+ down(&sem);
+
+ /* Hmm.. Have to ask about this one */
+ while (SCpnt->request.dev != 0xfffe) schedule();
+ }
+
+ the_result = SCpnt->result;
+
#if defined(DEBUG) || defined(DEBUG_INIT)
if (!the_result)
printk("scsi: INQUIRY successful\n");
else
printk("scsi: INQUIRY failed with code %08x\n", the_result);
#endif
-
- if(the_result) break;
-
+
+ if(the_result) break;
+
/* skip other luns on this device */
-
+
if (!the_result)
{
/* It would seem some TOSHIBA CD-ROM gets things wrong */
@@ -350,29 +455,36 @@ static void scan_scsis (struct Scsi_Host * shpnt)
scsi_result[1] |= 0x80; /* removable */
}
- SDpnt->manufacturer = SCSI_MAN_UNKNOWN;
- if (!strncmp(scsi_result+8,"NEC",3))
- SDpnt->manufacturer = SCSI_MAN_NEC;
- if (!strncmp(scsi_result+8,"TOSHIBA",7))
- SDpnt->manufacturer = SCSI_MAN_TOSHIBA;
+ if (!strncmp(scsi_result+8,"NEC",3)) {
+ if (!strncmp(scsi_result+16,"CD-ROM DRIVE:84 ",16) ||
+ !strncmp(scsi_result+16,"CD-ROM DRIVE:25",15))
+ SDpnt->manufacturer = SCSI_MAN_NEC_OLDCDR;
+ else
+ SDpnt->manufacturer = SCSI_MAN_NEC;
+ } else if (!strncmp(scsi_result+8,"TOSHIBA",7))
+ SDpnt->manufacturer = SCSI_MAN_TOSHIBA;
+ else
+ SDpnt->manufacturer = SCSI_MAN_UNKNOWN;
- SDpnt->removable = (0x80 &
+ SDpnt->removable = (0x80 &
scsi_result[1]) >> 7;
SDpnt->lockable = SDpnt->removable;
SDpnt->changed = 0;
SDpnt->access_count = 0;
SDpnt->busy = 0;
-/*
+/*
* Currently, all sequential devices are assumed to be tapes,
- * all random devices disk, with the appropriate read only
+ * all random devices disk, with the appropriate read only
* flags set for ROM / WORM treated as RO.
- */
+ */
- switch (type = scsi_result[0])
+ switch (type = (scsi_result[0] & 0x1f))
{
case TYPE_TAPE :
case TYPE_DISK :
case TYPE_MOD :
+ case TYPE_PROCESSOR :
+ case TYPE_SCANNER :
SDpnt->writeable = 1;
break;
case TYPE_WORM :
@@ -388,12 +500,12 @@ static void scan_scsis (struct Scsi_Host * shpnt)
type = -1;
#endif
}
-
- SDpnt->soft_reset =
+
+ SDpnt->soft_reset =
(scsi_result[7] & 1) && ((scsi_result[3] & 7) == 2);
SDpnt->random = (type == TYPE_TAPE) ? 0 : 1;
- SDpnt->type = type;
-
+ SDpnt->type = (type & 0x1f);
+
if (type != -1)
{
print_inquiry(scsi_result);
@@ -407,19 +519,19 @@ static void scan_scsis (struct Scsi_Host * shpnt)
(SDpnt->scsi_level == 1 &&
(scsi_result[3] & 0x0f) == 1))
SDpnt->scsi_level++;
-/*
+/*
* Set the tagged_queue flag for SCSI-II devices that purport to support
* tagged queuing in the INQUIRY data.
*/
-
+
SDpnt->tagged_queue = 0;
-
+
if ((SDpnt->scsi_level >= SCSI_2) &&
(scsi_result[7] & 2)) {
SDpnt->tagged_supported = 1;
SDpnt->current_tag = 0;
}
-
+
/*
* Accommodate drivers that want to sleep when they should be in a polling
* loop.
@@ -430,14 +542,14 @@ static void scan_scsis (struct Scsi_Host * shpnt)
/*
* Some revisions of the Texel CD ROM drives have handshaking
* problems when used with the Seagate controllers. Before we
- * know what type of device we're talking to, we assume it's
+ * know what type of device we're talking to, we assume it's
* borken and then change it here if it turns out that it isn't
* a TEXEL drive.
*/
if(strncmp("TEXEL", (char *) &scsi_result[8], 5) != 0 ||
- strncmp("CD-ROM", (char *) &scsi_result[16], 6) != 0
-/*
+ strncmp("CD-ROM", (char *) &scsi_result[16], 6) != 0
+/*
* XXX 1.06 has problems, some one should figure out the others too so
* ALL TEXEL drives don't suffer in performance, especially when I finish
* integrating my seagate patches which do multiple I_T_L nexuses.
@@ -448,8 +560,8 @@ static void scan_scsis (struct Scsi_Host * shpnt)
#endif
)
SDpnt->borken = 0;
-
-
+
+
/* These devices need this "key" to unlock the device
so we can use it */
if(memcmp("INSITE", &scsi_result[8], 6) == 0 &&
@@ -463,17 +575,26 @@ static void scan_scsis (struct Scsi_Host * shpnt)
scsi_cmd[3] = 0;
scsi_cmd[4] = 0x2a;
scsi_cmd[5] = 0;
-
- SCmd.request.dev = 0xffff; /* Mark not busy */
- SCmd.cmd_len = 0;
-
- scsi_do_cmd (&SCmd,
- (void *) scsi_cmd, (void *)
- scsi_result, 0x2a, scan_scsis_done,
+
+ SCpnt->request.dev = 0xffff; /* Mark not busy */
+ SCpnt->cmd_len = 0;
+
+ scsi_do_cmd (SCpnt, (void *) scsi_cmd,
+ (void *) scsi_result, 0x2a, scan_scsis_done,
SCSI_TIMEOUT, 3);
-
- while (SCmd.request.dev != 0xfffe);
- };
+
+ if (current == task[0])
+ while (SCpnt->request.dev != 0xfffe) barrier();
+ else if (SCpnt->request.dev != 0xfffe) {
+ struct semaphore sem = MUTEX_LOCKED;
+
+ SCpnt->request.sem = &sem;
+ down(&sem);
+
+ /* Hmm.. Have to ask about this one */
+ while (SCpnt->request.dev != 0xfffe) schedule();
+ }
+ }
/* Add this device to the linked list at the end */
if(SDtail)
SDtail->next = SDpnt;
@@ -481,7 +602,7 @@ static void scan_scsis (struct Scsi_Host * shpnt)
scsi_devices = SDpnt;
SDtail = SDpnt;
- SDpnt = (Scsi_Device *) scsi_init_malloc(sizeof (Scsi_Device));
+ SDpnt = (Scsi_Device *) scsi_init_malloc(sizeof (Scsi_Device), GFP_ATOMIC);
/* Some scsi devices cannot be polled for lun != 0
due to firmware bugs */
if(blacklisted(scsi_result)) break;
@@ -490,44 +611,43 @@ static void scan_scsis (struct Scsi_Host * shpnt)
break;
/* Some scsi-1 peripherals do not handle lun != 0.
I am assuming that scsi-2 peripherals do better */
- if((scsi_result[2] & 0x07) == 1 &&
+ if((scsi_result[2] & 0x07) == 1 &&
(scsi_result[3] & 0x0f) == 0) break;
}
} /* if result == DID_OK ends */
} /* for lun ends */
+
shpnt->host_queue = NULL; /* No longer needed here */
-
- printk("scsi : detected ");
- for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
- if(sdtpnt->dev_noticed && sdtpnt->name)
- printk("%d SCSI %s%s ", sdtpnt->dev_noticed, sdtpnt->name,
- (sdtpnt->dev_noticed != 1) ? "s" : "");
-
- printk("total.\n");
-
+
/* Last device block does not exist. Free memory. */
scsi_init_free((char *) SDpnt, sizeof(Scsi_Device));
-
+
+ scsi_init_free((char *) SCpnt, sizeof(Scsi_Cmnd));
+
+
+ /* If we allocated a buffer so we could do DMA, free it now */
+ if (scsi_result != &scsi_result0[0]) scsi_free(scsi_result, 512);
+
in_scan_scsis = 0;
} /* scan_scsis ends */
/*
- * Flag bits for the internal_timeout array
+ * Flag bits for the internal_timeout array
*/
#define NORMAL_TIMEOUT 0
#define IN_ABORT 1
#define IN_RESET 2
/*
- This is our time out function, called when the timer expires for a
- given host adapter. It will attempt to abort the currently executing
+ This is our time out function, called when the timer expires for a
+ given host adapter. It will attempt to abort the currently executing
command, that failing perform a kernel panic.
-*/
+*/
-static void scsi_times_out (Scsi_Cmnd * SCpnt)
+static void scsi_times_out (Scsi_Cmnd * SCpnt, int pid)
{
-
- switch (SCpnt->internal_timeout & (IN_ABORT | IN_RESET))
+
+ switch (SCpnt->internal_timeout & (IN_ABORT | IN_RESET))
{
case NORMAL_TIMEOUT:
if (!in_scan_scsis) {
@@ -535,13 +655,13 @@ static void scsi_times_out (Scsi_Cmnd * SCpnt)
scsi_dump_status();
#endif
}
-
- if (!scsi_abort (SCpnt, DID_TIME_OUT))
- return;
+
+ if (!scsi_abort (SCpnt, DID_TIME_OUT, pid))
+ return;
case IN_ABORT:
printk("SCSI host %d abort() timed out - resetting\n",
SCpnt->host->host_no);
- if (!scsi_reset (SCpnt))
+ if (!scsi_reset (SCpnt))
return;
case IN_RESET:
case (IN_ABORT | IN_RESET):
@@ -555,7 +675,7 @@ static void scsi_times_out (Scsi_Cmnd * SCpnt)
default:
INTERNAL_ERROR;
}
-
+
}
@@ -573,22 +693,21 @@ Scsi_Cmnd * request_queueable (struct request * req, Scsi_Device * device)
if (!device)
panic ("No device passed to request_queueable().\n");
-
+
if (req && req->dev <= 0)
panic("Invalid device in request_queueable");
-
+
SCpnt = device->host->host_queue;
while(SCpnt){
if(SCpnt->target == device->id &&
SCpnt->lun == device->lun)
if(SCpnt->request.dev < 0) break;
SCpnt = SCpnt->next;
- };
+ }
if (!SCpnt) return NULL;
- if (device->host->can_queue
- && device->host->host_busy >= device->host->can_queue) return NULL;
+ if (SCSI_BLOCK(device->host)) return NULL;
if (req) {
memcpy(&SCpnt->request, req, sizeof(struct request));
@@ -604,7 +723,7 @@ Scsi_Cmnd * request_queueable (struct request * req, Scsi_Device * device)
req->sector += bh->b_size >> 9;
if(!tablesize) break;
bh = bhp;
- };
+ }
if(req->nr_sectors && bh && bh->b_reqnext){ /* Any leftovers? */
SCpnt->request.bhtail = bh;
req->bh = bh->b_reqnext; /* Divide request */
@@ -619,11 +738,11 @@ Scsi_Cmnd * request_queueable (struct request * req, Scsi_Device * device)
} else {
req->dev = -1;
wake_up(&wait_for_request);
- };
+ }
} else {
SCpnt->request.dev = 0xffff; /* Busy, but no request */
SCpnt->request.sem = NULL; /* And no one is waiting for the device either */
- };
+ }
SCpnt->use_sg = 0; /* Reset the scatter-gather flag */
SCpnt->old_use_sg = 0;
@@ -648,6 +767,7 @@ Scsi_Cmnd * allocate_device (struct request ** reqp, Scsi_Device * device,
int dev = -1;
struct request * req = NULL;
int tablesize;
+ unsigned int flags;
struct buffer_head * bh, *bhp;
struct Scsi_Host * host;
Scsi_Cmnd * SCpnt = NULL;
@@ -655,14 +775,16 @@ Scsi_Cmnd * allocate_device (struct request ** reqp, Scsi_Device * device,
if (!device)
panic ("No device passed to allocate_device().\n");
-
+
if (reqp) req = *reqp;
/* See if this request has already been queued by an interrupt routine */
if (req && (dev = req->dev) <= 0) return NULL;
-
+
host = device->host;
-
+
+ if (intr_count && SCSI_BLOCK(host)) return NULL;
+
while (1==1){
SCpnt = host->host_queue;
while(SCpnt){
@@ -670,25 +792,26 @@ Scsi_Cmnd * allocate_device (struct request ** reqp, Scsi_Device * device,
SCpnt->lun == device->lun) {
SCwait = SCpnt;
if(SCpnt->request.dev < 0) break;
- };
+ }
SCpnt = SCpnt->next;
- };
+ }
+ save_flags(flags);
cli();
/* See if this request has already been queued by an interrupt routine */
if (req && ((req->dev < 0) || (req->dev != dev))) {
- sti();
+ restore_flags(flags);
return NULL;
- };
+ }
if (!SCpnt || SCpnt->request.dev >= 0) /* Might have changed */
{
- sti();
+ restore_flags(flags);
if(!wait) return NULL;
if (!SCwait) {
printk("Attempt to allocate device target %d, lun %d\n",
device->id ,device->lun);
panic("No device found in allocate_device\n");
- };
- SCSI_SLEEP(&device->device_wait,
+ }
+ SCSI_SLEEP(&device->device_wait,
(SCwait->request.dev > 0));
} else {
if (req) {
@@ -705,7 +828,7 @@ Scsi_Cmnd * allocate_device (struct request ** reqp, Scsi_Device * device,
req->sector += bh->b_size >> 9;
if(!tablesize) break;
bh = bhp;
- };
+ }
if(req->nr_sectors && bh && bh->b_reqnext){ /* Any leftovers? */
SCpnt->request.bhtail = bh;
req->bh = bh->b_reqnext; /* Divide request */
@@ -717,20 +840,20 @@ Scsi_Cmnd * allocate_device (struct request ** reqp, Scsi_Device * device,
req->buffer = bh->b_data;
SCpnt->request.sem = NULL; /* Wait until whole thing done */
}
- else
+ else
{
req->dev = -1;
*reqp = req->next;
wake_up(&wait_for_request);
- };
+ }
} else {
SCpnt->request.dev = 0xffff; /* Busy */
SCpnt->request.sem = NULL; /* And no one is waiting for this to complete */
- };
- sti();
+ }
+ restore_flags(flags);
break;
- };
- };
+ }
+ }
SCpnt->use_sg = 0; /* Reset the scatter-gather flag */
SCpnt->old_use_sg = 0;
@@ -743,12 +866,13 @@ Scsi_Cmnd * allocate_device (struct request ** reqp, Scsi_Device * device,
/*
This is inline because we have stack problemes if we recurse to deeply.
*/
-
+
inline void internal_cmnd (Scsi_Cmnd * SCpnt)
{
int temp;
struct Scsi_Host * host;
-#ifdef DEBUG_DELAY
+ unsigned int flags;
+#ifdef DEBUG_DELAY
int clock;
#endif
@@ -758,27 +882,30 @@ inline void internal_cmnd (Scsi_Cmnd * SCpnt)
host = SCpnt->host;
/*
- We will wait MIN_RESET_DELAY clock ticks after the last reset so
+ We will wait MIN_RESET_DELAY clock ticks after the last reset so
we can avoid the drive not being ready.
-*/
-temp = host->last_reset;
+*/
+save_flags(flags);
+sti();
+temp = host->last_reset + MIN_RESET_DELAY;
while (jiffies < temp);
+restore_flags(flags);
update_timeout(SCpnt, SCpnt->timeout_per_command);
/*
We will use a queued command if possible, otherwise we will emulate the
- queuing and calling of completion function ourselves.
+ queuing and calling of completion function ourselves.
*/
#ifdef DEBUG
- printk("internal_cmnd (host = %d, target = %d, command = %08x, buffer = %08x, \n"
- "bufflen = %d, done = %08x)\n", SCpnt->host->host_no, SCpnt->target, SCpnt->cmnd, SCpnt->buffer, SCpnt->bufflen, SCpnt->done);
+ printk("internal_cmnd (host = %d, target = %d, command = %p, buffer = %p, \n"
+ "bufflen = %d, done = %p)\n", SCpnt->host->host_no, SCpnt->target, SCpnt->cmnd, SCpnt->buffer, SCpnt->bufflen, SCpnt->done);
#endif
- if (host->can_queue)
+ if (host->can_queue)
{
#ifdef DEBUG
- printk("queuecommand : routine at %08x\n",
+ printk("queuecommand : routine at %p\n",
host->hostt->queuecommand);
#endif
/* This locking tries to prevent all sorts of races between
@@ -792,7 +919,7 @@ update_timeout(SCpnt, SCpnt->timeout_per_command);
if(!intr_count && SCpnt->host->irq)
disable_irq(SCpnt->host->irq);
- host->hostt->queuecommand (SCpnt, scsi_done);
+ host->hostt->queuecommand (SCpnt, scsi_done);
if(!intr_count && SCpnt->host->irq)
enable_irq(SCpnt->host->irq);
@@ -801,34 +928,37 @@ update_timeout(SCpnt, SCpnt->timeout_per_command);
{
#ifdef DEBUG
- printk("command() : routine at %08x\n", host->hostt->command);
+ printk("command() : routine at %p\n", host->hostt->command);
#endif
temp=host->hostt->command (SCpnt);
- SCpnt->result = temp;
+ SCpnt->result = temp;
#ifdef DEBUG_DELAY
- clock = jiffies + 400;
+ clock = jiffies + 4*HZ;
while (jiffies < clock);
- printk("done(host = %d, result = %04x) : routine at %08x\n", host->host_no, temp, done);
+ printk("done(host = %d, result = %04x) : routine at %08x\n", host->host_no, temp);
#endif
- scsi_done(SCpnt);
- }
+ scsi_done(SCpnt);
+ }
#ifdef DEBUG
printk("leaving internal_cmnd()\n");
#endif
- }
+ }
static void scsi_request_sense (Scsi_Cmnd * SCpnt)
{
+ unsigned int flags;
+
+ save_flags(flags);
cli();
SCpnt->flags |= WAS_SENSE | ASKED_FOR_SENSE;
update_timeout(SCpnt, SENSE_TIMEOUT);
- sti();
-
+ restore_flags(flags);
+
memcpy ((void *) SCpnt->cmnd , (void *) generic_sense,
sizeof(generic_sense));
- SCpnt->cmnd[1] = SCpnt->lun << 5;
+ SCpnt->cmnd[1] = SCpnt->lun << 5;
SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer);
SCpnt->request_buffer = &SCpnt->sense_buffer;
@@ -836,77 +966,70 @@ static void scsi_request_sense (Scsi_Cmnd * SCpnt)
SCpnt->use_sg = 0;
SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]);
internal_cmnd (SCpnt);
- SCpnt->use_sg = SCpnt->old_use_sg;
- SCpnt->cmd_len = SCpnt->old_cmd_len;
}
/*
- scsi_do_cmd sends all the commands out to the low-level driver. It
- handles the specifics required for each low level driver - ie queued
- or non queued. It also prevents conflicts when different high level
+ scsi_do_cmd sends all the commands out to the low-level driver. It
+ handles the specifics required for each low level driver - ie queued
+ or non queued. It also prevents conflicts when different high level
drivers go for the same host at the same time.
*/
-void scsi_do_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
+void scsi_do_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
void *buffer, unsigned bufflen, void (*done)(Scsi_Cmnd *),
- int timeout, int retries
+ int timeout, int retries
)
- {
+ {
+ unsigned long flags;
struct Scsi_Host * host = SCpnt->host;
#ifdef DEBUG
{
- int i;
+ int i;
int target = SCpnt->target;
- printk ("scsi_do_cmd (host = %d, target = %d, buffer =%08x, "
- "bufflen = %d, done = %08x, timeout = %d, retries = %d)\n"
+ printk ("scsi_do_cmd (host = %d, target = %d, buffer =%p, "
+ "bufflen = %d, done = %p, timeout = %d, retries = %d)\n"
"command : " , host->host_no, target, buffer, bufflen, done, timeout, retries);
for (i = 0; i < 10; ++i)
- printk ("%02x ", ((unsigned char *) cmnd)[i]);
+ printk ("%02x ", ((unsigned char *) cmnd)[i]);
printk("\n");
- };
+ }
#endif
-
+
if (!host)
{
panic ("Invalid or not present host.\n");
}
-
+
/*
- We must prevent reentrancy to the lowlevel host driver. This prevents
- it - we enter a loop until the host we want to talk to is not busy.
+ We must prevent reentrancy to the lowlevel host driver. This prevents
+ it - we enter a loop until the host we want to talk to is not busy.
Race conditions are prevented, as interrupts are disabled in between the
time we check for the host being not busy, and the time we mark it busy
ourselves.
*/
- SCpnt->pid = scsi_pid++;
+ save_flags(flags);
+ cli();
+ SCpnt->pid = scsi_pid++;
+
+ while (SCSI_BLOCK(host)) {
+ restore_flags(flags);
+ SCSI_SLEEP(&host->host_wait, SCSI_BLOCK(host));
+ cli();
+ }
+
+ if (host->block) host_active = host;
+
+ host->host_busy++;
+ restore_flags(flags);
- while (1==1){
- cli();
- if (host->can_queue
- && host->host_busy >= host->can_queue)
- {
- sti();
- SCSI_SLEEP(&host->host_wait,
- (host->host_busy >= host->can_queue));
- } else {
- host->host_busy++;
- if (host->block) {
- struct Scsi_Host * block;
- for(block = host->block; block != host; block = block->block)
- block->host_busy |= ~SCSI_HOST_BLOCK;
- }
- sti();
- break;
- };
- };
/*
- Our own function scsi_done (which marks the host as not busy, disables
- the timeout counter, etc) will be called by us or by the
+ Our own function scsi_done (which marks the host as not busy, disables
+ the timeout counter, etc) will be called by us or by the
scsi_hosts[host].queuecommand() function needs to also call
the completion function for the high level driver.
@@ -925,7 +1048,7 @@ void scsi_do_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
SCpnt->allowed=retries;
SCpnt->done = done;
SCpnt->timeout_per_command = timeout;
-
+
memcpy ((void *) SCpnt->cmnd , (void *) cmnd, 12);
/* Zero the sense buffer. Some host adapters automatically request
sense on error. 0 is not a valid sense code. */
@@ -946,13 +1069,13 @@ void scsi_do_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
#ifdef DEBUG
printk ("Leaving scsi_do_cmd()\n");
#endif
- }
+ }
/*
- The scsi_done() function disables the timeout timer for the scsi host,
- marks the host as not busy, and calls the user specified completion
+ The scsi_done() function disables the timeout timer for the scsi host,
+ marks the host as not busy, and calls the user specified completion
function for that host's current command.
*/
@@ -961,23 +1084,23 @@ static void reset (Scsi_Cmnd * SCpnt)
#ifdef DEBUG
printk("scsi: reset(%d)\n", SCpnt->host->host_no);
#endif
-
+
SCpnt->flags |= (WAS_RESET | IS_RESETTING);
scsi_reset(SCpnt);
-
+
#ifdef DEBUG
printk("performing request sense\n");
#endif
-
+
#if 0 /* FIXME - remove this when done */
if(SCpnt->flags & NEEDS_JUMPSTART) {
SCpnt->flags &= ~NEEDS_JUMPSTART;
scsi_request_sense (SCpnt);
- };
+ }
#endif
}
-
-
+
+
static int check_sense (Scsi_Cmnd * SCpnt)
{
@@ -990,7 +1113,7 @@ static int check_sense (Scsi_Cmnd * SCpnt)
else
return SUGGEST_RETRY;
}
-
+
SCpnt->flags &= ~ASKED_FOR_SENSE;
#ifdef DEBUG_INIT
@@ -998,7 +1121,7 @@ static int check_sense (Scsi_Cmnd * SCpnt)
print_sense("", SCpnt);
printk("\n");
#endif
- if (SCpnt->sense_buffer[2] &0xe0)
+ if (SCpnt->sense_buffer[2] &0xe0)
return SUGGEST_ABORT;
switch (SCpnt->sense_buffer[2] & 0xf)
@@ -1006,22 +1129,19 @@ static int check_sense (Scsi_Cmnd * SCpnt)
case NO_SENSE:
return 0;
case RECOVERED_ERROR:
- if (SCpnt->device->type == TYPE_TAPE)
- return SUGGEST_IS_OK;
- else
- return 0;
+ return SUGGEST_IS_OK;
case ABORTED_COMMAND:
- return SUGGEST_RETRY;
+ return SUGGEST_RETRY;
case NOT_READY:
case UNIT_ATTENTION:
return SUGGEST_ABORT;
- /* these three are not supported */
+ /* these three are not supported */
case COPY_ABORTED:
case VOLUME_OVERFLOW:
case MISCOMPARE:
-
+
case MEDIUM_ERROR:
return SUGGEST_REMAP;
case BLANK_CHECK:
@@ -1072,7 +1192,7 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
/* If we requested an abort, (and we got it) then fix up the return
status to say why */
if(host_byte(result) == DID_ABORT && SCpnt->abort_reason)
- SCpnt->result = result = (result & 0xff00ffff) |
+ SCpnt->result = result = (result & 0xff00ffff) |
(SCpnt->abort_reason << 16);
@@ -1084,7 +1204,14 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
#ifdef DEBUG
printk("In scsi_done(host = %d, result = %06x)\n", host->host_no, result);
#endif
- switch (host_byte(result))
+
+ if(SCpnt->flags & WAS_SENSE)
+ {
+ SCpnt->use_sg = SCpnt->old_use_sg;
+ SCpnt->cmd_len = SCpnt->old_cmd_len;
+ }
+
+ switch (host_byte(result))
{
case DID_OK:
if (status_byte(result) && (SCpnt->flags & WAS_SENSE))
@@ -1095,7 +1222,7 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
if (!(SCpnt->flags & WAS_RESET))
{
- printk("scsi%d : target %d lun %d request sense failed, performing reset.\n",
+ printk("scsi%d : target %d lun %d request sense failed, performing reset.\n",
SCpnt->host->host_no, SCpnt->target, SCpnt->lun);
reset(SCpnt);
return;
@@ -1120,11 +1247,11 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
SCpnt->flags &= ~WAS_SENSE;
SCpnt->internal_timeout &= ~SENSE_TIMEOUT;
-
+
switch (checked = check_sense(SCpnt))
{
case SUGGEST_SENSE:
- case 0:
+ case 0:
#ifdef DEBUG
printk("NO SENSE. status = REDO\n");
#endif
@@ -1132,10 +1259,10 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
update_timeout(SCpnt, oldto);
status = REDO;
break;
- case SUGGEST_IS_OK:
+ case SUGGEST_IS_OK:
break;
- case SUGGEST_REMAP:
- case SUGGEST_RETRY:
+ case SUGGEST_REMAP:
+ case SUGGEST_RETRY:
#ifdef DEBUG
printk("SENSE SUGGEST REMAP or SUGGEST RETRY - status = MAYREDO\n");
#endif
@@ -1152,10 +1279,10 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
exit = DRIVER_SENSE | SUGGEST_ABORT;
break;
default:
- printk ("Internal error %s %d \n", __FILE__,
+ printk ("Internal error %s %d \n", __FILE__,
__LINE__);
- }
- }
+ }
+ }
else
{
#ifdef DEBUG
@@ -1165,16 +1292,16 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
exit = DRIVER_OK;
status = FINISHED;
}
- break;
+ break;
case CHECK_CONDITION:
switch (check_sense(SCpnt))
{
- case 0:
+ case 0:
update_timeout(SCpnt, oldto);
status = REDO;
break;
- case SUGGEST_REMAP:
+ case SUGGEST_REMAP:
case SUGGEST_RETRY:
status = MAYREDO;
exit = DRIVER_SENSE | SUGGEST_RETRY;
@@ -1186,22 +1313,22 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
case SUGGEST_SENSE:
scsi_request_sense (SCpnt);
status = PENDING;
- break;
+ break;
}
- break;
-
+ break;
+
case CONDITION_GOOD:
case INTERMEDIATE_GOOD:
case INTERMEDIATE_C_GOOD:
break;
-
+
case BUSY:
update_timeout(SCpnt, oldto);
status = REDO;
break;
case RESERVATION_CONFLICT:
- printk("scsi%d : RESERVATION CONFLICT performing reset.\n",
+ printk("scsi%d : RESERVATION CONFLICT performing reset.\n",
SCpnt->host->host_no);
reset(SCpnt);
return;
@@ -1212,16 +1339,16 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
#endif
default:
printk ("Internal error %s %d \n"
- "status byte = %d \n", __FILE__,
+ "status byte = %d \n", __FILE__,
__LINE__, status_byte(result));
-
+
}
break;
default:
- panic("scsi: unsupported message byte %d received\n", msg_byte(result));
+ panic("scsi: unsupported message byte %d received\n", msg_byte(result));
}
break;
- case DID_TIME_OUT:
+ case DID_TIME_OUT:
#ifdef DEBUG
printk("Host returned DID_TIME_OUT - ");
#endif
@@ -1230,10 +1357,10 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
{
#ifdef DEBUG
printk("Aborting\n");
-#endif
+#endif
exit = (DRIVER_TIMEOUT | SUGGEST_ABORT);
- }
- else
+ }
+ else
{
#ifdef DEBUG
printk ("Retrying.\n");
@@ -1253,15 +1380,15 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
#endif
exit = (DRIVER_HARD | SUGGEST_ABORT);
break;
- case DID_ERROR:
+ case DID_ERROR:
status = MAYREDO;
exit = (DRIVER_HARD | SUGGEST_ABORT);
break;
case DID_BAD_TARGET:
case DID_ABORT:
exit = (DRIVER_INVALID | SUGGEST_ABORT);
- break;
- case DID_RESET:
+ break;
+ case DID_RESET:
if (SCpnt->flags & IS_RESETTING)
{
SCpnt->flags &= ~IS_RESETTING;
@@ -1269,14 +1396,14 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
break;
}
- if(msg_byte(result) == GOOD &&
- status_byte(result) == CHECK_CONDITION) {
+ if(msg_byte(result) == GOOD &&
+ status_byte(result) == CHECK_CONDITION) {
switch (check_sense(SCpnt)) {
- case 0:
+ case 0:
update_timeout(SCpnt, oldto);
status = REDO;
break;
- case SUGGEST_REMAP:
+ case SUGGEST_REMAP:
case SUGGEST_RETRY:
status = MAYREDO;
exit = DRIVER_SENSE | SUGGEST_RETRY;
@@ -1286,20 +1413,20 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
exit = DRIVER_SENSE | SUGGEST_ABORT;
break;
case SUGGEST_SENSE:
- scsi_request_sense (SCpnt);
- status = PENDING;
- break;
+ scsi_request_sense (SCpnt);
+ status = PENDING;
+ break;
}
} else {
- status=REDO;
- exit = SUGGEST_RETRY;
+ status=REDO;
+ exit = SUGGEST_RETRY;
}
- break;
- default :
+ break;
+ default :
exit = (DRIVER_ERROR | SUGGEST_DIE);
}
- switch (status)
+ switch (status)
{
case FINISHED:
case PENDING:
@@ -1314,13 +1441,14 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
if ((++SCpnt->retries) < SCpnt->allowed)
{
if ((SCpnt->retries >= (SCpnt->allowed >> 1))
+ && !(jiffies < SCpnt->host->last_reset + MIN_RESET_PERIOD)
&& !(SCpnt->flags & WAS_RESET))
- {
+ {
printk("scsi%d : resetting for second half of retries.\n",
SCpnt->host->host_no);
reset(SCpnt);
break;
- }
+ }
}
else
@@ -1331,51 +1459,52 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
/* fall through to REDO */
case REDO:
- if (SCpnt->flags & WAS_SENSE)
- scsi_request_sense(SCpnt);
- else
+
+ if (SCpnt->flags & WAS_SENSE)
+ scsi_request_sense(SCpnt);
+ else
{
memcpy ((void *) SCpnt->cmnd,
- (void*) SCpnt->data_cmnd,
+ (void*) SCpnt->data_cmnd,
sizeof(SCpnt->data_cmnd));
SCpnt->request_buffer = SCpnt->buffer;
SCpnt->request_bufflen = SCpnt->bufflen;
SCpnt->use_sg = SCpnt->old_use_sg;
SCpnt->cmd_len = SCpnt->old_cmd_len;
internal_cmnd (SCpnt);
- };
- break;
- default:
+ }
+ break;
+ default:
INTERNAL_ERROR;
}
- if (status == FINISHED)
- {
+
+ if (status == FINISHED) {
#ifdef DEBUG
- printk("Calling done function - at address %08x\n", SCpnt->done);
+ printk("Calling done function - at address %p\n", SCpnt->done);
#endif
- host->host_busy--; /* Indicate that we are free */
- if (host->host_busy == 0 && host->block) {
- struct Scsi_Host * block;
- /*
- * Now remove the locks for all of the related hosts.
- */
- for(block = host->block; block != host; block = block->block)
- block->host_busy &= ~SCSI_HOST_BLOCK;
- /*
- * Now wake them up. We do this in two separate stages to prevent
- * race conditions.
- */
- for(block = host->block; block != host; block = block->block)
- wake_up(&block->host_wait);
- }
- wake_up(&host->host_wait);
- SCpnt->result = result | ((exit & 0xff) << 24);
- SCpnt->use_sg = SCpnt->old_use_sg;
- SCpnt->cmd_len = SCpnt->old_cmd_len;
- SCpnt->done (SCpnt);
- }
+ host->host_busy--; /* Indicate that we are free */
+
+ if (host->block && host->host_busy == 0) {
+ host_active = NULL;
+
+ /* For block devices "wake_up" is done in end_scsi_request */
+ if (MAJOR(SCpnt->request.dev) != SCSI_DISK_MAJOR &&
+ MAJOR(SCpnt->request.dev) != SCSI_CDROM_MAJOR) {
+ struct Scsi_Host * next;
+ for (next = host->block; next != host; next = next->block)
+ wake_up(&next->host_wait);
+ }
+
+ }
+
+ wake_up(&host->host_wait);
+ SCpnt->result = result | ((exit & 0xff) << 24);
+ SCpnt->use_sg = SCpnt->old_use_sg;
+ SCpnt->cmd_len = SCpnt->old_cmd_len;
+ SCpnt->done (SCpnt);
+ }
#undef FINISHED
#undef REDO
@@ -1385,13 +1514,13 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
/*
The scsi_abort function interfaces with the abort() function of the host
- we are aborting, and causes the current command to not complete. The
- caller should deal with any error messages or status returned on the
+ we are aborting, and causes the current command to not complete. The
+ caller should deal with any error messages or status returned on the
next call.
-
+
This will not be called reentrantly for a given host.
*/
-
+
/*
Since we're nice guys and specified that abort() and reset()
can be non-reentrant. The internal_timeout flags are used for
@@ -1399,42 +1528,57 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
*/
-int scsi_abort (Scsi_Cmnd * SCpnt, int why)
+int scsi_abort (Scsi_Cmnd * SCpnt, int why, int pid)
{
int oldto;
+ unsigned long flags;
struct Scsi_Host * host = SCpnt->host;
-
- while(1)
+
+ while(1)
{
+ save_flags(flags);
cli();
- if (SCpnt->internal_timeout & IN_ABORT)
+
+ /*
+ * Protect against races here. If the command is done, or we are
+ * on a different command forget it.
+ */
+ if (SCpnt->request.dev == -1 || pid != SCpnt->pid) {
+ restore_flags(flags);
+ return 0;
+ }
+
+ if (SCpnt->internal_timeout & IN_ABORT)
{
- sti();
- while (SCpnt->internal_timeout & IN_ABORT);
+ restore_flags(flags);
+ while (SCpnt->internal_timeout & IN_ABORT)
+ barrier();
}
else
- {
+ {
SCpnt->internal_timeout |= IN_ABORT;
oldto = update_timeout(SCpnt, ABORT_TIMEOUT);
- if ((SCpnt->flags & IS_RESETTING) &&
+ if ((SCpnt->flags & IS_RESETTING) &&
SCpnt->device->soft_reset) {
/* OK, this command must have died when we did the
reset. The device itself must have lied. */
printk("Stale command on %d:%d appears to have died when"
" the bus was reset\n", SCpnt->target, SCpnt->lun);
}
-
- sti();
+
+ restore_flags(flags);
if (!host->host_busy) {
SCpnt->internal_timeout &= ~IN_ABORT;
update_timeout(SCpnt, oldto);
return 0;
}
printk("scsi : aborting command due to timeout : pid %lu, scsi%d, id %d, lun %d ",
- SCpnt->pid, SCpnt->host->host_no, (int) SCpnt->target, (int)
+ SCpnt->pid, SCpnt->host->host_no, (int) SCpnt->target, (int)
SCpnt->lun);
print_command (SCpnt->cmnd);
+ if (SCpnt->request.dev == -1 || pid != SCpnt->pid)
+ return 0;
SCpnt->abort_reason = why;
switch(host->hostt->abort(SCpnt)) {
/* We do not know how to abort. Try waiting another
@@ -1445,10 +1589,11 @@ int scsi_abort (Scsi_Cmnd * SCpnt, int why)
this is too severe */
case SCSI_ABORT_SNOOZE:
if(why == DID_TIME_OUT) {
+ save_flags(flags);
cli();
SCpnt->internal_timeout &= ~IN_ABORT;
if(SCpnt->flags & WAS_TIMEDOUT) {
- sti();
+ restore_flags(flags);
return 1; /* Indicate we cannot handle this.
We drop down into the reset handler
and try again */
@@ -1457,14 +1602,15 @@ int scsi_abort (Scsi_Cmnd * SCpnt, int why)
oldto = SCpnt->timeout_per_command;
update_timeout(SCpnt, oldto);
}
- sti();
+ restore_flags(flags);
}
return 0;
case SCSI_ABORT_PENDING:
if(why != DID_TIME_OUT) {
+ save_flags(flags);
cli();
update_timeout(SCpnt, oldto);
- sti();
+ restore_flags(flags);
}
return 0;
case SCSI_ABORT_SUCCESS:
@@ -1472,6 +1618,7 @@ int scsi_abort (Scsi_Cmnd * SCpnt, int why)
need to adjust timeout */
case SCSI_ABORT_NOT_RUNNING:
SCpnt->internal_timeout &= ~IN_ABORT;
+ update_timeout(SCpnt, 0);
return 0;
case SCSI_ABORT_ERROR:
default:
@@ -1479,56 +1626,61 @@ int scsi_abort (Scsi_Cmnd * SCpnt, int why)
return 1;
}
}
- }
+ }
}
-
+
int scsi_reset (Scsi_Cmnd * SCpnt)
{
int temp, oldto;
+ unsigned long flags;
Scsi_Cmnd * SCpnt1;
struct Scsi_Host * host = SCpnt->host;
-
+
#ifdef DEBUG
printk("Danger Will Robinson! - SCSI bus for host %d is being reset.\n",host->host_no);
#endif
while (1) {
- cli();
+ save_flags(flags);
+ cli();
if (SCpnt->internal_timeout & IN_RESET)
{
- sti();
- while (SCpnt->internal_timeout & IN_RESET);
+ restore_flags(flags);
+ while (SCpnt->internal_timeout & IN_RESET)
+ barrier();
}
else
{
SCpnt->internal_timeout |= IN_RESET;
- oldto = update_timeout(SCpnt, RESET_TIMEOUT);
-
+ oldto = update_timeout(SCpnt, RESET_TIMEOUT);
+
if (host->host_busy)
- {
- sti();
+ {
+ restore_flags(flags);
SCpnt1 = host->host_queue;
while(SCpnt1) {
if (SCpnt1->request.dev > 0) {
-#if 0
- if (!(SCpnt1->flags & IS_RESETTING) &&
+#if 0
+ if (!(SCpnt1->flags & IS_RESETTING) &&
!(SCpnt1->internal_timeout & IN_ABORT))
- scsi_abort(SCpnt1, DID_RESET);
+ scsi_abort(SCpnt1, DID_RESET, SCpnt->pid);
#endif
SCpnt1->flags |= IS_RESETTING;
}
SCpnt1 = SCpnt1->next;
- };
+ }
- temp = host->hostt->reset(SCpnt);
- }
+ host->last_reset = jiffies;
+ temp = host->hostt->reset(SCpnt);
+ host->last_reset = jiffies;
+ }
else
{
- host->host_busy++;
-
- sti();
+ if (!host->block) host->host_busy++;
+ restore_flags(flags);
+ host->last_reset = jiffies;
temp = host->hostt->reset(SCpnt);
host->last_reset = jiffies;
- host->host_busy--;
+ if (!host->block) host->host_busy--;
}
#ifdef DEBUG
@@ -1536,10 +1688,11 @@ int scsi_reset (Scsi_Cmnd * SCpnt)
#endif
switch(temp) {
case SCSI_RESET_SUCCESS:
+ save_flags(flags);
cli();
SCpnt->internal_timeout &= ~IN_RESET;
update_timeout(SCpnt, oldto);
- sti();
+ restore_flags(flags);
return 0;
case SCSI_RESET_PENDING:
return 0;
@@ -1548,26 +1701,27 @@ int scsi_reset (Scsi_Cmnd * SCpnt)
SCpnt->internal_timeout &= ~IN_RESET;
scsi_request_sense (SCpnt);
return 0;
- case SCSI_RESET_SNOOZE:
+ case SCSI_RESET_SNOOZE:
/* In this case, we set the timeout field to 0
so that this command does not time out any more,
and we return 1 so that we get a message on the
screen. */
+ save_flags(flags);
cli();
SCpnt->internal_timeout &= ~IN_RESET;
update_timeout(SCpnt, 0);
- sti();
+ restore_flags(flags);
/* If you snooze, you lose... */
case SCSI_RESET_ERROR:
default:
return 1;
}
-
- return temp;
+
+ return temp;
}
}
}
-
+
static void scsi_main_timeout(void)
{
@@ -1575,11 +1729,13 @@ static void scsi_main_timeout(void)
We must not enter update_timeout with a timeout condition still pending.
*/
- int timed_out;
+ int timed_out, pid;
+ unsigned long flags;
struct Scsi_Host * host;
Scsi_Cmnd * SCpnt = NULL;
- do {
+ do {
+ save_flags(flags);
cli();
update_timeout(NULL, 0);
@@ -1587,58 +1743,62 @@ static void scsi_main_timeout(void)
Find all timers such that they have 0 or negative (shouldn't happen)
time remaining on them.
*/
-
+
timed_out = 0;
for(host = scsi_hostlist; host; host = host->next) {
for(SCpnt = host->host_queue; SCpnt; SCpnt = SCpnt->next)
if (SCpnt->timeout == -1)
{
- sti();
SCpnt->timeout = 0;
- scsi_times_out(SCpnt);
- ++timed_out;
+ pid = SCpnt->pid;
+ restore_flags(flags);
+ scsi_times_out(SCpnt, pid);
+ ++timed_out;
+ save_flags(flags);
cli();
}
- };
- } while (timed_out);
- sti();
+ }
+ } while (timed_out);
+ restore_flags(flags);
}
/*
The strategy is to cause the timer code to call scsi_times_out()
- when the soonest timeout is pending.
+ when the soonest timeout is pending.
The arguments are used when we are queueing a new command, because
we do not want to subtract the time used from this time, but when we
set the timer, we want to take this value into account.
*/
-
+
static int update_timeout(Scsi_Cmnd * SCset, int timeout)
{
unsigned int least, used;
unsigned int oldto;
+ unsigned long flags;
struct Scsi_Host * host;
Scsi_Cmnd * SCpnt = NULL;
+ save_flags(flags);
cli();
-/*
- Figure out how much time has passed since the last time the timeouts
- were updated
+/*
+ Figure out how much time has passed since the last time the timeouts
+ were updated
*/
used = (time_start) ? (jiffies - time_start) : 0;
/*
Find out what is due to timeout soonest, and adjust all timeouts for
- the amount of time that has passed since the last time we called
- update_timeout.
+ the amount of time that has passed since the last time we called
+ update_timeout.
*/
-
+
oldto = 0;
if(SCset){
oldto = SCset->timeout - used;
SCset->timeout = timeout + used;
- };
+ }
least = 0xffffffff;
@@ -1649,91 +1809,106 @@ static int update_timeout(Scsi_Cmnd * SCset, int timeout)
if(SCpnt->timeout <= 0) SCpnt->timeout = -1;
if(SCpnt->timeout > 0 && SCpnt->timeout < least)
least = SCpnt->timeout;
- };
+ }
/*
- If something is due to timeout again, then we will set the next timeout
+ If something is due to timeout again, then we will set the next timeout
interrupt to occur. Otherwise, timeouts are disabled.
*/
-
+
if (least != 0xffffffff)
{
- time_start = jiffies;
- timer_table[SCSI_TIMER].expires = (time_elapsed = least) + jiffies;
+ time_start = jiffies;
+ timer_table[SCSI_TIMER].expires = (time_elapsed = least) + jiffies;
timer_active |= 1 << SCSI_TIMER;
}
else
{
timer_table[SCSI_TIMER].expires = time_start = time_elapsed = 0;
timer_active &= ~(1 << SCSI_TIMER);
- }
- sti();
+ }
+ restore_flags(flags);
return oldto;
- }
+ }
-static unsigned short * dma_malloc_freelist = NULL;
+static unsigned char * dma_malloc_freelist = NULL;
+static int scsi_need_isa_bounce_buffers;
static unsigned int dma_sectors = 0;
unsigned int dma_free_sectors = 0;
unsigned int need_isa_buffer = 0;
-static unsigned char * dma_malloc_buffer = NULL;
+static unsigned char ** dma_malloc_pages = NULL;
+#define MALLOC_PAGEBITS 12
+
+static int scsi_register_host(Scsi_Host_Template *);
+static void scsi_unregister_host(Scsi_Host_Template *);
void *scsi_malloc(unsigned int len)
{
unsigned int nbits, mask;
+ unsigned long flags;
int i, j;
- if((len & 0x1ff) || len > 8192)
+ if((len & 0x1ff) || len > (1<<MALLOC_PAGEBITS))
return NULL;
-
+
+ save_flags(flags);
cli();
nbits = len >> 9;
mask = (1 << nbits) - 1;
-
- for(i=0;i < (dma_sectors >> 4); i++)
- for(j=0; j<17-nbits; j++){
+
+ for(i=0;i < (dma_sectors >> (MALLOC_PAGEBITS - 9)); i++)
+ for(j=0; j<=(sizeof(*dma_malloc_freelist) * 8) - nbits; j++){
if ((dma_malloc_freelist[i] & (mask << j)) == 0){
dma_malloc_freelist[i] |= (mask << j);
- sti();
+ restore_flags(flags);
dma_free_sectors -= nbits;
#ifdef DEBUG
- printk("SMalloc: %d %x ",len, dma_malloc_buffer + (i << 13) + (j << 9));
+ printk("SMalloc: %d %p ",len, dma_malloc_pages[i] + (j << 9));
#endif
- return (void *) ((unsigned long) dma_malloc_buffer + (i << 13) + (j << 9));
- };
- };
- sti();
+ return (void *) ((unsigned long) dma_malloc_pages[i] + (j << 9));
+ }
+ }
+ restore_flags(flags);
return NULL; /* Nope. No more */
}
int scsi_free(void *obj, unsigned int len)
{
- int offset;
int page, sector, nbits, mask;
+ long offset;
+ unsigned long flags;
#ifdef DEBUG
- printk("Sfree %x %d\n",obj, len);
+ printk("Sfree %p %d\n",obj, len);
#endif
- offset = ((int) obj) - ((int) dma_malloc_buffer);
+ offset = -1;
+ for (page = 0; page < (dma_sectors >> 3); page++)
+ if ((unsigned long) obj >= (unsigned long) dma_malloc_pages[page] &&
+ (unsigned long) obj < (unsigned long) dma_malloc_pages[page] + (1 << MALLOC_PAGEBITS))
+ {
+ offset = ((unsigned long) obj) - ((unsigned long)dma_malloc_pages[page]);
+ break;
+ }
- if (offset < 0) panic("Bad offset");
- page = offset >> 13;
+ if (page == (dma_sectors >> 3)) panic("Bad offset");
sector = offset >> 9;
if(sector >= dma_sectors) panic ("Bad page");
- sector = (offset >> 9) & 15;
+ sector = (offset >> 9) & (sizeof(*dma_malloc_freelist) * 8 - 1);
nbits = len >> 9;
mask = (1 << nbits) - 1;
if ((mask << sector) > 0xffff) panic ("Bad memory alignment");
+ save_flags(flags);
cli();
if(dma_malloc_freelist[page] & (mask << sector) != (mask<<sector))
panic("Trying to free unused memory");
dma_free_sectors += nbits;
dma_malloc_freelist[page] &= ~(mask << sector);
- sti();
+ restore_flags(flags);
return 0;
}
@@ -1742,36 +1917,60 @@ int scsi_free(void *obj, unsigned int len)
They act line a malloc function, but they simply take memory from the
pool */
-static unsigned int scsi_init_memory_start = 0;
+static unsigned long scsi_init_memory_start = 0;
+static unsigned long scsi_memory_lower_value = 0;
+static unsigned long scsi_memory_upper_value = 0;
int scsi_loadable_module_flag; /* Set after we scan builtin drivers */
-void * scsi_init_malloc(unsigned int size)
+void * scsi_init_malloc(unsigned int size, int priority)
{
- unsigned int retval;
- if(scsi_loadable_module_flag) {
- retval = (unsigned int) kmalloc(size, GFP_ATOMIC);
- } else {
- retval = scsi_init_memory_start;
- scsi_init_memory_start += size;
- }
+ unsigned long retval;
+
+/* Use the statically allocated memory instead of kmalloc (DB) */
+#if defined(USE_STATIC_SCSI_MEMORY)
+ if(scsi_loadable_module_flag && !(priority & GFP_DMA))
+#else
+ if(scsi_loadable_module_flag)
+#endif
+ retval = (unsigned long) kmalloc(size, priority);
+ else {
+ /*
+ * Keep all memory aligned on 16-byte boundaries. Some host adaptors
+ * (e.g. BusLogic BT-445S) require DMA buffers to be aligned that way.
+ */
+ size = (size + 15) & ~15;
+
+ if(scsi_loadable_module_flag &&
+ (scsi_init_memory_start + size) > scsi_memory_upper_value) {
+ retval = 0;
+ printk("scsi_init_malloc: no more statically allocated memory.\n");
+ }
+ else {
+ retval = scsi_init_memory_start;
+ scsi_init_memory_start += size;
+ }
+ }
+ memset((void *) retval, 0, size);
return (void *) retval;
}
void scsi_init_free(char * ptr, unsigned int size)
-{ /* FIXME - not right. We need to compare addresses to see whether this was
- kmalloc'd or not */
- if((unsigned int) ptr < scsi_loadable_module_flag) {
- kfree(ptr);
- } else {
- if(((unsigned int) ptr) + size == scsi_init_memory_start)
- scsi_init_memory_start = (unsigned int) ptr;
- }
+{ /* We need to compare addresses to see whether this was kmalloc'd or not */
+
+ if((unsigned long) ptr >= scsi_init_memory_start ||
+ (unsigned long) ptr < scsi_memory_lower_value) kfree(ptr);
+ else {
+ size = (size + 15) & ~15; /* Use the same alignment as scsi_init_malloc() */
+
+ if(((unsigned long) ptr) + size == scsi_init_memory_start)
+ scsi_init_memory_start = (unsigned long) ptr;
+ }
}
/*
- scsi_dev_init() is our initialization routine, which in turn calls host
- initialization, bus scanning, and sd/st initialization routines. It
+ scsi_dev_init() is our initialization routine, which in turn calls host
+ initialization, bus scanning, and sd/st initialization routines. It
should be called from main().
*/
@@ -1782,25 +1981,35 @@ unsigned long scsi_dev_init (unsigned long memory_start,unsigned long memory_end
struct Scsi_Host * shpnt;
struct Scsi_Device_Template * sdtpnt;
Scsi_Cmnd * SCpnt;
+ int i;
#ifdef FOO_ON_YOU
return;
-#endif
+#endif
/* Init a few things so we can "malloc" memory. */
scsi_loadable_module_flag = 0;
- scsi_init_memory_start = memory_start;
+ /* Align everything on 16-byte boundaries. */
+ scsi_init_memory_start = (memory_start + 15) & ~ 15;
+ scsi_memory_lower_value = scsi_init_memory_start;
timer_table[SCSI_TIMER].fn = scsi_main_timeout;
timer_table[SCSI_TIMER].expires = 0;
/* initialize all hosts */
- scsi_init();
-
+ scsi_init();
+
scsi_devices = (Scsi_Device *) NULL;
for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
scan_scsis(shpnt); /* scan for scsi devices */
+ printk("scsi : detected ");
+ for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if (sdtpnt->dev_noticed && sdtpnt->name)
+ printk("%d SCSI %s%s ", sdtpnt->dev_noticed, sdtpnt->name,
+ (sdtpnt->dev_noticed != 1) ? "s" : "");
+ printk("total.\n");
+
for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
if(sdtpnt->init && sdtpnt->dev_noticed) (*sdtpnt->init)();
@@ -1808,10 +2017,11 @@ unsigned long scsi_dev_init (unsigned long memory_start,unsigned long memory_end
int j;
SDpnt->scsi_request_fn = NULL;
for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
- if(sdtpnt->attach) (*sdtpnt->attach)(SDpnt);
- if(SDpnt->type != -1){
- for(j=0;j<SDpnt->host->hostt->cmd_per_lun;j++){
- SCpnt = (Scsi_Cmnd *) scsi_init_malloc(sizeof(Scsi_Cmnd));
+ if(sdtpnt->attach) (*sdtpnt->attach)(SDpnt);
+
+ if(SDpnt->attached){
+ for(j=0;j<SDpnt->host->cmd_per_lun;j++){
+ SCpnt = (Scsi_Cmnd *) scsi_init_malloc(sizeof(Scsi_Cmnd), GFP_ATOMIC);
SCpnt->host = SDpnt->host;
SCpnt->device = SDpnt;
SCpnt->target = SDpnt->id;
@@ -1821,8 +2031,8 @@ unsigned long scsi_dev_init (unsigned long memory_start,unsigned long memory_end
SCpnt->old_use_sg = 0;
SCpnt->old_cmd_len = 0;
SCpnt->timeout = 0;
- SCpnt->underflow = 0;
- SCpnt->transfersize = 0;
+ SCpnt->underflow = 0;
+ SCpnt->transfersize = 0;
SCpnt->host_scribble = NULL;
host = SDpnt->host;
if(host->host_queue)
@@ -1830,84 +2040,105 @@ unsigned long scsi_dev_init (unsigned long memory_start,unsigned long memory_end
SCpnt->next = host->host_queue;
SCpnt->prev = NULL;
host->host_queue = SCpnt;
- };
- };
- };
+ }
+ }
+ }
if (scsi_devicelist)
dma_sectors = 16; /* Base value we use */
+ if (memory_end-1 > ISA_DMA_THRESHOLD)
+ scsi_need_isa_bounce_buffers = 1;
+ else
+ scsi_need_isa_bounce_buffers = 0;
+
for (SDpnt=scsi_devices; SDpnt; SDpnt = SDpnt->next) {
host = SDpnt->host;
-
+
if(SDpnt->type != TYPE_TAPE)
- dma_sectors += ((host->sg_tablesize *
- sizeof(struct scatterlist) + 511) >> 9) *
- host->hostt->cmd_per_lun;
-
+ dma_sectors += ((host->sg_tablesize *
+ sizeof(struct scatterlist) + 511) >> 9) *
+ host->cmd_per_lun;
+
if(host->unchecked_isa_dma &&
memory_end - 1 > ISA_DMA_THRESHOLD &&
SDpnt->type != TYPE_TAPE) {
dma_sectors += (PAGE_SIZE >> 9) * host->sg_tablesize *
- host->hostt->cmd_per_lun;
+ host->cmd_per_lun;
need_isa_buffer++;
- };
- };
+ }
+ }
dma_sectors = (dma_sectors + 15) & 0xfff0;
dma_free_sectors = dma_sectors; /* This must be a multiple of 16 */
- scsi_init_memory_start = (scsi_init_memory_start + 3) & 0xfffffffc;
- dma_malloc_freelist = (unsigned short *)
- scsi_init_malloc(dma_sectors >> 3);
+ dma_malloc_freelist = (unsigned char *)
+ scsi_init_malloc(dma_sectors >> 3, GFP_ATOMIC);
memset(dma_malloc_freelist, 0, dma_sectors >> 3);
- /* Some host adapters require buffers to be word aligned */
- if(scsi_init_memory_start & 1) scsi_init_memory_start++;
+ dma_malloc_pages = (unsigned char **)
+ scsi_init_malloc(dma_sectors >> 1, GFP_ATOMIC);
+ memset(dma_malloc_pages, 0, dma_sectors >> 1);
+
+ for(i=0; i< dma_sectors >> 3; i++)
+ dma_malloc_pages[i] = (unsigned char *)
+ scsi_init_malloc(PAGE_SIZE, GFP_ATOMIC | GFP_DMA);
+
- dma_malloc_buffer = (unsigned char *)
- scsi_init_malloc(dma_sectors << 9);
-
/* OK, now we finish the initialization by doing spin-up, read
capacity, etc, etc */
for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
if(sdtpnt->finish && sdtpnt->nr_dev)
(*sdtpnt->finish)();
-
+
scsi_loadable_module_flag = 1;
+
+
+/* This allocates statically some extra memory to be used for modules,
+ until the kmalloc problem is fixed (DB) */
+
+#if defined(USE_STATIC_SCSI_MEMORY)
+ scsi_memory_upper_value = scsi_init_memory_start + 256 * 1024;
+ printk ("SCSI memory: total %ldKb, used %ldKb, free %ldKb.\n",
+ (scsi_memory_upper_value - scsi_memory_lower_value) / 1024,
+ (scsi_init_memory_start - scsi_memory_lower_value) / 1024,
+ (scsi_memory_upper_value - scsi_init_memory_start) / 1024);
+ return scsi_memory_upper_value;
+#else
return scsi_init_memory_start;
+#endif
}
static void print_inquiry(unsigned char *data)
{
- int i;
+ int i;
printk(" Vendor: ");
for (i = 8; i < 16; i++)
- {
- if (data[i] >= 0x20 && i < data[4] + 5)
- printk("%c", data[i]);
- else
- printk(" ");
- }
+ {
+ if (data[i] >= 0x20 && i < data[4] + 5)
+ printk("%c", data[i]);
+ else
+ printk(" ");
+ }
printk(" Model: ");
for (i = 16; i < 32; i++)
- {
- if (data[i] >= 0x20 && i < data[4] + 5)
- printk("%c", data[i]);
- else
- printk(" ");
- }
+ {
+ if (data[i] >= 0x20 && i < data[4] + 5)
+ printk("%c", data[i]);
+ else
+ printk(" ");
+ }
printk(" Rev: ");
for (i = 32; i < 36; i++)
- {
- if (data[i] >= 0x20 && i < data[4] + 5)
- printk("%c", data[i]);
- else
- printk(" ");
- }
+ {
+ if (data[i] >= 0x20 && i < data[4] + 5)
+ printk("%c", data[i]);
+ else
+ printk(" ");
+ }
printk("\n");
@@ -1922,20 +2153,359 @@ static void print_inquiry(unsigned char *data)
printk("\n");
}
+/*
+ * This entry point should be called by a loadable module if it is trying
+ * add a low level scsi driver to the system.
+ */
+static int scsi_register_host(Scsi_Host_Template * tpnt)
+{
+ int pcount;
+ struct Scsi_Host * shpnt;
+ struct Scsi_Host * host = NULL;
+ unsigned long flags;
+ Scsi_Device * SDpnt;
+ Scsi_Cmnd * SCpnt;
+ struct Scsi_Device_Template * sdtpnt;
+ int j, i;
+ const char * name;
+
+ if (tpnt->next || !tpnt->detect) return 1; /* Must be already loaded, or
+ no detect routine available */
+ pcount = next_scsi_host;
+ if ((tpnt->present = tpnt->detect(tpnt)))
+ {
+ if(pcount == next_scsi_host) {
+ if(tpnt->present > 1) {
+ printk("Failure to register low-level scsi driver");
+ scsi_unregister_host(tpnt);
+ return 1;
+ }
+ /* The low-level driver failed to register a driver. We
+ can do this now. */
+ scsi_register(tpnt,0);
+ }
+ tpnt->next = scsi_hosts; /* Add to the linked list */
+ scsi_hosts = tpnt;
+
+ for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+ if(shpnt->hostt == tpnt)
+ {
+ if(tpnt->info)
+ name = tpnt->info(shpnt);
+ else
+ name = tpnt->name;
+ printk ("scsi%d : %s\n", /* And print a little message */
+ shpnt->host_no, name);
+ }
+
+ printk ("scsi : %d host%s.\n", next_scsi_host,
+ (next_scsi_host == 1) ? "" : "s");
+
+ scsi_make_blocked_list();
+
+ /* The next step is to call scan_scsis here. This generates the
+ Scsi_Devices entries */
+
+ for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+ if(shpnt->hostt == tpnt) scan_scsis(shpnt);
+
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->init && sdtpnt->dev_noticed) (*sdtpnt->init)();
+
+ /* Next we create the Scsi_Cmnd structures for this host */
+
+ for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next)
+ if(SDpnt->host->hostt == tpnt)
+ {
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->attach) (*sdtpnt->attach)(SDpnt);
+ if(SDpnt->attached){
+ for(j=0;j<SDpnt->host->cmd_per_lun;j++){
+ SCpnt = (Scsi_Cmnd *) scsi_init_malloc(sizeof(Scsi_Cmnd), GFP_ATOMIC);
+ SCpnt->host = SDpnt->host;
+ SCpnt->device = SDpnt;
+ SCpnt->target = SDpnt->id;
+ SCpnt->lun = SDpnt->lun;
+ SCpnt->request.dev = -1; /* Mark not busy */
+ SCpnt->request.sem = NULL;
+ SCpnt->use_sg = 0;
+ SCpnt->old_use_sg = 0;
+ SCpnt->underflow = 0;
+ SCpnt->timeout = 0;
+ SCpnt->transfersize = 0;
+ SCpnt->host_scribble = NULL;
+ host = SDpnt->host;
+ SCpnt->next = host->host_queue;
+ SCpnt->prev = NULL;
+ host->host_queue = SCpnt;
+ if(host->host_queue)
+ host->host_queue->prev = SCpnt;
+ }
+ }
+ }
+ /* Next, check to see if we need to extend the DMA buffer pool */
+ {
+ unsigned char * new_dma_malloc_freelist = NULL;
+ unsigned int new_dma_sectors = 0;
+ unsigned int new_need_isa_buffer = 0;
+ unsigned char ** new_dma_malloc_pages = NULL;
+
+ if (scsi_devicelist)
+ new_dma_sectors = 16; /* Base value we use */
+
+ for (SDpnt=scsi_devices; SDpnt; SDpnt = SDpnt->next) {
+ host = SDpnt->host;
+
+ if(SDpnt->type != TYPE_TAPE)
+ new_dma_sectors += ((host->sg_tablesize *
+ sizeof(struct scatterlist) + 511) >> 9) *
+ host->cmd_per_lun;
+
+ if(host->unchecked_isa_dma &&
+ scsi_need_isa_bounce_buffers &&
+ SDpnt->type != TYPE_TAPE) {
+ new_dma_sectors += (PAGE_SIZE >> 9) * host->sg_tablesize *
+ host->cmd_per_lun;
+ new_need_isa_buffer++;
+ }
+ }
+
+ new_dma_sectors = (new_dma_sectors + 15) & 0xfff0;
+
+ new_dma_malloc_freelist = (unsigned char *)
+ scsi_init_malloc(new_dma_sectors >> 3, GFP_ATOMIC);
+ memset(new_dma_malloc_freelist, 0, new_dma_sectors >> 3);
+
+ new_dma_malloc_pages = (unsigned char **)
+ scsi_init_malloc(new_dma_sectors >> 1, GFP_ATOMIC);
+ memset(new_dma_malloc_pages, 0, new_dma_sectors >> 1);
+
+ for(i=dma_sectors >> 3; i< new_dma_sectors >> 3; i++)
+ new_dma_malloc_pages[i] = (unsigned char *)
+ scsi_init_malloc(PAGE_SIZE, GFP_ATOMIC | GFP_DMA);
+
+
+ /* When we dick with the actual DMA list, we need to protect things */
+
+ save_flags(flags);
+ cli();
+ memcpy(new_dma_malloc_freelist, dma_malloc_freelist, dma_sectors >> 3);
+ scsi_init_free(dma_malloc_freelist, dma_sectors>>3);
+ dma_malloc_freelist = new_dma_malloc_freelist;
+
+ memcpy(new_dma_malloc_pages, dma_malloc_pages, dma_sectors >> 1);
+ scsi_init_free((char *) dma_malloc_pages, dma_sectors>>1);
+
+ dma_free_sectors += new_dma_sectors - dma_sectors;
+ dma_malloc_pages = new_dma_malloc_pages;
+ dma_sectors = new_dma_sectors;
+ need_isa_buffer = new_need_isa_buffer;
+ restore_flags(flags);
+
+
+ }
+ /* This does any final handling that is required. */
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->finish && sdtpnt->nr_dev)
+ (*sdtpnt->finish)();
+ }
+
+#if defined(USE_STATIC_SCSI_MEMORY)
+ printk ("SCSI memory: total %ldKb, used %ldKb, free %ldKb.\n",
+ (scsi_memory_upper_value - scsi_memory_lower_value) / 1024,
+ (scsi_init_memory_start - scsi_memory_lower_value) / 1024,
+ (scsi_memory_upper_value - scsi_init_memory_start) / 1024);
+#endif
+
+ return 0;
+}
+
+/*
+ * Similarly, this entry point should be called by a loadable module if it
+ * is trying to remove a low level scsi driver from the system.
+ */
+static void scsi_unregister_host(Scsi_Host_Template * tpnt)
+{
+ Scsi_Host_Template * SHT, *SHTp;
+ Scsi_Device *sdpnt, * sdppnt, * sdpnt1;
+ Scsi_Cmnd * SCpnt;
+ unsigned long flags;
+ struct Scsi_Device_Template * sdtpnt;
+ struct Scsi_Host * shpnt, *sh1;
+ int pcount;
+
+ /* First verify that this host adapter is completely free with no pending
+ commands */
+
+ for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt->next)
+ if(sdpnt->host->hostt == tpnt && sdpnt->host->hostt->usage_count
+ && *sdpnt->host->hostt->usage_count) return;
+
+ for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+ {
+ if (shpnt->hostt != tpnt) continue;
+ for(SCpnt = shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
+ {
+ save_flags(flags);
+ cli();
+ if(SCpnt->request.dev != -1) {
+ restore_flags(flags);
+ for(SCpnt = shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
+ if(SCpnt->request.dev == 0xffe0) SCpnt->request.dev = -1;
+ printk("Device busy???\n");
+ return;
+ }
+ SCpnt->request.dev = 0xffe0; /* Mark as busy */
+ restore_flags(flags);
+ }
+ }
+ /* Next we detach the high level drivers from the Scsi_Device structures */
+
+ for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt->next)
+ if(sdpnt->host->hostt == tpnt)
+ {
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->detach) (*sdtpnt->detach)(sdpnt);
+ /* If something still attached, punt */
+ if (sdpnt->attached) {
+ printk("Attached usage count = %d\n", sdpnt->attached);
+ return;
+ }
+ }
+
+ /* Next we free up the Scsi_Cmnd structures for this host */
+
+ for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt->next)
+ if(sdpnt->host->hostt == tpnt)
+ while (sdpnt->host->host_queue) {
+ SCpnt = sdpnt->host->host_queue->next;
+ scsi_init_free((char *) sdpnt->host->host_queue, sizeof(Scsi_Cmnd));
+ sdpnt->host->host_queue = SCpnt;
+ if (SCpnt) SCpnt->prev = NULL;
+ }
+
+ /* Next free up the Scsi_Device structures for this host */
+
+ sdppnt = NULL;
+ for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt1)
+ {
+ sdpnt1 = sdpnt->next;
+ if (sdpnt->host->hostt == tpnt) {
+ if (sdppnt)
+ sdppnt->next = sdpnt->next;
+ else
+ scsi_devices = sdpnt->next;
+ scsi_init_free((char *) sdpnt, sizeof (Scsi_Device));
+ } else
+ sdppnt = sdpnt;
+ }
+
+ /* Next we go through and remove the instances of the individual hosts
+ that were detected */
+
+ shpnt = scsi_hostlist;
+ while(shpnt) {
+ sh1 = shpnt->next;
+ if(shpnt->hostt == tpnt) {
+ if(shpnt->loaded_as_module) {
+ pcount = next_scsi_host;
+ if(tpnt->release)
+ (*tpnt->release)(shpnt);
+ else {
+ /* This is the default case for the release function. It should do the right
+ thing for most correctly written host adapters. */
+ if (shpnt->irq) free_irq(shpnt->irq);
+ if (shpnt->dma_channel != 0xff) free_dma(shpnt->dma_channel);
+ if (shpnt->io_port && shpnt->n_io_port)
+ release_region(shpnt->io_port, shpnt->n_io_port);
+ }
+ if(pcount == next_scsi_host) scsi_unregister(shpnt);
+ tpnt->present--;
+ }
+ }
+ shpnt = sh1;
+ }
+
+ printk ("scsi : %d host%s.\n", next_scsi_host,
+ (next_scsi_host == 1) ? "" : "s");
+
+#if defined(USE_STATIC_SCSI_MEMORY)
+ printk ("SCSI memory: total %ldKb, used %ldKb, free %ldKb.\n",
+ (scsi_memory_upper_value - scsi_memory_lower_value) / 1024,
+ (scsi_init_memory_start - scsi_memory_lower_value) / 1024,
+ (scsi_memory_upper_value - scsi_init_memory_start) / 1024);
+#endif
+
+ scsi_make_blocked_list();
+
+ /* There were some hosts that were loaded at boot time, so we cannot
+ do any more than this */
+ if (tpnt->present) return;
+
+ /* OK, this is the very last step. Remove this host adapter from the
+ linked list. */
+ for(SHTp=NULL, SHT=scsi_hosts; SHT; SHTp=SHT, SHT=SHT->next)
+ if(SHT == tpnt) {
+ if(SHTp)
+ SHTp->next = SHT->next;
+ else
+ scsi_hosts = SHT->next;
+ SHT->next = NULL;
+ break;
+ }
+}
+
+int scsi_register_module(int module_type, void * ptr)
+{
+ switch(module_type){
+ case MODULE_SCSI_HA:
+ return scsi_register_host((Scsi_Host_Template *) ptr);
+ /* The rest of these are not yet implemented */
+
+ /* Load constants.o */
+ case MODULE_SCSI_CONST:
+
+ /* Load specialized ioctl handler for some device. Intended for cdroms that
+ have non-SCSI2 audio command sets. */
+ case MODULE_SCSI_IOCTL:
+
+ /* Load upper level device handler of some kind */
+ case MODULE_SCSI_DEV:
+ default:
+ return 1;
+ }
+}
+
+void scsi_unregister_module(int module_type, void * ptr)
+{
+ switch(module_type) {
+ case MODULE_SCSI_HA:
+ scsi_unregister_host((Scsi_Host_Template *) ptr);
+ break;
+ /* The rest of these are not yet implemented. */
+ case MODULE_SCSI_CONST:
+ case MODULE_SCSI_IOCTL:
+ case MODULE_SCSI_DEV:
+ default:
+ }
+ return;
+}
+
#ifdef DEBUG_TIMEOUT
-static void
+static void
scsi_dump_status(void)
{
- int i, i1;
- Scsi_Host * shpnt;
+ int i;
+ struct Scsi_Host * shpnt;
Scsi_Cmnd * SCpnt;
printk("Dump of scsi parameters:\n");
- for(shpnt = scsi_hosts; shpnt; shpnt = shpnt->next)
+ i = 0;
+ for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
for(SCpnt=shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
{
/* (0) 0:0:0 (802 123434 8 8 0) (3 3 2) (%d %d %d) %d %x */
- printk("(%d) %d:%d:%d (%4.4x %d %d %d %d) (%d %d %x) (%d %d %d) %x %x %x\n",
- i, SCpnt->host->host_no,
+ printk("(%d) %d:%d:%d (%4.4x %ld %ld %ld %ld) (%d %d %x) (%d %d %d) %x %x %x\n",
+ i++, SCpnt->host->host_no,
SCpnt->target,
SCpnt->lun,
SCpnt->request.dev,
@@ -1952,8 +2522,8 @@ scsi_dump_status(void)
SCpnt->cmnd[0],
SCpnt->sense_buffer[2],
SCpnt->result);
- };
- printk("wait_for_request = %x\n", wait_for_request);
+ }
+ printk("wait_for_request = %p\n", wait_for_request);
/* Now dump the request lists for each block device */
printk("Dump of pending block device requests\n");
for(i=0; i<MAX_BLKDEV; i++)
@@ -1963,7 +2533,7 @@ scsi_dump_status(void)
printk("%d: ", i);
req = blk_dev[i].current_request;
while(req) {
- printk("(%x %d %d %d %d) ",
+ printk("(%x %d %ld %ld %ld) ",
req->dev,
req->cmd,
req->sector,
@@ -1976,4 +2546,19 @@ scsi_dump_status(void)
}
#endif
-
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h
index 9eb839876..69283efb2 100644
--- a/drivers/scsi/scsi.h
+++ b/drivers/scsi/scsi.h
@@ -50,6 +50,7 @@
#define SEND_DIAGNOSTIC 0x1d
#define ALLOW_MEDIUM_REMOVAL 0x1e
+#define SET_WINDOW 0x24
#define READ_CAPACITY 0x25
#define READ_10 0x28
#define WRITE_10 0x2a
@@ -65,17 +66,28 @@
#define SYNCHRONIZE_CACHE 0x35
#define LOCK_UNLOCK_CACHE 0x36
#define READ_DEFECT_DATA 0x37
+#define MEDIUM_SCAN 0x38
#define COMPARE 0x39
#define COPY_VERIFY 0x3a
#define WRITE_BUFFER 0x3b
#define READ_BUFFER 0x3c
+#define UPDATE_BLOCK 0x3d
#define READ_LONG 0x3e
+#define WRITE_LONG 0x3f
#define CHANGE_DEFINITION 0x40
+#define WRITE_SAME 0x41
#define LOG_SELECT 0x4c
#define LOG_SENSE 0x4d
#define MODE_SELECT_10 0x55
#define MODE_SENSE_10 0x5a
-
+#define WRITE_12 0xaa
+#define WRITE_VERIFY_12 0xae
+#define SEARCH_HIGH_12 0xb0
+#define SEARCH_EQUAL_12 0xb1
+#define SEARCH_LOW_12 0xb2
+#define SEND_VOLUME_TAG 0xb6
+
+extern void scsi_make_blocked_list(void);
extern volatile int in_scan_scsis;
extern const unsigned char scsi_command_size[8];
#define COMMAND_SIZE(opcode) scsi_command_size[((opcode) >> 5) & 7]
@@ -126,6 +138,7 @@ extern const unsigned char scsi_command_size[8];
#define INTERMEDIATE_GOOD 0x08
#define INTERMEDIATE_C_GOOD 0x0a
#define RESERVATION_CONFLICT 0x0c
+#define QUEUE_FULL 0x1a
#define STATUS_MASK 0x1e
@@ -234,8 +247,10 @@ extern const unsigned char scsi_command_size[8];
#define TYPE_DISK 0x00
#define TYPE_TAPE 0x01
+#define TYPE_PROCESSOR 0x03 /* HP scanners use this */
#define TYPE_WORM 0x04 /* Treated as ROM by our system */
#define TYPE_ROM 0x05
+#define TYPE_SCANNER 0x06
#define TYPE_MOD 0x07 /* Magneto-optical disk - treated as TYPE_DISK */
#define TYPE_NO_LUN 0x7f
@@ -266,6 +281,7 @@ extern const unsigned char scsi_command_size[8];
#define SCSI_MAN_UNKNOWN 0
#define SCSI_MAN_NEC 1
#define SCSI_MAN_TOSHIBA 2
+#define SCSI_MAN_NEC_OLDCDR 3
/*
The scsi_device struct contains what we know about each given scsi
@@ -388,8 +404,8 @@ struct scatterlist {
code does not need to do anything special to keep the commands alive. */
#define SCSI_RESET_SUCCESS 2
-/* We called for an reset of this bus, and we should get an interrupt
- when this succeeds. Each command should get it's own status
+/* We called for a reset of this bus, and we should get an interrupt
+ when this succeeds. Each command should get its own status
passed up to scsi_done, but this has not happened yet. */
#define SCSI_RESET_PENDING 3
@@ -510,7 +526,7 @@ typedef struct scsi_cmnd {
DID_ABORT is returned in the hostbyte.
*/
-extern int scsi_abort (Scsi_Cmnd *, int code);
+extern int scsi_abort (Scsi_Cmnd *, int code, int pid);
extern void scsi_do_cmd (Scsi_Cmnd *, const void *cmnd ,
void *buffer, unsigned bufflen, void (*done)(struct scsi_cmnd *),
@@ -525,6 +541,7 @@ extern int scsi_reset (Scsi_Cmnd *);
extern int max_scsi_hosts;
#if defined(MAJOR_NR) && (MAJOR_NR != SCSI_TAPE_MAJOR)
+#include "hosts.h"
static Scsi_Cmnd * end_scsi_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors)
{
struct request * req;
@@ -563,7 +580,17 @@ static Scsi_Cmnd * end_scsi_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors
if (req->sem != NULL) {
up(req->sem);
}
+
+ if (SCpnt->host->block) {
+ struct Scsi_Host * next;
+
+ for (next = SCpnt->host->block; next != SCpnt->host;
+ next = next->block)
+ wake_up(&next->host_wait);
+ }
+
req->dev = -1;
+ wake_up(&wait_for_request);
wake_up(&SCpnt->device->device_wait);
return NULL;
}
@@ -576,7 +603,7 @@ static Scsi_Cmnd * end_scsi_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors
#define INIT_SCSI_REQUEST \
if (!CURRENT) {\
CLEAR_INTR; \
- sti(); \
+ restore_flags(flags); \
return; \
} \
if (MAJOR(CURRENT->dev) != MAJOR_NR) \
@@ -591,14 +618,36 @@ static Scsi_Cmnd * end_scsi_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors
if (CONDITION) { \
struct wait_queue wait = { current, NULL}; \
add_wait_queue(QUEUE, &wait); \
-sleep_repeat: \
+ for(;;) { \
current->state = TASK_UNINTERRUPTIBLE; \
if (CONDITION) { \
- schedule(); \
- goto sleep_repeat; \
+ if (intr_count) \
+ panic("scsi: trying to call schedule() in interrupt" \
+ ", file %s, line %d.\n", __FILE__, __LINE__); \
+ schedule(); \
+ } \
+ else \
+ break; \
} \
remove_wait_queue(QUEUE, &wait); \
current->state = TASK_RUNNING; \
}; }
#endif
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index b564f81f0..b9db52b97 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -164,6 +164,7 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
struct scatterlist * sgpnt;
int target = SCpnt->target;
int bufflen = SCpnt->request_bufflen;
+ unsigned long flags;
int i;
sgcount = 0;
sgpnt = NULL;
@@ -372,7 +373,8 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
return 0;
};
- cli();
+ save_flags(flags);
+ cli();
for(i=0;i<SCSI_DEBUG_MAILBOXES; i++){
if (SCint[i] == 0) break;
};
@@ -405,7 +407,7 @@ int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
};
SCpnt->result = scsi_debug_errsts;
- sti();
+ restore_flags(flags);
#if 0
printk("Sending command (%d %x %d %d)...", i, done, timeout[i],jiffies);
@@ -441,7 +443,8 @@ static void scsi_debug_intr_handle(void)
Scsi_Cmnd * SCtmp;
int i, pending;
void (*my_done)(Scsi_Cmnd *);
- int to;
+ unsigned long flags;
+ int to;
#ifndef IMMEDIATE
timer_table[SCSI_DEBUG_TIMER].expires = 0;
@@ -449,6 +452,7 @@ static void scsi_debug_intr_handle(void)
#endif
repeat:
+ save_flags(flags);
cli();
for(i=0;i<SCSI_DEBUG_MAILBOXES; i++) {
if (SCint[i] == 0) continue;
@@ -466,7 +470,7 @@ static void scsi_debug_intr_handle(void)
for(i=0;i<SCSI_DEBUG_MAILBOXES; i++) {
if (SCint[i] == 0) continue;
if (timeout[i] == 0) continue;
- if (timeout[i] <= jiffies) {sti(); goto repeat;};
+ if (timeout[i] <= jiffies) {restore_flags(flags); goto repeat;};
if (timeout[i] > jiffies) {
if (pending > timeout[i]) pending = timeout[i];
continue;
@@ -477,7 +481,7 @@ static void scsi_debug_intr_handle(void)
(pending <= jiffies ? jiffies+1 : pending);
timer_active |= 1 << SCSI_DEBUG_TIMER;
};
- sti();
+ restore_flags(flags);
#endif
return;
};
@@ -490,7 +494,7 @@ static void scsi_debug_intr_handle(void)
timeout[i] = 0;
SCtmp = (Scsi_Cmnd *) SCint[i];
SCint[i] = NULL;
- sti();
+ restore_flags(flags);
if (!my_done) {
printk("scsi_debug_intr_handle: Unexpected interrupt\n");
@@ -525,17 +529,20 @@ int scsi_debug_abort(Scsi_Cmnd * SCpnt)
{
int j;
void (*my_done)(Scsi_Cmnd *);
+ unsigned long flags;
+
DEB(printk("scsi_debug_abort\n"));
SCpnt->result = SCpnt->abort_reason << 16;
for(j=0;j<SCSI_DEBUG_MAILBOXES; j++) {
if(SCpnt == SCint[j]) {
my_done = do_done[j];
my_done(SCpnt);
+ save_flags(flags);
cli();
timeout[j] = 0;
SCint[j] = NULL;
do_done[j] = NULL;
- sti();
+ restore_flags(flags);
};
};
return 0;
@@ -553,6 +560,8 @@ int scsi_debug_biosparam(Disk * disk, int dev, int* info){
int scsi_debug_reset(Scsi_Cmnd * SCpnt)
{
int i;
+ unsigned long flags;
+
void (*my_done)(Scsi_Cmnd *);
DEB(printk("scsi_debug_reset called\n"));
for(i=0;i<SCSI_DEBUG_MAILBOXES; i++) {
@@ -560,11 +569,12 @@ int scsi_debug_reset(Scsi_Cmnd * SCpnt)
SCint[i]->result = DID_ABORT << 16;
my_done = do_done[i];
my_done(SCint[i]);
+ save_flags(flags);
cli();
SCint[i] = NULL;
do_done[i] = NULL;
timeout[i] = 0;
- sti();
+ restore_flags(flags);
};
return 0;
}
diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c
index c0be923c6..92f3eef3e 100644
--- a/drivers/scsi/scsi_ioctl.c
+++ b/drivers/scsi/scsi_ioctl.c
@@ -5,6 +5,7 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/sched.h>
+#include <linux/mm.h>
#include <linux/string.h>
#include "../block/blk.h"
@@ -13,7 +14,7 @@
#include "scsi_ioctl.h"
#define MAX_RETRIES 5
-#define MAX_TIMEOUT 200
+#define MAX_TIMEOUT 900
#define MAX_BUF 4096
#define max(a,b) (((a) > (b)) ? (a) : (b))
@@ -168,6 +169,7 @@ static int ioctl_command(Scsi_Device *dev, void *buffer)
if (buf_needed > MAX_BUF) buf_needed = MAX_BUF;
buf = (char *) scsi_malloc(buf_needed);
if (!buf) return -ENOMEM;
+ memset(buf, 0, buf_needed);
} else
buf = NULL;
@@ -309,3 +311,20 @@ int kernel_scsi_ioctl (Scsi_Device *dev, int cmd, void *arg) {
set_fs(oldfs);
return tmp;
}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/drivers/scsi/scsi_module.c b/drivers/scsi/scsi_module.c
new file mode 100644
index 000000000..e6d7a607d
--- /dev/null
+++ b/drivers/scsi/scsi_module.c
@@ -0,0 +1,49 @@
+/*
+ * scsi_module.c Copyright (1994, 1995) Eric Youngdale.
+ *
+ * Support for loading low-level scsi drivers using the linux kernel loadable
+ * module interface.
+ *
+ * To use, the host adapter should first define and initialize the variable
+ * driver_template (datatype Scsi_Host_Template), and then include this file.
+ * This should also be wrapped in a #ifdef MODULE/#endif.
+ *
+ * The low -level driver must also define a release function which will
+ * free any irq assignments, release any dma channels, release any I/O
+ * address space that might be reserved, and otherwise clean up after itself.
+ * The idea is that the same driver should be able to be reloaded without
+ * any difficulty. This makes debugging new drivers easier, as you should
+ * be able to load the driver, test it, unload, modify and reload.
+ *
+ * One *very* important caveat. If the driver may need to do DMA on the
+ * ISA bus, you must have unchecked_isa_dma set in the device template,
+ * even if this might be changed during the detect routine. This is
+ * because the shpnt structure will be allocated in a special way so that
+ * it will be below the appropriate DMA limit - thus if your driver uses
+ * the hostdata field of shpnt, and the board must be able to access this
+ * via DMA, the shpnt structure must be in a DMA accessible region of
+ * memory. This comment would be relevant for something like the buslogic
+ * driver where there are many boards, only some of which do DMA onto the
+ * ISA bus. There is no convenient way of specifying whether the host
+ * needs to be in a ISA DMA accessible region of memory when you call
+ * scsi_register.
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+
+char kernel_version[] = UTS_RELEASE;
+
+int init_module(void) {
+ driver_template.usage_count = &mod_use_count_;
+ scsi_register_module(MODULE_SCSI_HA, &driver_template);
+ return (driver_template.present == 0);
+}
+
+void cleanup_module( void) {
+ if (MOD_IN_USE) {
+ printk(KERN_INFO __FILE__ ": module is in use, remove rejected\n");
+ }
+ scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
+}
+
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 253f8d2dc..334e6fb60 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1,19 +1,25 @@
/*
* sd.c Copyright (C) 1992 Drew Eckhardt
- * Copyright (C) 1993, 1994 Eric Youngdale
- * Linux scsi disk driver by
- * Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ * Linux scsi disk driver
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
*
* <drew@colorado.edu>
*
* Modified by Eric Youngdale ericy@cais.com to
* add scatter-gather, multiple outstanding request, and other
* enhancements.
+ *
+ * Modified by Eric Youngdale eric@aib.com to support loadable
+ * low-level scsi drivers.
*/
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/sched.h>
+#include <linux/mm.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <asm/system.h>
@@ -45,10 +51,12 @@ static const char RCSid[] = "$Header:";
SC->device->type != TYPE_MOD)
struct hd_struct * sd;
+int revalidate_scsidisk(int dev, int maxusage);
-Scsi_Disk * rscsi_disks;
+Scsi_Disk * rscsi_disks = NULL;
static int * sd_sizes;
static int * sd_blocksizes;
+static int * sd_hardsizes; /* Hardware sector size */
extern int sd_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
@@ -61,13 +69,14 @@ static void requeue_sd_request (Scsi_Cmnd * SCpnt);
static void sd_init(void);
static void sd_finish(void);
-static void sd_attach(Scsi_Device *);
+static int sd_attach(Scsi_Device *);
static int sd_detect(Scsi_Device *);
+static void sd_detach(Scsi_Device *);
struct Scsi_Device_Template sd_template = {NULL, "disk", "sd", TYPE_DISK,
SCSI_DISK_MAJOR, 0, 0, 0, 1,
sd_detect, sd_init,
- sd_finish, sd_attach, NULL};
+ sd_finish, sd_attach, sd_detach};
static int sd_open(struct inode * inode, struct file * filp)
{
@@ -80,7 +89,8 @@ static int sd_open(struct inode * inode, struct file * filp)
/* Make sure that only one process can do a check_change_disk at one time.
This is also used to lock out further access when the partition table is being re-read. */
- while (rscsi_disks[target].device->busy);
+ while (rscsi_disks[target].device->busy)
+ barrier();
if(rscsi_disks[target].device->removable) {
check_disk_change(inode->i_rdev);
@@ -88,7 +98,16 @@ static int sd_open(struct inode * inode, struct file * filp)
if(!rscsi_disks[target].device->access_count)
sd_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
};
+ /*
+ * See if we are requesting a non-existent partition. Do this
+ * after checking for disk change.
+ */
+ if(sd_sizes[MINOR(inode->i_rdev)] == 0)
+ return -ENXIO;
+
rscsi_disks[target].device->access_count++;
+ if (rscsi_disks[target].device->host->hostt->usage_count)
+ (*rscsi_disks[target].device->host->hostt->usage_count)++;
return 0;
}
@@ -100,6 +119,8 @@ static void sd_release(struct inode * inode, struct file * file)
target = DEVICE_NR(MINOR(inode->i_rdev));
rscsi_disks[target].device->access_count--;
+ if (rscsi_disks[target].device->host->hostt->usage_count)
+ (*rscsi_disks[target].device->host->hostt->usage_count)--;
if(rscsi_disks[target].device->removable) {
if(!rscsi_disks[target].device->access_count)
@@ -146,7 +167,10 @@ static void sd_geninit (void)
for (i = 0; i < sd_template.dev_max; ++i)
if(rscsi_disks[i].device)
sd[i << 4].nr_sects = rscsi_disks[i].capacity;
+#if 0
+ /* No longer needed - we keep track of this as we attach/detach */
sd_gendisk.nr_real = sd_template.dev_max;
+#endif
}
/*
@@ -332,11 +356,14 @@ static void do_sd_request (void)
{
Scsi_Cmnd * SCpnt = NULL;
struct request * req = NULL;
+ unsigned long flags;
int flag = 0;
+
+ save_flags(flags);
while (1==1){
cli();
if (CURRENT != NULL && CURRENT->dev == -1) {
- sti();
+ restore_flags(flags);
return;
};
@@ -358,7 +385,13 @@ static void do_sd_request (void)
SCpnt = allocate_device(&CURRENT,
rscsi_disks[DEVICE_NR(MINOR(CURRENT->dev))].device, 0);
else SCpnt = NULL;
- sti();
+
+ /*
+ * The following restore_flags leads to latency problems. FIXME.
+ * Using a "sti()" gets rid of the latency problems but causes
+ * race conditions and crashes.
+ */
+ restore_flags(flags);
/* This is a performance enhancement. We dig down into the request list and
try and find a queueable request (i.e. device not busy, and host able to
@@ -385,7 +418,7 @@ static void do_sd_request (void)
else
req1->next = req->next;
};
- sti();
+ restore_flags(flags);
};
if (!SCpnt) return; /* Could not find anything to do */
@@ -493,9 +526,9 @@ repeat:
if (contiguous && SCpnt->request.bh &&
- ((int) SCpnt->request.bh->b_data) + (SCpnt->request.nr_sectors << 9) - 1 >
+ ((long) SCpnt->request.bh->b_data) + (SCpnt->request.nr_sectors << 9) - 1 >
ISA_DMA_THRESHOLD && SCpnt->host->unchecked_isa_dma) {
- if(((int) SCpnt->request.bh->b_data) > ISA_DMA_THRESHOLD)
+ if(((long) SCpnt->request.bh->b_data) > ISA_DMA_THRESHOLD)
bounce_buffer = (char *) scsi_malloc(bounce_size);
if(!bounce_buffer) contiguous = 0;
};
@@ -553,7 +586,7 @@ repeat:
if(!bhp || !CONTIGUOUS_BUFFERS(bhp,bh) ||
!CLUSTERABLE_DEVICE(SCpnt) ||
(SCpnt->host->unchecked_isa_dma &&
- ((unsigned int) bh->b_data-1) == ISA_DMA_THRESHOLD)) {
+ ((unsigned long) bh->b_data-1) == ISA_DMA_THRESHOLD)) {
if (count < SCpnt->host->sg_tablesize) count++;
else break;
};
@@ -592,7 +625,7 @@ repeat:
sgpnt[count].length += bh->b_size;
counted += bh->b_size >> 9;
- if (((int) sgpnt[count].address) + sgpnt[count].length - 1 >
+ if (((long) sgpnt[count].address) + sgpnt[count].length - 1 >
ISA_DMA_THRESHOLD && (SCpnt->host->unchecked_isa_dma) &&
!sgpnt[count].alt_address) {
sgpnt[count].alt_address = sgpnt[count].address;
@@ -635,7 +668,7 @@ repeat:
if(bhp && CONTIGUOUS_BUFFERS(bh,bhp) && CLUSTERABLE_DEVICE(SCpnt)) {
char * tmp;
- if (((int) sgpnt[count].address) + sgpnt[count].length +
+ if (((long) sgpnt[count].address) + sgpnt[count].length +
bhp->b_size - 1 > ISA_DMA_THRESHOLD &&
(SCpnt->host->unchecked_isa_dma) &&
!sgpnt[count].alt_address) continue;
@@ -691,7 +724,7 @@ repeat:
/* Now handle the possibility of DMA to addresses > 16Mb */
if(SCpnt->use_sg == 0){
- if (((int) buff) + (this_count << 9) - 1 > ISA_DMA_THRESHOLD &&
+ if (((long) buff) + (this_count << 9) - 1 > ISA_DMA_THRESHOLD &&
(SCpnt->host->unchecked_isa_dma)) {
if(bounce_buffer)
buff = bounce_buffer;
@@ -818,7 +851,7 @@ static int sd_init_onedisk(int i)
{
unsigned char cmd[10];
unsigned char *buffer;
- char spintime;
+ unsigned long spintime;
int the_result, retries;
Scsi_Cmnd * SCpnt;
@@ -847,7 +880,7 @@ static int sd_init_onedisk(int i)
512, sd_init_done, SD_TIMEOUT,
MAX_RETRIES);
- while(SCpnt->request.dev != 0xfffe);
+ while(SCpnt->request.dev != 0xfffe) barrier();
the_result = SCpnt->result;
@@ -873,7 +906,7 @@ static int sd_init_onedisk(int i)
512, sd_init_done, SD_TIMEOUT,
MAX_RETRIES);
- while(SCpnt->request.dev != 0xfffe);
+ while(SCpnt->request.dev != 0xfffe) barrier();
spintime = jiffies;
};
@@ -882,7 +915,7 @@ static int sd_init_onedisk(int i)
while(jiffies < time1 + HZ); /* Wait 1 second for next try */
printk( "." );
};
- } while(the_result && spintime && spintime+5000 > jiffies);
+ } while(the_result && spintime && spintime+50*HZ > jiffies);
if (spintime) {
if (the_result)
printk( "not responding...\n" );
@@ -909,7 +942,7 @@ static int sd_init_onedisk(int i)
MAX_RETRIES);
if (current == task[0])
- while(SCpnt->request.dev != 0xfffe);
+ while(SCpnt->request.dev != 0xfffe) barrier();
else
if (SCpnt->request.dev != 0xfffe){
struct semaphore sem = MUTEX_LOCKED;
@@ -994,6 +1027,21 @@ static int sd_init_onedisk(int i)
return i;
};
}
+ {
+ /*
+ The msdos fs need to know the hardware sector size
+ So I have created this table. See ll_rw_blk.c
+ Jacques Gelinas (Jacques@solucorp.qc.ca)
+ */
+ int m;
+ int hard_sector = rscsi_disks[i].sector_size;
+ /* There is 16 minor allocated for each devices */
+ for (m=i<<4; m<((i+1)<<4); m++){
+ sd_hardsizes[m] = hard_sector;
+ }
+ printk ("SCSI Hardware sector size is %d bytes on device sd%c\n"
+ ,hard_sector,i+'a');
+ }
if(rscsi_disks[i].sector_size == 1024)
rscsi_disks[i].capacity <<= 1; /* Change this into 512 byte sectors */
if(rscsi_disks[i].sector_size == 256)
@@ -1028,25 +1076,33 @@ static void sd_init()
}
/* We do not support attaching loadable devices yet. */
- if(scsi_loadable_module_flag) return;
+ if(rscsi_disks) return;
- sd_template.dev_max = sd_template.dev_noticed;
+ sd_template.dev_max = sd_template.dev_noticed + SD_EXTRA_DEVS;
rscsi_disks = (Scsi_Disk *)
- scsi_init_malloc(sd_template.dev_max * sizeof(Scsi_Disk));
+ scsi_init_malloc(sd_template.dev_max * sizeof(Scsi_Disk), GFP_ATOMIC);
memset(rscsi_disks, 0, sd_template.dev_max * sizeof(Scsi_Disk));
sd_sizes = (int *) scsi_init_malloc((sd_template.dev_max << 4) *
- sizeof(int));
+ sizeof(int), GFP_ATOMIC);
memset(sd_sizes, 0, (sd_template.dev_max << 4) * sizeof(int));
sd_blocksizes = (int *) scsi_init_malloc((sd_template.dev_max << 4) *
- sizeof(int));
- for(i=0;i<(sd_template.dev_max << 4);i++) sd_blocksizes[i] = 1024;
- blksize_size[MAJOR_NR] = sd_blocksizes;
+ sizeof(int), GFP_ATOMIC);
+
+ sd_hardsizes = (int *) scsi_init_malloc((sd_template.dev_max << 4) *
+ sizeof(struct hd_struct), GFP_ATOMIC);
+ for(i=0;i<(sd_template.dev_max << 4);i++){
+ sd_blocksizes[i] = 1024;
+ sd_hardsizes[i] = 512;
+ }
+ blksize_size[MAJOR_NR] = sd_blocksizes;
+ hardsect_size[MAJOR_NR] = sd_hardsizes;
sd = (struct hd_struct *) scsi_init_malloc((sd_template.dev_max << 4) *
- sizeof(struct hd_struct));
+ sizeof(struct hd_struct),
+ GFP_ATOMIC);
sd_gendisk.max_nr = sd_template.dev_max;
@@ -1060,28 +1116,37 @@ static void sd_finish()
{
int i;
- for (i = 0; i < sd_template.dev_max; ++i)
- if (rscsi_disks[i].device) i = sd_init_onedisk(i);
-
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ sd_gendisk.next = gendisk_head;
+ gendisk_head = &sd_gendisk;
+
+ for (i = 0; i < sd_template.dev_max; ++i)
+ if (!rscsi_disks[i].capacity &&
+ rscsi_disks[i].device)
+ {
+ i = sd_init_onedisk(i);
+ if (scsi_loadable_module_flag
+ && !rscsi_disks[i].has_part_table) {
+ sd_sizes[i << 4] = rscsi_disks[i].capacity;
+ revalidate_scsidisk(i << 4, 0);
+ }
+ rscsi_disks[i].has_part_table = 1;
+ }
+
/* If our host adapter is capable of scatter-gather, then we increase
the read-ahead to 16 blocks (32 sectors). If not, we use
a two block (4 sector) read ahead. */
- if(rscsi_disks[0].device->host->sg_tablesize)
+ if(rscsi_disks[0].device && rscsi_disks[0].device->host->sg_tablesize)
read_ahead[MAJOR_NR] = 120;
/* 64 sector read-ahead */
else
read_ahead[MAJOR_NR] = 4; /* 4 sector read-ahead */
- sd_gendisk.next = gendisk_head;
- gendisk_head = &sd_gendisk;
return;
}
static int sd_detect(Scsi_Device * SDp){
- /* We do not support attaching loadable devices yet. */
- if(scsi_loadable_module_flag) return 0;
if(SDp->type != TYPE_DISK && SDp->type != TYPE_MOD) return 0;
printk("Detected scsi disk sd%c at scsi%d, id %d, lun %d\n",
@@ -1092,17 +1157,17 @@ static int sd_detect(Scsi_Device * SDp){
}
-static void sd_attach(Scsi_Device * SDp){
+static int sd_attach(Scsi_Device * SDp){
Scsi_Disk * dpnt;
int i;
- /* We do not support attaching loadable devices yet. */
- if(scsi_loadable_module_flag) return;
- if(SDp->type != TYPE_DISK && SDp->type != TYPE_MOD) return;
-
- if(sd_template.nr_dev >= sd_template.dev_max)
- panic ("scsi_devices corrupt (sd)");
+ if(SDp->type != TYPE_DISK && SDp->type != TYPE_MOD) return 0;
+ if(sd_template.nr_dev >= sd_template.dev_max) {
+ SDp->attached--;
+ return 1;
+ }
+
for(dpnt = rscsi_disks, i=0; i<sd_template.dev_max; i++, dpnt++)
if(!dpnt->device) break;
@@ -1110,8 +1175,11 @@ static void sd_attach(Scsi_Device * SDp){
SDp->scsi_request_fn = do_sd_request;
rscsi_disks[i].device = SDp;
+ rscsi_disks[i].has_part_table = 0;
sd_template.nr_dev++;
-};
+ sd_gendisk.nr_real++;
+ return 0;
+}
#define DEVICE_BUSY rscsi_disks[target].device->busy
#define USAGE rscsi_disks[target].device->access_count
@@ -1129,6 +1197,7 @@ static void sd_attach(Scsi_Device * SDp){
int revalidate_scsidisk(int dev, int maxusage){
int target, major;
struct gendisk * gdev;
+ unsigned long flags;
int max_p;
int start;
int i;
@@ -1136,14 +1205,15 @@ int revalidate_scsidisk(int dev, int maxusage){
target = DEVICE_NR(MINOR(dev));
gdev = &GENDISK_STRUCT;
+ save_flags(flags);
cli();
if (DEVICE_BUSY || USAGE > maxusage) {
- sti();
+ restore_flags(flags);
printk("Device busy for revalidation (usage=%d)\n", USAGE);
return -EBUSY;
};
DEVICE_BUSY = 1;
- sti();
+ restore_flags(flags);
max_p = gdev->max_p;
start = target << gdev->minor_shift;
@@ -1172,3 +1242,57 @@ static int fop_revalidate_scsidisk(dev_t dev){
return revalidate_scsidisk(dev, 0);
}
+
+static void sd_detach(Scsi_Device * SDp)
+{
+ Scsi_Disk * dpnt;
+ int i;
+ int max_p;
+ int major;
+ int start;
+
+ for(dpnt = rscsi_disks, i=0; i<sd_template.dev_max; i++, dpnt++)
+ if(dpnt->device == SDp) {
+
+ /* If we are disconnecting a disk driver, sync and invalidate everything */
+ max_p = sd_gendisk.max_p;
+ start = i << sd_gendisk.minor_shift;
+ major = MAJOR_NR << 8;
+
+ for (i=max_p - 1; i >=0 ; i--) {
+ sync_dev(major | start | i);
+ invalidate_inodes(major | start | i);
+ invalidate_buffers(major | start | i);
+ sd_gendisk.part[start+i].start_sect = 0;
+ sd_gendisk.part[start+i].nr_sects = 0;
+ sd_sizes[start+i] = 0;
+ };
+
+ dpnt->has_part_table = 0;
+ dpnt->device = NULL;
+ dpnt->capacity = 0;
+ SDp->attached--;
+ sd_template.dev_noticed--;
+ sd_template.nr_dev--;
+ sd_gendisk.nr_real--;
+ return;
+ }
+ return;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/drivers/scsi/sd_ioctl.c b/drivers/scsi/sd_ioctl.c
index 599a2a320..5ca3f63e9 100644
--- a/drivers/scsi/sd_ioctl.c
+++ b/drivers/scsi/sd_ioctl.c
@@ -1,5 +1,6 @@
#include <linux/kernel.h>
#include <linux/sched.h>
+#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/hdreg.h>
#include <linux/errno.h>
@@ -72,3 +73,19 @@ int sd_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigne
return scsi_ioctl(rscsi_disks[MINOR(dev) >> 4].device , cmd, (void *) arg);
}
}
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/drivers/scsi/seagate.c b/drivers/scsi/seagate.c
index 0b377911c..5ea690a53 100644
--- a/drivers/scsi/seagate.c
+++ b/drivers/scsi/seagate.c
@@ -52,6 +52,8 @@
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/string.h>
+#include <linux/config.h>
+
#include "../block/blk.h"
#include "scsi.h"
#include "hosts.h"
@@ -94,7 +96,9 @@ static void *base_address = NULL; /*
used to calculate memory mapped
register location.
*/
+#ifdef notyet
static volatile int abort_confirm = 0;
+#endif
static volatile void *st0x_cr_sr; /*
control register write,
@@ -177,6 +181,7 @@ static const Signature signatures[] = {
{"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90",5, 47, FD},
{"FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90", 5, 46, FD},
{"FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92", 5, 44, FD},
+{"IBM F1 BIOS V1.1004/30/92", 5, 25, FD},
{"FUTURE DOMAIN TMC-950", 5, 21, FD},
#endif /* CONFIG_SCSI_SEAGATE */
}
@@ -190,7 +195,7 @@ static const Signature signatures[] = {
*/
static int hostno = -1;
-static void seagate_reconnect_intr(int);
+static void seagate_reconnect_intr(int, struct pt_regs *);
#ifdef FAST
static int fast = 1;
@@ -279,7 +284,7 @@ int seagate_st0x_detect (Scsi_Host_Template * tpnt)
* First, we try for the manual override.
*/
#ifdef DEBUG
- printk("Autodetecting seagate ST0x\n");
+ printk("Autodetecting ST0x / TMC-8xx\n");
#endif
if (hostno != -1)
@@ -328,13 +333,14 @@ int seagate_st0x_detect (Scsi_Host_Template * tpnt)
} /* (! controller_type) */
tpnt->this_id = (controller_type == SEAGATE) ? 7 : 6;
+ tpnt->name = (controller_type == SEAGATE) ? ST0X_ID_STR : FD_ID_STR;
if (base_address)
{
st0x_cr_sr =(void *) (((unsigned char *) base_address) + (controller_type == SEAGATE ? 0x1a00 : 0x1c00));
st0x_dr = (void *) (((unsigned char *) base_address ) + (controller_type == SEAGATE ? 0x1c00 : 0x1e00));
#ifdef DEBUG
- printk("ST0x detected. Base address = %x, cr = %x, dr = %x\n", base_address, st0x_cr_sr, st0x_dr);
+ printk("%s detected. Base address = %x, cr = %x, dr = %x\n", tpnt->name, base_address, st0x_cr_sr, st0x_dr);
#endif
/*
* At all times, we will use IRQ 5. Should also check for IRQ3 if we
@@ -342,7 +348,8 @@ int seagate_st0x_detect (Scsi_Host_Template * tpnt)
*/
instance = scsi_register(tpnt, 0);
hostno = instance->host_no;
- if (request_irq((int) irq, seagate_reconnect_intr, SA_INTERRUPT, "seagate")) {
+ if (request_irq((int) irq, seagate_reconnect_intr, SA_INTERRUPT,
+ (controller_type == SEAGATE) ? "seagate" : "tmc-8xx")) {
printk("scsi%d : unable to allocate IRQ%d\n",
hostno, (int) irq);
return 0;
@@ -351,39 +358,40 @@ int seagate_st0x_detect (Scsi_Host_Template * tpnt)
borken_init();
#endif
- return 1;
- }
- else
- {
-#ifdef DEBUG
- printk("ST0x not detected.\n");
-#endif
- return 0;
- }
- }
-
-const char *seagate_st0x_info(struct Scsi_Host * shpnt) {
- static char buffer[256];
- sprintf(buffer, "scsi%d : %s at irq %d address %p options :"
+ printk("%s options:"
#ifdef ARBITRATE
-" ARBITRATE"
+ " ARBITRATE"
#endif
#ifdef SLOW_HANDSHAKE
-" SLOW_HANDSHAKE"
+ " SLOW_HANDSHAKE"
#endif
#ifdef FAST
#ifdef FAST32
-" FAST32"
+ " FAST32"
#else
-" FAST"
+ " FAST"
#endif
#endif
-
#ifdef LINKED
-" LINKED"
+ " LINKED"
#endif
- "\n", hostno, (controller_type == SEAGATE) ? "seagate" :
- "FD TMC-8xx", irq, base_address);
+ "\n", tpnt->name);
+ return 1;
+ }
+ else
+ {
+#ifdef DEBUG
+ printk("ST0x / TMC-8xx not detected.\n");
+#endif
+ return 0;
+ }
+ }
+
+const char *seagate_st0x_info(struct Scsi_Host * shpnt) {
+ static char buffer[64];
+ sprintf(buffer, "%s at irq %d, address 0x%05X",
+ (controller_type == SEAGATE) ? ST0X_ID_STR : FD_ID_STR,
+ irq, (unsigned int)base_address);
return buffer;
}
@@ -401,7 +409,7 @@ static int current_bufflen;
#ifdef LINKED
/*
- * linked_connected indicates weather or not we are currently connected to
+ * linked_connected indicates whether or not we are currently connected to
* linked_target, linked_lun and in an INFORMATION TRANSFER phase,
* using linked commands.
*/
@@ -447,7 +455,7 @@ static int should_reconnect = 0;
* asserting SEL.
*/
-static void seagate_reconnect_intr (int unused)
+static void seagate_reconnect_intr(int irq, struct pt_regs * regs)
{
int temp;
Scsi_Cmnd * SCtmp;
diff --git a/drivers/scsi/seagate.h b/drivers/scsi/seagate.h
index abc1c930a..4f6ef3efd 100644
--- a/drivers/scsi/seagate.h
+++ b/drivers/scsi/seagate.h
@@ -129,6 +129,8 @@ extern volatile int seagate_st0x_timeout;
#define SEAGATE 1 /* these determine the type of the controller */
#define FD 2
+#define ST0X_ID_STR "Seagate ST-01/ST-02"
+#define FD_ID_STR "TMC-8XX/TMC-950"
#endif
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index f60d1ffce..9224533c1 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
+#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/mtio.h>
#include <linux/ioctl.h>
@@ -26,14 +27,15 @@
#include "sg.h"
static void sg_init(void);
-static void sg_attach(Scsi_Device *);
+static int sg_attach(Scsi_Device *);
static int sg_detect(Scsi_Device *);
+static void sg_detach(Scsi_Device *);
struct Scsi_Device_Template sg_template = {NULL, NULL, "sg", 0xff,
SCSI_GENERIC_MAJOR, 0, 0, 0, 0,
sg_detect, sg_init,
- NULL, sg_attach, NULL};
+ NULL, sg_attach, sg_detach};
#ifdef SG_BIG_BUFF
static char *big_buff;
@@ -58,6 +60,7 @@ struct scsi_generic
};
static struct scsi_generic *scsi_generics=NULL;
+static void sg_free(char *buff,int size);
static int sg_ioctl(struct inode * inode,struct file * file,
unsigned int cmd_in, unsigned long arg)
@@ -108,11 +111,15 @@ static int sg_open(struct inode * inode, struct file * filp)
}
if (!scsi_generics[dev].users && scsi_generics[dev].pending && scsi_generics[dev].complete)
{
- scsi_free(scsi_generics[dev].buff,scsi_generics[dev].buff_len);
+ if (scsi_generics[dev].buff != NULL)
+ sg_free(scsi_generics[dev].buff,scsi_generics[dev].buff_len);
+ scsi_generics[dev].buff=NULL;
scsi_generics[dev].pending=0;
}
if (!scsi_generics[dev].users)
scsi_generics[dev].timeout=SG_DEFAULT_TIMEOUT;
+ if (scsi_generics[dev].device->host->hostt->usage_count)
+ (*scsi_generics[dev].device->host->hostt->usage_count)++;
scsi_generics[dev].users++;
return 0;
}
@@ -121,6 +128,8 @@ static void sg_close(struct inode * inode, struct file * filp)
{
int dev=MINOR(inode->i_rdev);
scsi_generics[dev].users--;
+ if (scsi_generics[dev].device->host->hostt->usage_count)
+ (*scsi_generics[dev].device->host->hostt->usage_count)--;
scsi_generics[dev].exclude=0;
wake_up(&scsi_generics[dev].generic_wait);
}
@@ -188,6 +197,7 @@ static int sg_read(struct inode *inode,struct file *filp,char *buf,int count)
else
count=0;
sg_free(device->buff,device->buff_len);
+ device->buff = NULL;
device->pending=0;
wake_up(&device->write_wait);
return count;
@@ -226,7 +236,11 @@ static int sg_write(struct inode *inode,struct file *filp,char *buf,int count)
if ((i=verify_area(VERIFY_READ,buf,count)))
return i;
- if (count<sizeof(struct sg_header))
+ /*
+ * The minimum scsi command length is 6 bytes. If we get anything less than this,
+ * it is clearly bogus.
+ */
+ if (count<(sizeof(struct sg_header) + 6))
return -EIO;
/* make sure we can fit */
while(device->pending)
@@ -266,6 +280,7 @@ static int sg_write(struct inode *inode,struct file *filp,char *buf,int count)
device->pending=0;
wake_up(&device->write_wait);
sg_free(device->buff,device->buff_len);
+ device->buff = NULL;
return -EWOULDBLOCK;
}
#ifdef DEBUG
@@ -274,10 +289,22 @@ static int sg_write(struct inode *inode,struct file *filp,char *buf,int count)
/* now issue command */
SCpnt->request.dev=dev;
SCpnt->sense_buffer[0]=0;
- size=COMMAND_SIZE(get_fs_byte(buf));
+ opcode = get_fs_byte(buf);
size=COMMAND_SIZE(opcode);
if (opcode >= 0xc0 && device->header.twelve_byte) size = 12;
SCpnt->cmd_len = size;
+ /*
+ * Verify that the user has actually passed enough bytes for this command.
+ */
+ if (count<(sizeof(struct sg_header) + size))
+ {
+ device->pending=0;
+ wake_up(&device->write_wait);
+ sg_free(device->buff,device->buff_len);
+ device->buff = NULL;
+ return -EIO;
+ }
+
memcpy_fromfs(cmnd,buf,size);
buf+=size;
memcpy_fromfs(device->buff,buf,device->header.pack_len-size-sizeof(struct sg_header));
@@ -286,7 +313,8 @@ static int sg_write(struct inode *inode,struct file *filp,char *buf,int count)
printk("do cmd\n");
#endif
scsi_do_cmd (SCpnt,(void *) cmnd,
- (void *) device->buff,amt,sg_command_done,device->timeout,SG_DEFAULT_RETRIES);
+ (void *) device->buff,amt,
+ sg_command_done,device->timeout,SG_DEFAULT_RETRIES);
#ifdef DEBUG
printk("done cmd\n");
#endif
@@ -308,9 +336,6 @@ static struct file_operations sg_fops = {
static int sg_detect(Scsi_Device * SDp){
- /* We do not support attaching loadable devices yet. */
- if(scsi_loadable_module_flag) return 0;
-
++sg_template.dev_noticed;
return 1;
}
@@ -332,33 +357,36 @@ static void sg_init()
sg_registered++;
}
- /* We do not support attaching loadable devices yet. */
- if(scsi_loadable_module_flag) return;
+ /* If we have already been through here, return */
+ if(scsi_generics) return;
+
#ifdef DEBUG
printk("sg: Init generic device.\n");
#endif
#ifdef SG_BIG_BUFF
- big_buff= (char *) scsi_init_malloc(SG_BIG_BUFF);
+ big_buff= (char *) scsi_init_malloc(SG_BIG_BUFF, GFP_ATOMIC | GFP_DMA);
#endif
scsi_generics = (struct scsi_generic *)
- scsi_init_malloc(sg_template.dev_noticed * sizeof(struct scsi_generic));
- memset(scsi_generics, 0, sg_template.dev_noticed * sizeof(struct scsi_generic));
+ scsi_init_malloc((sg_template.dev_noticed + SG_EXTRA_DEVS)
+ * sizeof(struct scsi_generic), GFP_ATOMIC);
+ memset(scsi_generics, 0, (sg_template.dev_noticed + SG_EXTRA_DEVS)
+ * sizeof(struct scsi_generic));
- sg_template.dev_max = sg_template.dev_noticed;
+ sg_template.dev_max = sg_template.dev_noticed + SG_EXTRA_DEVS;
}
-static void sg_attach(Scsi_Device * SDp)
+static int sg_attach(Scsi_Device * SDp)
{
struct scsi_generic * gpnt;
int i;
- /* We do not support attaching loadable devices yet. */
- if(scsi_loadable_module_flag) return;
-
if(sg_template.nr_dev >= sg_template.dev_max)
- panic ("scsi_devices corrupt (sg)");
+ {
+ SDp->attached--;
+ return 1;
+ }
for(gpnt = scsi_generics, i=0; i<sg_template.dev_max; i++, gpnt++)
if(!gpnt->device) break;
@@ -370,8 +398,44 @@ static void sg_attach(Scsi_Device * SDp)
scsi_generics[i].generic_wait=NULL;
scsi_generics[i].read_wait=NULL;
scsi_generics[i].write_wait=NULL;
+ scsi_generics[i].buff=NULL;
scsi_generics[i].exclude=0;
scsi_generics[i].pending=0;
scsi_generics[i].timeout=SG_DEFAULT_TIMEOUT;
sg_template.nr_dev++;
+ return 0;
};
+
+
+
+static void sg_detach(Scsi_Device * SDp)
+{
+ struct scsi_generic * gpnt;
+ int i;
+
+ for(gpnt = scsi_generics, i=0; i<sg_template.dev_max; i++, gpnt++)
+ if(gpnt->device == SDp) {
+ gpnt->device = NULL;
+ SDp->attached--;
+ sg_template.nr_dev--;
+ return;
+ }
+ return;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/drivers/scsi/sg.h b/drivers/scsi/sg.h
index ea4ae8193..241a132f8 100644
--- a/drivers/scsi/sg.h
+++ b/drivers/scsi/sg.h
@@ -27,7 +27,7 @@ struct sg_header
#define SG_SET_TIMEOUT 0x2201 /* set timeout *(int *)arg==timeout */
#define SG_GET_TIMEOUT 0x2202 /* get timeout return timeout */
-#define SG_DEFAULT_TIMEOUT 6000 /* 1 minute timeout */
+#define SG_DEFAULT_TIMEOUT (60*HZ) /* 1 minute timeout */
#define SG_DEFAULT_RETRIES 1
#define SG_MAX_QUEUE 4 /* maximum outstanding request, arbitrary, may be
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index 82dfe55e0..5d58df15a 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -1,6 +1,6 @@
/*
* sr.c Copyright (C) 1992 David Giller
- * Copyright (C) 1993, 1994 Eric Youngdale
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
*
* adapted from:
* sd.c Copyright (C) 1992 Drew Eckhardt
@@ -12,11 +12,15 @@
* Modified by Eric Youngdale ericy@cais.com to
* add scatter-gather, multiple outstanding request, and other
* enhancements.
+ *
+ * Modified by Eric Youngdale eric@aib.com to support loadable
+ * low-level scsi drivers.
*/
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/sched.h>
+#include <linux/mm.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/cdrom.h>
@@ -31,17 +35,18 @@
#include "constants.h"
#define MAX_RETRIES 3
-#define SR_TIMEOUT 5000
+#define SR_TIMEOUT 15000
static void sr_init(void);
static void sr_finish(void);
-static void sr_attach(Scsi_Device *);
+static int sr_attach(Scsi_Device *);
static int sr_detect(Scsi_Device *);
+static void sr_detach(Scsi_Device *);
struct Scsi_Device_Template sr_template = {NULL, "cdrom", "sr", TYPE_ROM,
SCSI_CDROM_MAJOR, 0, 0, 0, 1,
sr_detect, sr_init,
- sr_finish, sr_attach, NULL};
+ sr_finish, sr_attach, sr_detach};
Scsi_CD * scsi_CDs;
static int * sr_sizes;
@@ -61,6 +66,8 @@ static void sr_release(struct inode * inode, struct file * file)
sync_dev(inode->i_rdev);
if(! --scsi_CDs[MINOR(inode->i_rdev)].device->access_count)
sr_ioctl(inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0);
+ if (scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)
+ (*scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)--;
}
static struct file_operations sr_fops =
@@ -233,14 +240,16 @@ static void rw_intr (Scsi_Cmnd * SCpnt)
}
if (SCpnt->sense_buffer[2] == ILLEGAL_REQUEST) {
- printk("CD-ROM error: Drive reports ILLEGAL REQUEST.\n");
+ printk("CD-ROM error: ");
+ print_sense("sr", SCpnt);
+ printk("command was: ");
+ print_command(SCpnt->cmnd);
if (scsi_CDs[DEVICE_NR(SCpnt->request.dev)].ten) {
scsi_CDs[DEVICE_NR(SCpnt->request.dev)].ten = 0;
requeue_sr_request(SCpnt);
result = 0;
return;
} else {
- printk("CD-ROM error: Drive reports %d.\n", SCpnt->sense_buffer[2]);
SCpnt = end_scsi_request(SCpnt, 0, this_count);
requeue_sr_request(SCpnt); /* Do next request */
return;
@@ -278,119 +287,185 @@ static void rw_intr (Scsi_Cmnd * SCpnt)
* Much of this has do be done with vendor-specific SCSI-commands.
* So I have to complete it step by step. Useful information is welcome.
*
- * Actually works: (should work ;-)
+ * Actually works:
* - NEC: Detection and support of multisession CD's. Special handling
* for XA-disks is not necessary.
*
* - TOSHIBA: setting density is done here now, mounting PhotoCD's should
* work now without running the program "set_density"
- * multisession-CD's are supported too.
+ * Multisession CD's are supported too.
*
- * Gerd Knorr (mailto:kraxel@cs.tu-berlin.de,
- * http://www.cs.tu-berlin.de/~kraxel/)
+ * kraxel@cs.tu-berlin.de (Gerd Knorr)
*/
-static void sr_photocd_done(Scsi_Cmnd *SCpnt)
-{
- SCpnt->request.dev = 0xfffe;
-}
-
static void sr_photocd(struct inode *inode)
{
unsigned long sector,min,sec,frame;
- Scsi_Cmnd *SCpnt;
- unsigned char scsi_cmd[10];
- unsigned char *buffer;
- int rc;
+ unsigned char buf[40]; /* the buffer for the ioctl */
+ unsigned char *cmd; /* the scsi-command */
+ unsigned char *send; /* the data we send to the drive ... */
+ unsigned char *rec; /* ... and get back */
+ int rc,is_xa,no_multi;
+
+ if (scsi_CDs[MINOR(inode->i_rdev)].xa_flags & 0x02) {
+#ifdef DEBUG
+ printk("sr_photocd: CDROM and/or the driver does not support multisession CD's");
+#endif
+ return;
+ }
+
+ if (!suser()) {
+ /* I'm not the superuser, so SCSI_IOCTL_SEND_COMMAND isn't allowed for me.
+ * That's why mpcd_sector will be initialized with zero, because I'm not
+ * able to get the right value. Necessary only if access_count is 1, else
+ * no disk change happened since the last call of this function and we can
+ * keep the old value.
+ */
+ if (1 == scsi_CDs[MINOR(inode->i_rdev)].device->access_count) {
+ scsi_CDs[MINOR(inode->i_rdev)].mpcd_sector = 0;
+ scsi_CDs[MINOR(inode->i_rdev)].xa_flags &= ~0x01;
+ }
+ return;
+ }
+
+ sector = 0;
+ is_xa = 0;
+ no_multi = 0;
+ cmd = rec = &buf[8];
switch(scsi_CDs[MINOR(inode->i_rdev)].device->manufacturer) {
case SCSI_MAN_NEC:
+#ifdef DEBUG
printk("sr_photocd: use NEC code\n");
- SCpnt = allocate_device(NULL, scsi_CDs[MINOR(inode->i_rdev)].device,1);
- memset(scsi_cmd,0,10);
- scsi_cmd[0] = 0xde;
- scsi_cmd[1] = ((scsi_CDs[MINOR(inode->i_rdev)].device->lun) << 5) | 0x03;
- scsi_cmd[2] = 0xb0;
- buffer = (unsigned char*) scsi_malloc(512);
- scsi_do_cmd(SCpnt, scsi_cmd, buffer, 0x16,
- sr_photocd_done, SR_TIMEOUT, MAX_RETRIES);
- while (SCpnt->request.dev != 0xfffe);
- rc = SCpnt->result;
- if (driver_byte(rc) != 0) {
- printk("sr_photocd: oops, CD-ROM reports an error.\n");
- sector = 0; }
- else {
- min = (unsigned long)buffer[15]/16*10 + (unsigned long)buffer[15]%16;
- sec = (unsigned long)buffer[16]/16*10 + (unsigned long)buffer[16]%16;
- frame = (unsigned long)buffer[17]/16*10 + (unsigned long)buffer[17]%16;
- sector = min*60*75 + sec*75 + frame;
- if (sector) {
- sector -= CD_BLOCK_OFFSET;
- printk("sr_photocd: multisession PhotoCD detected\n");
- }
+#endif
+ memset(buf,0,40);
+ *((unsigned long*)buf) = 0x0; /* we send nothing... */
+ *((unsigned long*)buf+1) = 0x16; /* and receive 0x16 bytes */
+ cmd[0] = 0xde;
+ cmd[1] = 0x03;
+ cmd[2] = 0xb0;
+ rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_SEND_COMMAND, buf);
+ if (rc != 0) {
+ printk("sr_photocd: ioctl error (NEC): 0x%x\n",rc);
+ break;
}
- scsi_free(buffer,512);
- SCpnt->request.dev = -1;
+ if (rec[14] != 0 && rec[14] != 0xb0) {
+ printk("sr_photocd: Hmm, seems the CDROM doesn't support multisession CD's\n");
+ no_multi = 1;
+ break;
+ }
+ min = (unsigned long) rec[15]/16*10 + (unsigned long) rec[15]%16;
+ sec = (unsigned long) rec[16]/16*10 + (unsigned long) rec[16]%16;
+ frame = (unsigned long) rec[17]/16*10 + (unsigned long) rec[17]%16;
+ sector = min*CD_SECS*CD_FRAMES + sec*CD_FRAMES + frame;
+ is_xa = (rec[14] == 0xb0);
+#ifdef DEBUG
+ if (sector) {
+ printk("sr_photocd: multisession CD detected. start: %lu\n",sector);
+ }
+#endif
break;
case SCSI_MAN_TOSHIBA:
+#ifdef DEBUG
printk("sr_photocd: use TOSHIBA code\n");
+#endif
+
+ /* we request some disc information (is it a XA-CD ?,
+ where starts the last session ?) */
+ memset(buf,0,40);
+ *((unsigned long*)buf) = 0;
+ *((unsigned long*)buf+1) = 4; /* we receive 4 bytes from the drive */
+ cmd[0] = 0xc7;
+ cmd[1] = 3;
+ rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_SEND_COMMAND, buf);
+ if (rc != 0) {
+ if (rc == 0x28000002) {
+ /* Got a "not ready" - error. No chance to find out if this is
+ because there is no CD in the drive or because the drive
+ don't knows multisession CD's. So I need to do an extra check... */
+ if (kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_TEST_UNIT_READY, NULL)) {
+ printk("sr_photocd: drive not ready\n");
+ } else {
+ printk("sr_photocd: Hmm, seems the CDROM doesn't support multisession CD's\n");
+ no_multi = 1;
+ }
+ } else
+ printk("sr_photocd: ioctl error (TOSHIBA #1): 0x%x\n",rc);
+ break; /* if the first ioctl fails, we don't call the second one */
+ }
+ is_xa = (rec[0] == 0x20);
+ min = (unsigned long) rec[1]/16*10 + (unsigned long) rec[1]%16;
+ sec = (unsigned long) rec[2]/16*10 + (unsigned long) rec[2]%16;
+ frame = (unsigned long) rec[3]/16*10 + (unsigned long) rec[3]%16;
+ sector = min*CD_SECS*CD_FRAMES + sec*CD_FRAMES + frame;
+ if (sector) {
+ sector -= CD_BLOCK_OFFSET;
+#ifdef DEBUG
+ printk("sr_photocd: multisession CD detected: start: %lu\n",sector);
+#endif
+ }
- /* first I do a set_density-call (for reading XA-sectors) ... */
- SCpnt = allocate_device(NULL, scsi_CDs[MINOR(inode->i_rdev)].device,1);
- memset(scsi_cmd,0,10);
- scsi_cmd[0] = 0x15;
- scsi_cmd[1] = ((scsi_CDs[MINOR(inode->i_rdev)].device->lun) << 5)|(1 << 4);
- scsi_cmd[4] = 12;
- buffer = (unsigned char*) scsi_malloc(512);
- memset(buffer,0,512);
- buffer[ 3] = 0x08;
- buffer[ 4] = 0x83;
- buffer[10] = 0x08;
- scsi_do_cmd(SCpnt, scsi_cmd, buffer, 12,
- sr_photocd_done, SR_TIMEOUT, MAX_RETRIES);
- while (SCpnt->request.dev != 0xfffe);
- rc = SCpnt->result;
- if (driver_byte(rc) != 0) {
- printk("sr_photocd: oops, CD-ROM reports an error.\n"); }
- scsi_free(buffer,512);
- SCpnt->request.dev = -1;
-
- /* ... and then I ask, if there is a multisession-Disk */
- SCpnt = allocate_device(NULL, scsi_CDs[MINOR(inode->i_rdev)].device,1);
- memset(scsi_cmd,0,10);
- scsi_cmd[0] = 0xc7;
- scsi_cmd[1] = ((scsi_CDs[MINOR(inode->i_rdev)].device->lun) << 5) | 3;
- buffer = (unsigned char*) scsi_malloc(512);
- memset(buffer,0,512);
- scsi_do_cmd(SCpnt, scsi_cmd, buffer, 4,
- sr_photocd_done, SR_TIMEOUT, MAX_RETRIES);
- while (SCpnt->request.dev != 0xfffe);
- rc = SCpnt->result;
- if (driver_byte(rc) != 0) {
- printk("sr_photocd: oops, CD-ROM reports an error.\n");
- sector = 0; }
- else {
- min = (unsigned long)buffer[1]/16*10 + (unsigned long)buffer[1]%16;
- sec = (unsigned long)buffer[2]/16*10 + (unsigned long)buffer[2]%16;
- frame = (unsigned long)buffer[3]/16*10 + (unsigned long)buffer[3]%16;
- sector = min*60*75 + sec*75 + frame;
- if (sector) {
- sector -= CD_BLOCK_OFFSET;
- printk("sr_photocd: multisession PhotoCD detected: %lu\n",sector);
+ /* now we do a get_density... */
+ memset(buf,0,40);
+ *((unsigned long*)buf) = 0;
+ *((unsigned long*)buf+1) = 12;
+ cmd[0] = 0x1a;
+ cmd[2] = 1;
+ cmd[4] = 12;
+ rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_SEND_COMMAND, buf);
+ if (rc != 0) {
+ printk("sr_photocd: ioctl error (TOSHIBA #2): 0x%x\n",rc);
+ break;
+ }
+#ifdef DEBUG
+ printk("sr_photocd: get_density: 0x%x\n",rec[4]);
+#endif
+
+ /* ...and only if necessary a set_density */
+ if ((rec[4] != 0x81 && is_xa) || (rec[4] != 0 && !is_xa)) {
+#ifdef DEBUG
+ printk("sr_photocd: doing set_density\n");
+#endif
+ memset(buf,0,40);
+ *((unsigned long*)buf) = 12; /* sending 12 bytes... */
+ *((unsigned long*)buf+1) = 0;
+ cmd[0] = 0x15;
+ cmd[1] = (1 << 4);
+ cmd[4] = 12;
+ send = &cmd[6]; /* this is a 6-Byte command */
+ send[ 3] = 0x08; /* the data for the command */
+ send[ 4] = (is_xa) ? 0x81 : 0; /* density 0x81 for XA-CD's, 0 else */
+ send[10] = 0x08;
+ rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_SEND_COMMAND, buf);
+ if (rc != 0) {
+ printk("sr_photocd: ioctl error (TOSHIBA #3): 0x%x\n",rc);
}
+ /* The set_density command may have changed the sector size or capacity. */
+ scsi_CDs[MINOR(inode->i_rdev)].needs_sector_size = 1;
}
- scsi_free(buffer,512);
- SCpnt->request.dev = -1;
break;
+
+ case SCSI_MAN_NEC_OLDCDR:
case SCSI_MAN_UNKNOWN:
default:
- printk("sr_photocd: there is no special photocd-code for this drive\n");
sector = 0;
+ no_multi = 1;
break; }
scsi_CDs[MINOR(inode->i_rdev)].mpcd_sector = sector;
+ if (is_xa)
+ scsi_CDs[MINOR(inode->i_rdev)].xa_flags |= 0x01;
+ else
+ scsi_CDs[MINOR(inode->i_rdev)].xa_flags &= ~0x01;
+ if (no_multi)
+ scsi_CDs[MINOR(inode->i_rdev)].xa_flags |= 0x02;
return;
}
@@ -406,6 +481,10 @@ static int sr_open(struct inode * inode, struct file * filp)
if(!scsi_CDs[MINOR(inode->i_rdev)].device->access_count++)
sr_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
+ if (scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)
+ (*scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)++;
+
+ sr_photocd(inode);
/* If this device did not have media in the drive at boot time, then
we would have been unable to get the sector size. Check to see if
@@ -415,10 +494,6 @@ static int sr_open(struct inode * inode, struct file * filp)
if(scsi_CDs[MINOR(inode->i_rdev)].needs_sector_size)
get_sectorsize(MINOR(inode->i_rdev));
-#if 0 /* don't use for now - it doesn't seem to work for everybody */
- sr_photocd(inode);
-#endif
-
return 0;
}
@@ -432,12 +507,14 @@ static void do_sr_request (void)
{
Scsi_Cmnd * SCpnt = NULL;
struct request * req = NULL;
+ unsigned long flags;
int flag = 0;
while (1==1){
+ save_flags(flags);
cli();
if (CURRENT != NULL && CURRENT->dev == -1) {
- sti();
+ restore_flags(flags);
return;
};
@@ -447,7 +524,7 @@ static void do_sr_request (void)
SCpnt = allocate_device(&CURRENT,
scsi_CDs[DEVICE_NR(MINOR(CURRENT->dev))].device, 0);
else SCpnt = NULL;
- sti();
+ restore_flags(flags);
/* This is a performance enhancement. We dig down into the request list and
try and find a queueable request (i.e. device not busy, and host able to
@@ -459,6 +536,7 @@ static void do_sr_request (void)
if (!SCpnt && sr_template.nr_dev > 1){
struct request *req1;
req1 = NULL;
+ save_flags(flags);
cli();
req = CURRENT;
while(req){
@@ -474,7 +552,7 @@ static void do_sr_request (void)
else
req1->next = req->next;
};
- sti();
+ restore_flags(flags);
};
if (!SCpnt)
@@ -564,10 +642,6 @@ work around the fact that the buffer cache has a block size of 1024,
and we have 2048 byte sectors. This code should work for buffers that
are any multiple of 512 bytes long. */
- /* this is for support of multisession-CD's */
- if (block >= 64 && block < 68) {
- block += scsi_CDs[dev].mpcd_sector*4; }
-
SCpnt->use_sg = 0;
if (SCpnt->host->sg_tablesize > 0 &&
@@ -637,7 +711,7 @@ are any multiple of 512 bytes long. */
if (count+1 != SCpnt->use_sg) panic("Bad sr request list");
break;
};
- if (((int) sgpnt[count].address) + sgpnt[count].length >
+ if (((long) sgpnt[count].address) + sgpnt[count].length >
ISA_DMA_THRESHOLD & (SCpnt->host->unchecked_isa_dma)) {
sgpnt[count].alt_address = sgpnt[count].address;
/* We try and avoid exhausting the DMA pool, since it is easier
@@ -666,7 +740,7 @@ are any multiple of 512 bytes long. */
}; /* if need DMA fixup */
}; /* for loop to fill list */
#ifdef DEBUG
- printk("SG: %d %d %d %d %d *** ",SCpnt->use_sg, SCpnt->request.sector,
+ printk("SR: %d %d %d %d %d *** ",SCpnt->use_sg, SCpnt->request.sector,
this_count,
SCpnt->request.current_nr_sectors,
SCpnt->request.nr_sectors);
@@ -701,7 +775,7 @@ are any multiple of 512 bytes long. */
{
this_count -= this_count % 4;
buffer = (unsigned char *) SCpnt->request.buffer;
- if (((int) buffer) + (this_count << 9) > ISA_DMA_THRESHOLD &
+ if (((long) buffer) + (this_count << 9) > ISA_DMA_THRESHOLD &
(SCpnt->host->unchecked_isa_dma))
buffer = (unsigned char *) scsi_malloc(this_count << 9);
}
@@ -780,8 +854,6 @@ are any multiple of 512 bytes long. */
static int sr_detect(Scsi_Device * SDp){
- /* We do not support attaching loadable devices yet. */
- if(scsi_loadable_module_flag) return 0;
if(SDp->type != TYPE_ROM && SDp->type != TYPE_WORM) return 0;
printk("Detected scsi CD-ROM sr%d at scsi%d, id %d, lun %d\n",
@@ -791,17 +863,17 @@ static int sr_detect(Scsi_Device * SDp){
return 1;
}
-static void sr_attach(Scsi_Device * SDp){
+static int sr_attach(Scsi_Device * SDp){
Scsi_CD * cpnt;
int i;
- /* We do not support attaching loadable devices yet. */
-
- if(scsi_loadable_module_flag) return;
- if(SDp->type != TYPE_ROM && SDp->type != TYPE_WORM) return;
+ if(SDp->type != TYPE_ROM && SDp->type != TYPE_WORM) return 1;
if (sr_template.nr_dev >= sr_template.dev_max)
- panic ("scsi_devices corrupt (sr)");
+ {
+ SDp->attached--;
+ return 1;
+ }
for(cpnt = scsi_CDs, i=0; i<sr_template.dev_max; i++, cpnt++)
if(!cpnt->device) break;
@@ -813,6 +885,7 @@ static void sr_attach(Scsi_Device * SDp){
sr_template.nr_dev++;
if(sr_template.nr_dev > sr_template.dev_max)
panic ("scsi_devices corrupt (sr)");
+ return 0;
}
@@ -853,7 +926,7 @@ static void get_sectorsize(int i){
MAX_RETRIES);
if (current == task[0])
- while(SCpnt->request.dev != 0xfffe);
+ while(SCpnt->request.dev != 0xfffe) barrier();
else
if (SCpnt->request.dev != 0xfffe){
struct semaphore sem = MUTEX_LOCKED;
@@ -892,6 +965,7 @@ static void get_sectorsize(int i){
if(scsi_CDs[i].sector_size == 2048)
scsi_CDs[i].capacity *= 4;
scsi_CDs[i].needs_sector_size = 0;
+ sr_sizes[i] = scsi_CDs[i].capacity;
};
scsi_free(buffer, 512);
}
@@ -908,20 +982,20 @@ static void sr_init()
printk("Unable to get major %d for SCSI-CD\n",MAJOR_NR);
return;
}
+ sr_registered++;
}
- /* We do not support attaching loadable devices yet. */
- if(scsi_loadable_module_flag) return;
-
- sr_template.dev_max = sr_template.dev_noticed;
- scsi_CDs = (Scsi_CD *) scsi_init_malloc(sr_template.dev_max * sizeof(Scsi_CD));
+
+ if (scsi_CDs) return;
+ sr_template.dev_max = sr_template.dev_noticed + SR_EXTRA_DEVS;
+ scsi_CDs = (Scsi_CD *) scsi_init_malloc(sr_template.dev_max * sizeof(Scsi_CD), GFP_ATOMIC);
memset(scsi_CDs, 0, sr_template.dev_max * sizeof(Scsi_CD));
- sr_sizes = (int *) scsi_init_malloc(sr_template.dev_max * sizeof(int));
+ sr_sizes = (int *) scsi_init_malloc(sr_template.dev_max * sizeof(int), GFP_ATOMIC);
memset(sr_sizes, 0, sr_template.dev_max * sizeof(int));
sr_blocksizes = (int *) scsi_init_malloc(sr_template.dev_max *
- sizeof(int));
+ sizeof(int), GFP_ATOMIC);
for(i=0;i<sr_template.dev_max;i++) sr_blocksizes[i] = 2048;
blksize_size[MAJOR_NR] = sr_blocksizes;
@@ -931,26 +1005,84 @@ void sr_finish()
{
int i;
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ blk_size[MAJOR_NR] = sr_sizes;
+
for (i = 0; i < sr_template.nr_dev; ++i)
{
+ /* If we have already seen this, then skip it. Comes up
+ with loadable modules. */
+ if (scsi_CDs[i].capacity) continue;
+ scsi_CDs[i].capacity = 0x1fffff;
+ scsi_CDs[i].sector_size = 2048; /* A guess, just in case */
+ scsi_CDs[i].needs_sector_size = 1;
+#if 0
+ /* seems better to leave this for later */
get_sectorsize(i);
- printk("Scd sectorsize = %d bytes\n", scsi_CDs[i].sector_size);
+ printk("Scd sectorsize = %d bytes.\n", scsi_CDs[i].sector_size);
+#endif
scsi_CDs[i].use = 1;
scsi_CDs[i].ten = 1;
scsi_CDs[i].remap = 1;
sr_sizes[i] = scsi_CDs[i].capacity;
}
- blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
- blk_size[MAJOR_NR] = sr_sizes;
/* If our host adapter is capable of scatter-gather, then we increase
the read-ahead to 16 blocks (32 sectors). If not, we use
a two block (4 sector) read ahead. */
- if(scsi_CDs[0].device->host->sg_tablesize)
+ if(scsi_CDs[0].device && scsi_CDs[0].device->host->sg_tablesize)
read_ahead[MAJOR_NR] = 32; /* 32 sector read-ahead. Always removable. */
else
read_ahead[MAJOR_NR] = 4; /* 4 sector read-ahead */
return;
}
+
+static void sr_detach(Scsi_Device * SDp)
+{
+ Scsi_CD * cpnt;
+ int i, major;
+
+ major = MAJOR_NR << 8;
+
+ for(cpnt = scsi_CDs, i=0; i<sr_template.dev_max; i++, cpnt++)
+ if(cpnt->device == SDp) {
+ /*
+ * Since the cdrom is read-only, no need to sync the device.
+ * We should be kind to our buffer cache, however.
+ */
+ invalidate_inodes(major | i);
+ invalidate_buffers(major | i);
+
+ /*
+ * Reset things back to a sane state so that one can re-load a new
+ * driver (perhaps the same one).
+ */
+ cpnt->device = NULL;
+ cpnt->capacity = 0;
+ SDp->attached--;
+ sr_template.nr_dev--;
+ sr_template.dev_noticed--;
+ sr_sizes[i] = 0;
+ return;
+ }
+ return;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/drivers/scsi/sr.h b/drivers/scsi/sr.h
index e549551ba..ef7d88328 100644
--- a/drivers/scsi/sr.h
+++ b/drivers/scsi/sr.h
@@ -25,6 +25,7 @@ typedef struct
unsigned sector_size; /* size in bytes */
Scsi_Device *device;
unsigned long mpcd_sector; /* for reading multisession-CD's */
+ char xa_flags; /* some flags for handling XA-CD's */
unsigned char sector_bit_size; /* sector size = 2^sector_bit_size */
unsigned char sector_bit_shift; /* sectors/FS block = 2^sector_bit_shift*/
unsigned needs_sector_size:1; /* needs to get sector size */
diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c
index ab779c5df..a5839a2b1 100644
--- a/drivers/scsi/sr_ioctl.c
+++ b/drivers/scsi/sr_ioctl.c
@@ -1,5 +1,6 @@
#include <linux/kernel.h>
#include <linux/sched.h>
+#include <linux/mm.h>
#include <linux/fs.h>
#include <asm/segment.h>
#include <linux/errno.h>
@@ -14,7 +15,8 @@
#define IOCTL_RETRIES 3
/* The CDROM is fairly slow, so we need a little extra time */
-#define IOCTL_TIMEOUT 200
+/* In fact, it is very slow if it has to spin up first */
+#define IOCTL_TIMEOUT 3000
extern int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
@@ -384,6 +386,38 @@ int sr_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigne
return -EINVAL;
case CDROMREADMODE1:
return -EINVAL;
+
+ /* block-copy from ../block/sbpcd.c with some adjustments... */
+ case CDROMMULTISESSION: /* tell start-of-last-session to user */
+ {
+ struct cdrom_multisession ms_info;
+ long lba;
+
+ err = verify_area(VERIFY_READ, (void *) arg,
+ sizeof(struct cdrom_multisession));
+ if (err) return (err);
+
+ memcpy_fromfs(&ms_info, (void *) arg, sizeof(struct cdrom_multisession));
+
+ if (ms_info.addr_format==CDROM_MSF) { /* MSF-bin requested */
+ lba = scsi_CDs[target].mpcd_sector+CD_BLOCK_OFFSET;
+ ms_info.addr.msf.minute = lba / (CD_SECS*CD_FRAMES);
+ lba %= CD_SECS*CD_FRAMES;
+ ms_info.addr.msf.second = lba / CD_FRAMES;
+ ms_info.addr.msf.frame = lba % CD_FRAMES;
+ } else if (ms_info.addr_format==CDROM_LBA) /* lba requested */
+ ms_info.addr.lba=scsi_CDs[target].mpcd_sector;
+ else return (-EINVAL);
+
+ ms_info.xa_flag=scsi_CDs[target].xa_flags & 0x01;
+
+ err=verify_area(VERIFY_WRITE,(void *) arg,
+ sizeof(struct cdrom_multisession));
+ if (err) return (err);
+
+ memcpy_tofs((void *) arg, &ms_info, sizeof(struct cdrom_multisession));
+ return (0);
+ }
case BLKRASET:
if(!suser()) return -EACCES;
@@ -396,3 +430,20 @@ int sr_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigne
return scsi_ioctl(scsi_CDs[target].device,cmd,(void *) arg);
}
}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index ea78d19c6..384fb6462 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -5,18 +5,19 @@
History:
Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara.
Contribution and ideas from several people including (in alphabetical
- order) Klaus Ehrenfried, Wolfgang Denk, J"org Weule, and
- Eric Youngdale.
+ order) Klaus Ehrenfried, Steve Hirsch, Wolfgang Denk, Andreas Koppenh"ofer,
+ J"org Weule, and Eric Youngdale.
- Copyright 1992, 1993, 1994 Kai Makisara
- email makisara@vtinsx.ins.vtt.fi or Kai.Makisara@vtt.fi
+ Copyright 1992, 1993, 1994, 1995 Kai Makisara
+ email Kai.Makisara@metla.fi
- Last modified: Tue Oct 25 19:37:33 1994 by root@kai.home
+ Last modified: Sat Feb 18 10:51:25 1995 by root@kai.home
*/
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/sched.h>
+#include <linux/mm.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/mtio.h>
@@ -43,6 +44,8 @@
#define ST_TWO_FM 0
+#define ST_FAST_MTEOM 0
+
#define ST_BUFFER_WRITES 1
#define ST_ASYNC_WRITES 1
@@ -51,7 +54,7 @@
#define ST_BLOCK_SIZE 1024
-#define ST_MAX_BUFFERS 2
+#define ST_MAX_BUFFERS (2 + ST_EXTRA_DEVS)
#define ST_BUFFER_BLOCKS 32
@@ -71,10 +74,11 @@ static int debugging = 1;
#endif
#define MAX_RETRIES 0
+#define MAX_WRITE_RETRIES 0
#define MAX_READY_RETRIES 5
#define NO_TAPE NOT_READY
-#define ST_TIMEOUT 27000
+#define ST_TIMEOUT 90000
#define ST_LONG_TIMEOUT 200000
static int st_nbr_buffers;
@@ -86,13 +90,14 @@ static int st_max_buffers = ST_MAX_BUFFERS;
static Scsi_Tape * scsi_tapes;
static void st_init(void);
-static void st_attach(Scsi_Device *);
+static int st_attach(Scsi_Device *);
static int st_detect(Scsi_Device *);
+static void st_detach(Scsi_Device *);
struct Scsi_Device_Template st_template = {NULL, "tape", "st", TYPE_TAPE,
SCSI_TAPE_MAJOR, 0, 0, 0, 0,
st_detect, st_init,
- NULL, st_attach, NULL};
+ NULL, st_attach, st_detach};
static int st_int_ioctl(struct inode * inode,struct file * file,
unsigned int cmd_in, unsigned long arg);
@@ -106,36 +111,48 @@ st_chk_result(Scsi_Cmnd * SCpnt)
{
int dev = SCpnt->request.dev;
int result = SCpnt->result;
- unsigned char * sense = SCpnt->sense_buffer;
+ unsigned char * sense = SCpnt->sense_buffer, scode;
char *stp;
- if (!result && SCpnt->sense_buffer[0] == 0)
+ if (!result /* && SCpnt->sense_buffer[0] == 0 */ )
return 0;
#ifdef DEBUG
if (debugging) {
printk("st%d: Error: %x, cmd: %x %x %x %x %x %x Len: %d\n", dev, result,
- SCpnt->cmnd[0], SCpnt->cmnd[1], SCpnt->cmnd[2],
- SCpnt->cmnd[3], SCpnt->cmnd[4], SCpnt->cmnd[5],
+ SCpnt->data_cmnd[0], SCpnt->data_cmnd[1], SCpnt->data_cmnd[2],
+ SCpnt->data_cmnd[3], SCpnt->data_cmnd[4], SCpnt->data_cmnd[5],
SCpnt->request_bufflen);
if (driver_byte(result) & DRIVER_SENSE)
print_sense("st", SCpnt);
}
#endif
-/* if ((sense[0] & 0x70) == 0x70 &&
- ((sense[2] & 0x80) ))
- return 0; */
+ scode = sense[2] & 0x0f;
+ if (!(driver_byte(result) & DRIVER_SENSE) ||
+ ((sense[0] & 0x70) == 0x70 &&
+ scode != NO_SENSE &&
+ scode != RECOVERED_ERROR &&
+ scode != UNIT_ATTENTION &&
+ scode != BLANK_CHECK &&
+ scode != VOLUME_OVERFLOW)) { /* Abnormal conditions for tape */
+ printk("st%d: Error %x. ", dev, result);
+ if (driver_byte(result) & DRIVER_SENSE)
+ print_sense("st", SCpnt);
+ else
+ printk("\n");
+ }
+
if ((sense[0] & 0x70) == 0x70 &&
- sense[2] == RECOVERED_ERROR
+ scode == RECOVERED_ERROR
#ifdef ST_RECOVERED_WRITE_FATAL
- && SCpnt->cmnd[0] != WRITE_6
- && SCpnt->cmnd[0] != WRITE_FILEMARKS
+ && SCpnt->data_cmnd[0] != WRITE_6
+ && SCpnt->data_cmnd[0] != WRITE_FILEMARKS
#endif
) {
scsi_tapes[dev].recover_count++;
scsi_tapes[dev].mt_status->mt_erreg += (1 << MT_ST_SOFTERR_SHIFT);
- if (SCpnt->cmnd[0] == READ_6)
+ if (SCpnt->data_cmnd[0] == READ_6)
stp = "read";
- else if (SCpnt->cmnd[0] == WRITE_6)
+ else if (SCpnt->data_cmnd[0] == WRITE_6)
stp = "write";
else
stp = "ioctl";
@@ -195,17 +212,19 @@ write_behind_check(int dev)
{
Scsi_Tape * STp;
ST_buffer * STbuffer;
+ unsigned long flags;
STp = &(scsi_tapes[dev]);
STbuffer = STp->buffer;
+ save_flags(flags);
cli();
if (STbuffer->last_result < 0) {
STbuffer->writing = (- STbuffer->writing);
sleep_on( &(STp->waiting) );
STbuffer->writing = (- STbuffer->writing);
}
- sti();
+ restore_flags(flags);
if (STbuffer->writing < STbuffer->buffer_bytes)
memcpy(STbuffer->b_data,
@@ -232,6 +251,7 @@ back_over_eof(int dev)
Scsi_Cmnd *SCpnt;
Scsi_Tape *STp = &(scsi_tapes[dev]);
unsigned char cmd[10];
+ unsigned int flags;
cmd[0] = SPACE;
cmd[1] = 0x01; /* Space FileMarks */
@@ -245,11 +265,17 @@ back_over_eof(int dev)
(void *) cmd, (void *) (STp->buffer)->b_data, 0,
st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
+ /* need to do the check with interrupts off. -RAB */
+ save_flags(flags);
+ cli();
if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ restore_flags(flags);
+
SCpnt->request.dev = -1;
if ((STp->buffer)->last_result != 0) {
printk("st%d: Backing over filemark failed.\n", dev);
- (STp->mt_status)->mt_fileno += 1;
+ if ((STp->mt_status)->mt_fileno >= 0)
+ (STp->mt_status)->mt_fileno += 1;
(STp->mt_status)->mt_blkno = 0;
}
@@ -263,6 +289,7 @@ flush_write_buffer(int dev)
{
int offset, transfer, blks;
int result;
+ unsigned int flags;
unsigned char cmd[10];
Scsi_Cmnd *SCpnt;
Scsi_Tape *STp = &(scsi_tapes[dev]);
@@ -305,10 +332,14 @@ flush_write_buffer(int dev)
SCpnt->request.dev = dev;
scsi_do_cmd (SCpnt,
(void *) cmd, (STp->buffer)->b_data, transfer,
- st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
+ st_sleep_done, ST_TIMEOUT, MAX_WRITE_RETRIES);
+ /* this must be done with interrupts off */
+ save_flags (flags);
+ cli();
if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
-
+ restore_flags(flags);
+
if ((STp->buffer)->last_result_fatal != 0) {
printk("st%d: Error on flush.\n", dev);
if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
@@ -348,6 +379,9 @@ flush_buffer(struct inode * inode, struct file * filp, int seek_next)
STp = &(scsi_tapes[dev]);
STbuffer = STp->buffer;
+ if (STp->ready != ST_READY)
+ return 0;
+
if (STp->rw == ST_WRITING) /* Writing */
return flush_write_buffer(dev);
@@ -383,6 +417,7 @@ scsi_tape_open(struct inode * inode, struct file * filp)
{
int dev;
unsigned short flags;
+ unsigned int processor_flags;
int i;
unsigned char cmd[10];
Scsi_Cmnd * SCpnt;
@@ -415,6 +450,7 @@ scsi_tape_open(struct inode * inode, struct file * filp)
STp->dirty = 0;
STp->rw = ST_IDLE;
+ STp->ready = ST_READY;
if (STp->eof != ST_EOD) /* Save EOD across opens */
STp->eof = ST_NOEOF;
STp->eof_hit = 0;
@@ -435,7 +471,12 @@ scsi_tape_open(struct inode * inode, struct file * filp)
0, st_sleep_done, ST_LONG_TIMEOUT,
MAX_READY_RETRIES);
+
+ /* this must be done with interrupts off */
+ save_flags (processor_flags);
+ cli();
if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ restore_flags(processor_flags);
if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
(SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */
@@ -449,7 +490,13 @@ scsi_tape_open(struct inode * inode, struct file * filp)
0, st_sleep_done, ST_LONG_TIMEOUT,
MAX_READY_RETRIES);
- if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+
+ /* this must be done with interrupts off */
+ save_flags (processor_flags);
+ cli();
+ if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ restore_flags(processor_flags);
+
(STp->mt_status)->mt_fileno = STp->drv_block = 0;
STp->eof = ST_NOEOF;
}
@@ -459,14 +506,22 @@ scsi_tape_open(struct inode * inode, struct file * filp)
(SCpnt->sense_buffer[2] & 0x0f) == NO_TAPE) {
(STp->mt_status)->mt_fileno = STp->drv_block = 0 ;
printk("st%d: No tape.\n", dev);
+ STp->ready = ST_NO_TAPE;
} else {
- printk("st%d: Error %x.\n", dev, SCpnt->result);
(STp->mt_status)->mt_fileno = STp->drv_block = (-1);
+ STp->ready = ST_NOT_READY;
}
- (STp->buffer)->in_use = 0;
- STp->in_use = 0;
SCpnt->request.dev = -1; /* Mark as not busy */
- return (-EIO);
+ (STp->buffer)->in_use = 0;
+ STp->buffer = NULL;
+ STp->density = 0; /* Clear the erroneous "residue" */
+ STp->write_prot = 0;
+ STp->block_size = 0;
+ STp->eof = ST_NOEOF;
+ (STp->mt_status)->mt_fileno = STp->drv_block = 0;
+ if (scsi_tapes[dev].device->host->hostt->usage_count)
+ (*scsi_tapes[dev].device->host->hostt->usage_count)++;
+ return 0;
}
SCpnt->sense_buffer[0]=0;
@@ -477,7 +532,12 @@ scsi_tape_open(struct inode * inode, struct file * filp)
(void *) cmd, (void *) (STp->buffer)->b_data,
6, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES);
+
+ /* this must be done with interrupts off */
+ save_flags (processor_flags);
+ cli();
if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ restore_flags(processor_flags);
if (!SCpnt->result && !SCpnt->sense_buffer[0]) {
STp->max_block = ((STp->buffer)->b_data[1] << 16) |
@@ -507,7 +567,12 @@ scsi_tape_open(struct inode * inode, struct file * filp)
(void *) cmd, (void *) (STp->buffer)->b_data,
12, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES);
+
+ /* this must be done with interrupts off */
+ save_flags (processor_flags);
+ cli();
if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ restore_flags(processor_flags);
if ((STp->buffer)->last_result_fatal != 0) {
#ifdef DEBUG
@@ -533,10 +598,10 @@ scsi_tape_open(struct inode * inode, struct file * filp)
(STp->buffer)->b_data[10] * 256 + (STp->buffer)->b_data[11];
#ifdef DEBUG
if (debugging)
- printk("st%d: Density %x, tape length: %x, blocksize: %d, drv buffer: %d\n",
+ printk("st%d: Density %x, tape length: %x, drv buffer: %d\n",
dev, STp->density, (STp->buffer)->b_data[5] * 65536 +
(STp->buffer)->b_data[6] * 256 + (STp->buffer)->b_data[7],
- STp->block_size, STp->drv_buffer);
+ STp->drv_buffer);
#endif
if (STp->block_size > st_buffer_size) {
printk("st%d: Blocksize %d too large for buffer.\n", dev,
@@ -575,8 +640,17 @@ scsi_tape_open(struct inode * inode, struct file * filp)
if (debugging)
printk( "st%d: Write protected\n", dev);
#endif
+ if ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR) {
+ (STp->buffer)->in_use = 0;
+ STp->buffer = 0;
+ STp->in_use = 0;
+ return (-EROFS);
+ }
}
+ if (scsi_tapes[dev].device->host->hostt->usage_count)
+ (*scsi_tapes[dev].device->host->hostt->usage_count)++;
+
return 0;
}
@@ -591,6 +665,7 @@ scsi_tape_close(struct inode * inode, struct file * filp)
static unsigned char cmd[10];
Scsi_Cmnd * SCpnt;
Scsi_Tape * STp;
+ unsigned int flags;
dev = MINOR(inode->i_rdev);
rewind = (dev & 0x80) == 0;
@@ -616,9 +691,15 @@ scsi_tape_close(struct inode * inode, struct file * filp)
SCpnt->request.dev = dev;
scsi_do_cmd( SCpnt,
(void *) cmd, (void *) (STp->buffer)->b_data,
- 0, st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
+ 0, st_sleep_done, ST_TIMEOUT, MAX_WRITE_RETRIES);
+
+ /* this must be done with interrupts off */
+ save_flags (flags);
+ cli();
if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ restore_flags(flags);
+
if ((STp->buffer)->last_result_fatal != 0) {
SCpnt->request.dev = -1; /* Mark as not busy */
@@ -626,7 +707,8 @@ scsi_tape_close(struct inode * inode, struct file * filp)
}
else {
SCpnt->request.dev = -1; /* Mark as not busy */
- (STp->mt_status)->mt_fileno++ ;
+ if ((STp->mt_status)->mt_fileno >= 0)
+ (STp->mt_status)->mt_fileno++ ;
STp->drv_block = 0;
if (STp->two_fm)
back_over_eof(dev);
@@ -651,9 +733,13 @@ scsi_tape_close(struct inode * inode, struct file * filp)
if (rewind)
st_int_ioctl(inode, filp, MTREW, 1);
- (STp->buffer)->in_use = 0;
+ if (STp->buffer != NULL)
+ (STp->buffer)->in_use = 0;
STp->in_use = 0;
+ if (scsi_tapes[dev].device->host->hostt->usage_count)
+ (*scsi_tapes[dev].device->host->hostt->usage_count)--;
+
return;
}
@@ -665,13 +751,17 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
int dev;
int total, do_count, blks, retval, transfer;
int write_threshold;
+ int doing_write = 0;
static unsigned char cmd[10];
char *b_point;
Scsi_Cmnd * SCpnt;
Scsi_Tape * STp;
+ unsigned int flags;
dev = MINOR(inode->i_rdev) & 127;
STp = &(scsi_tapes[dev]);
+ if (STp->ready != ST_READY)
+ return (-EIO);
#ifdef DEBUG
if (!STp->in_use) {
printk("st%d: Incorrect device.\n", dev);
@@ -743,6 +833,7 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
(STp->block_size != 0 &&
(STp->buffer)->buffer_bytes + count > write_threshold))
{
+ doing_write = 1;
if (STp->block_size == 0)
do_count = count;
else {
@@ -767,9 +858,14 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
SCpnt->request.dev = dev;
scsi_do_cmd (SCpnt,
(void *) cmd, (STp->buffer)->b_data, transfer,
- st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
+ st_sleep_done, ST_TIMEOUT, MAX_WRITE_RETRIES);
+
- if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ /* this must be done with interrupts off */
+ save_flags (flags);
+ cli();
+ if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ restore_flags(flags);
if ((STp->buffer)->last_result_fatal != 0) {
#ifdef DEBUG
@@ -850,7 +946,7 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
count = 0;
}
- if ((STp->buffer)->last_result_fatal != 0) {
+ if (doing_write && (STp->buffer)->last_result_fatal != 0) {
SCpnt->request.dev = -1;
return (STp->buffer)->last_result_fatal;
}
@@ -864,7 +960,8 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
else
(STp->buffer)->writing = ((STp->buffer)->buffer_bytes /
STp->block_size) * STp->block_size;
- STp->dirty = 0;
+ STp->dirty = !((STp->buffer)->writing ==
+ (STp->buffer)->buffer_bytes);
if (STp->block_size == 0)
blks = (STp->buffer)->writing;
@@ -879,11 +976,12 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
scsi_do_cmd (SCpnt,
(void *) cmd, (STp->buffer)->b_data,
(STp->buffer)->writing,
- st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
+ st_sleep_done, ST_TIMEOUT, MAX_WRITE_RETRIES);
}
else
SCpnt->request.dev = -1; /* Mark as not busy */
+ STp->at_sm &= (total != 0);
return( total);
}
@@ -898,9 +996,12 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
static unsigned char cmd[10];
Scsi_Cmnd * SCpnt;
Scsi_Tape * STp;
+ unsigned int flags;
dev = MINOR(inode->i_rdev) & 127;
STp = &(scsi_tapes[dev]);
+ if (STp->ready != ST_READY)
+ return (-EIO);
#ifdef DEBUG
if (!STp->in_use) {
printk("st%d: Incorrect device.\n", dev);
@@ -930,8 +1031,8 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
(STp->buffer)->buffer_bytes);
#endif
if (((STp->buffer)->buffer_bytes == 0) &&
- STp->eof == ST_EOM_OK) /* EOM or Blank Check */
- return (-EIO);
+ (STp->eof == ST_EOM_OK || STp->eof == ST_EOD))
+ return (-EIO); /* EOM or Blank Check */
STp->rw = ST_READING;
@@ -968,13 +1069,18 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
SCpnt->request.dev = dev;
scsi_do_cmd (SCpnt,
(void *) cmd, (STp->buffer)->b_data,
- (STp->buffer)->buffer_size,
- st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
+ bytes, st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
+
+ /* this must be done with interrupts off */
+ save_flags (flags);
+ cli();
if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ restore_flags(flags);
(STp->buffer)->read_pointer = 0;
STp->eof_hit = 0;
+ STp->at_sm = 0;
if ((STp->buffer)->last_result_fatal) {
#ifdef DEBUG
@@ -1105,7 +1211,8 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
STp->drv_block = 0;
if (STp->moves_after_eof > 1)
STp->moves_after_eof = 0;
- (STp->mt_status)->mt_fileno++;
+ if ((STp->mt_status)->mt_fileno >= 0)
+ (STp->mt_status)->mt_fileno++;
}
if (total == 0 && STp->eof == ST_EOM_OK)
return (-EIO); /* ST_EOM_ERROR not used in read */
@@ -1138,14 +1245,15 @@ st_set_options(struct inode * inode, long options)
STp->do_async_writes = (options & MT_ST_ASYNC_WRITES) != 0;
STp->do_read_ahead = (options & MT_ST_READ_AHEAD) != 0;
STp->two_fm = (options & MT_ST_TWO_FM) != 0;
+ STp->fast_mteom = (options & MT_ST_FAST_MTEOM) != 0;
#ifdef DEBUG
debugging = (options & MT_ST_DEBUGGING) != 0;
printk(
"st%d: options: buffer writes: %d, async writes: %d, read ahead: %d\n",
dev, STp->do_buffer_writes, STp->do_async_writes,
STp->do_read_ahead);
- printk(" two FMs: %d, debugging: %d\n", STp->two_fm,
- debugging);
+ printk(" two FMs: %d, fast mteom: %d debugging: %d\n",
+ STp->two_fm, STp->fast_mteom, debugging);
#endif
}
else if ((options & MT_ST_OPTIONS) == MT_ST_WRITE_THRESHOLD) {
@@ -1180,12 +1288,16 @@ st_int_ioctl(struct inode * inode,struct file * file,
unsigned char cmd[10];
Scsi_Cmnd * SCpnt;
Scsi_Tape * STp;
- int fileno, blkno, undone, datalen;
+ int fileno, blkno, at_sm, undone, datalen;
+ unsigned int flags;
dev = dev & 127;
STp = &(scsi_tapes[dev]);
+ if (STp->ready != ST_READY)
+ return (-EIO);
fileno = (STp->mt_status)->mt_fileno ;
blkno = STp->drv_block;
+ at_sm = STp->at_sm;
memset(cmd, 0, 10);
datalen = 0;
@@ -1202,8 +1314,10 @@ st_int_ioctl(struct inode * inode,struct file * file,
printk("st%d: Spacing tape forward over %d filemarks.\n", dev,
cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
#endif
- fileno += arg;
+ if (fileno >= 0)
+ fileno += arg;
blkno = 0;
+ at_sm &= (arg != 0);
break;
case MTBSF:
case MTBSFM:
@@ -1221,10 +1335,12 @@ st_int_ioctl(struct inode * inode,struct file * file,
printk("st%d: Spacing tape backward over %ld filemarks.\n", dev, (-ltmp));
}
#endif
- fileno -= arg;
+ if (fileno >= 0)
+ fileno -= arg;
blkno = (-1); /* We can't know the block number */
+ at_sm &= (arg != 0);
break;
- case MTFSR:
+ case MTFSR:
cmd[0] = SPACE;
cmd[1] = 0x00; /* Space Blocks */
cmd[2] = (arg >> 16);
@@ -1237,6 +1353,7 @@ st_int_ioctl(struct inode * inode,struct file * file,
#endif
if (blkno >= 0)
blkno += arg;
+ at_sm &= (arg != 0);
break;
case MTBSR:
cmd[0] = SPACE;
@@ -1255,8 +1372,9 @@ st_int_ioctl(struct inode * inode,struct file * file,
#endif
if (blkno >= 0)
blkno -= arg;
+ at_sm &= (arg != 0);
break;
- case MTFSS:
+ case MTFSS:
cmd[0] = SPACE;
cmd[1] = 0x04; /* Space Setmarks */
cmd[2] = (arg >> 16);
@@ -1267,8 +1385,10 @@ st_int_ioctl(struct inode * inode,struct file * file,
printk("st%d: Spacing tape forward %d setmarks.\n", dev,
cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
#endif
- if (arg != 0)
+ if (arg != 0) {
blkno = fileno = (-1);
+ at_sm = 1;
+ }
break;
case MTBSS:
cmd[0] = SPACE;
@@ -1285,8 +1405,10 @@ st_int_ioctl(struct inode * inode,struct file * file,
printk("st%d: Spacing tape backward %ld setmarks.\n", dev, (-ltmp));
}
#endif
- if (arg != 0)
+ if (arg != 0) {
blkno = fileno = (-1);
+ at_sm = 1;
+ }
break;
case MTWEOF:
case MTWSM:
@@ -1309,8 +1431,10 @@ st_int_ioctl(struct inode * inode,struct file * file,
cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
}
#endif
- fileno += arg;
+ if (fileno >= 0)
+ fileno += arg;
blkno = 0;
+ at_sm = (cmd_in == MTWSM);
break;
case MTREW:
cmd[0] = REZERO_UNIT;
@@ -1322,7 +1446,7 @@ st_int_ioctl(struct inode * inode,struct file * file,
if (debugging)
printk("st%d: Rewinding tape.\n", dev);
#endif
- fileno = blkno = 0 ;
+ fileno = blkno = at_sm = 0 ;
break;
case MTOFFL:
cmd[0] = START_STOP;
@@ -1334,7 +1458,7 @@ st_int_ioctl(struct inode * inode,struct file * file,
if (debugging)
printk("st%d: Unloading tape.\n", dev);
#endif
- fileno = blkno = 0 ;
+ fileno = blkno = at_sm = 0 ;
break;
case MTNOP:
#ifdef DEBUG
@@ -1354,34 +1478,47 @@ st_int_ioctl(struct inode * inode,struct file * file,
if (debugging)
printk("st%d: Retensioning tape.\n", dev);
#endif
- fileno = blkno = 0 ;
+ fileno = blkno = at_sm = 0;
break;
case MTEOM:
- /* space to the end of tape */
- ioctl_result = st_int_ioctl(inode, file, MTFSF, 0x3fff);
- fileno = (STp->mt_status)->mt_fileno ;
- /* The next lines would hide the number of spaced FileMarks
- That's why I inserted the previous lines. I had no luck
- with detecting EOM with FSF, so we go now to EOM.
- Joerg Weule */
+ if (!STp->fast_mteom) {
+ /* space to the end of tape */
+ ioctl_result = st_int_ioctl(inode, file, MTFSF, 0x3fff);
+ fileno = (STp->mt_status)->mt_fileno ;
+ if (STp->eof == ST_EOD || STp->eof == ST_EOM_OK)
+ return 0;
+ /* The next lines would hide the number of spaced FileMarks
+ That's why I inserted the previous lines. I had no luck
+ with detecting EOM with FSF, so we go now to EOM.
+ Joerg Weule */
+ }
+ else
+ fileno = (-1);
cmd[0] = SPACE;
cmd[1] = 3;
#ifdef DEBUG
if (debugging)
printk("st%d: Spacing to end of recorded medium.\n", dev);
#endif
- blkno = (-1);
+ blkno = 0;
+ at_sm = 0;
break;
case MTERASE:
if (STp->write_prot)
return (-EACCES);
cmd[0] = ERASE;
cmd[1] = 1; /* To the end of tape */
+#ifdef ST_NOWAIT
+ cmd[1] |= 2; /* Don't wait for completion */
+ timeout = ST_TIMEOUT;
+#else
+ timeout = ST_LONG_TIMEOUT * 8;
+#endif
#ifdef DEBUG
if (debugging)
printk("st%d: Erasing tape.\n", dev);
#endif
- fileno = blkno = 0 ;
+ fileno = blkno = at_sm = 0 ;
break;
case MTSEEK:
if ((STp->device)->scsi_level < SCSI_2) {
@@ -1408,6 +1545,7 @@ st_int_ioctl(struct inode * inode,struct file * file,
printk("st%d: Seeking tape to block %ld.\n", dev, arg);
#endif
fileno = blkno = (-1);
+ at_sm = 0;
break;
case MTSETBLK: /* Set block length */
case MTSETDENSITY: /* Set tape density */
@@ -1471,23 +1609,32 @@ st_int_ioctl(struct inode * inode,struct file * file,
(void *) cmd, (void *) (STp->buffer)->b_data, datalen,
st_sleep_done, timeout, MAX_RETRIES);
- if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+
+ /* this must be done with interrupts off */
+ save_flags (flags);
+ cli();
+ if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ restore_flags(flags);
+
ioctl_result = (STp->buffer)->last_result_fatal;
SCpnt->request.dev = -1; /* Mark as not busy */
- if (!ioctl_result) {
+ if (cmd_in == MTFSF)
+ STp->moves_after_eof = 0;
+ else
+ STp->moves_after_eof = 1;
+ if (!ioctl_result) { /* SCSI command successful */
if (cmd_in != MTSEEK) {
STp->drv_block = blkno;
(STp->mt_status)->mt_fileno = fileno;
+ STp->at_sm = at_sm;
}
- else
+ else {
STp->drv_block = (STp->mt_status)->mt_fileno = (-1);
- if (cmd_in == MTFSF)
- STp->moves_after_eof = 0;
- else
- STp->moves_after_eof = 1;
+ STp->at_sm = 0;
+ }
if (cmd_in == MTBSFM)
ioctl_result = st_int_ioctl(inode, file, MTFSF, 1);
else if (cmd_in == MTFSFM)
@@ -1512,16 +1659,18 @@ st_int_ioctl(struct inode * inode,struct file * file,
else if (cmd_in == MTSETDENSITY)
STp->density = arg;
else if (cmd_in == MTEOM) {
- STp->eof = ST_EOM_OK;
+ STp->eof = ST_EOD;
STp->eof_hit = 0;
}
else if (cmd_in != MTSETBLK && cmd_in != MTNOP) {
STp->eof = ST_NOEOF;
STp->eof_hit = 0;
}
- } else {
+ } else { /* SCSI command was not completely successful */
if (SCpnt->sense_buffer[2] & 0x40) {
- STp->eof = ST_EOM_OK;
+ if (cmd_in != MTBSF && cmd_in != MTBSFM &&
+ cmd_in != MTBSR && cmd_in != MTBSS)
+ STp->eof = ST_EOM_OK;
STp->eof_hit = 0;
STp->drv_block = 0;
}
@@ -1530,21 +1679,44 @@ st_int_ioctl(struct inode * inode,struct file * file,
(SCpnt->sense_buffer[4] << 16) +
(SCpnt->sense_buffer[5] << 8) +
SCpnt->sense_buffer[6] );
- if ( (cmd_in == MTFSF) || (cmd_in == MTFSFM) )
- (STp->mt_status)->mt_fileno = fileno - undone ;
- else if ( (cmd_in == MTBSF) || (cmd_in == MTBSFM) )
+ if ( (cmd_in == MTFSF) || (cmd_in == MTFSFM) ) {
+ if (fileno >= 0)
+ (STp->mt_status)->mt_fileno = fileno - undone ;
+ else
+ (STp->mt_status)->mt_fileno = fileno;
+ STp->drv_block = 0;
+ }
+ else if ( (cmd_in == MTBSF) || (cmd_in == MTBSFM) ) {
(STp->mt_status)->mt_fileno = fileno + undone ;
+ STp->drv_block = 0;
+ }
else if (cmd_in == MTFSR) {
- if (blkno >= undone)
- STp->drv_block = blkno - undone;
- else
- STp->drv_block = (-1);
+ if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */
+ (STp->mt_status)->mt_fileno++;
+ STp->drv_block = 0;
+ }
+ else {
+ if (blkno >= undone)
+ STp->drv_block = blkno - undone;
+ else
+ STp->drv_block = (-1);
+ }
}
- else if (cmd_in == MTBSR && blkno >= 0) {
- if (blkno >= 0)
- STp->drv_block = blkno + undone;
- else
+ else if (cmd_in == MTBSR) {
+ if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */
+ (STp->mt_status)->mt_fileno--;
STp->drv_block = (-1);
+ }
+ else {
+ if (blkno >= 0)
+ STp->drv_block = blkno + undone;
+ else
+ STp->drv_block = (-1);
+ }
+ }
+ else if (cmd_in == MTEOM || cmd_in == MTSEEK) {
+ (STp->mt_status)->mt_fileno = (-1);
+ STp->drv_block = (-1);
}
if (STp->eof == ST_NOEOF &&
(SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK)
@@ -1568,6 +1740,7 @@ st_ioctl(struct inode * inode,struct file * file,
unsigned char scmd[10];
Scsi_Cmnd *SCpnt;
Scsi_Tape *STp;
+ unsigned int flags;
dev = dev & 127;
STp = &(scsi_tapes[dev]);
@@ -1590,11 +1763,15 @@ st_ioctl(struct inode * inode,struct file * file,
memcpy_fromfs((char *) &mtc, (char *)arg, sizeof(struct mtop));
- i = flush_buffer(inode, file, mtc.mt_op == MTNOP || mtc.mt_op == MTSEEK ||
+ i = flush_buffer(inode, file, mtc.mt_op == MTSEEK ||
mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM);
if (i < 0)
return i;
+ if (mtc.mt_op != MTNOP && mtc.mt_op != MTSETBLK &&
+ mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTWSM &&
+ mtc.mt_op != MTSETDRVBUFFER)
+ STp->rw = ST_IDLE; /* Prevent automatic WEOF */
if (mtc.mt_op == MTSETDRVBUFFER &&
(mtc.mt_count & MT_ST_OPTIONS) != 0)
@@ -1642,6 +1819,12 @@ st_ioctl(struct inode * inode,struct file * file,
(STp->mt_status)->mt_gstat |= GMT_D_1600(0xffffffff);
else if (STp->density == 3)
(STp->mt_status)->mt_gstat |= GMT_D_6250(0xffffffff);
+ if (STp->ready == ST_READY)
+ (STp->mt_status)->mt_gstat |= GMT_ONLINE(0xffffffff);
+ if (STp->ready == ST_NO_TAPE)
+ (STp->mt_status)->mt_gstat |= GMT_DR_OPEN(0xffffffff);
+ if (STp->at_sm)
+ (STp->mt_status)->mt_gstat |= GMT_SM(0xffffffff);
memcpy_tofs((char *)arg, (char *)(STp->mt_status),
sizeof(struct mtget));
@@ -1650,6 +1833,8 @@ st_ioctl(struct inode * inode,struct file * file,
return 0;
}
else if (cmd == (MTIOCPOS & IOCCMD_MASK)) {
+ if (STp->ready != ST_READY)
+ return (-EIO);
#ifdef DEBUG
if (debugging)
printk("st%d: get tape position.\n", dev);
@@ -1683,7 +1868,13 @@ st_ioctl(struct inode * inode,struct file * file,
(void *) scmd, (void *) (STp->buffer)->b_data,
20, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES);
- if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+
+ /* this must be done with interrupts off */
+ save_flags (flags);
+ cli();
+ if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+ restore_flags(flags);
+
if ((STp->buffer)->last_result_fatal != 0) {
mt_pos.mt_blkno = (-1);
@@ -1712,8 +1903,10 @@ st_ioctl(struct inode * inode,struct file * file,
memcpy_tofs((char *)arg, (char *) (&mt_pos), sizeof(struct mtpos));
return result;
}
- else
+ else if (STp->ready == ST_READY)
return scsi_ioctl(STp->device, cmd_in, (void *) arg);
+ else
+ return (-EIO);
}
@@ -1748,16 +1941,17 @@ static struct file_operations st_fops = {
NULL /* fsync */
};
-static void st_attach(Scsi_Device * SDp){
+static int st_attach(Scsi_Device * SDp){
Scsi_Tape * tpnt;
int i;
- /* We do not support attaching loadable devices yet. */
- if(scsi_loadable_module_flag) return;
- if(SDp->type != TYPE_TAPE) return;
+ if(SDp->type != TYPE_TAPE) return 1;
if(st_template.nr_dev >= st_template.dev_max)
- panic ("scsi_devices corrupt (st)");
+ {
+ SDp->attached--;
+ return 1;
+ }
for(tpnt = scsi_tapes, i=0; i<st_template.dev_max; i++, tpnt++)
if(!tpnt->device) break;
@@ -1765,13 +1959,17 @@ static void st_attach(Scsi_Device * SDp){
if(i >= st_template.dev_max) panic ("scsi_devices corrupt (st)");
scsi_tapes[i].device = SDp;
+ if (SDp->scsi_level <= 2)
+ scsi_tapes[i].mt_status->mt_type = MT_ISSCSI1;
+ else
+ scsi_tapes[i].mt_status->mt_type = MT_ISSCSI2;
+
st_template.nr_dev++;
+ return 0;
};
-static int st_detect(Scsi_Device * SDp){
-
- /* We do not support attaching loadable devices yet. */
- if(scsi_loadable_module_flag) return 0;
+static int st_detect(Scsi_Device * SDp)
+{
if(SDp->type != TYPE_TAPE) return 0;
printk("Detected scsi tape st%d at scsi%d, id %d, lun %d\n",
@@ -1786,7 +1984,6 @@ static void st_init()
{
int i;
Scsi_Tape * STp;
- Scsi_Device * SDp;
static int st_registered = 0;
if (st_template.dev_noticed == 0) return;
@@ -1799,19 +1996,18 @@ static void st_init()
st_registered++;
}
- /* We do not support attaching loadable devices yet. */
- if(scsi_loadable_module_flag) return;
-
- scsi_tapes = (Scsi_Tape *) scsi_init_malloc(st_template.dev_noticed *
- sizeof(Scsi_Tape));
- st_template.dev_max = st_template.dev_noticed;
+ if (scsi_tapes) return;
+ scsi_tapes = (Scsi_Tape *) scsi_init_malloc(
+ (st_template.dev_noticed + ST_EXTRA_DEVS) *
+ sizeof(Scsi_Tape), GFP_ATOMIC);
+ st_template.dev_max = st_template.dev_noticed + ST_EXTRA_DEVS;
#ifdef DEBUG
printk("st: Buffer size %d bytes, write threshold %d bytes.\n",
st_buffer_size, st_write_threshold);
#endif
- for (i=0, SDp = scsi_devices; i < st_template.dev_noticed; ++i) {
+ for (i=0; i < st_template.dev_max; ++i) {
STp = &(scsi_tapes[i]);
STp->device = NULL;
STp->capacity = 0xfffff;
@@ -1826,35 +2022,30 @@ static void st_init()
STp->do_async_writes = ST_ASYNC_WRITES;
STp->do_read_ahead = ST_READ_AHEAD;
STp->two_fm = ST_TWO_FM;
+ STp->fast_mteom = ST_FAST_MTEOM;
STp->write_threshold = st_write_threshold;
STp->drv_block = 0;
STp->moves_after_eof = 1;
- STp->mt_status = (struct mtget *) scsi_init_malloc(sizeof(struct mtget));
+ STp->at_sm = 0;
+ STp->mt_status = (struct mtget *) scsi_init_malloc(sizeof(struct mtget), GFP_ATOMIC);
/* Initialize status */
memset((void *) scsi_tapes[i].mt_status, 0, sizeof(struct mtget));
- for (; SDp; SDp = SDp->next)
- if (SDp->type == TYPE_TAPE)
- break;
- if (!SDp)
- printk("st%d: ERROR: Not found in scsi chain.\n", i);
- else {
- if (SDp->scsi_level <= 2)
- STp->mt_status->mt_type = MT_ISSCSI1;
- else
- STp->mt_status->mt_type = MT_ISSCSI2;
- }
- SDp = SDp->next;
}
/* Allocate the buffers */
- st_nbr_buffers = st_template.dev_noticed;
+ st_nbr_buffers = st_template.dev_noticed + ST_EXTRA_DEVS;
if (st_nbr_buffers > st_max_buffers)
st_nbr_buffers = st_max_buffers;
st_buffers = (ST_buffer **) scsi_init_malloc(st_nbr_buffers *
- sizeof(ST_buffer *));
+ sizeof(ST_buffer *), GFP_ATOMIC);
+ /* FIXME - if we are hitting this because we are loading a tape module
+ as a loadable driver, we should not use kmalloc - it will allocate
+ a 64Kb region in order to buffer about 32Kb. Try using 31 blocks
+ instead. */
+
for (i=0; i < st_nbr_buffers; i++) {
st_buffers[i] = (ST_buffer *) scsi_init_malloc(sizeof(ST_buffer) -
- 1 + st_buffer_size);
+ 1 + st_buffer_size, GFP_ATOMIC | GFP_DMA);
#ifdef DEBUG
/* printk("st: Buffer address: %p\n", st_buffers[i]); */
#endif
@@ -1863,3 +2054,19 @@ static void st_init()
}
return;
}
+
+static void st_detach(Scsi_Device * SDp)
+{
+ Scsi_Tape * tpnt;
+ int i;
+
+ for(tpnt = scsi_tapes, i=0; i<st_template.dev_max; i++, tpnt++)
+ if(tpnt->device == SDp) {
+ tpnt->device = NULL;
+ SDp->attached--;
+ st_template.nr_dev--;
+ st_template.dev_noticed--;
+ return;
+ }
+ return;
+}
diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h
index d2b66f9bc..e6632159e 100644
--- a/drivers/scsi/st.h
+++ b/drivers/scsi/st.h
@@ -27,6 +27,7 @@ typedef struct {
Scsi_Device* device;
unsigned char dirty;
unsigned char rw;
+ unsigned char ready;
unsigned char eof;
unsigned char write_prot;
unsigned char drv_write_prot;
@@ -37,6 +38,7 @@ typedef struct {
unsigned char do_async_writes;
unsigned char do_read_ahead;
unsigned char two_fm;
+ unsigned char fast_mteom;
unsigned char density;
ST_buffer * buffer;
int block_size;
@@ -46,6 +48,7 @@ typedef struct {
int recover_count;
int drv_block; /* The block where the drive head is */
unsigned char moves_after_eof;
+ unsigned char at_sm;
struct mtget * mt_status;
Scsi_Cmnd SCpnt;
} Scsi_Tape;
@@ -62,6 +65,11 @@ typedef struct {
#define ST_READING 1
#define ST_WRITING 2
+/* Values of ready state */
+#define ST_READY 0
+#define ST_NOT_READY 1
+#define ST_NO_TAPE 2
+
/* Positioning SCSI-commands for Tandberg, etc. drives */
#define QFA_REQUEST_BLOCK 0x02
#define QFA_SEEK_BLOCK 0x0c
diff --git a/drivers/scsi/t128.c b/drivers/scsi/t128.c
index 59a9643e1..eac110542 100644
--- a/drivers/scsi/t128.c
+++ b/drivers/scsi/t128.c
@@ -320,9 +320,9 @@ static inline int NCR5380_pread (struct Scsi_Host *instance, unsigned char *dst,
#if 0
for (; i; --i) {
- while (!(instance->base[T_STATUS_REG_OFFSET]) & T_ST_RDY);
+ while (!(instance->base[T_STATUS_REG_OFFSET]) & T_ST_RDY) barrier();
#else
- while (!(instance->base[T_STATUS_REG_OFFSET]) & T_ST_RDY);
+ while (!(instance->base[T_STATUS_REG_OFFSET]) & T_ST_RDY) barrier();
for (; i; --i) {
#endif
*d++ = *reg;
@@ -363,9 +363,9 @@ static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src
#if 0
for (; i; --i) {
- while (!(instance->base[T_STATUS_REG_OFFSET]) & T_ST_RDY);
+ while (!(instance->base[T_STATUS_REG_OFFSET]) & T_ST_RDY) barrier();
#else
- while (!(instance->base[T_STATUS_REG_OFFSET]) & T_ST_RDY);
+ while (!(instance->base[T_STATUS_REG_OFFSET]) & T_ST_RDY) barrier();
for (; i; --i) {
#endif
*reg = *s++;
diff --git a/drivers/scsi/u14-34f.c b/drivers/scsi/u14-34f.c
index 32cd24ab2..7ce244ab1 100644
--- a/drivers/scsi/u14-34f.c
+++ b/drivers/scsi/u14-34f.c
@@ -1,5 +1,42 @@
/*
- * u14-34f.c - Low-level SCSI driver for UltraStor 14F/34F
+ * u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters.
+ *
+ * 11 Mar 1995 rev. 2.00 for linux 1.2.0
+ * Fixed a bug which prevented media change detection for removable
+ * disk drives.
+ *
+ * 23 Feb 1995 rev. 1.18 for linux 1.1.94
+ * Added a check for scsi_register returning NULL.
+ *
+ * 11 Feb 1995 rev. 1.17 for linux 1.1.91
+ * U14F qualified to run with 32 sglists.
+ * Now DEBUG_RESET is disabled by default.
+ *
+ * 9 Feb 1995 rev. 1.16 for linux 1.1.90
+ * Use host->wish_block instead of host->block.
+ *
+ * 8 Feb 1995 rev. 1.15 for linux 1.1.89
+ * Cleared target_time_out counter while performing a reset.
+ *
+ * 28 Jan 1995 rev. 1.14 for linux 1.1.86
+ * Added module support.
+ * Log and do a retry when a disk drive returns a target status
+ * different from zero on a recovered error.
+ * Auto detects if U14F boards have an old firmware revision.
+ * Max number of scatter/gather lists set to 16 for all boards
+ * (most installation run fine using 33 sglists, while other
+ * has problems when using more then 16).
+ *
+ * 16 Jan 1995 rev. 1.13 for linux 1.1.81
+ * Display a message if check_region detects a port address
+ * already in use.
+ *
+ * 15 Dec 1994 rev. 1.12 for linux 1.1.74
+ * The host->block flag is set for all the detected ISA boards.
+ *
+ * 30 Nov 1994 rev. 1.11 for linux 1.1.68
+ * Redo i/o on target status CHECK_CONDITION for TYPE_DISK only.
+ * Added optional support for using a single board at a time.
*
* 14 Nov 1994 rev. 1.10 for linux 1.1.63
*
@@ -14,14 +51,13 @@
*
* Multiple U14F and/or U34F host adapters are supported.
*
- * Released by Dario Ballabio (Dario_Ballabio@milano.europe.dg.com)
+ * Copyright (C) 1994, 1995 Dario Ballabio (dario@milano.europe.dg.com)
*
- * WARNING: if your 14F board has old firmware revision (see below)
- * keep the following statement, otherwise comment it.
+ * WARNING: if your 14F board has an old firmware revision (see below)
+ * you must change "#undef" into "#define" in the following
+ * statement.
*/
-#if 0
-#define HAVE_OLD_U14F_FIRMWARE
-#endif
+#undef HAVE_OLD_U14F_FIRMWARE
/*
* The UltraStor 14F, 24F, and 34F are a family of intelligent, high
* performance SCSI-2 host adapters.
@@ -29,7 +65,7 @@
*
* 14F - ISA first-party DMA HA with floppy support and WD1003 emulation.
* 24F - EISA Bus Master HA with floppy support and WD1003 emulation.
- * 34F - VL-Bus Bus Master HA (no WD1003 emulation).
+ * 34F - VESA Local-Bus Bus Master HA (no WD1003 emulation).
*
* This code has been tested with up to two U14F boards, using both
* firmware 28004-005/38004-004 (BIOS rev. 2.00) and the latest firmware
@@ -44,13 +80,13 @@
*
* Here a sample configuration using two U14F boards:
*
- U14F0: PORT 0x330, BIOS 0xc8000, IRQ 11, DMA 5, SG 33, Mbox 16, CmdLun 2, C1.
- U14F1: PORT 0x340, BIOS 0x00000, IRQ 10, DMA 6, SG 33, Mbox 16, CmdLun 2, C1.
+ U14F0: PORT 0x330, BIOS 0xc8000, IRQ 11, DMA 5, SG 32, Mbox 16, CmdLun 2, C1.
+ U14F1: PORT 0x340, BIOS 0x00000, IRQ 10, DMA 6, SG 32, Mbox 16, CmdLun 2, C1.
*
* The boot controller must have its BIOS enabled, while other boards can
* have their BIOS disabled, or enabled to an higher address.
* Boards are named Ux4F0, Ux4F1..., according to the port address order in
- * the isa_io_port[] array.
+ * the io_port[] array.
*
* The following facts are based on real testing results (not on
* documentation) on the above U14F board.
@@ -86,11 +122,17 @@
* when DISABLE_CLUSTERING is in effect, but unscattered requests could be
* larger than 16Kbyte.
*
- * The new firmware has fixed all the above problems and has been tested
- * with up to 33 scatter/gather lists.
+ * The new firmware has fixed all the above problems.
*
+ * In order to support multiple ISA boards in a reliable way,
+ * the driver sets host->wish_block = TRUE for all ISA boards.
*/
+#if defined(MODULE)
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -105,13 +147,13 @@
#include <asm/irq.h>
#include "u14-34f.h"
-/* Values for the PRODUCT_ID ports for the 14F */
-#define U14F_PRODUCT_ID1 0x56
-#define U14F_PRODUCT_ID2 0x40 /* NOTE: Only upper nibble is used */
+/* Values for the PRODUCT_ID ports for the 14/34F */
+#define PRODUCT_ID1 0x56
+#define PRODUCT_ID2 0x40 /* NOTE: Only upper nibble is used */
/* Subversion values */
-#define U14F 0
-#define U34F 1
+#define ISA 0
+#define ESA 1
#define OP_HOST_ADAPTER 0x1
#define OP_SCSI 0x2
@@ -125,27 +167,17 @@
#define HA_CMD_READ_BUFF 0x3
#define HA_CMD_WRITE_BUFF 0x4
-#if defined (HAVE_OLD_U14F_FIRMWARE)
-#define U14F_MAX_SGLIST 16
-#define U14F_CLUSTERING DISABLE_CLUSTERING
-#else
-#define U14F_MAX_SGLIST 33
-#define U14F_CLUSTERING ENABLE_CLUSTERING
-#endif
-
-#define U34F_MAX_SGLIST 33
-#define U34F_CLUSTERING ENABLE_CLUSTERING
-
-#define DO_BUS_RESET /* Reset SCSI bus on error */
-#define NO_DEBUG_DETECT
-#define NO_DEBUG_INTERRUPT
-#define NO_DEBUG_STATISTICS
+#undef DEBUG_DETECT
+#undef DEBUG_INTERRUPT
+#undef DEBUG_STATISTICS
+#undef DEBUG_RESET
#define MAX_TARGET 8
#define MAX_IRQ 16
#define MAX_BOARDS 4
#define MAX_MAILBOXES 16
-#define MAX_SGLIST 33
+#define MAX_SGLIST 32
+#define MAX_SAFE_SGLIST 16
#define MAX_CMD_PER_LUN 2
#define FALSE 0
@@ -154,8 +186,10 @@
#define IN_USE 1
#define LOCKED 2
#define IN_RESET 3
+#define IGNORE 4
#define NO_IRQ 0xff
-#define MAXLOOP 20000
+#define NO_DMA 0xff
+#define MAXLOOP 200000
#define REG_LCL_MASK 0
#define REG_LCL_INTR 1
@@ -167,9 +201,9 @@
#define REG_CONFIG2 7
#define REG_OGM 8
#define REG_ICM 12
-#define REG_REGION 0x0c
+#define REGION_SIZE 13
#define BSY_ASSERTED 0x01
-#define INTR_ASSERTED 0x01
+#define IRQ_ASSERTED 0x01
#define CMD_RESET 0xc0
#define CMD_OGM_INTR 0x01
#define CMD_CLR_INTR 0x01
@@ -219,13 +253,13 @@ struct hostdata {
unsigned int multicount; /* Total ... in second ihdlr loop */
int board_number; /* Number of this board */
char board_name[16]; /* Name of this board */
+ char board_id[256]; /* data from INQUIRY on this board */
int in_reset; /* True if board is doing a reset */
int target_time_out[MAX_TARGET]; /* N. of timeout errors on target */
int target_reset[MAX_TARGET]; /* If TRUE redo operation on target */
- unsigned char bios_drive_number: 1;
+ unsigned char subversion; /* Bus type, either ISA or ESA */
unsigned char heads;
unsigned char sectors;
- unsigned char subversion: 4;
/* slot != 0 for the U24F, slot == 0 for both the U14F and U34F */
unsigned char slot;
@@ -238,7 +272,7 @@ static unsigned int irqlist[MAX_IRQ], calls[MAX_IRQ];
#define HD(board) ((struct hostdata *) &sh[board]->hostdata)
#define BN(board) (HD(board)->board_name)
-static void u14_34f_interrupt_handler(int);
+static void u14_34f_interrupt_handler(int, struct pt_regs *);
static int do_trace = FALSE;
static inline unchar wait_on_busy(ushort iobase) {
@@ -250,9 +284,53 @@ static inline unchar wait_on_busy(ushort iobase) {
return FALSE;
}
+static int board_inquiry(unsigned int j) {
+ struct mscp *cpp;
+ unsigned int time, limit = 0;
+
+ cpp = &HD(j)->cp[0];
+ memset(cpp, 0, sizeof(struct mscp));
+ cpp->opcode = OP_HOST_ADAPTER;
+ cpp->xdir = DTD_IN;
+ cpp->data_address = (unsigned int) HD(j)->board_id;
+ cpp->data_len = sizeof(HD(j)->board_id);
+ cpp->scsi_cdbs_len = 6;
+ cpp->scsi_cdbs[0] = HA_CMD_INQUIRY;
+
+ if (wait_on_busy(sh[j]->io_port)) {
+ printk("%s: board_inquiry, adapter busy.\n", BN(j));
+ return TRUE;
+ }
+
+ HD(j)->cp_stat[0] = IGNORE;
+
+ /* Clear the interrupt indication */
+ outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR);
+
+ /* Store pointer in OGM address bytes */
+ outl((unsigned int)cpp, sh[j]->io_port + REG_OGM);
+
+ /* Issue OGM interrupt */
+ outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR);
+
+ sti();
+ time = jiffies;
+ while (jiffies < (time + 100) && limit++ < 100000000);
+ cli();
+
+ if (cpp->adapter_status || HD(j)->cp_stat[0] != FREE) {
+ HD(j)->cp_stat[0] = FREE;
+ printk("%s: board_inquiry, err 0x%x.\n", BN(j), cpp->adapter_status);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static inline int port_detect(ushort *port_base, unsigned int j,
Scsi_Host_Template * tpnt) {
- unsigned char irq, dma_channel, in_byte, subversion, sys_mask, lcl_mask;
+ unsigned char irq, dma_channel, subversion;
+ unsigned char in_byte;
/* Allowed BIOS base addresses (NULL indicates reserved) */
void *bios_segment_table[8] = {
@@ -262,10 +340,10 @@ static inline int port_detect(ushort *port_base, unsigned int j,
};
/* Allowed IRQs */
- unsigned char interrupt_table_14f[4] = { 15, 14, 11, 10 };
+ unsigned char interrupt_table[4] = { 15, 14, 11, 10 };
- /* Allowed DMA channels for 14f (0 indicates reserved) */
- unsigned char dma_channel_table_14f[4] = { 5, 6, 7, 0 };
+ /* Allowed DMA channels for ISA (0 indicates reserved) */
+ unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
/* Head/sector mappings */
struct {
@@ -293,28 +371,33 @@ static inline int port_detect(ushort *port_base, unsigned int j,
sprintf(name, "%s%d", driver_name, j);
- if(check_region(*port_base, REG_REGION)) return FALSE;
+ if(check_region(*port_base, REGION_SIZE)) {
+ printk("%s: address 0x%03x in use, skipping probe.\n",
+ name, *port_base);
+ return FALSE;
+ }
- if (inb(*port_base + REG_PRODUCT_ID1) != U14F_PRODUCT_ID1) return FALSE;
+ if (inb(*port_base + REG_PRODUCT_ID1) != PRODUCT_ID1) return FALSE;
in_byte = inb(*port_base + REG_PRODUCT_ID2);
- if ((in_byte & 0xf0) != U14F_PRODUCT_ID2) return FALSE;
+ if ((in_byte & 0xf0) != PRODUCT_ID2) return FALSE;
*(char *)&config_1 = inb(*port_base + REG_CONFIG1);
*(char *)&config_2 = inb(*port_base + REG_CONFIG2);
- irq = interrupt_table_14f[config_1.interrupt];
- dma_channel = dma_channel_table_14f[config_1.dma_channel];
+ irq = interrupt_table[config_1.interrupt];
+ dma_channel = dma_channel_table[config_1.dma_channel];
subversion = (in_byte & 0x0f);
+ /* Board detected, allocate its IRQ if not already done */
if ((irq >= MAX_IRQ) || ((irqlist[irq] == NO_IRQ) && request_irq
(irq, u14_34f_interrupt_handler, SA_INTERRUPT, driver_name))) {
printk("%s: unable to allocate IRQ %u, detaching.\n", name, irq);
return FALSE;
}
- if (subversion == U14F && request_dma(dma_channel, driver_name)) {
+ if (subversion == ISA && request_dma(dma_channel, driver_name)) {
printk("%s: unable to allocate DMA channel %u, detaching.\n",
name, dma_channel);
free_irq(irq);
@@ -322,54 +405,64 @@ static inline int port_detect(ushort *port_base, unsigned int j,
}
sh[j] = scsi_register(tpnt, sizeof(struct hostdata));
+
+ if (sh[j] == NULL) {
+ printk("%s: unable to register host, detaching.\n", name);
+
+ if (irqlist[irq] == NO_IRQ) free_irq(irq);
+
+ if (subversion == ISA) free_dma(dma_channel);
+
+ return FALSE;
+ }
+
sh[j]->io_port = *port_base;
+ sh[j]->n_io_port = REGION_SIZE;
sh[j]->base = bios_segment_table[config_1.bios_segment];
sh[j]->irq = irq;
+ sh[j]->sg_tablesize = MAX_SGLIST;
sh[j]->this_id = config_2.ha_scsi_id;
sh[j]->can_queue = MAX_MAILBOXES;
- sh[j]->hostt->cmd_per_lun = MAX_CMD_PER_LUN;
- sys_mask = inb(sh[j]->io_port + REG_SYS_MASK);
- lcl_mask = inb(sh[j]->io_port + REG_LCL_MASK);
+ sh[j]->cmd_per_lun = MAX_CMD_PER_LUN;
#if defined(DEBUG_DETECT)
+ {
+ unsigned char sys_mask, lcl_mask;
+
+ sys_mask = inb(sh[j]->io_port + REG_SYS_MASK);
+ lcl_mask = inb(sh[j]->io_port + REG_LCL_MASK);
printk("SYS_MASK 0x%x, LCL_MASK 0x%x.\n", sys_mask, lcl_mask);
+ }
#endif
/* If BIOS is disabled, force enable interrupts */
if (sh[j]->base == 0) outb(CMD_ENA_INTR, sh[j]->io_port + REG_SYS_MASK);
-#if defined (DO_BUS_RESET)
- lcl_mask = 0xc2;
-#else
- lcl_mask = 0x82;
-#endif
-
- outb(lcl_mask, sh[j]->io_port + REG_LCL_MASK);
-
/* Register the I/O space that we use */
- snarf_region(sh[j]->io_port, REG_REGION);
+ request_region(sh[j]->io_port, REGION_SIZE, driver_name);
memset(HD(j), 0, sizeof(struct hostdata));
HD(j)->heads = mapping_table[config_2.mapping_mode].heads;
HD(j)->sectors = mapping_table[config_2.mapping_mode].sectors;
- HD(j)->bios_drive_number = config_2.bios_drive_number;
HD(j)->subversion = subversion;
HD(j)->board_number = j;
irqlist[irq] = j;
- if (HD(j)->subversion == U34F) {
- sh[j]->dma_channel = 0;
+ if (HD(j)->subversion == ESA) {
+ sh[j]->dma_channel = NO_DMA;
sh[j]->unchecked_isa_dma = FALSE;
- sh[j]->sg_tablesize = U34F_MAX_SGLIST;
- sh[j]->hostt->use_clustering = U34F_CLUSTERING;
sprintf(BN(j), "U34F%d", j);
}
else {
+ sh[j]->wish_block = TRUE;
+
+#if defined (HAVE_OLD_U14F_FIRMWARE)
+ sh[j]->hostt->use_clustering = DISABLE_CLUSTERING;
+ sh[j]->sg_tablesize = MAX_SAFE_SGLIST;
+#endif
+
sh[j]->dma_channel = dma_channel;
sh[j]->unchecked_isa_dma = TRUE;
- sh[j]->sg_tablesize = U14F_MAX_SGLIST;
- /*if (j > 0) sh[j]->sg_tablesize = 0;*/
- sh[j]->hostt->use_clustering = U14F_CLUSTERING;
sprintf(BN(j), "U14F%d", j);
disable_dma(dma_channel);
clear_dma_ff(dma_channel);
@@ -377,11 +470,23 @@ static inline int port_detect(ushort *port_base, unsigned int j,
enable_dma(dma_channel);
}
+ if (HD(j)->subversion == ISA && !board_inquiry(j)) {
+ HD(j)->board_id[40] = 0;
+
+ if (strcmp(&HD(j)->board_id[32], "06000600")) {
+ printk("%s: %s.\n", BN(j), &HD(j)->board_id[8]);
+ printk("%s: firmware %s is outdated, BIOS rev. should be 2.01.\n",
+ BN(j), &HD(j)->board_id[32]);
+ sh[j]->hostt->use_clustering = DISABLE_CLUSTERING;
+ sh[j]->sg_tablesize = MAX_SAFE_SGLIST;
+ }
+ }
+
printk("%s: PORT 0x%03x, BIOS 0x%05x, IRQ %u, DMA %u, SG %d, "\
"Mbox %d, CmdLun %d, C%d.\n", BN(j), sh[j]->io_port,
(int)sh[j]->base, sh[j]->irq,
sh[j]->dma_channel, sh[j]->sg_tablesize,
- sh[j]->can_queue, sh[j]->hostt->cmd_per_lun,
+ sh[j]->can_queue, sh[j]->cmd_per_lun,
sh[j]->hostt->use_clustering);
return TRUE;
}
@@ -389,11 +494,11 @@ static inline int port_detect(ushort *port_base, unsigned int j,
int u14_34f_detect (Scsi_Host_Template * tpnt) {
unsigned int j = 0, k, flags;
- ushort isa_io_port[] = {
+ ushort io_port[] = {
0x330, 0x340, 0x230, 0x240, 0x210, 0x130, 0x140, 0x0
};
- ushort *port_base = isa_io_port;
+ ushort *port_base = io_port;
save_flags(flags);
cli();
@@ -407,11 +512,14 @@ int u14_34f_detect (Scsi_Host_Template * tpnt) {
while (*port_base) {
- if(j < MAX_BOARDS && port_detect(port_base, j, tpnt)) j++;
+ if (j < MAX_BOARDS && port_detect(port_base, j, tpnt)) j++;
port_base++;
}
+ if (j > 0)
+ printk("UltraStor 14F/34F: Copyright (C) 1994, 1995 Dario Ballabio.\n");
+
restore_flags(flags);
return j;
}
@@ -422,7 +530,7 @@ static inline void build_sg_list(struct mscp *cpp, Scsi_Cmnd *SCpnt) {
sgpnt = (struct scatterlist *) SCpnt->request_buffer;
- for(k = 0; k < SCpnt->use_sg; k++) {
+ for (k = 0; k < SCpnt->use_sg; k++) {
cpp->sglist[k].address = (unsigned int) sgpnt[k].address;
cpp->sglist[k].num_bytes = sgpnt[k].length;
data_len += sgpnt[k].length;
@@ -448,7 +556,7 @@ int u14_34f_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
starting from last_cp_used */
i = HD(j)->last_cp_used + 1;
- for(k = 0; k < sh[j]->can_queue; k++, i++) {
+ for (k = 0; k < sh[j]->can_queue; k++, i++) {
if (i >= sh[j]->can_queue) i = 0;
@@ -612,6 +720,8 @@ int u14_34f_reset(Scsi_Cmnd * SCarg) {
for (k = 0; k < MAX_TARGET; k++) HD(j)->target_reset[k] = TRUE;
+ for (k = 0; k < MAX_TARGET; k++) HD(j)->target_time_out[k] = 0;
+
for (i = 0; i < sh[j]->can_queue; i++) {
if (HD(j)->cp_stat[i] == FREE) continue;
@@ -650,11 +760,15 @@ int u14_34f_reset(Scsi_Cmnd * SCarg) {
outb(CMD_RESET, sh[j]->io_port + REG_LCL_INTR);
printk("%s: reset, board reset done, enabling interrupts.\n", BN(j));
+
+#if defined (DEBUG_RESET)
do_trace = TRUE;
+#endif
+
HD(j)->in_reset = TRUE;
sti();
time = jiffies;
- while (jiffies < (time + 200) && limit++ < 100000000) sti();
+ while (jiffies < (time + 100) && limit++ < 100000000);
cli();
printk("%s: reset, interrupts disabled, loops %d.\n", BN(j), limit);
@@ -701,9 +815,9 @@ int u14_34f_biosparam(Disk * disk, int dev, int * dkinfo) {
return 0;
}
-static void u14_34f_interrupt_handler(int irq) {
+static void u14_34f_interrupt_handler(int irq, struct pt_regs * regs) {
Scsi_Cmnd *SCpnt;
- unsigned int i, j, k, flags, status, loops, total_loops = 0;
+ unsigned int i, j, k, flags, status, tstatus, loops, total_loops = 0;
struct mscp *spp;
save_flags(flags);
@@ -726,7 +840,7 @@ static void u14_34f_interrupt_handler(int irq) {
loops = 0;
/* Loop until all interrupts for a board are serviced */
- while (inb(sh[j]->io_port + REG_SYS_INTR) & INTR_ASSERTED) {
+ while (inb(sh[j]->io_port + REG_SYS_INTR) & IRQ_ASSERTED) {
total_loops++;
loops++;
@@ -743,7 +857,11 @@ static void u14_34f_interrupt_handler(int irq) {
if (i >= sh[j]->can_queue)
panic("%s: ihdlr, invalid mscp address.\n", BN(j));
- if (HD(j)->cp_stat[i] == LOCKED) {
+ if (HD(j)->cp_stat[i] == IGNORE) {
+ HD(j)->cp_stat[i] = FREE;
+ continue;
+ }
+ else if (HD(j)->cp_stat[i] == LOCKED) {
HD(j)->cp_stat[i] = FREE;
printk("%s: ihdlr, mbox %d unlocked, count %d.\n",
BN(j), i, HD(j)->iocount);
@@ -774,29 +892,43 @@ static void u14_34f_interrupt_handler(int irq) {
" irq %d.\n", BN(j), i, SCpnt->pid,
*(unsigned int *)SCpnt->host_scribble, irq);
+ tstatus = status_byte(spp->target_status);
+
switch (spp->adapter_status) {
case ASOK: /* status OK */
- /* Fix a "READ CAPACITY failed" error on some disk drives */
- if (spp->target_status == INTERMEDIATE_GOOD
- && SCpnt->device->type != TYPE_TAPE)
+ /* Forces a reset if a disk drive keeps returning BUSY */
+ if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE)
status = DID_ERROR << 16;
/* If there was a bus reset, redo operation on each target */
- else if (spp->target_status == CONDITION_GOOD
- && SCpnt->device->type != TYPE_TAPE
- && HD(j)->target_reset[SCpnt->target])
+ else if (tstatus != GOOD
+ && SCpnt->device->type == TYPE_DISK
+ && HD(j)->target_reset[SCpnt->target])
+ status = DID_BUS_BUSY << 16;
+
+ /* Works around a flaw in scsi.c */
+ else if (tstatus == CHECK_CONDITION
+ && SCpnt->device->type == TYPE_DISK
+ && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
status = DID_BUS_BUSY << 16;
+
else
status = DID_OK << 16;
- if (spp->target_status == 0)
+ if (tstatus == GOOD)
HD(j)->target_reset[SCpnt->target] = FALSE;
+ if (spp->target_status && SCpnt->device->type == TYPE_DISK)
+ printk("%s: ihdlr, target %d:%d, pid %ld, target_status "\
+ "0x%x, sense key 0x%x.\n", BN(j),
+ SCpnt->target, SCpnt->lun, SCpnt->pid,
+ spp->target_status, SCpnt->sense_buffer[2]);
+
HD(j)->target_time_out[SCpnt->target] = 0;
break;
- case ASST: /* SCSI bus selection time out */
+ case ASST: /* Selection Time Out */
if (HD(j)->target_time_out[SCpnt->target] > 1)
status = DID_ERROR << 16;
@@ -881,3 +1013,9 @@ static void u14_34f_interrupt_handler(int irq) {
restore_flags(flags);
return;
}
+
+#if defined(MODULE)
+Scsi_Host_Template driver_template = ULTRASTOR_14_34F;
+
+#include "scsi_module.c"
+#endif
diff --git a/drivers/scsi/u14-34f.h b/drivers/scsi/u14-34f.h
index 92d40121b..a4885efea 100644
--- a/drivers/scsi/u14-34f.h
+++ b/drivers/scsi/u14-34f.h
@@ -1,5 +1,5 @@
/*
- * u14-34f.h - used by low-level scsi driver for UltraStor 14F/34F
+ * u14-34f.h - used by the low-level driver for UltraStor 14F/34F
*/
#ifndef _U14_34F_H
#define _U14_34F_H
@@ -10,15 +10,14 @@ int u14_34f_abort(Scsi_Cmnd *);
int u14_34f_reset(Scsi_Cmnd *);
int u14_34f_biosparam(Disk *, int, int *);
-#define U14_34F_VERSION "1.10.01"
+#define U14_34F_VERSION "2.00.00"
#define ULTRASTOR_14_34F { \
- NULL, \
- NULL, \
- "UltraStor 14F/34F rev. " U14_34F_VERSION " by " \
- "Dario_Ballabio@milano.europe.dg.com.",\
+ NULL, /* Ptr for modules */ \
+ NULL, /* usage count for modules */ \
+ "UltraStor 14F/34F rev. " U14_34F_VERSION " ", \
u14_34f_detect, \
- NULL, \
+ NULL, /* Release */ \
NULL, \
NULL, \
u14_34f_queuecommand, \
@@ -31,7 +30,7 @@ int u14_34f_biosparam(Disk *, int, int *);
0, /* sg_tablesize, reset by detect */ \
0, /* cmd_per_lun, reset by detect */ \
0, /* number of boards present */ \
- 0, /* unchecked isa dma, reset by detect */ \
- 0, /* use_clustering, reset by detect */ \
+ 1, /* unchecked isa dma, reset by detect */ \
+ ENABLE_CLUSTERING \
}
#endif
diff --git a/drivers/scsi/ultrastor.c b/drivers/scsi/ultrastor.c
index 9d7dca68c..5f0b9bee2 100644
--- a/drivers/scsi/ultrastor.c
+++ b/drivers/scsi/ultrastor.c
@@ -283,18 +283,21 @@ static const unsigned short ultrastor_ports_14f[] = {
};
#endif
-static void ultrastor_interrupt(int cpl);
+static void ultrastor_interrupt(int, struct pt_regs *);
static inline void build_sg_list(struct mscp *, Scsi_Cmnd *SCpnt);
static inline int find_and_clear_bit_16(unsigned short *field)
{
int rv;
+ unsigned long flags;
+
+ save_flags(flags);
cli();
if (*field == 0) panic("No free mscp");
asm("xorl %0,%0\n0:\tbsfw %1,%w0\n\tbtr %0,%1\n\tjnc 0b"
: "=&r" (rv), "=m" (*field) : "1" (*field));
- sti();
+ restore_flags(flags);
return rv;
}
@@ -435,7 +438,8 @@ static int ultrastor_14f_detect(Scsi_Host_Template * tpnt)
/* All above tests passed, must be the right thing. Get some useful
info. */
- snarf_region(config.port_address, 0x0c); /* Register the I/O space that we use */
+ request_region(config.port_address, 0x0c,"ultrastor");
+ /* Register the I/O space that we use */
*(char *)&config_1 = inb(CONFIG(config.port_address + 0));
*(char *)&config_2 = inb(CONFIG(config.port_address + 1));
@@ -729,13 +733,13 @@ int ultrastor_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
retry:
if (config.slot)
while (inb(config.ogm_address - 1) != 0 &&
- config.aborted[mscp_index] == 0xff);
+ config.aborted[mscp_index] == 0xff) barrier();
/* else??? */
while ((inb(LCL_DOORBELL_INTR(config.doorbell_address)) &
(config.slot ? 2 : 1))
- && config.aborted[mscp_index] == 0xff);
+ && config.aborted[mscp_index] == 0xff) barrier();
/* To avoid race conditions, make the code to write to the adapter
atomic. This simplifies the abort code. */
@@ -869,7 +873,7 @@ int ultrastor_abort(Scsi_Cmnd *SCpnt)
printk("Ux4F: abort while completed command pending\n");
restore_flags(flags);
cli();
- ultrastor_interrupt(0);
+ ultrastor_interrupt(0, NULL);
restore_flags(flags);
return SCSI_ABORT_SUCCESS; /* FIXME - is this correct? -ERY */
}
@@ -1010,7 +1014,7 @@ int ultrastor_biosparam(Disk * disk, int dev, int * dkinfo)
return 0;
}
-static void ultrastor_interrupt(int cpl)
+static void ultrastor_interrupt(int irq, struct pt_regs *regs)
{
unsigned int status;
#if ULTRASTOR_MAX_CMDS > 1
diff --git a/drivers/scsi/ultrastor.h b/drivers/scsi/ultrastor.h
index b80a66924..cc92225a4 100644
--- a/drivers/scsi/ultrastor.h
+++ b/drivers/scsi/ultrastor.h
@@ -30,7 +30,7 @@ int ultrastor_biosparam(Disk *, int, int *);
#define ULTRASTOR_14F { NULL, NULL, /* Ptr for modules*/ \
- NULL, \
+ "UltraStor 14F/24F/34F", \
ultrastor_detect, \
NULL, /* Release */ \
ultrastor_info, \
diff --git a/drivers/scsi/wd7000.c b/drivers/scsi/wd7000.c
index c3eed4294..1905014c5 100644
--- a/drivers/scsi/wd7000.c
+++ b/drivers/scsi/wd7000.c
@@ -606,7 +606,7 @@ static inline Scb *alloc_scbs(int needed)
save_flags(flags);
cli();
while (busy) { /* someone else is allocating */
- sti();
+ sti(); /* Yes this is really needed here */
now = jiffies; while (jiffies == now) /* wait a jiffy */;
cli();
}
@@ -615,7 +615,7 @@ static inline Scb *alloc_scbs(int needed)
while (freescbs < needed) {
timeout = jiffies + WAITnexttimeout;
do {
- sti();
+ sti(); /* Yes this is really needed here */
now = jiffies; while (jiffies == now) /* wait a jiffy */;
cli();
} while (freescbs < needed && jiffies <= timeout);
@@ -797,15 +797,8 @@ static void wd7000_scsi_done(Scsi_Cmnd * SCpnt)
#define wd7000_intr_ack(host) outb(0,host->iobase+ASC_INTR_ACK)
-void wd7000_intr_handle(int irq)
+void wd7000_intr_handle(int irq, struct pt_regs * regs)
{
-#ifdef 0
- /*
- * Use irqp as the parm, and the following declaration, if
- * SA_INTERRUPT is not used.
- */
- register int irq = *(((int *)irqp)-2);
-#endif
register int flag, icmb, errstatus, icmb_status;
register int host_error, scsi_error;
register Scb *scb; /* for SCSI commands */
@@ -948,7 +941,7 @@ int wd7000_command(Scsi_Cmnd *SCpnt)
{
wd7000_queuecommand(SCpnt, wd7000_scsi_done);
- while (SCpnt->SCp.phase > 0); /* phase counts scbs down to 0 */
+ while (SCpnt->SCp.phase > 0) barrier(); /* phase counts scbs down to 0 */
return SCpnt->result;
}
@@ -971,7 +964,8 @@ int wd7000_diagnostics( Adapter *host, int code )
*/
mail_out(host, (struct scb *) &icb);
timeout = jiffies + WAITnexttimeout; /* wait up to 2 seconds */
- while (icb.phase && jiffies < timeout) /* wait for completion */;
+ while (icb.phase && jiffies < timeout)
+ barrier(); /* wait for completion */
if (icb.phase) {
printk("wd7000_diagnostics: timed out.\n");
@@ -1081,7 +1075,8 @@ void wd7000_revision(Adapter *host)
* which in turn means that scatter/gather will be disabled.
*/
mail_out(host, (struct scb *) &icb);
- while (icb.phase) /* wait for completion */;
+ while (icb.phase)
+ barrier(); /* wait for completion */
host->rev1 = icb.primary;
host->rev2 = icb.secondary;
}
@@ -1162,7 +1157,7 @@ int wd7000_detect(Scsi_Host_Template * tpnt)
printk("using IO %xh IRQ %d DMA %d.\n",
host->iobase, host->irq, host->dma);
- snarf_region(host->iobase, 4); /* Register our ports */
+ request_region(host->iobase, 4,"wd7000"); /* Register our ports */
/*
* For boards before rev 6.0, scatter/gather isn't supported.
*/
@@ -1189,7 +1184,7 @@ int wd7000_abort(Scsi_Cmnd * SCpnt)
if (inb(host->iobase+ASC_STAT) & INT_IM) {
printk("wd7000_abort: lost interrupt\n");
- wd7000_intr_handle(host->irq);
+ wd7000_intr_handle(host->irq, NULL);
return SCSI_ABORT_SUCCESS;
}
diff --git a/drivers/sound/.blurb b/drivers/sound/.blurb
index aac91a8d5..acc3c1de2 100644
--- a/drivers/sound/.blurb
+++ b/drivers/sound/.blurb
@@ -1,28 +1,3 @@
-NOTE!
-
- This is an ALPHA TEST VERSION (pre 3.0). The latest
- released version of this driver is now part of
- Linux kernel distribution. For other operating systems
- use the snd-driv-2.5.tar.gz package.
-
- This particular version contains lots of new features
- BUT THERE ARE NO APPLICATIONS WHICH USE THEM. So there
- is no need to install this version as long as you are
- not developing the driver or applications which use it.
- All new features are in the /dev/sequencer and /dev/midi
- parts of the driver.
-
-
- This version is little bit incomplete. Some features have
- not been implemented for each soundcards yet. All features
- of v2.4 should work OK.
-
-CAUTION!
- This version of driver works with applications written and
- compiled for v2.*. The problem is that APPLICATIONS COMPILED
- WITH soundcard.h OF THIS VERSION WILL NOT WORK WITH OLDER DRIVER.
- Be careful when distributing applications compiled with this
- version (just the apps using /dev/sequencer are incompatible).
*********************************************************
* IF YOU HAVE ANY PROBLEMS WITH THE SOUND DRIVER, *
@@ -30,5 +5,3 @@ CAUTION!
* NEAREST LINUX FTP SITE AND CONTAINS ANSWER TO YOUR *
* PROBLEM. *
*********************************************************
-
-Hannu
diff --git a/drivers/sound/.blurb.orig b/drivers/sound/.blurb.orig
deleted file mode 100644
index 165e01ac8..000000000
--- a/drivers/sound/.blurb.orig
+++ /dev/null
@@ -1,27 +0,0 @@
-NOTE!
-
- This is an ALPHA TEST VERSION (pre 3.0). The latest
- released version of this driver is now part of
- Linux kernel distribution. For other operating systems
- use the snd-driv-2.5.tar.gz package.
-
- This particular version contains lots of new features
- BUT THERE ARE NO APPLICATIONS WHICH USE THEM. So there
- is no need to install this version as long as you are
- not developing the driver or applications which use it.
- All new features are in the /dev/sequencer and /dev/midi
- parts of the driver.
-
-
- This version is little bit incomplete. Some features have
- not been implemented for each soundcards yet. All features
- of v2.4 should work OK.
-
-CAUTION!
- This version of driver works with applications written and
- compiled for v2.*. The problem is that APPLICATIONS COMPILED
- WITH soundcard.h OF THIS VERSION WILL NOT WORK WITH OLDER DRIVER.
- Be carefull when distributing applications compiled with this
- version (just the apps using /dev/sequencer are incompatible).
-
-Hannu
diff --git a/drivers/sound/CHANGELOG b/drivers/sound/CHANGELOG
index b04d7a8d6..8cacac458 100644
--- a/drivers/sound/CHANGELOG
+++ b/drivers/sound/CHANGELOG
@@ -91,7 +91,7 @@ Since 2.2
Since 2.1
- Preliminary support for SB16.
- - The SB16 mixer is supported in it's native mode.
+ - The SB16 mixer is supported in its native mode.
- Digitized voice capability up to 44.1 kHz/8 bit/mono
(16 bit and stereo support coming in the next release).
- Fixed some bugs in the digitized voice driver for PAS16.
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile
index 128075ffe..2575118c1 100644
--- a/drivers/sound/Makefile
+++ b/drivers/sound/Makefile
@@ -71,6 +71,8 @@ dep:
$(MAKE) /usr/include/sys/soundcard.h
$(CPP) -M *.c > .depend
+modules:
+
#
# include a dependency file if one exists
#
diff --git a/drivers/sound/Readme b/drivers/sound/Readme
index 60d18cbe8..1c31ac66c 100644
--- a/drivers/sound/Readme
+++ b/drivers/sound/Readme
@@ -74,7 +74,13 @@ Hannu Savolainen
hannu@voxware.pp.fi
Snail mail: Hannu Savolainen
- Pallaksentie 4 A 2
- 00970 Helsinki
+ Hiekkalaiturintie 3 A 8
+ 00980 Helsinki
Finland
-FAX: +358 0 395 1968 (usually not connected)
+FAX: +358 0 341 6272 (answers if I have my machine (mgetty) on).
+
+NOTE! I probably don't answer to Snail mail or FAX messages. Sending answer
+ to each of them is simply too expensive and time consuming. However I
+ try to reply every email message I get (within a week). If you don't
+ get response, please check how your address is written in the message
+ header. I can't answer if I don't have a valid reply address.
diff --git a/drivers/sound/Readme.v30 b/drivers/sound/Readme.v30
index cade461d2..da09c7fbc 100644
--- a/drivers/sound/Readme.v30
+++ b/drivers/sound/Readme.v30
@@ -138,7 +138,7 @@ distribute binaries compiled with soundcard.h of v3.X.
- The basic API usage is similar to the current one. There are some new
macros but the older ones should work as earlier. The most important
incompatibility is that the /dev/sequencer2 driver allocates voices itself.
-The other one is that the application must send SEQ_START_TIMER() as it's
+The other one is that the application must send SEQ_START_TIMER() as its
first event. Otherwise the timer is not started and the application waits
infinitely.
diff --git a/drivers/sound/ad1848.c b/drivers/sound/ad1848.c
index f5603357d..57117f680 100644
--- a/drivers/sound/ad1848.c
+++ b/drivers/sound/ad1848.c
@@ -93,7 +93,6 @@ static void ad1848_start_input (int dev, unsigned long buf, int count, int i
static int ad1848_prepare_for_IO (int dev, int bsize, int bcount);
static void ad1848_reset (int dev);
static void ad1848_halt (int dev);
-void ad1848_interrupt (int dev);
static int
ad_read (ad1848_info * devc, int reg)
@@ -684,7 +683,7 @@ ad1848_detect (int io_base)
* Check that the I/O address is in use.
*
* The bit 0x80 of the base I/O port is known to be 0 after the
- * chip has performed it's power on initialization. Just assume
+ * chip has performed its power on initialization. Just assume
* this has happened before the OS is starting.
*
* If the I/O address is unused, it typically returns 0xff.
@@ -866,7 +865,7 @@ ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture
}
void
-ad1848_interrupt (int irq)
+ad1848_interrupt (int irq, struct pt_regs * regs)
{
unsigned char status;
ad1848_info *devc;
diff --git a/drivers/sound/configure.c b/drivers/sound/configure.c
index a2e8c4b26..b1defaeb9 100644
--- a/drivers/sound/configure.c
+++ b/drivers/sound/configure.c
@@ -135,7 +135,7 @@ char *questions[] =
"SoundBlaster Pro support",
"SoundBlaster 16 support",
- "digitized voice support",
+ "/dev/dsp and /dev/audio support (_recommended_)",
"This should not be asked",
"MIDI interface support",
"This should not be asked",
diff --git a/drivers/sound/dma.h b/drivers/sound/dma.h
deleted file mode 100644
index 1196fdff1..000000000
--- a/drivers/sound/dma.h
+++ /dev/null
@@ -1,266 +0,0 @@
-/* $Id: dma.h,v 1.7 1992/12/14 00:29:34 root Exp root $
- * linux/include/asm/dma.h: Defines for using and allocating dma channels.
- * Written by Hennus Bergman, 1992.
- * High DMA channel support & info by Hannu Savolainen
- * and John Boyd, Nov. 1992.
- */
-
-#ifndef _ASM_DMA_H
-#define _ASM_DMA_H
-
-#include <asm/io.h> /* need byte IO */
-
-#define deb_outb(x,y) {printk("out %02x, %02x\n", x, y);outb(x,y);}
-
-
-#ifdef HAVE_REALLY_SLOW_DMA_CONTROLLER
-#define outb outb_p
-#endif
-
-/*
- * NOTES about DMA transfers:
- *
- * controller 1: channels 0-3, byte operations, ports 00-1F
- * controller 2: channels 4-7, word operations, ports C0-DF
- *
- * - ALL registers are 8 bits only, regardless of transfer size
- * - channel 4 is not used - cascades 1 into 2.
- * - channels 0-3 are byte - addresses/counts are for physical bytes
- * - channels 5-7 are word - addresses/counts are for physical words
- * - transfers must not cross physical 64K (0-3) or 128K (5-7) boundaries
- * - transfer count loaded to registers is 1 less than actual count
- * - controller 2 offsets are all even (2x offsets for controller 1)
- * - page registers for 5-7 don't use data bit 0, represent 128K pages
- * - page registers for 0-3 use bit 0, represent 64K pages
- *
- * DMA transfers are limited to the lower 16MB of _physical_ memory.
- * Note that addresses loaded into registers must be _physical_ addresses,
- * not logical addresses (which may differ if paging is active).
- *
- * Address mapping for channels 0-3:
- *
- * A23 ... A16 A15 ... A8 A7 ... A0 (Physical addresses)
- * | ... | | ... | | ... |
- * | ... | | ... | | ... |
- * | ... | | ... | | ... |
- * P7 ... P0 A7 ... A0 A7 ... A0
- * | Page | Addr MSB | Addr LSB | (DMA registers)
- *
- * Address mapping for channels 5-7:
- *
- * A23 ... A17 A16 A15 ... A9 A8 A7 ... A1 A0 (Physical addresses)
- * | ... | \ \ ... \ \ \ ... \ \
- * | ... | \ \ ... \ \ \ ... \ (not used)
- * | ... | \ \ ... \ \ \ ... \
- * P7 ... P1 (0) A7 A6 ... A0 A7 A6 ... A0
- * | Page | Addr MSB | Addr LSB | (DMA registers)
- *
- * Again, channels 5-7 transfer _physical_ words (16 bits), so addresses
- * and counts _must_ be word-aligned (the lowest address bit is _ignored_ at
- * the hardware level, so odd-byte transfers aren't possible).
- *
- * Transfer count (_not # bytes_) is limited to 64K, represented as actual
- * count - 1 : 64K => 0xFFFF, 1 => 0x0000. Thus, count is always 1 or more,
- * and up to 128K bytes may be transferred on channels 5-7 in one operation.
- *
- */
-
-#define MAX_DMA_CHANNELS 8
-
-/* 8237 DMA controllers */
-#define IO_DMA1_BASE 0x00 /* 8 bit slave DMA, channels 0..3 */
-#define IO_DMA2_BASE 0xC0 /* 16 bit master DMA, ch 4(=slave input)..7 */
-
-/* DMA controller registers */
-#define DMA1_CMD_REG 0x08 /* command register (w) */
-#define DMA1_STAT_REG 0x08 /* status register (r) */
-#define DMA1_REQ_REG 0x09 /* request register (w) */
-#define DMA1_MASK_REG 0x0A /* single-channel mask (w) */
-#define DMA1_MODE_REG 0x0B /* mode register (w) */
-#define DMA1_CLEAR_FF_REG 0x0C /* clear pointer flip-flop (w) */
-#define DMA1_TEMP_REG 0x0D /* Temporary Register (r) */
-#define DMA1_RESET_REG 0x0D /* Master Clear (w) */
-#define DMA1_CLR_MASK_REG 0x0E /* Clear Mask */
-#define DMA1_MASK_ALL_REG 0x0F /* all-channels mask (w) */
-
-#define DMA2_CMD_REG 0xD0 /* command register (w) */
-#define DMA2_STAT_REG 0xD0 /* status register (r) */
-#define DMA2_REQ_REG 0xD2 /* request register (w) */
-#define DMA2_MASK_REG 0xD4 /* single-channel mask (w) */
-#define DMA2_MODE_REG 0xD6 /* mode register (w) */
-#define DMA2_CLEAR_FF_REG 0xD8 /* clear pointer flip-flop (w) */
-#define DMA2_TEMP_REG 0xDA /* Temporary Register (r) */
-#define DMA2_RESET_REG 0xDA /* Master Clear (w) */
-#define DMA2_CLR_MASK_REG 0xDC /* Clear Mask */
-#define DMA2_MASK_ALL_REG 0xDE /* all-channels mask (w) */
-
-#define DMA_ADDR_0 0x00 /* DMA address registers */
-#define DMA_ADDR_1 0x02
-#define DMA_ADDR_2 0x04
-#define DMA_ADDR_3 0x06
-#define DMA_ADDR_4 0xC0
-#define DMA_ADDR_5 0xC4
-#define DMA_ADDR_6 0xC8
-#define DMA_ADDR_7 0xCC
-
-#define DMA_CNT_0 0x01 /* DMA count registers */
-#define DMA_CNT_1 0x03
-#define DMA_CNT_2 0x05
-#define DMA_CNT_3 0x07
-#define DMA_CNT_4 0xC2
-#define DMA_CNT_5 0xC6
-#define DMA_CNT_6 0xCA
-#define DMA_CNT_7 0xCE
-
-#define DMA_PAGE_0 0x87 /* DMA page registers */
-#define DMA_PAGE_1 0x83
-#define DMA_PAGE_2 0x81
-#define DMA_PAGE_3 0x82
-#define DMA_PAGE_5 0x8B
-#define DMA_PAGE_6 0x89
-#define DMA_PAGE_7 0x8A
-
-#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */
-#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */
-#define DMA_MODE_CASCADE 0xC0 /* pass thru DREQ->HRQ, DACK<-HLDA only */
-
-/* enable/disable a specific DMA channel */
-static __inline__ void enable_dma(unsigned int dmanr)
-{
- if (dmanr<=3)
- deb_outb(dmanr, DMA1_MASK_REG)
- else
- deb_outb(dmanr & 3, DMA2_MASK_REG);
-}
-
-static __inline__ void disable_dma(unsigned int dmanr)
-{
- if (dmanr<=3)
- deb_outb(dmanr | 4, DMA1_MASK_REG)
- else
- deb_outb((dmanr & 3) | 4, DMA2_MASK_REG);
-}
-
-/* Clear the 'DMA Pointer Flip Flop'.
- * Write 0 for LSB/MSB, 1 for MSB/LSB access.
- * Use this once to initialize the FF to a known state.
- * After that, keep track of it. :-)
- * --- In order to do that, the DMA routines below should ---
- * --- only be used while interrupts are disabled! ---
- */
-static __inline__ void clear_dma_ff(unsigned int dmanr)
-{
- if (dmanr<=3)
- deb_outb(0, DMA1_CLEAR_FF_REG)
- else
- deb_outb(0, DMA2_CLEAR_FF_REG);
-}
-
-/* set mode (above) for a specific DMA channel */
-static __inline__ void set_dma_mode(unsigned int dmanr, char mode)
-{
- if (dmanr<=3)
- deb_outb(mode | dmanr, DMA1_MODE_REG)
- else
- deb_outb(mode | (dmanr&3), DMA2_MODE_REG);
-}
-
-/* Set only the page register bits of the transfer address.
- * This is used for successive transfers when we know the contents of
- * the lower 16 bits of the DMA current address register, but a 64k boundary
- * may have been crossed.
- */
-static __inline__ void set_dma_page(unsigned int dmanr, char pagenr)
-{
- switch(dmanr) {
- case 0:
- deb_outb(pagenr, DMA_PAGE_0);
- break;
- case 1:
- deb_outb(pagenr, DMA_PAGE_1);
- break;
- case 2:
- deb_outb(pagenr, DMA_PAGE_2);
- break;
- case 3:
- deb_outb(pagenr, DMA_PAGE_3);
- break;
- case 5:
- deb_outb(pagenr & 0xfe, DMA_PAGE_5);
- break;
- case 6:
- deb_outb(pagenr & 0xfe, DMA_PAGE_6);
- break;
- case 7:
- deb_outb(pagenr & 0xfe, DMA_PAGE_7);
- break;
- }
-}
-
-
-/* Set transfer address & page bits for specific DMA channel.
- * Assumes dma flipflop is clear.
- */
-static __inline__ void set_dma_addr(unsigned int dmanr, unsigned int a)
-{
- set_dma_page(dmanr, a>>16);
- if (dmanr <= 3) {
- deb_outb( a & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE );
- deb_outb( (a>>8) & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE )
- } else {
- deb_outb( (a>>1) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE );
- deb_outb( (a>>9) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE );
- }
-}
-
-
-/* Set transfer size (max 64k for DMA1..3, 128k for DMA5..7) for
- * a specific DMA channel.
- * You must ensure the parameters are valid.
- * NOTE: from a manual: "the number of transfers is one more
- * than the initial word count"! This is taken into account.
- * Assumes dma flip-flop is clear.
- * NOTE 2: "count" represents _bytes_ and must be even for channels 5-7.
- */
-static __inline__ void set_dma_count(unsigned int dmanr, unsigned int count)
-{
- count--;
- if (dmanr <= 3) {
- deb_outb( count & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE );
- deb_outb( (count>>8) & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE );
- } else {
- deb_outb( (count>>1) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE );
- deb_outb( (count>>9) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE );
- }
-}
-
-
-/* Get DMA residue count. After a DMA transfer, this
- * should return zero. Reading this while a DMA transfer is
- * still in progress will return unpredictable results.
- * If called before the channel has been used, it may return 1.
- * Otherwise, it returns the number of _bytes_ left to transfer.
- *
- * Assumes DMA flip-flop is clear.
- */
-static __inline__ int get_dma_residue(unsigned int dmanr)
-{
- unsigned int io_port = (dmanr<=3)? ((dmanr&3)<<1) + 1 + IO_DMA1_BASE
- : ((dmanr&3)<<2) + 2 + IO_DMA2_BASE;
-
- /* using short to get 16-bit wrap around */
- unsigned short count;
-
- count = 1 + inb(io_port);
- count += inb(io_port) << 8;
-
- return (dmanr<=3)? count : (count<<1);
-}
-
-
-/* These are in kernel/dma.c: */
-extern int request_dma(unsigned int dmanr,char * deviceID); /* reserve a DMA channel */
-extern void free_dma(unsigned int dmanr); /* release it again */
-
-
-#endif /* _ASM_DMA_H */
diff --git a/drivers/sound/dmabuf.c b/drivers/sound/dmabuf.c
index 6f0794824..a7c396f2f 100644
--- a/drivers/sound/dmabuf.c
+++ b/drivers/sound/dmabuf.c
@@ -38,7 +38,7 @@
DEFINE_WAIT_QUEUES (dev_sleeper[MAX_AUDIO_DEV], dev_sleep_flag[MAX_AUDIO_DEV]);
static struct dma_buffparms dmaps[MAX_AUDIO_DEV] =
-{0}; /*
+{{0}}; /*
* Primitive way to allocate
* such a large array.
* Needs dynamic run-time allocation.
@@ -740,7 +740,7 @@ DMAbuf_outputintr (int dev, int event_type)
* buffer.
* 1 = DMA transfer done. Device doesn't have local buffer or it's
* empty now.
- * 2 = No DMA transfer but the device has now more space in it's local
+ * 2 = No DMA transfer but the device has now more space in its local
* buffer.
*/
diff --git a/drivers/sound/experimental.txt b/drivers/sound/experimental.txt
index 180c86836..8323daf7b 100644
--- a/drivers/sound/experimental.txt
+++ b/drivers/sound/experimental.txt
@@ -127,7 +127,7 @@ less, depending on how much it needs time to do other things. The maximum
delay between writing a byte and the time when it finally plays is
at most 3 times the 'fragment_time'.
-The delay depends on how much time the program needs to do it's
+The delay depends on how much time the program needs to do its
computations for the next sample (updating screen etc). If it's about
80% of the 'fragment_time' the game will run almost without delays. If it
uses more time, there is a risk that the audio buffer gets empty.
diff --git a/drivers/sound/gus_card.c b/drivers/sound/gus_card.c
index 614532c7d..9560babb7 100644
--- a/drivers/sound/gus_card.c
+++ b/drivers/sound/gus_card.c
@@ -33,7 +33,7 @@
#include "gus_hw.h"
-void gusintr (int);
+void gusintr (int, struct pt_regs * regs);
int gus_base, gus_irq, gus_dma;
extern int gus_wave_volume;
@@ -118,7 +118,7 @@ probe_gus (struct address_info *hw_config)
}
void
-gusintr (int irq)
+gusintr (int irq, struct pt_regs * regs)
{
unsigned char src;
@@ -128,7 +128,7 @@ gusintr (int irq)
#ifndef EXCLUDE_GUSMAX
if (have_gus_max)
- ad1848_interrupt (irq);
+ ad1848_interrupt (irq, regs);
#endif
while (1)
diff --git a/drivers/sound/gus_wave.c b/drivers/sound/gus_wave.c
index 1b95c0593..3182bb608 100644
--- a/drivers/sound/gus_wave.c
+++ b/drivers/sound/gus_wave.c
@@ -781,7 +781,7 @@ gus_initialize (void)
gus_select_voice (0); /* This disables writes to IRQ/DMA reg */
- gusintr (0); /* Serve pending interrupts */
+ gusintr (0,NULL); /* Serve pending interrupts */
RESTORE_INTR (flags);
}
@@ -3052,7 +3052,6 @@ do_loop_irq (int voice)
pcm_active = 0; /* Signal to the play_next_pcm_block routine */
case LMODE_PCM:
{
- int orig_qlen = pcm_qlen;
int flag; /* 0 or 2 */
pcm_qlen--;
@@ -3068,7 +3067,7 @@ do_loop_irq (int voice)
pcm_active = 0;
}
- /*
+/*
* If the queue was full before this interrupt, the DMA transfer was
* suspended. Let it continue now.
*/
diff --git a/drivers/sound/mpu401.c b/drivers/sound/mpu401.c
index 40be06638..9a9fc3daa 100644
--- a/drivers/sound/mpu401.c
+++ b/drivers/sound/mpu401.c
@@ -479,7 +479,7 @@ mpu401_input_loop (struct mpu_config *devc)
}
void
-mpuintr (int irq)
+mpuintr (int irq, struct pt_regs * regs)
{
struct mpu_config *devc;
int dev;
@@ -1209,7 +1209,7 @@ clocks2ticks (unsigned long clocks)
/*
* The MPU-401 supports just a limited set of possible timebase values.
* Since the applications require more choices, the driver has to
- * program the HW to do it's best and to convert between the HW and
+ * program the HW to do its best and to convert between the HW and
* actual timebases.
*/
diff --git a/drivers/sound/pas2_card.c b/drivers/sound/pas2_card.c
index 1bfd73fcc..524cc38b2 100644
--- a/drivers/sound/pas2_card.c
+++ b/drivers/sound/pas2_card.c
@@ -79,7 +79,7 @@ pas2_msg (char *foo)
/******************* Begin of the Interrupt Handler ********************/
void
-pasintr (int unused)
+pasintr (int unused, struct pt_regs * regs)
{
int status;
diff --git a/drivers/sound/pss.c b/drivers/sound/pss.c
index 6e35d1b7f..978231e88 100644
--- a/drivers/sound/pss.c
+++ b/drivers/sound/pss.c
@@ -159,7 +159,7 @@ pss_setaddr (int addr, int configAddr)
/*_____ pss_checkint
This function tests an interrupt number to see if
it is available. It takes the interrupt button
- as it's argument and returns TRUE if the interrupt
+ as its argument and returns TRUE if the interrupt
is ok.
*/
static int
diff --git a/drivers/sound/sb_dsp.c b/drivers/sound/sb_dsp.c
index cb491b5cc..02a679335 100644
--- a/drivers/sound/sb_dsp.c
+++ b/drivers/sound/sb_dsp.c
@@ -131,7 +131,7 @@ sb_dsp_command (unsigned char val)
}
void
-sbintr (int unit)
+sbintr (int unit, struct pt_regs *regs)
{
int status;
@@ -803,7 +803,7 @@ sb_dsp_init (long mem_start, struct address_info *hw_config)
if (sbc_major >= 3)
{
-#ifndef SCO
+#if !defined(SCO) && !defined(EXCLUDE_AUDIO)
# ifdef __SGNXPRO__
if (mixer_type == 2)
{
diff --git a/drivers/sound/sound_calls.h b/drivers/sound/sound_calls.h
index eccd9b673..4d8fdf76f 100644
--- a/drivers/sound/sound_calls.h
+++ b/drivers/sound/sound_calls.h
@@ -100,7 +100,7 @@ void tenmicrosec(void);
void request_sound_timer (int count);
void sound_stop_timer(void);
int snd_ioctl_return(int *addr, int value);
-int snd_set_irq_handler (int interrupt_level, void(*hndlr)(int));
+int snd_set_irq_handler (int interrupt_level, void(*hndlr)(int, struct pt_regs *));
void snd_release_irq(int vect);
void sound_dma_malloc(int dev);
void sound_dma_free(int dev);
@@ -177,7 +177,7 @@ void pas_midi_interrupt(void);
long attach_gus_card(long mem_start, struct address_info * hw_config);
int probe_gus(struct address_info *hw_config);
int gus_set_midi_irq(int num);
-void gusintr(int);
+void gusintr(int, struct pt_regs * regs);
long attach_gus_db16(long mem_start, struct address_info * hw_config);
int probe_gus_db16(struct address_info *hw_config);
@@ -225,7 +225,7 @@ void sound_timer_interrupt(void);
/* From ad1848.c */
void ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture);
int ad1848_detect (int io_base);
-void ad1848_interrupt (int dev);
+void ad1848_interrupt (int dev, struct pt_regs *regs);
long attach_ms_sound(long mem_start, struct address_info * hw_config);
int probe_ms_sound(struct address_info *hw_config);
diff --git a/drivers/sound/soundcard.c b/drivers/sound/soundcard.c
index 425378ca2..ab90bf5da 100644
--- a/drivers/sound/soundcard.c
+++ b/drivers/sound/soundcard.c
@@ -32,6 +32,7 @@
#ifdef CONFIGURE_SOUNDCARD
#include <linux/major.h>
+#include <linux/mm.h>
static int soundcards_installed = 0; /* Number of installed
@@ -221,7 +222,7 @@ tenmicrosec (void)
}
int
-snd_set_irq_handler (int interrupt_level, void (*hndlr) (int))
+snd_set_irq_handler (int interrupt_level, void (*hndlr) (int, struct pt_regs *))
{
int retcode;
diff --git a/drivers/sound/uart6850.c b/drivers/sound/uart6850.c
index ca6313521..338f6507f 100644
--- a/drivers/sound/uart6850.c
+++ b/drivers/sound/uart6850.c
@@ -93,7 +93,7 @@ uart6850_input_loop (void)
}
void
-m6850intr (int unit)
+m6850intr (int unit, struct pt_regs * regs)
{
printk ("M");
if (input_avail ())