]> git.neil.brown.name Git - history.git/commitdiff
The LDT fixes in particular fix some potentially random strange behaviour. 2.4.0-test13pre7
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:41:10 +0000 (15:41 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:41:10 +0000 (15:41 -0500)
And the alpha memmove() thing was a showstopper bug on alphas.

Linus

- x86 LDT handling fixes: revert some cleanups (the LDT really
  doesn't act like a TLB context)
- Richard Henderson: alpha update (working memmove() from Ivan
  Kokshaysky etc)
- Manfred: winbond-840.c net driver update (fix oops on module unload etc)
- Alan Cox: more synchronizations (with some fixes from Andrew Morton)

76 files changed:
Documentation/devices.txt
Documentation/kernel-parameters.txt
Documentation/video4linux/bttv/Insmod-options
Documentation/video4linux/bttv/Modules.conf
Documentation/video4linux/bttv/README
Makefile
arch/alpha/lib/Makefile
arch/alpha/lib/ev6-memcpy.S
arch/alpha/lib/ev67-strrchr.S [new file with mode: 0644]
arch/alpha/lib/memmove.S
arch/alpha/math-emu/Makefile
arch/i386/kernel/process.c
drivers/media/radio/radio-gemtek.c
drivers/media/video/bttv-cards.c
drivers/media/video/bttv-driver.c
drivers/media/video/bttv.h
drivers/media/video/bttvp.h
drivers/net/3c523.c
drivers/net/Config.in
drivers/net/acenic.c
drivers/net/acenic.h
drivers/net/acenic_firmware.h
drivers/net/appletalk/cops.c
drivers/net/arlan.c
drivers/net/fc/iph5526.c
drivers/net/fc/tach_structs.h
drivers/net/hydra.c
drivers/net/lance.c
drivers/net/mace.c
drivers/net/ni5010.c
drivers/net/ni52.c
drivers/net/oaknet.c
drivers/net/stnic.c
drivers/net/tulip/ChangeLog
drivers/net/tulip/eeprom.c
drivers/net/tulip/timer.c
drivers/net/tulip/tulip.h
drivers/net/tulip/tulip_core.c
drivers/net/winbond-840.c
drivers/pci/quirks.c
drivers/scsi/Makefile
drivers/scsi/README.osst [new file with mode: 0644]
drivers/scsi/osst.c [new file with mode: 0644]
drivers/scsi/osst.h [new file with mode: 0644]
drivers/scsi/osst_detect.h [new file with mode: 0644]
drivers/scsi/osst_options.h [new file with mode: 0644]
drivers/scsi/st.c
drivers/sound/i810_audio.c
drivers/sound/mad16.c
drivers/sound/via82cxxx_audio.c
fs/buffer.c
fs/ramfs/inode.c
include/asm-alpha/processor.h
include/asm-arm/processor.h
include/asm-i386/mmu_context.h
include/asm-i386/processor.h
include/asm-ia64/processor.h
include/asm-m68k/processor.h
include/asm-mips/processor.h
include/asm-mips64/processor.h
include/asm-parisc/processor.h
include/asm-ppc/processor.h
include/asm-s390/processor.h
include/asm-sh/processor.h
include/asm-sparc/processor.h
include/asm-sparc64/processor.h
include/linux/blk.h
include/linux/ixjuser.h
include/linux/major.h
include/linux/mm.h
include/linux/mtio.h
include/linux/pci.h
kernel/fork.c
mm/filemap.c
mm/mmap.c
net/atm/lec.c

index 6332b478edf8e08b0ccb067d2bf7fe4da41e9d87..5c250ac97fafe132ce741d61085af71e185fdafd 100644 (file)
@@ -1,7 +1,7 @@
                       LINUX ALLOCATED DEVICES
-            Maintained by H. Peter Anvin <hpa@zytor.com>
+            Maintained by H. Peter Anvin <device@lanana.org>
 
-                    Last revised: March 23, 2000
+                  Last revised: December 29, 2000
 
 This list is the Linux Device List, the official registry of allocated
 device numbers and /dev directory nodes for the Linux operating
@@ -36,7 +36,7 @@ on this list.  Any such information requests will be deleted without
 reply.
 
 
-     **** PLEASE READ THIS BEFORE SUBMITTING A DEVICE ENTRY ****
+         **** DEVICE DRIVERS AUTHORS PLEASE READ THIS ****
 
 To have a major number allocated, or a minor number in situations
 where that applies (e.g. busmice), please contact me with the
@@ -44,24 +44,32 @@ appropriate device information.      Also, if you have additional
 information regarding any of the devices listed below, or if I have
 made a mistake, I would greatly appreciate a note.
 
-I do, however, make two requests about the nature of your report.
+I do, however, make a few requests about the nature of your report.
 This is necessary for me to be able to keep this list up to date and
-correct in a timely manner.  First of all, *please* include the word
-"device" in the subject so your mail won't accidentally get buried!  I
-receive hundreds of email messages a day, so mail sent with other
-subjects may very well get lost in the avalanche.
+correct in a timely manner.  First of all, *please* send it to the
+correct address... <device@lanana.org>.  I receive hundreds of email
+messages a day, so mail sent to other addresses may very well get lost
+in the avalanche.  Please put in a descriptive subject, so I can find
+your mail again should I need to.  Too many people send me email
+saying just "device number request" in the subject.
 
 Second, please include a description of the device *in the same format
 as this list*. The reason for this is that it is the only way I have
 found to ensure I have all the requisite information to publish your
 device and avoid conflicts.
 
+Third, please don't assume that the distributed version of the list is
+up to date.  Due to the number of registrations I have to maintain it
+in "batch mode", so there is likely additional registrations that
+haven't been listed yet.
+
 Finally, sometimes I have to play "namespace police."  Please don't be
 offended.  I often get submissions for /dev names that would be bound
 to cause conflicts down the road.  I am trying to avoid getting in a
 situation where we would have to suffer an incompatible forward
-change.
-
+change.  Therefore, please consult with me *before* you make your
+device names and numbers in any way public, at least to the point
+where it would be at all difficult to get them changed.
 
 Your cooperation is appreciated.
 
@@ -79,16 +87,18 @@ Your cooperation is appreciated.
                  7 = /dev/full         Returns ENOSPC on write
                  8 = /dev/random       Nondeterministic random number gen.
                  9 = /dev/urandom      Faster, less secure random number gen.
+                10 = /dev/aio          Asyncronous I/O notification interface
     block      RAM disk
                  0 = /dev/ram0         First RAM disk
+                 1 = /dev/ram1         Second RAM disk
                    ...
-                 7 = /dev/ram7         Eighth RAM disk
-               250 = /dev/initrd       Initial RAM disk
+               250 = /dev/initrd       Initial RAM disk {2.6}
 
                Older kernels had /dev/ramdisk (1, 1) here.
                /dev/initrd refers to a RAM disk which was preloaded
-               by the boot loader.
-                   
+               by the boot loader; newer kernels use /dev/ram0 for
+               the initrd.
+
   2 char       Pseudo-TTY masters
                  0 = /dev/ptyp0        First PTY master
                  1 = /dev/ptyp1        Second PTY master
@@ -199,6 +209,8 @@ Your cooperation is appreciated.
                    ...
                255 = /dev/ttyS191      192nd UART serial port
 
+               UART serial ports refer to 8250/16450/16550 series devices.
+
                Older versions of the Linux kernel used this major
                number for BSD PTY devices.  As of Linux 2.1.115, this
                is no longer supported.  Use major numbers 2 and 3.
@@ -310,6 +322,8 @@ Your cooperation is appreciated.
                 10 = /dev/adbmouse     Apple Desktop Bus mouse
                 11 = /dev/vrtpanel     Vr41xx embedded touch panel
                 13 = /dev/vpcmouse     Connectix Virtual PC Mouse
+                14 = /dev/touchscreen/ucb1x00  UCB 1x00 touchscreen
+                15 = /dev/touchscreen/mk712    MK712 touchscreen
                128 = /dev/beep         Fancy beep device
                129 = /dev/modreq       Kernel module load request {2.6}
                130 = /dev/watchdog     Watchdog timer port
@@ -321,7 +335,7 @@ Your cooperation is appreciated.
                139 = /dev/openprom     SPARC OpenBoot PROM
                140 = /dev/relay8       Berkshire Products Octal relay card
                141 = /dev/relay16      Berkshire Products ISO-16 relay card
-               142 = /dev/msr          x86 model-specific registers
+               142 = /dev/msr          x86 model-specific registers {2.6}
                143 = /dev/pciconf      PCI configuration space
                144 = /dev/nvram        Non-volatile configuration RAM
                145 = /dev/hfmodem      Soundcard shortwave modem control {2.6}
@@ -363,6 +377,26 @@ Your cooperation is appreciated.
                182 = /dev/perfctr      Performance-monitoring counters
                183 = /dev/intel_rng    Intel i8x0 random number generator
                184 = /dev/cpu/microcode CPU microcode update interface
+               186 = /dev/atomicps     Atomic shapshot of process state data
+               187 = /dev/irnet        IrNET device
+               188 = /dev/smbusbios    SMBus BIOS
+               189 = /dev/ussp_ctl     User space serial port control
+               190 = /dev/crash        Mission Critical Linux crash dump facility
+               191 = /dev/pcl181       <information missing>
+               192 = /dev/nas_xbus     NAS xbus LCD/buttons access
+               193 = /dev/d7s          SPARC 7-segment display
+               194 = /dev/zkshim       Zero-Knowledge network shim control
+               195 = /dev/elographics/e2201    Elographics touchscreen E271-2201
+               198 = /dev/sexec        Signed executable interface
+               199 = /dev/scanners/cuecat :CueCat barcode scanner
+               200 = /dev/net/tun      TAP/TUN network device
+               201 = /dev/button/gulpb Transmeta GULP-B buttons
+               204 = /dev/video/em8300     EM8300 DVD decoder control
+               205 = /dev/video/em8300_mv  EM8300 DVD decoder video
+               206 = /dev/video/em8300_ma  EM8300 DVD decoder audio
+               207 = /dev/video/em8300_sp  EM8300 DVD decoder subpicture
+               208 = /dev/compaq/cpqphpc Compaq PCI Hot Plug Controller
+               209 = /dev/compaq/cpqrid  Compaq Remote Insight Driver
                240-255                 Reserved for local use
 
  11 char       Raw keyboard device
@@ -901,29 +935,62 @@ Your cooperation is appreciated.
                  0 = /dev/ttyL0        First RISCom port
                  1 = /dev/ttyL1        Second RISCom port
                    ...
-    block      Reserved for Mylex DAC960 PCI RAID controller
+    block      Mylex DAC960 PCI RAID controller; first controller
+                 0 = /dev/rd/c0d0      First disk, whole disk
+                 8 = /dev/rd/c0d1      Second disk, whole disk
+                   ...
+               248 = /dev/rd/c0d15     16th disk, whole disk
+
+               For partitions add:
+                 0 = /dev/rd/c?d?      Whole disk
+                 1 = /dev/rd/c?d?p1    First partition
+                   ...
+                 7 = /dev/rd/c?d?p7    Seventh partition
 
  49 char       SDL RISCom serial card - alternate devices
                  0 = /dev/cul0         Callout device for ttyL0
                  1 = /dev/cul1         Callout device for ttyL1
                    ...
-    block      Reserved for Mylex DAC960 PCI RAID controller
+    block      Mylex DAC960 PCI RAID controller; second controller
+                 0 = /dev/rd/c1d0      First disk, whole disk
+                 8 = /dev/rd/c1d1      Second disk, whole disk
+                   ...
+               248 = /dev/rd/c1d15     16th disk, whole disk
+
+               Partitions are handled as for major 48.
 
  50 char       Reserved for GLINT
-    block      Reserved for Mylex DAC960 PCI RAID controller
+
+    block      Mylex DAC960 PCI RAID controller; third controller
+                 0 = /dev/rd/c2d0      First disk, whole disk
+                 8 = /dev/rd/c2d1      Second disk, whole disk
+                   ...
+               248 = /dev/rd/c2d15     16th disk, whole disk
 
  51 char       Baycom radio modem
                  0 = /dev/bc0          First Baycom radio modem
                  1 = /dev/bc1          Second Baycom radio modem
                    ...
-    block      Reserved for Mylex DAC960 PCI RAID controller
+    block      Mylex DAC960 PCI RAID controller; fourth controller
+                 0 = /dev/rd/c3d0      First disk, whole disk
+                 8 = /dev/rd/c3d1      Second disk, whole disk
+                   ...
+               248 = /dev/rd/c3d15     16th disk, whole disk
+
+               Partitions are handled as for major 48.
 
  52 char       Spellcaster DataComm/BRI ISDN card
                  0 = /dev/dcbri0       First DataComm card
                  1 = /dev/dcbri1       Second DataComm card
                  2 = /dev/dcbri2       Third DataComm card
                  3 = /dev/dcbri3       Fourth DataComm card
-    block      Reserved for Mylex DAC960 PCI RAID controller
+    block      Mylex DAC960 PCI RAID controller; fifth controller
+                 0 = /dev/rd/c4d0      First disk, whole disk
+                 8 = /dev/rd/c4d1      Second disk, whole disk
+                   ...
+               248 = /dev/rd/c4d15     16th disk, whole disk
+
+               Partitions are handled as for major 48.
 
  53 char       BDM interface for remote debugging MC683xx microcontrollers
                  0 = /dev/pd_bdm0      PD BDM interface on lp0
@@ -939,7 +1006,13 @@ Your cooperation is appreciated.
                Domain Interface and ICD is the commercial interface
                by P&E.
 
-    block      Reserved for Mylex DAC960 PCI RAID controller
+    block      Mylex DAC960 PCI RAID controller; sixth controller
+                 0 = /dev/rd/c5d0      First disk, whole disk
+                 8 = /dev/rd/c5d1      Second disk, whole disk
+                   ...
+               248 = /dev/rd/c5d15     16th disk, whole disk
+
+               Partitions are handled as for major 48.
 
  54 char       Electrocardiognosis Holter serial card
                  0 = /dev/holter0      First Holter port
@@ -950,11 +1023,23 @@ Your cooperation is appreciated.
                <mseritan@ottonel.pub.ro> to transfer data from Holter
                24-hour heart monitoring equipment.
 
-    block      Reserved for Mylex DAC960 PCI RAID controller
+    block      Mylex DAC960 PCI RAID controller; seventh controller
+                 0 = /dev/rd/c6d0      First disk, whole disk
+                 8 = /dev/rd/c6d1      Second disk, whole disk
+                   ...
+               248 = /dev/rd/c6d15     16th disk, whole disk
+
+               Partitions are handled as for major 48.
 
  55 char       DSP56001 digital signal processor
                  0 = /dev/dsp56k       First DSP56001
-    block      Reserved for Mylex DAC960 PCI RAID controller
+    block      Mylex DAC960 PCI RAID controller; eigth controller
+                 0 = /dev/rd/c7d0      First disk, whole disk
+                 8 = /dev/rd/c7d1      Second disk, whole disk
+                   ...
+               248 = /dev/rd/c7d15     16th disk, whole disk
+
+               Partitions are handled as for major 48.
 
  56 char       Apple Desktop Bus
                  0 = /dev/adb          ADB bus control
@@ -1001,6 +1086,8 @@ Your cooperation is appreciated.
                running small fs translation drivers) through serial /
                IRDA / parallel links.
 
+               NAMING CONFLICT -- PROPOSED REVISED NAME /dev/rpda0 etc
+
  60-63         LOCAL/EXPERIMENTAL USE
                Allocated for local/experimental use.  For devices not
                assigned official numbers, these ranges should be
@@ -1175,6 +1262,16 @@ Your cooperation is appreciated.
                    ...
                255 = /dev/cuf255       Callout device for ttyF255
 
+    block      Compaq Intelligent Drive Array, first controller
+                 0 = /dev/ida/c0d0     First logical drive whole disk
+                16 = /dev/ida/c0d1     Second logical drive whole disk
+                   ...
+               240 = /dev/ida/c0d15    16th logical drive whole disk
+
+               Partitions are handled the same way as for Mylex
+               DAC960 (see major number 48) except that the limit on
+               partitions is 15.
+
  73 char       Computone IntelliPort II serial card - control devices
                  0 = /dev/ip2ipl0      Loadware device for board 0
                  1 = /dev/ip2stat0     Status device for board 0
@@ -1185,6 +1282,16 @@ Your cooperation is appreciated.
                 12 = /dev/ip2ipl3      Loadware device for board 3
                 13 = /dev/ip2stat3     Status device for board 3
 
+    block      Compaq Intelligent Drive Array, second controller
+                 0 = /dev/ida/c1d0     First logical drive whole disk
+                16 = /dev/ida/c1d1     Second logical drive whole disk
+                   ...
+               240 = /dev/ida/c1d15    16th logical drive whole disk
+
+               Partitions are handled the same way as for Mylex
+               DAC960 (see major number 48) except that the limit on
+               partitions is 15.
+
  74 char       SCI bridge
                  0 = /dev/SCI/0        SCI device 0
                  1 = /dev/SCI/1        SCI device 1
@@ -1193,6 +1300,16 @@ Your cooperation is appreciated.
                Currently for Dolphin Interconnect Solutions' PCI-SCI
                bridge.
 
+    block      Compaq Intelligent Drive Array, third controller
+                 0 = /dev/ida/c2d0     First logical drive whole disk
+                16 = /dev/ida/c2d1     Second logical drive whole disk
+                   ...
+               240 = /dev/ida/c2d15    16th logical drive whole disk
+
+               Partitions are handled the same way as for Mylex
+               DAC960 (see major number 48) except that the limit on
+               partitions is 15.
+
  75 char       Specialix IO8+ serial card
                  0 = /dev/ttyW0        First IO8+ port, first card
                  1 = /dev/ttyW1        Second IO8+ port, first card
@@ -1200,6 +1317,16 @@ Your cooperation is appreciated.
                  8 = /dev/ttyW8        First IO8+ port, second card
                    ...
 
+    block      Compaq Intelligent Drive Array, fourth controller
+                 0 = /dev/ida/c3d0     First logical drive whole disk
+                16 = /dev/ida/c3d1     Second logical drive whole disk
+                   ...
+               240 = /dev/ida/c3d15    16th logical drive whole disk
+
+               Partitions are handled the same way as for Mylex
+               DAC960 (see major number 48) except that the limit on
+               partitions is 15.
+
  76 char       Specialix IO8+ serial card - alternate devices
                  0 = /dev/cuw0         Callout device for ttyW0
                  1 = /dev/cuw1         Callout device for ttyW1
@@ -1207,19 +1334,63 @@ Your cooperation is appreciated.
                  8 = /dev/cuw8         Callout device for ttyW8
                    ...
 
+    block      Compaq Intelligent Drive Array, fifth controller
+                 0 = /dev/ida/c4d0     First logical drive whole disk
+                16 = /dev/ida/c4d1     Second logical drive whole disk
+                   ...
+               240 = /dev/ida/c4d15    16th logical drive whole disk
+
+               Partitions are handled the same way as for Mylex
+               DAC960 (see major number 48) except that the limit on
+               partitions is 15.
+
+
  77 char       ComScire Quantum Noise Generator
                  0 = /dev/qng          ComScire Quantum Noise Generator
 
+    block      Compaq Intelligent Drive Array, sixth controller
+                 0 = /dev/ida/c5d0     First logical drive whole disk
+                16 = /dev/ida/c5d1     Second logical drive whole disk
+                   ...
+               240 = /dev/ida/c5d15    16th logical drive whole disk
+
+               Partitions are handled the same way as for Mylex
+               DAC960 (see major number 48) except that the limit on
+               partitions is 15.
+
+
  78 char       PAM Software's multimodem boards
                  0 = /dev/ttyM0        First PAM modem
                  1 = /dev/ttyM1        Second PAM modem
                    ...
 
+    block      Compaq Intelligent Drive Array, seventh controller
+                 0 = /dev/ida/c6d0     First logical drive whole disk
+                16 = /dev/ida/c6d1     Second logical drive whole disk
+                   ...
+               240 = /dev/ida/c6d15    16th logical drive whole disk
+
+               Partitions are handled the same way as for Mylex
+               DAC960 (see major number 48) except that the limit on
+               partitions is 15.
+
+
  79 char       PAM Software's multimodem boards - alternate devices
                  0 = /dev/cum0         Callout device for ttyM0
                  1 = /dev/cum1         Callout device for ttyM1
                    ...
 
+    block      Compaq Intelligent Drive Array, eigth controller
+                 0 = /dev/ida/c7d0     First logical drive whole disk
+                16 = /dev/ida/c7d1     Second logical drive whole disk
+                   ...
+               240 = /dev/ida/c715     16th logical drive whole disk
+
+               Partitions are handled the same way as for Mylex
+               DAC960 (see major number 48) except that the limit on
+               partitions is 15.
+
+
  80 char       Photometrics AT200 CCD camera
                  0 = /dev/at200        Photometrics AT200 CCD camera
 
@@ -1496,14 +1667,13 @@ Your cooperation is appreciated.
                  1 = /dev/parport1     Second parallel port
                    ...
 
-100 char       POTS (analogue telephone) A/B port {2.6}
-                 0 = /dev/phone0       First telephone port
-                 1 = /dev/phone1       Second telephone port
-                   ...
+    block      JavaStation flash disk
+                 0 = /dev/jsfd         JavaStation flash disk
 
-               The names have been reallocated to Telephony For
-               Linux, major 159.  All use of major 100 should be
-               considered legacy and deprecated.
+100 char       Telephony for Linux
+                 0 = /dev/phone0       First telephony device
+                 1 = /dev/phone1       Second telephony device
+                   ...
 
 101 char       Motorola DSP 56xxx board
                  0 = /dev/mdspstat     Status information
@@ -1511,6 +1681,19 @@ Your cooperation is appreciated.
                    ...
                 16 = /dev/mdsp16       16th DSP board I/O controls
 
+    block      AMI HyperDisk RAID controller
+                 0 = /dev/amiraid/ar0  First array whole disk
+                16 = /dev/amiraid/ar1  Second array whole disk
+                   ...
+               240 = /dev/amiraid/ar15 16th array whole disk
+
+               For each device, partitions are added as:
+                 0 = /dev/amiraid/ar?    Whole disk
+                 1 = /dev/amiraid/ar?p1  First partition
+                 2 = /dev/amiraid/ar?p2  Second partition
+                   ...
+                15 = /dev/amiraid/ar?p15 15th partition
+
 102 char       Philips SAA5249 Teletext signal decoder {2.6}
                  0 = /dev/tlk0         First Teletext decoder
                  1 = /dev/tlk1         Second Teletext decoder
@@ -1527,36 +1710,119 @@ Your cooperation is appreciated.
                to the arla announce mailing list by sending a mail to
                <arla-announce-request@stacken.kth.se>.
 
+    block      Audit device
+                 0 = /dev/audit        Audit device
+
 104 char       Flash BIOS support
 
+    block      Compaq Next Generation Drive Array, first controller
+                 0 = /dev/cciss/c0d0   First logical drive, whole disk
+                16 = /dev/cciss/c0d1   Second logical drive, whole disk
+                   ...
+               240 = /dev/cciss/c0d15  16th logical drive, whole disk
+
+               Partitions are handled the same way as for Mylex
+               DAC960 (see major number 48) except that the limit on
+               partitions is 15.
+
 105 char       Comtrol VS-1000 serial controller
                  0 = /dev/ttyV0        First VS-1000 port
                  1 = /dev/ttyV1        Second VS-1000 port
                    ...
 
+    block      Compaq Next Generation Drive Array, second controller
+                 0 = /dev/cciss/c1d0   First logical drive, whole disk
+                16 = /dev/cciss/c1d1   Second logical drive, whole disk
+                   ...
+               240 = /dev/cciss/c1d15  16th logical drive, whole disk
+
+               Partitions are handled the same way as for Mylex
+               DAC960 (see major number 48) except that the limit on
+               partitions is 15.
+
 106 char       Comtrol VS-1000 serial controller - alternate devices
                  0 = /dev/cuv0         First VS-1000 port
                  1 = /dev/cuv1         Second VS-1000 port
                    ...
 
+    block      Compaq Next Generation Drive Array, third controller
+                 0 = /dev/cciss/c2d0   First logical drive, whole disk
+                16 = /dev/cciss/c2d1   Second logical drive, whole disk
+                   ...
+               240 = /dev/cciss/c2d15  16th logical drive, whole disk
+
+               Partitions are handled the same way as for Mylex
+               DAC960 (see major number 48) except that the limit on
+               partitions is 15.
+
 107 char       3Dfx Voodoo Graphics device
                  0 = /dev/3dfx         Primary 3Dfx graphics device
 
+    block      Compaq Next Generation Drive Array, fourth controller
+                 0 = /dev/cciss/c3d0   First logical drive, whole disk
+                16 = /dev/cciss/c3d1   Second logical drive, whole disk
+                   ...
+               240 = /dev/cciss/c3d15  16th logical drive, whole disk
+
+               Partitions are handled the same way as for Mylex
+               DAC960 (see major number 48) except that the limit on
+               partitions is 15.
+
 108 char       Device independent PPP interface
                  0 = /dev/ppp          Device independent PPP interface
 
+    block      Compaq Next Generation Drive Array, fifth controller
+                 0 = /dev/cciss/c4d0   First logical drive, whole disk
+                16 = /dev/cciss/c4d1   Second logical drive, whole disk
+                   ...
+               240 = /dev/cciss/c4d15  16th logical drive, whole disk
+
+               Partitions are handled the same way as for Mylex
+               DAC960 (see major number 48) except that the limit on
+               partitions is 15.
+
 109 char       Reserved for logical volume manager
 
+    block      Compaq Next Generation Drive Array, sixth controller
+                 0 = /dev/cciss/c5d0   First logical drive, whole disk
+                16 = /dev/cciss/c5d1   Second logical drive, whole disk
+                   ...
+               240 = /dev/cciss/c5d15  16th logical drive, whole disk
+
+               Partitions are handled the same way as for Mylex
+               DAC960 (see major number 48) except that the limit on
+               partitions is 15.
+
 110 char       miroMEDIA Surround board
                  0 = /dev/srnd0        First miroMEDIA Surround board
                  1 = /dev/srnd1        Second miroMEDIA Surround board
                    ...
 
+    block      Compaq Next Generation Drive Array, seventh controller
+                 0 = /dev/cciss/c6d0   First logical drive, whole disk
+                16 = /dev/cciss/c6d1   Second logical drive, whole disk
+                   ...
+               240 = /dev/cciss/c6d15  16th logical drive, whole disk
+
+               Partitions are handled the same way as for Mylex
+               DAC960 (see major number 48) except that the limit on
+               partitions is 15.
+
 111 char       Philips SAA7146-based audio/video card {2.6}
                  0 = /dev/av0          First A/V card
                  1 = /dev/av1          Second A/V card
                    ...
 
+    block      Compaq Next Generation Drive Array, eigth controller
+                 0 = /dev/cciss/c7d0   First logical drive, whole disk
+                16 = /dev/cciss/c7d1   Second logical drive, whole disk
+                   ...
+               240 = /dev/cciss/c7d15  16th logical drive, whole disk
+
+               Partitions are handled the same way as for Mylex
+               DAC960 (see major number 48) except that the limit on
+               partitions is 15.
+
 112 char       ISI serial card
                  0 = /dev/ttyM0        First ISI port
                  1 = /dev/ttyM1        Second ISI port
@@ -1717,10 +1983,7 @@ Your cooperation is appreciated.
                  1 = /dev/gfax1        GammaLink channel 1
                    ...
 
-159 char       Telephony for Linux
-                 0 = /dev/phone0       First telephony device
-                 1 = /dev/phone1       Second telephony device
-                   ...
+159            RESERVED
 
 160 char       General Purpose Instrument Bus (GPIB)
                  0 = /dev/gpib0        First GPIB bus
@@ -1944,7 +2207,244 @@ Your cooperation is appreciated.
                240 = /dev/mvideo/status15      16th device 
                    ...
 
-195-239                UNALLOCATED
+195 char       Nvidia graphics devices
+                 0 = /dev/nvidia0              First Nvidia card
+                 1 = /dev/nvidia1              Second Nvidia card
+                   ...
+               255 = /dev/nvidiactl            Nvidia card control device
+
+196-197                UNASSIGNED
+
+198 char       Total Impact TPMP2 quad coprocessor PCI card
+                 0 = /dev/tpmp2/0              First card
+                 1 = /dev/tpmp2/1              Second card
+                   ...
+
+199 char       Veritas volume manager (VxVM) volumes
+                 0 = /dev/vx/rdsk/*/*          First volume
+                 1 = /dev/vx/rdsk/*/*          Second volume
+                   ...
+    block      Veritas volume manager (VxVM) volumes
+                 0 = /dev/vx/dsk/*/*           First volume
+                 1 = /dev/vx/dsk/*/*           First volume
+                   ...
+
+               The namespace in these directories is maintained by
+               the user space VxVM software.
+
+200 char       Veritas VxVM configuration interface
+                  0 = /dev/vx/config           Configuration access node
+                  1 = /dev/vx/trace            Volume i/o trace access node
+                  2 = /dev/vx/iod              Volume i/o daemon access node
+                  3 = /dev/vx/info             Volume information access node
+                  4 = /dev/vx/task             Volume tasks access node
+                  5 = /dev/vx/taskmon          Volume tasks monitor daemon
+
+201 char       Veritas VxVM dynamic multipathing driver
+                 0 = /dev/vx/rdmp/*            First multipath device
+                 1 = /dev/vx/rdmp/*            Second multipath device
+                   ...
+    block      Veritas VxVM dynamic multipathing driver
+                 0 = /dev/vx/dmp/*             First multipath device
+                 1 = /dev/vx/dmp/*             Second multipath device
+                   ...
+
+               The namespace in these directories is maintained by
+               the user space VxVM software.
+
+202 char       CPU model-specific registers
+                 0 = /dev/cpu/0/msr            MSRs on CPU 0
+                 1 = /dev/cpu/1/msr            MSRs on CPU 1
+                   ...
+
+203 char       CPU CPUID information
+                 0 = /dev/cpu/0/cpuid          CPUID on CPU 0
+                 1 = /dev/cpu/1/cpuid          CPUID on CPU 1
+                   ...
+
+204 char       Low-density serial ports
+                 0 = /dev/ttyLU0               LinkUp Systems L72xx UART - port 0
+                 1 = /dev/ttyLU1               LinkUp Systems L72xx UART - port 1
+                 2 = /dev/ttyLU2               LinkUp Systems L72xx UART - port 2
+                 3 = /dev/ttyLU3               LinkUp Systems L72xx UART - port 3
+                 4 = /dev/ttyFB0               Intel Footbridge (ARM)
+                 5 = /dev/ttySA0               StrongARM builtin serial port 0
+                 6 = /dev/ttySA1               StrongARM builtin serial port 1
+                 7 = /dev/ttySA2               StrongARM builtin serial port 2
+                 8 = /dev/ttySC0               SCI serial port (SuperH) - port 0
+                 9 = /dev/ttySC1               SCI serial port (SuperH) - port 1
+                10 = /dev/ttySC2               SCI serial port (SuperH) - port 2
+                11 = /dev/ttySC3               SCI serial port (SuperH) - port 3
+                12 = /dev/ttyFW0               Firmware console - port 0
+                13 = /dev/ttyFW1               Firmware console - port 1
+                14 = /dev/ttyFW2               Firmware console - port 2
+                15 = /dev/ttyFW3               Firmware console - port 3
+                16 = /dev/ttyAM0               ARM "AMBA" serial port 0
+                   ...
+                31 = /dev/ttyAM15              ARM "AMBA" serial port 15
+
+205 char       Low-density serial ports (alternate device)
+                 0 = /dev/culu0                Callout device for ttyLU0
+                 1 = /dev/culu1                Callout device for ttyLU1
+                 2 = /dev/culu2                Callout device for ttyLU2
+                 3 = /dev/culu3                Callout device for ttyLU3
+                 4 = /dev/cufb0                Callout device for ttyFB0
+                 5 = /dev/cusa0                Callout device for ttySA0
+                 6 = /dev/cusa1                Callout device for ttySA1
+                 7 = /dev/cusa2                Callout device for ttySA2
+                 8 = /dev/cusc0                Callout device for ttySC0
+                 9 = /dev/cusc1                Callout device for ttySC1
+                10 = /dev/cusc2                Callout device for ttySC2
+                11 = /dev/cusc3                Callout device for ttySC3
+                12 = /dev/cufw0                Callout device for ttyFW0
+                13 = /dev/cufw1                Callout device for ttyFW1
+                14 = /dev/cufw2                Callout device for ttyFW2
+                15 = /dev/cufw3                Callout device for ttyFW3
+                16 = /dev/cuam0                Callout device for ttyAM0
+                   ...
+                31 = /dev/cuam15               Callout device for ttyAM15
+
+206 char       OnStream SC-x0 tape devices
+                 0 = /dev/osst0                First OnStream SCSI tape, mode 0
+                 1 = /dev/osst1                Second OnStream SCSI tape, mode 0
+                   ...
+                32 = /dev/osst0l               First OnStream SCSI tape, mode 1
+                33 = /dev/osst1l               Second OnStream SCSI tape, mode 1
+                   ...
+                64 = /dev/osst0m               First OnStream SCSI tape, mode 2
+                65 = /dev/osst1m               Second OnStream SCSI tape, mode 2
+                   ...
+                96 = /dev/osst0a               First OnStream SCSI tape, mode 3
+                97 = /dev/osst1a               Second OnStream SCSI tape, mode 3
+                   ...
+               128 = /dev/nosst0               No rewind version of /dev/osst0
+               129 = /dev/nosst1               No rewind version of /dev/osst1
+                   ...
+               160 = /dev/nosst0l              No rewind version of /dev/osst0l
+               161 = /dev/nosst1l              No rewind version of /dev/osst1l
+                   ...
+               192 = /dev/nosst0m              No rewind version of /dev/osst0m
+               193 = /dev/nosst1m              No rewind version of /dev/osst1m
+                   ...
+               224 = /dev/nosst0a              No rewind version of /dev/osst0a
+               225 = /dev/nosst1a              No rewind version of /dev/osst1a
+                   ...
+
+               The OnStream SC-x0 SCSI tapes do not support the
+               standard SCSI SASD command set and therefore need
+               their own driver "osst". Note that the IDE, USB (and
+               maybe ParPort) versions may be driven via ide-scsi or
+               usb-storage SCSI emulation and this osst device and
+               driver as well.  The ADR-x0 drives are QIC-157
+               compliant and don't need osst.
+
+207 char       Compaq ProLiant health feature indicate
+                 0 = /dev/cpqhealth/cpqw       Redirector interface
+                 1 = /dev/cpqhealth/crom       EISA CROM
+                 2 = /dev/cpqhealth/cdt        Data Table
+                 3 = /dev/cpqhealth/cevt       Event Log
+                 4 = /dev/cpqhealth/casr       Automatic Server Recovery
+                 5 = /dev/cpqhealth/cecc       ECC Memory
+                 6 = /dev/cpqhealth/cmca       Machine Check Architecture
+                 7 = /dev/cpqhealth/ccsm       Deprecated CDT
+                 8 = /dev/cpqhealth/cnmi       NMI Handling
+                 9 = /dev/cpqhealth/css        Sideshow Management
+                10 = /dev/cpqhealth/cram       CMOS interface
+                11 = /dev/cpqhealth/cpci       PCI IRQ interface
+
+208 char       User space serial ports
+                 0 = /dev/ttyU0                First user space serial port
+                 1 = /dev/ttyU1                Second user space serial port
+                   ...
+
+209 char       User space serial ports (alternate devices)
+                 0 = /dev/cuu0                 Callout device for ttyU0
+                 1 = /dev/cuu1                 Callout device for ttyU1
+                   ...
+
+210 char       SBE, Inc. sync/async serial card
+                 0 = /dev/sbei/wxcfg0          Configuration device for board 0
+                 1 = /dev/sbei/dld0            Download device for board 0
+                 2 = /dev/sbei/wan00           WAN device, port 0, board 0
+                 3 = /dev/sbei/wan01           WAN device, port 1, board 0
+                 4 = /dev/sbei/wan02           WAN device, port 2, board 0
+                 5 = /dev/sbei/wan03           WAN device, port 3, board 0
+                 6 = /dev/sbei/wanc00          WAN clone device, port 0, board 0
+                 7 = /dev/sbei/wanc01          WAN clone device, port 1, board 0
+                 8 = /dev/sbei/wanc02          WAN clone device, port 2, board 0
+                 9 = /dev/sbei/wanc03          WAN clone device, port 3, board 0
+                10 = /dev/sbei/wxcfg1          Configuration device for board 1
+                11 = /dev/sbei/dld1            Download device for board 1
+                12 = /dev/sbei/wan10           WAN device, port 0, board 1
+                13 = /dev/sbei/wan11           WAN device, port 1, board 1
+                14 = /dev/sbei/wan12           WAN device, port 2, board 1
+                15 = /dev/sbei/wan13           WAN device, port 3, board 1
+                16 = /dev/sbei/wanc10          WAN clone device, port 0, board 1
+                17 = /dev/sbei/wanc11          WAN clone device, port 1, board 1
+                18 = /dev/sbei/wanc12          WAN clone device, port 2, board 1
+                19 = /dev/sbei/wanc13          WAN clone device, port 3, board 1
+                   ...
+
+               Yes, each board is really spaced 10 (decimal) apart.
+
+211 char       Addinum CPCI1500 digital I/O card
+                 0 = /dev/addinum/cpci1500/0   First CPCI1500 card
+                 1 = /dev/addinum/cpci1500/1   Second CPCI1500 card
+                   ...
+
+216 char       USB BlueTooth devices
+                 0 = /dev/ttyUB0               First USB BlueTooth device
+                 1 = /dev/ttyUB1               Second USB BlueTooth device
+                   ...
+
+217 char       USB BlueTooth devices (alternate devices)
+                 0 = /dev/cuub0                Callout device for ttyUB0
+                 1 = /dev/cuub1                Callout device for ttyUB1
+                   ...
+
+218 char       The Logical Company bus Unibus/Qbus adapters
+                 0 = /dev/logicalco/bci/0      First bus adapter
+                 1 = /dev/logicalco/bci/1      First bus adapter
+                   ...
+
+219 char       The Logical Company DCI-1300 digital I/O card
+                 0 = /dev/logicalco/dci1300/0  First DCI-1300 card
+                 1 = /dev/logicalco/dci1300/1  Second DCI-1300 card
+                   ...
+
+220 char       Myricom Myrinet "GM" board
+                 0 = /dev/myricom/gm0          First Myrinet GM board
+                 1 = /dev/myricom/gmp0         First board "root access"
+                 2 = /dev/myricom/gm1          Second Myrinet GM board
+                 3 = /dev/myricom/gmp1         Second board "root access"
+                   ...
+
+221 char       VME bus
+                 0 = /dev/bus/vme/m0           First master image
+                 1 = /dev/bus/vme/m1           Second master image
+                 2 = /dev/bus/vme/m2           Third master image
+                 3 = /dev/bus/vme/m3           Fourth master image
+                 4 = /dev/bus/vme/s0           First slave image
+                 5 = /dev/bus/vme/s1           Second slave image
+                 6 = /dev/bus/vme/s2           Third slave image
+                 7 = /dev/bus/vme/s3           Fourth slave image
+                 8 = /dev/bus/vme/ctl          Control
+
+               It is expected that all VME bus drivers will use the
+               same interface.  For interface documentation see
+               http://www.vmelinux.org/.
+
+224 char       A2232 serial card
+                 0 = /dev/ttyY0                First A2232 port
+                 1 = /dev/cuy0                 Second A2232 port
+                   ...
+
+225 char       A2232 serial card (alternate devices)
+                 0 = /dev/cuy0                 Callout device for ttyY0
+                 1 = /dev/cuy1                 Callout device for ttyY1
+                   ...
+
+226-239                UNASSIGNED
 
 240-254                LOCAL/EXPERIMENTAL USE
 
@@ -2025,6 +2525,14 @@ Non-transient sockets and named pipes may exist in /dev.  Common entries are:
 /dev/log       socket          syslog local socket
 /dev/gpmdata   socket          gpm mouse multiplexer
 
+       Mount points
+
+The following names are reserved for mounting special filesystems
+under /dev.  These special filesystems provide kernel interfaces that
+cannot be provided with standard device nodes.
+
+/dev/pts       devpts          PTY slave filesystem
+/dev/shm       shmfs           POSIX shared memory maintenance access
 
  ****  TERMINAL DEVICES
 
index 495c9a0be7ac3006cca469c2c02d478b99a9cfbb..e1f608b84aa4e70e8fb39126b7c3cbad2393c575 100644 (file)
@@ -43,6 +43,7 @@ restrictions referred to are that the relevant option is valid if:
        SERIAL  Serial support is enabled.
        SMP     The kernel is an SMP kernel.
        SOUND   Appropriate sound system support is enabled.
+       V4L     Video For Linux support is enabled.
        VGA     The VGA console has been enabled.
        VT      Virtual terminal support is enabled.
        XT      IBM PC/XT MFM hard disk support is enabled.
@@ -116,6 +117,11 @@ running once the system is up.
 
        bmouse=         [HW,MOUSE,PS2] Bus mouse.
 
+       bttv.card=      [HW,V4L] bttv (bt848 + bt878 based grabber cards), most
+       bttv.radio=     important insmod options are available as kernel args too.
+       bttv.pll=       see Documentation/video4linux/bttv/Insmod-options
+       bttv.tuner=     and Documentation/video4linux/bttv/CARDLIST
+
        BusLogic=       [HW,SCSI]
 
        cdu31a=         [HW,CD]
index 4cb55586dda30b357904e981d3306c67af1b3846..97b87f674c0f429c60e07040eafe4f0f384ebac7 100644 (file)
@@ -4,6 +4,7 @@ bttv.o
 
        insmod args:
                card=n          card type, see CARDLIST for a list.
+               tuner=n         tuner type, see CARDLIST for a list.
                radio=0/1       card supports radio
                pll=0/1/2       pll settings
                        0: don't use PLL
index 84c79d659cebfac114ec37e8bffe8d00779284c8..55f14650d8cd2237675e087a10c018cb238af987 100644 (file)
@@ -9,7 +9,3 @@ alias char-major-81-0   bttv
 options        bttv            card=2 radio=1
 options        tuner           debug=1
 
-# make alsa + msp3400 play nicely
-options        snd-card-ens    snd_index=0
-options        msp3400         mixer=1
-
index bce7e55eef08114035a39b61074a11d228351c8c..4af739a5cef18557046153af979e9aa6894f7751 100644 (file)
@@ -31,8 +31,9 @@ Compile bttv
 ------------
 
 If you are compiling the kernel version, just say 'm' if you are asked
-for bttv.  I /strongly/ suggest to compile bttv as module, because
-there are some insmod options for configuring the driver.
+for bttv.  I /strongly/ recommend to compile bttv as module, because
+there are some insmod options for configuring the driver.  Starting
+with 0.7.49 the most important ones are available as kernel args too.
 
 If you downloaded the separate bttv bundle:  You need configured kernel
 sources to compile the bttv driver.  The driver uses some Makefile
index 1308977149a2cd53163bd23841ae6580511ce4f0..5a67ef713891b2499bbd8717c9a050a6b209c44f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 0
-EXTRAVERSION = -test13-pre6
+EXTRAVERSION = -test13-pre7
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
index cb23a987d491d8173bd92136fb2070aa0221b132..1e3e485b5a594c94a956caaee09217e7a0a2bd6a 100644 (file)
@@ -35,7 +35,7 @@ OBJS =        __divqu.o __remqu.o __divlu.o __remlu.o \
        $(ev6)stxcpy.o \
        $(ev6)stxncpy.o \
        $(ev67)strchr.o \
-       strrchr.o \
+       $(ev67)strrchr.o \
        $(ev6)memchr.o \
        $(ev6)copy_user.o \
        $(ev6)clear_user.o \
index 7ebcbc27b7deb3903f5c93cd6d5b8a03d03909f1..c708a6fb96177d847793eb1febbb17725dd7d01e 100644 (file)
@@ -76,7 +76,7 @@ $single_head_quad:
 
 $do_unroll:
        addq    $16, 64, $7             # E : Initial (+1 trip) wh64 address
-       cmple   $18, 63, $1             # E : Can we go through the unrolled loop?
+       cmple   $18, 127, $1            # E : Can we go through the unrolled loop?
        bne     $1, $tail_quads         # U : Nope
        nop                             # E : 
 
diff --git a/arch/alpha/lib/ev67-strrchr.S b/arch/alpha/lib/ev67-strrchr.S
new file mode 100644 (file)
index 0000000..7fe1be0
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * arch/alpha/lib/ev67-strrchr.S
+ * 21264 version by Rick Gorton <rick.gorton@alpha-processor.com>
+ *
+ * Finds length of a 0-terminated string.  Optimized for the
+ * Alpha architecture:
+ *
+ *     - memory accessed as aligned quadwords only
+ *     - uses bcmpge to compare 8 bytes in parallel
+ *
+ * Much of the information about 21264 scheduling/coding comes from:
+ *     Compiler Writer's Guide for the Alpha 21264
+ *     abbreviated as 'CWG' in other comments here
+ *     ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html
+ * Scheduling notation:
+ *     E       - either cluster
+ *     U       - upper subcluster; U0 - subcluster U0; U1 - subcluster U1
+ *     L       - lower subcluster; L0 - subcluster L0; L1 - subcluster L1
+ */
+
+
+#include <alpha/regdef.h>
+
+       .set noreorder
+       .set noat
+
+       .align 4
+       .ent strrchr
+       .globl strrchr
+strrchr:
+       .frame sp, 0, ra
+       .prologue 0
+
+       and     a1, 0xff, t2    # E : 00000000000000ch
+       insbl   a1, 1, t4       # U : 000000000000ch00
+       insbl   a1, 2, t5       # U : 0000000000ch0000
+       ldq_u   t0, 0(a0)       # L : load first quadword Latency=3
+
+       mov     zero, t6        # E : t6 is last match aligned addr
+       or      t2, t4, a1      # E : 000000000000chch
+       sll     t5, 8, t3       # U : 00000000ch000000
+       mov     zero, t8        # E : t8 is last match byte compare mask
+
+       andnot  a0, 7, v0       # E : align source addr
+       or      t5, t3, t3      # E : 00000000chch0000
+       sll     a1, 32, t2      # U : 0000chch00000000
+       sll     a1, 48, t4      # U : chch000000000000
+
+       or      t4, a1, a1      # E : chch00000000chch
+       or      t2, t3, t2      # E : 0000chchchch0000
+       or      a1, t2, a1      # E : chchchchchchchch
+       lda     t5, -1          # E : build garbage mask
+
+       cmpbge  zero, t0, t1    # E : bits set iff byte == zero
+       mskqh   t5, a0, t4      # E : Complete garbage mask
+       xor     t0, a1, t2      # E : make bytes == c zero
+       cmpbge  zero, t4, t4    # E : bits set iff byte is garbage
+
+       cmpbge  zero, t2, t3    # E : bits set iff byte == c
+       andnot  t1, t4, t1      # E : clear garbage from null test
+       andnot  t3, t4, t3      # E : clear garbage from char test
+       bne     t1, $eos        # U : did we already hit the terminator?
+
+       /* Character search main loop */
+$loop:
+       ldq     t0, 8(v0)       # L : load next quadword
+       cmovne  t3, v0, t6      # E : save previous comparisons match
+       nop                     #   : Latency=2, extra map slot (keep nop with cmov)
+       nop
+
+       cmovne  t3, t3, t8      # E : Latency=2, extra map slot
+       nop                     #   : keep with cmovne
+       addq    v0, 8, v0       # E :
+       xor     t0, a1, t2      # E :
+
+       cmpbge  zero, t0, t1    # E : bits set iff byte == zero
+       cmpbge  zero, t2, t3    # E : bits set iff byte == c
+       beq     t1, $loop       # U : if we havnt seen a null, loop
+       nop
+
+       /* Mask out character matches after terminator */
+$eos:
+       negq    t1, t4          # E : isolate first null byte match
+       and     t1, t4, t4      # E :
+       subq    t4, 1, t5       # E : build a mask of the bytes upto...
+       or      t4, t5, t4      # E : ... and including the null
+
+       and     t3, t4, t3      # E : mask out char matches after null
+       cmovne  t3, t3, t8      # E : save it, if match found Latency=2, extra map slot
+       nop                     #   : Keep with cmovne
+       nop
+
+       cmovne  t3, v0, t6      # E :
+       nop                     #   : Keep with cmovne
+       /* Locate the address of the last matched character */
+       ctlz    t8, t2          # U0 : Latency=3 (0x40 for t8=0)
+       nop
+
+       cmoveq  t8, 0x3f, t2    # E : Compensate for case when no match is seen
+       nop                     # E : hide the cmov latency (2) behind ctlz latency
+       lda     t5, 0x3f($31)   # E :
+       subq    t5, t2, t5      # E : Normalize leading zero count
+
+       addq    t6, t5, v0      # E : and add to quadword address
+       ret                     # L0 : Latency=3
+       nop
+       nop
+
+       .end strrchr
index 3c8567e4e58ec9606e639bfc52a77b4afd4b087c..73aed92537d05ee434911bac894e8de2d597ecdb 100644 (file)
@@ -26,12 +26,16 @@ memmove:
        bne $1,memcpy
 
        and $2,7,$2                     /* Test for src/dest co-alignment.  */
-       bne $2,$misaligned
+       and $16,7,$1
+       cmpule $16,$17,$3
+       bne $3,$memmove_up              /* dest < src */
 
        and $4,7,$1
-       beq $1,$skip_aligned_byte_loop_head
+       bne $2,$misaligned_dn
+       unop
+       beq $1,$skip_aligned_byte_loop_head_dn
 
-$aligned_byte_loop_head:
+$aligned_byte_loop_head_dn:
        lda $4,-1($4)
        lda $5,-1($5)
        unop
@@ -48,13 +52,13 @@ $aligned_byte_loop_head:
        and $4,7,$6
 
        stq_u $1,0($4)
-       bne $6,$aligned_byte_loop_head
+       bne $6,$aligned_byte_loop_head_dn
 
-$skip_aligned_byte_loop_head:
+$skip_aligned_byte_loop_head_dn:
        lda $18,-8($18)
-       blt $18,$skip_aligned_word_loop
+       blt $18,$skip_aligned_word_loop_dn
 
-$aligned_word_loop:
+$aligned_word_loop_dn:
        ldq $1,-8($5)
        nop
        lda $5,-8($5)
@@ -63,22 +67,22 @@ $aligned_word_loop:
        stq $1,-8($4)
        nop
        lda $4,-8($4)
-       bge $18,$aligned_word_loop
+       bge $18,$aligned_word_loop_dn
 
-$skip_aligned_word_loop:
+$skip_aligned_word_loop_dn:
        lda $18,8($18)
-       bgt $18,$byte_loop_tail
+       bgt $18,$byte_loop_tail_dn
        unop
        ret $31,($26),1
 
        .align 4
-$misaligned:
+$misaligned_dn:
        nop
        fnop
        unop
        beq $18,$egress
 
-$byte_loop_tail:
+$byte_loop_tail_dn:
        ldq_u $3,-1($5)
        ldq_u $2,-1($4)
        lda $5,-1($5)
@@ -91,8 +95,77 @@ $byte_loop_tail:
 
        bis $1,$2,$1
        stq_u $1,0($4)
+       bgt $18,$byte_loop_tail_dn
+       br $egress
+
+$memmove_up:
+       mov $16,$4
+       mov $17,$5
+       bne $2,$misaligned_up
+       beq $1,$skip_aligned_byte_loop_head_up
+
+$aligned_byte_loop_head_up:
+       unop
+       ble $18,$egress
+       ldq_u $3,0($5)
+       ldq_u $2,0($4)
+
+       lda $18,-1($18)
+       extbl $3,$5,$1
+       insbl $1,$4,$1
+       mskbl $2,$4,$2
+
+       bis $1,$2,$1
+       lda $5,1($5)
+       stq_u $1,0($4)
+       lda $4,1($4)
+
+       and $4,7,$6
+       bne $6,$aligned_byte_loop_head_up
+
+$skip_aligned_byte_loop_head_up:
+       lda $18,-8($18)
+       blt $18,$skip_aligned_word_loop_up
+
+$aligned_word_loop_up:
+       ldq $1,0($5)
+       nop
+       lda $5,8($5)
+       lda $18,-8($18)
+
+       stq $1,0($4)
+       nop
+       lda $4,8($4)
+       bge $18,$aligned_word_loop_up
+
+$skip_aligned_word_loop_up:
+       lda $18,8($18)
+       bgt $18,$byte_loop_tail_up
+       unop
+       ret $31,($26),1
+
+       .align 4
+$misaligned_up:
+       nop
+       fnop
+       unop
+       beq $18,$egress
+
+$byte_loop_tail_up:
+       ldq_u $3,0($5)
+       ldq_u $2,0($4)
+       lda $18,-1($18)
+       extbl $3,$5,$1
+
+       insbl $1,$4,$1
+       mskbl $2,$4,$2
+       bis $1,$2,$1
+       stq_u $1,0($4)
+
+       lda $5,1($5)
+       lda $4,1($4)
        nop
-       bgt $18,$byte_loop_tail
+       bgt $18,$byte_loop_tail_up
 
 $egress:
        ret $31,($26),1
index 91e5ba660ecc4f7829730b6639cb6206f956c849..4486b79e338d5cbae1b3e26f329343e877a33885 100644 (file)
@@ -1,18 +1,22 @@
 #
 # Makefile for the FPU instruction emulation.
 #
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definition is now in the main makefile...
 
-O_TARGET := math-emu.o
-O_OBJS   := math.o qrnnd.o
 CFLAGS += -I. -I$(TOPDIR)/include/math-emu -w
 
-ifeq ($(CONFIG_MATHEMU),m)
-M_OBJS   := $(O_TARGET)
+ifeq ($(CONFIG_MATHEMU),y)
+
+O_TARGET       := math-emu.o
+obj-y          := math.o qrnnd.o
+
+else
+
+list-multi     := math-emu.o
+math-emu-objs  := math.o qrnnd.o
+obj-m          := math-emu.o
+math-emu.o: $(math-emu-objs)
+       $(LD) -r -o $@ $(math-emu-objs)
+
 endif
 
 include $(TOPDIR)/Rules.make
index 686821205500cdaa4a0b1d89129cdd3317e95e1f..ab052e2ff1a7eee11bd615cc12c0da10f008cf00 100644 (file)
@@ -412,7 +412,7 @@ void show_regs(struct pt_regs * regs)
 /*
  * No need to lock the MM as we are the last user
  */
-void destroy_context(struct mm_struct *mm)
+void release_segments(struct mm_struct *mm)
 {
        void * ldt = mm->context.segments;
 
@@ -493,7 +493,7 @@ void release_thread(struct task_struct *dead_task)
  * we do not have to muck with descriptors here, that is
  * done in switch_mm() as needed.
  */
-int init_new_context(struct task_struct *p, struct mm_struct *new_mm)
+void copy_segments(struct task_struct *p, struct mm_struct *new_mm)
 {
        struct mm_struct * old_mm;
        void *old_ldt, *ldt;
@@ -506,11 +506,11 @@ int init_new_context(struct task_struct *p, struct mm_struct *new_mm)
                 */
                ldt = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE);
                if (!ldt)
-                       return -ENOMEM;
-               memcpy(ldt, old_ldt, LDT_ENTRIES*LDT_ENTRY_SIZE);
+                       printk(KERN_WARNING "ldt allocation failed\n");
+               else
+                       memcpy(ldt, old_ldt, LDT_ENTRIES*LDT_ENTRY_SIZE);
        }
        new_mm->context.segments = ldt;
-       return 0;
 }
 
 /*
index 32351f653b8693af85fb05160f21470106cf3c19..9e140d1144660d31fa13dea556c3d159f0983153 100644 (file)
@@ -265,7 +265,7 @@ static int __init gemtek_init(void)
                return -EINVAL;
        }
 
-       if (request_region(io, 4, "gemtek")) 
+       if (!request_region(io, 4, "gemtek")) 
        {
                printk(KERN_ERR "gemtek: port 0x%x already in use\n", io);
                return -EBUSY;
index 886782f18f000c7819d8e0008a854df1cd259a24..d21eddec890cb4011bee3967743b7e6b1acb1f76 100644 (file)
@@ -1,7 +1,8 @@
 /*
-    bttv-cards.c  --  this file has card-specific stuff
+    bttv-cards.c
 
-    bttv - Bt848 frame grabber driver
+    this file has configuration informations - card-specific stuff
+    like the big tvcards array for the most part
 
     Copyright (C) 1996,97,98 Ralph  Metzler (rjkm@thp.uni-koeln.de)
                            & Marcus Metzler (mocm@thp.uni-koeln.de)
@@ -50,28 +51,55 @@ static void avermedia_tvphone_audio(struct bttv *btv, struct video_audio *v,
 static void terratv_audio(struct bttv *btv, struct video_audio *v, int set);
 static void gvbctv3pci_audio(struct bttv *btv, struct video_audio *v, int set);
 
-MODULE_PARM(card,"1-4i");
-MODULE_PARM_DESC(card,"specify TV/grabber card model, see CARDLIST file for a list");
-MODULE_PARM(pll,"1-4i");
-MODULE_PARM_DESC(pll,"specify installed crystal (0=none, 28=28 MHz, 35=35 MHz)");
-MODULE_PARM(autoload,"i");
-MODULE_PARM_DESC(autoload,"automatically load i2c modules like tuner.o, default is 1 (yes)");
-
-static unsigned int card[4] = { -1, -1, -1, -1 };
-static unsigned int pll[4]  = { -1, -1, -1, -1 };
+/* config variables */
+static int triton1=0;
+static unsigned int card[4]  = { -1, -1, -1, -1 };
+static unsigned int pll[4]   = { -1, -1, -1, -1 };
+static unsigned int tuner[4] = { -1, -1, -1, -1 };
 #ifdef MODULE
 static unsigned int autoload = 1;
 #else
 static unsigned int autoload = 0;
 #endif
+static unsigned int gpiomask = -1;
+static unsigned int audioall = -1;
+static unsigned int audiomux[5] = { -1, -1, -1, -1, -1 };
 
+/* insmod options */
+MODULE_PARM(triton1,"i");
+MODULE_PARM(card,"1-4i");
+MODULE_PARM_DESC(card,"specify TV/grabber card model, see CARDLIST file for a list");
+MODULE_PARM(pll,"1-4i");
+MODULE_PARM_DESC(pll,"specify installed crystal (0=none, 28=28 MHz, 35=35 MHz)");
+MODULE_PARM(tuner,"1-4i");
+MODULE_PARM_DESC(tuner,"specify installed tuner type");
+MODULE_PARM(autoload,"i");
+MODULE_PARM_DESC(autoload,"automatically load i2c modules like tuner.o, default is 1 (yes)");
 MODULE_PARM(gpiomask,"i");
 MODULE_PARM(audioall,"i");
 MODULE_PARM(audiomux,"1-5i");
-static unsigned int gpiomask = -1;
-static unsigned int audioall = -1;
-static unsigned int audiomux[5] = { -1, -1, -1, -1, -1 };
 
+/* kernel args */
+#ifndef MODULE
+static int __init p_card(char *str)  { return bttv_parse(str,BTTV_MAX,card);  }
+static int __init p_pll(char *str)   { return bttv_parse(str,BTTV_MAX,pll);   }
+static int __init p_tuner(char *str) { return bttv_parse(str,BTTV_MAX,tuner); }
+__setup("bttv.card=",  p_card);
+__setup("bttv.pll=",   p_pll);
+__setup("bttv.tuner=", p_tuner);
+
+int __init bttv_parse(char *str, int max, int *vals)
+{
+       int i,number,res = 2;
+       
+       for (i = 0; res == 2 && i < max; i++) {
+               res = get_option(&str,&number);
+               if (res)
+                       vals[i] = number;
+       }
+       return 1;
+}
+#endif
 
 /* ----------------------------------------------------------------------- */
 /* list of card IDs for bt878+ cards                                       */
@@ -176,12 +204,12 @@ struct tvcard bttv_tvcards[] = {
        tuner_type:     -1,
 },{
        name:           "Diamond DTV2000",
-       video_inputs:   3,
+       video_inputs:   4,
        audio_inputs:   1,
        tuner:          0,
        svhs:           2,
        gpiomask:       3,
-       muxsel:         { 2, 3, 1, 1},
+       muxsel:         { 2, 3, 1, 0},
        audiomux:       { 0, 1, 0, 1, 3},
        needs_tvaudio:  1,
        tuner_type:     -1,
@@ -606,7 +634,7 @@ struct tvcard bttv_tvcards[] = {
        audio_inputs:   4,
        tuner:          0,
        svhs:           2,
-       gpiomask:       4,
+       gpiomask:       12,
        muxsel:         { 2, 3, 1, 1},
        audiomux:       { 13, 14, 11, 7, 0, 0},
        needs_tvaudio:  1,
@@ -907,9 +935,11 @@ void __devinit bttv_init_card(struct bttv *btv)
                 }
         }
 
-       /* tuner configuration */
+       /* tuner configuration (from card list / insmod option) */
        if (-1 != bttv_tvcards[btv->type].tuner_type)
                 btv->tuner_type = bttv_tvcards[btv->type].tuner_type;
+       if (-1 != tuner[btv->nr])
+               btv->tuner_type = tuner[btv->nr];
        if (btv->tuner_type != -1)
                bttv_call_i2c_clients(btv,TUNER_SET_TYPE,&btv->tuner_type);
 
@@ -1296,7 +1326,26 @@ gvbctv3pci_audio(struct bttv *btv, struct video_audio *v, int set)
 static void
 avermedia_tvphone_audio(struct bttv *btv, struct video_audio *v, int set)
 {
-       /* TODO */
+#if 0 /* needs more testing -- might be we need two versions for PAL/NTSC */
+       int val = 0;
+
+       if (set) {
+               if (v->mode & VIDEO_SOUND_LANG1)   /* SAP */
+                       val = 0xce;
+               if (v->mode & VIDEO_SOUND_STEREO)
+                       val = 0xcd;
+               if (val) {
+                       btaor(val, 0xff, BT848_GPIO_OUT_EN);
+                       btaor(val, 0xff, BT848_GPIO_DATA);
+                       if (bttv_gpio)
+                               bttv_gpio_tracking(btv,"avermedia");
+               }
+       } else {
+               v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
+                       VIDEO_SOUND_LANG1;
+               return;
+       }
+#endif
 }
 
 static void
@@ -1319,6 +1368,74 @@ terratv_audio(struct bttv *btv, struct video_audio *v, int set)
        }
 }
 
+
+/* ----------------------------------------------------------------------- */
+/* motherboard chipset specific stuff                                      */
+
+static struct {
+       char            *name;
+       unsigned short  vendor;
+       unsigned short  device;
+} needs_etbf[] __devinitdata = {
+       { "Intel 82437FX [Triton PIIX]",
+         PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437  },
+       { "VIA VT82C597 [Apollo VP3]",
+         PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C597_0 },
+       { NULL, 0, 0 }
+};
+
+void __devinit bttv_check_chipset(void)
+{
+       int i;
+       struct pci_dev *dev = NULL;
+
+       if(pci_pci_problems & PCIPCI_FAIL)
+               printk(KERN_WARNING "BT848 and your chipset may not work together.\n");
+
+        while ((dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+                                     PCI_DEVICE_ID_INTEL_82441, dev))) {
+                unsigned char b;
+                pci_read_config_byte(dev, 0x53, &b);
+               if (bttv_debug)
+                       printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, "
+                              "bufcon=0x%02x\n",b);
+       }
+
+       if(pci_pci_problems & (PCIPCI_TRITON|PCIPCI_VIAETBF))
+       {
+               printk(KERN_INFO "bttv: Host bridge needs ETBF enabled.\n");
+               triton1=1;
+       }
+}
+
+int __devinit bttv_handle_chipset(struct bttv *btv)
+{
+       unsigned char command;
+
+       if (!triton1)
+               return 0;
+
+       if (bttv_verbose)
+               printk("bttv%d: enabling 430FX/VP3 compatibilty\n",btv->nr);
+
+       if (btv->id < 878) {
+               /* bt848 (mis)uses a bit in the irq mask */
+               btv->triton1 = BT848_INT_ETBF;
+       } else {
+               /* bt878 has a bit in the pci config space for it */
+                pci_read_config_byte(btv->dev, BT878_DEVCTRL, &command);
+                command |= BT878_EN_TBFX;
+                pci_write_config_byte(btv->dev, BT878_DEVCTRL, command);
+                pci_read_config_byte(btv->dev, BT878_DEVCTRL, &command);
+                if (!(command&BT878_EN_TBFX)) {
+                        printk("bttv: 430FX compatibility could not be enabled\n");
+                       return -1;
+                }
+        }
+       return 0;
+}
+
+
 #ifndef MODULE
 
 static int __init bttv_card_setup(char *str)
index 248bda8a02f0c0fdcf10a497d7e0fdf5b1715266..7ce4a4cf89393b2754c255964fd95491a0a9effa 100644 (file)
@@ -57,8 +57,23 @@ static void bt848_set_risc_jmps(struct bttv *btv, int state);
 int bttv_num;                  /* number of Bt848s in use */
 struct bttv bttvs[BTTV_MAX];
 
-/* insmod args */
-MODULE_PARM(triton1,"i");
+/* configuration variables */
+#if defined(__sparc__) || defined(__powerpc__) || defined(__hppa__)
+static unsigned int bigendian=1;
+#else
+static unsigned int bigendian=0;
+#endif
+static unsigned int radio[BTTV_MAX];
+static unsigned int fieldnr = 0;
+static unsigned int irq_debug = 0;
+static unsigned int gbuffers = 2;
+static unsigned int gbufsize = BTTV_MAX_FBUF;
+static unsigned int combfilter = 0;
+unsigned int bttv_debug = 0;
+unsigned int bttv_verbose = 1;
+unsigned int bttv_gpio = 0;
+
+/* insmod options */
 MODULE_PARM(radio,"1-4i");
 MODULE_PARM_DESC(radio,"The TV card supports radio, default is 0 (no)");
 MODULE_PARM(bigendian,"i");
@@ -82,21 +97,11 @@ MODULE_PARM(combfilter,"i");
 MODULE_DESCRIPTION("bttv - v4l driver module for bt848/878 based cards");
 MODULE_AUTHOR("Ralph  Metzler & Marcus Metzler & Gerd Knorr");
 
-#if defined(__sparc__) || defined(__powerpc__)
-static unsigned int bigendian=1;
-#else
-static unsigned int bigendian=0;
+/* kernel args */
+#ifndef MODULE
+static int __init p_radio(char *str) { return bttv_parse(str,BTTV_MAX,radio); }
+__setup("bttv.radio=", p_radio);
 #endif
-static int triton1=0;
-static unsigned int radio[BTTV_MAX];
-static unsigned int fieldnr = 0;
-static unsigned int irq_debug = 0;
-static unsigned int gbuffers = 2;
-static unsigned int gbufsize = BTTV_MAX_FBUF;
-static unsigned int combfilter = 0;
-unsigned int bttv_debug = 0;
-unsigned int bttv_verbose = 1;
-unsigned int bttv_gpio = 0;
 
 #define I2C_TIMING (0x7<<4)
 #define I2C_DELAY   10
@@ -382,7 +387,8 @@ static int set_pll(struct bttv *btv)
                         /* printk ("bttv%d: PLL: is off\n",btv->nr); */
                         return 0;
                 }
-                printk ("bttv%d: PLL: switching off\n",btv->nr);
+               if (bttv_verbose)
+                       printk ("bttv%d: PLL: switching off\n",btv->nr);
                 btwrite(0x00,BT848_TGCTRL);
                 btwrite(0x00,BT848_PLL_XCI);
                 btv->pll.pll_current = 0;
@@ -2341,44 +2347,6 @@ static struct video_device radio_template=
 };
 
 
-#define  TRITON_PCON              0x50 
-#define  TRITON_BUS_CONCURRENCY   (1<<0)
-#define  TRITON_STREAMING        (1<<1)
-#define  TRITON_WRITE_BURST      (1<<2)
-#define  TRITON_PEER_CONCURRENCY  (1<<3)
-  
-
-static void __devinit handle_chipset(void)
-{
-       struct pci_dev *dev = NULL;
-  
-       /*      Just in case some nut set this to something dangerous */
-       if (triton1)
-               triton1=BT848_INT_ETBF;
-       
-       while ((dev = pci_find_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_496, dev))) 
-       {
-               /* Beware the SiS 85C496 my friend - rev 49 don't work with a bttv */
-               printk(KERN_WARNING "BT848 and SIS 85C496 chipset don't always work together.\n");
-       }                       
-
-        while ((dev = pci_find_device(PCI_VENDOR_ID_INTEL,
-                                     PCI_DEVICE_ID_INTEL_82441, dev))) 
-        {
-                unsigned char b;
-                pci_read_config_byte(dev, 0x53, &b);
-                DEBUG(printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, "));
-                DEBUG(printk("bufcon=0x%02x\n",b));
-        }
-
-       while ((dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437, dev))) 
-       {
-               printk(KERN_INFO "bttv: Host bridge 82437FX Triton PIIX\n");
-               triton1=BT848_INT_ETBF;
-       }
-}
-
-
 static void bt848_set_risc_jmps(struct bttv *btv, int flags)
 {
        if (-1 == flags) {
@@ -2485,10 +2453,6 @@ static void bt848_set_risc_jmps(struct bttv *btv, int flags)
 
 static int __devinit init_video_dev(struct bttv *btv)
 {
-       memcpy(&btv->video_dev,&bttv_template, sizeof(bttv_template));
-       memcpy(&btv->vbi_dev,&vbi_template, sizeof(vbi_template));
-       memcpy(&btv->radio_dev,&radio_template,sizeof(radio_template));
-        
        audio(btv, AUDIO_MUTE, 1);
         
        if(video_register_device(&btv->video_dev,VFL_TYPE_GRABBER)<0)
@@ -2914,7 +2878,7 @@ static void __devexit bttv_remove(struct pci_dev *pci_dev)
 static int __devinit bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id)
 {
        int result;
-       unsigned char command,lat;
+       unsigned char lat;
        struct bttv *btv;
 #if defined(__powerpc__)
         unsigned int cmd;
@@ -2936,7 +2900,11 @@ static int __devinit bttv_probe(struct pci_dev *dev, const struct pci_device_id
        btv->s_lock = SPIN_LOCK_UNLOCKED;
        init_waitqueue_head(&btv->gpioq);
        btv->shutdown=0;
-
+       
+       memcpy(&btv->video_dev,&bttv_template, sizeof(bttv_template));
+       memcpy(&btv->vbi_dev,&vbi_template, sizeof(vbi_template));
+       memcpy(&btv->radio_dev,&radio_template,sizeof(radio_template));
+       
         btv->id=dev->device;
         btv->irq=dev->irq;
        btv->bt848_adr=pci_resource_start(dev,0);
@@ -2985,36 +2953,22 @@ static int __devinit bttv_probe(struct pci_dev *dev, const struct pci_device_id
         {
                 printk(KERN_ERR "bttv%d: Bad irq number or handler\n",
                        bttv_num);
-               goto fail;
+               goto fail1;
         }
         if (result==-EBUSY)
         {
                 printk(KERN_ERR "bttv%d: IRQ %d busy, change your PnP config in BIOS\n",bttv_num,btv->irq);
-               goto fail;
+               goto fail1;
         }
         if (result < 0) 
-               goto fail;
+               goto fail1;
         
+       if (0 != bttv_handle_chipset(btv)) {
+               result = -1;
+               goto fail2;
+       }
+       
         pci_set_master(dev);
-
-        btv->triton1=triton1 ? BT848_INT_ETBF : 0;
-        if (triton1 && btv->id >= 878) 
-        {
-                btv->triton1 = 0;
-                printk("bttv: Enabling 430FX compatibilty for bt878\n");
-                pci_read_config_byte(dev, BT878_DEVCTRL, &command);
-                command|=BT878_EN_TBFX;
-                pci_write_config_byte(dev, BT878_DEVCTRL, command);
-                pci_read_config_byte(dev, BT878_DEVCTRL, &command);
-                if (!(command&BT878_EN_TBFX)) 
-                {
-                        printk("bttv: 430FX compatibility could not be enabled\n");
-                       free_irq(btv->irq,btv);
-                       result = -1;
-                       goto fail;
-                }
-        }
-
        pci_set_drvdata(dev,btv);
 
        if(init_bt848(btv) < 0) {
@@ -3024,8 +2978,10 @@ static int __devinit bttv_probe(struct pci_dev *dev, const struct pci_device_id
        bttv_num++;
 
         return 0;
-       
- fail:
+
+ fail2:
+        free_irq(btv->irq,btv);
+ fail1:
        release_mem_region(pci_resource_start(btv->dev,0),
                           pci_resource_len(btv->dev,0));
        return result;
@@ -3068,7 +3024,7 @@ int bttv_init_module(void)
                printk(KERN_INFO "bttv: using %d buffers with %dk (%dk total) for capture\n",
                       gbuffers,gbufsize/1024,gbuffers*gbufsize/1024);
 
-       handle_chipset();
+       bttv_check_chipset();
 
        return pci_module_init(&bttv_pci_driver);
 }
@@ -3082,24 +3038,6 @@ void bttv_cleanup_module(void)
 module_init(bttv_init_module);
 module_exit(bttv_cleanup_module);
 
-#ifndef MODULE
-
-static int __init bttv_radio_setup(char *str)
-{
-       int i,number,res = 2;
-
-       for (i = 0; res == 2 && i < BTTV_MAX; i++) {
-               res = get_option(&str,&number);
-               if (res)
-                       radio[i] = number;
-       }
-       return 1;
-}
-
-__setup("bttv_radio=", bttv_radio_setup);
-
-#endif /* not MODULE */
-
 /*
  * Local variables:
  * c-basic-offset: 8
index c7aaa8bf3b5a80a2cb8c1fa6481e8f2eb2dc70fd..c88e0a906080617d36b5e590149a92be818d771a 100644 (file)
@@ -134,6 +134,13 @@ extern void bttv_init_card(struct bttv *btv);
 extern void tea5757_set_freq(struct bttv *btv, unsigned short freq);
 extern void bttv_hauppauge_boot_msp34xx(struct bttv *btv);
 
+/* kernel cmd line parse helper */
+extern int bttv_parse(char *str, int max, int *vals);
+
+/* extra tweaks for some chipsets */
+extern void bttv_check_chipset(void);
+extern int bttv_handle_chipset(struct bttv *btv);
+
 /* ---------------------------------------------------------- */
 /* exported by bttv-if.c                                      */
 /* interface for gpio access by other modules                 */
index 4676ee61b5ac8d06e0b6aace433c9751e3bbc64b..40fa9c5c680ac637be16b52bda099a5c48457145 100644 (file)
@@ -25,7 +25,7 @@
 #ifndef _BTTVP_H_
 #define _BTTVP_H_
 
-#define BTTV_VERSION_CODE KERNEL_VERSION(0,7,48)
+#define BTTV_VERSION_CODE KERNEL_VERSION(0,7,50)
 
 
 #include <linux/types.h>
@@ -42,7 +42,7 @@
 /* ---------------------------------------------------------- */
 /* bttv-driver.c                                              */
 
-/* insmod options */
+/* insmod options / kernel args */
 extern unsigned int bttv_verbose;
 extern unsigned int bttv_debug;
 extern unsigned int bttv_gpio;
index 12f9f0a9d1b212ffd6d5f051488daa2458b8abca..bf17e006fda41279be918eeaa36339cc97bbfd73 100644 (file)
@@ -158,15 +158,10 @@ sizeof(nop_cmd) = 8;
 
 /**************************************************************************/
 
-#define DELAY(x) {int i=jiffies; \
-                  if(loops_per_sec == 1) \
-                     while(time_after(i+(x), jiffies)); \
-                  else \
-                     __delay((loops_per_sec>>5)*x); \
-                 }
+#define DELAY(x) { mdelay(32 * x); }
 
 /* a much shorter delay: */
-#define DELAY_16(); { __delay( (loops_per_sec>>16)+1 ); }
+#define DELAY_16(); { udelay(16) ; }
 
 /* wait for command with timeout: */
 #define WAIT_4_SCB_CMD() { int i; \
index e578ca3f8c271c58b0be76557bf39ae8c304f61d..8c2a53c2e51e8ba50e94db9c9cdf609aa6a82e4c 100644 (file)
@@ -258,7 +258,7 @@ source drivers/net/tokenring/Config.in
 
 bool 'Fibre Channel driver support' CONFIG_NET_FC
 if [ "$CONFIG_NET_FC" = "y" ]; then
-   dep_tristate '  Interphase 5526 Tachyon chipset based adapter support' CONFIG_IPHASE5526 $CONFIG_SCSI
+   dep_tristate '  Interphase 5526 Tachyon chipset based adapter support' CONFIG_IPHASE5526 $CONFIG_SCSI $CONFIG_PCI
 fi
 
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
index 19bf1ef0fa708ee43ae95bdc31ba0c7eaaa2bd34..7bd21d874bfbf1ab1b4b55f5e65d1a0b48e73962 100644 (file)
@@ -2,7 +2,7 @@
  * acenic.c: Linux driver for the Alteon AceNIC Gigabit Ethernet card
  *           and other Tigon based cards.
  *
- * Copyright 1998-2000 by Jes Sorensen, <Jes.Sorensen@cern.ch>.
+ * Copyright 1998-2000 by Jes Sorensen, <jes@linuxcare.com>.
  *
  * Thanks to Alteon and 3Com for providing hardware and documentation
  * enabling me to write this driver.
  *                                       where the driver would disable
  *                                       bus master mode if it had to disable
  *                                       write and invalidate.
+ *   Stephen Hack <stephen_hack@hp.com>: Fixed ace_set_mac_addr for little
+ *                                       endian systems.
+ *   Val Henson <vhenson@esscom.com>:    Reset Jumbo skb producer and
+ *                                       rx producer index when
+ *                                       flushing the Jumbo ring.
  */
 
 #include <linux/config.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/mm.h>
+#include <linux/sockios.h>
 
-#undef INDEX_DEBUG
-
+#ifdef SIOCETHTOOL
 #include <linux/ethtool.h>
+#endif
+
 #include <net/sock.h>
 #include <net/ip.h>
 
@@ -69,6 +76,9 @@
 #include <asm/uaccess.h>
 
 
+#undef INDEX_DEBUG
+#define TX_HOST_RING   1
+
 #ifdef CONFIG_ACENIC_OMIT_TIGON_I
 #define ACE_IS_TIGON_I(ap)     0
 #else
 #define PCI_DEVICE_ID_NETGEAR_GA620T   0x630a
 #endif
 
+
 /*
  * Farallon used the DEC vendor ID by mistake and they seem not
  * to care - stinky!
 #define PCI_DEVICE_ID_SGI_ACENIC       0x0009
 #endif
 
+#if LINUX_VERSION_CODE >= 0x20400
+static struct pci_device_id acenic_pci_tbl[] __initdata = {
+       { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE,
+         PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, },
+       { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_COPPER,
+         PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, },
+       { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3C985,
+         PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, },
+       { PCI_VENDOR_ID_NETGEAR, PCI_DEVICE_ID_NETGEAR_GA620,
+         PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, },
+       { PCI_VENDOR_ID_NETGEAR, PCI_DEVICE_ID_NETGEAR_GA620T,
+         PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, },
+       /*
+        * Farallon used the DEC vendor ID on their cards incorrectly.
+        */
+       { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_FARALLON_PN9000SX,
+         PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, },
+       { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_ACENIC,
+         PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, },
+       { }
+};
+MODULE_DEVICE_TABLE(pci, acenic_pci_tbl);
+#endif
+
+
 #ifndef wmb
 #define wmb()  mb()
 #endif
 #define SMP_CACHE_BYTES        L1_CACHE_BYTES
 #endif
 
+#if (BITS_PER_LONG == 64)
+#define ACE_64BIT_PTR  1
+#endif
+
+#ifndef SET_MODULE_OWNER
+#define SET_MODULE_OWNER(dev)          {do{} while(0);}
+#define ACE_MOD_INC_USE_COUNT          MOD_INC_USE_COUNT
+#define ACE_MOD_DEC_USE_COUNT          MOD_DEC_USE_COUNT
+#else
+#define ACE_MOD_INC_USE_COUNT          {do{} while(0);}
+#define ACE_MOD_DEC_USE_COUNT          {do{} while(0);}
+#endif
+
 
 #if (LINUX_VERSION_CODE < 0x02030d)
 #define pci_resource_start(dev, bar)   dev->base_address[bar]
 #define net_device device
 #endif
 
-#if (LINUX_VERSION_CODE >= 0x02031b)
-#define NEW_NETINIT
-#endif
-
 #if (LINUX_VERSION_CODE < 0x02032a)
 typedef u32 dma_addr_t;
 
@@ -154,10 +199,20 @@ static inline void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
 #if (LINUX_VERSION_CODE < 0x02032b)
 /*
  * SoftNet
+ *
+ * For pre-softnet kernels we need to tell the upper layer not to
+ * re-enter start_xmit() while we are in there. However softnet
+ * guarantees not to enter while we are in there so there is no need
+ * to do the netif_stop_queue() dance unless the transmit queue really
+ * gets stuck. This should also improve performance according to tests
+ * done by Aman Singla.
  */
-#define dev_kfree_skb_irq(a)   dev_kfree_skb(a)
-#define netif_wake_queue(dev)  clear_bit(0, &dev->tbusy)
-#define netif_stop_queue(dev)  set_bit(0, &dev->tbusy)
+#define dev_kfree_skb_irq(a)                   dev_kfree_skb(a)
+#define netif_wake_queue(dev)                  clear_bit(0, &dev->tbusy)
+#define netif_stop_queue(dev)                  set_bit(0, &dev->tbusy)
+#define late_stop_netif_stop_queue(dev)                {do{} while(0);}
+#define early_stop_netif_stop_queue(dev)       test_and_set_bit(0,&dev->tbusy)
+#define early_stop_netif_wake_queue(dev)       netif_wake_queue(dev)
 
 static inline void netif_start_queue(struct net_device *dev)
 {
@@ -166,16 +221,42 @@ static inline void netif_start_queue(struct net_device *dev)
        dev->start = 1;
 }
 
-#define ace_mark_net_bh(foo)           mark_bh(foo)
-#define netif_queue_stopped(dev)       dev->tbusy
-#define netif_running(dev)             dev->start
-#define ace_if_down(dev)               {do{dev->start = 0;}while (0);}
+#define ace_mark_net_bh()                      mark_bh(NET_BH)
+#define netif_queue_stopped(dev)               dev->tbusy
+#define netif_running(dev)                     dev->start
+#define ace_if_down(dev)                       {do{dev->start = 0;} while(0);}
+
+#define tasklet_struct                         tq_struct
+static inline void tasklet_schedule(struct tasklet_struct *tasklet)
+{
+       queue_task(tasklet, &tq_immediate);
+       mark_bh(IMMEDIATE_BH);
+}
+
+static inline void tasklet_init(struct tasklet_struct *tasklet,
+                               void (*func)(unsigned long),
+                               unsigned long data)
+{
+       tasklet->next = NULL;
+       tasklet->sync = 0;
+       tasklet->routine = (void (*)(void *))func;
+       tasklet->data = (void *)data;
+}
+#define tasklet_kill(tasklet)                  {do{} while(0);}
 #else
-#define NET_BH                 0
-#define ace_mark_net_bh(foo)   {do{} while(0);}
-#define ace_if_down(dev)       {do{} while(0);}
+#define late_stop_netif_stop_queue(dev)                netif_stop_queue(dev)
+#define early_stop_netif_stop_queue(dev)       0
+#define early_stop_netif_wake_queue(dev)       {do{} while(0);}
+#define ace_mark_net_bh()                      {do{} while(0);}
+#define ace_if_down(dev)                       {do{} while(0);}
 #endif
 
+#if (LINUX_VERSION_CODE >= 0x02031b)
+#define NEW_NETINIT
+#define ACE_PROBE_ARG                          void
+#else
+#define ACE_PROBE_ARG                          struct net_device *dev
+#endif
 
 #define ACE_MAX_MOD_PARMS      8
 #define BOARD_IDX_STATIC       0
@@ -400,19 +481,15 @@ static int tx_ratio[ACE_MAX_MOD_PARMS];
 static int dis_pci_mem_inval[ACE_MAX_MOD_PARMS] = {1, 1, 1, 1, 1, 1, 1, 1};
 
 static char version[] __initdata = 
-  "acenic.c: v0.47 09/18/2000  Jes Sorensen, linux-acenic@SunSITE.auc.dk\n"
+  "acenic.c: v0.49 12/13/2000  Jes Sorensen, linux-acenic@SunSITE.auc.dk\n"
   "                            http://home.cern.ch/~jes/gige/acenic.html\n";
 
-static struct net_device *root_dev;
+static struct net_device *root_dev = NULL;
 
 static int probed __initdata = 0;
 
 
-#ifdef NEW_NETINIT
-int __init acenic_probe (void)
-#else
-int __init acenic_probe (struct net_device *dev)
-#endif
+int __init acenic_probe (ACE_PROBE_ARG)
 {
 #ifdef NEW_NETINIT
        struct net_device *dev;
@@ -425,7 +502,7 @@ int __init acenic_probe (struct net_device *dev)
 
        if (probed)
                return -ENODEV;
-       probed ++;
+       probed++;
 
        if (!pci_present())             /* is PCI support present? */
                return -ENODEV;
@@ -436,7 +513,7 @@ int __init acenic_probe (struct net_device *dev)
 
                if (!((pdev->vendor == PCI_VENDOR_ID_ALTEON) &&
                      ((pdev->device == PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE) ||
-                      (pdev->device == PCI_DEVICE_ID_ALTEON_ACENIC_COPPER)) ) &&
+                      (pdev->device == PCI_DEVICE_ID_ALTEON_ACENIC_COPPER)))&&
                    !((pdev->vendor == PCI_VENDOR_ID_3COM) &&
                      (pdev->device == PCI_DEVICE_ID_3COM_3C985)) &&
                    !((pdev->vendor == PCI_VENDOR_ID_NETGEAR) &&
@@ -460,6 +537,8 @@ int __init acenic_probe (struct net_device *dev)
                        break;
                }
 
+               SET_MODULE_OWNER(dev);
+
                if (!dev->priv)
                        dev->priv = kmalloc(sizeof(*ap), GFP_KERNEL);
                if (!dev->priv) {
@@ -615,7 +694,7 @@ int __init acenic_probe (struct net_device *dev)
 
 
 #ifdef MODULE
-MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@cern.ch>");
+MODULE_AUTHOR("Jes Sorensen <jes@linuxcare.com>");
 MODULE_DESCRIPTION("AceNIC/3C985/GA620 Gigabit Ethernet driver");
 MODULE_PARM(link, "1-" __MODULE_STRING(8) "i");
 MODULE_PARM(trace, "1-" __MODULE_STRING(8) "i");
@@ -634,8 +713,8 @@ void __exit ace_module_cleanup(void)
        short i;
 
        while (root_dev) {
-               next = ((struct ace_private *)root_dev->priv)->next;
                ap = root_dev->priv;
+               next = ap->next;
 
                regs = ap->regs;
 
@@ -730,8 +809,8 @@ int __init ace_module_init(void)
 }
 
 
-#ifdef MODULE
 #if (LINUX_VERSION_CODE < 0x02032a)
+#ifdef MODULE
 int init_module(void)
 {
        return ace_module_init();
@@ -742,11 +821,11 @@ void cleanup_module(void)
 {
        ace_module_cleanup();
 }
+#endif
 #else
 module_init(ace_module_init);
 module_exit(ace_module_cleanup);
 #endif
-#endif
 
 
 static void ace_free_descriptors(struct net_device *dev)
@@ -815,12 +894,18 @@ static int ace_allocate_descriptors(struct net_device *dev)
 
        size = (sizeof(struct event) * EVT_RING_ENTRIES);
 
-       ap->evt_ring = pci_alloc_consistent(ap->pdev, size,
-                                           &ap->evt_ring_dma);
+       ap->evt_ring = pci_alloc_consistent(ap->pdev, size, &ap->evt_ring_dma);
 
        if (ap->evt_ring == NULL)
                goto fail;
 
+       size = (sizeof(struct tx_desc) * TX_RING_ENTRIES);
+
+       ap->tx_ring = pci_alloc_consistent(ap->pdev, size, &ap->tx_ring_dma);
+
+       if (ap->tx_ring == NULL)
+               goto fail;
+
        ap->evt_prd = pci_alloc_consistent(ap->pdev, sizeof(u32),
                                           &ap->evt_prd_dma);
        if (ap->evt_prd == NULL)
@@ -1070,7 +1155,7 @@ static int __init ace_init(struct net_device *dev)
                } else if (ap->pci_command & PCI_COMMAND_INVALIDATE) {
                        printk(KERN_INFO "  PCI memory write & invalidate "
                               "enabled by BIOS, enabling counter measures\n");
-                       
+
                        switch(SMP_CACHE_BYTES) {
                        case 16:
                                tmp |= DMA_WRITE_MAX_16;
@@ -1084,7 +1169,7 @@ static int __init ace_init(struct net_device *dev)
                        default:
                                printk(KERN_INFO "  Cache line size %i not "
                                       "supported, PCI write and invalidate "
-                                      "disabled\n", L1_CACHE_BYTES);
+                                      "disabled\n", SMP_CACHE_BYTES);
                                ap->pci_command &= ~PCI_COMMAND_INVALIDATE;
                                pci_write_config_word(ap->pdev, PCI_COMMAND,
                                                      ap->pci_command);
@@ -1170,7 +1255,7 @@ static int __init ace_init(struct net_device *dev)
        ap->fw_running = 0;
 
        tmp_ptr = (unsigned long) ap->info_dma;
-#if (BITS_PER_LONG == 64)
+#ifdef ACE_64BIT_PTR
        writel(tmp_ptr >> 32, &regs->InfoPtrHi);
 #else
        writel(0, &regs->InfoPtrHi);
@@ -1269,18 +1354,19 @@ static int __init ace_init(struct net_device *dev)
        *(ap->rx_ret_prd) = 0;
 
        writel(TX_RING_BASE, &regs->WinBase);
-       ap->tx_ring = (struct tx_desc *)regs->Window;
-       for (i = 0; i < (TX_RING_ENTRIES * sizeof(struct tx_desc) / 4); i++) {
-               writel(0, (unsigned long)ap->tx_ring + i * 4);
-       }
+       memset(ap->tx_ring, 0, TX_RING_ENTRIES * sizeof(struct tx_desc));
+
+       set_aceaddr(&info->tx_ctrl.rngptr, ap->tx_ring_dma);
 
-       set_aceaddr(&info->tx_ctrl.rngptr, TX_RING_BASE);
        info->tx_ctrl.max_len = TX_RING_ENTRIES;
+
+       tmp = 0;
 #if TX_COAL_INTS_ONLY
-       info->tx_ctrl.flags = RCB_FLG_COAL_INT_ONLY;
-#else
-       info->tx_ctrl.flags = 0;
+       tmp |= RCB_FLG_COAL_INT_ONLY;
 #endif
+       tmp |= RCB_FLG_TX_HOST_RING;
+
+       info->tx_ctrl.flags = tmp;
 
        set_aceaddr(&info->tx_csm_ptr, ap->tx_csm_dma);
 
@@ -1524,9 +1610,9 @@ static void ace_timer(unsigned long data)
 }
 
 
-static void ace_bh(struct net_device *dev)
+static void ace_tasklet(unsigned long dev)
 {
-       struct ace_private *ap = dev->priv;
+       struct ace_private *ap = ((struct net_device *)dev)->priv;
        int cur_size;
 
        cur_size = atomic_read(&ap->cur_rx_bufs);
@@ -1558,7 +1644,7 @@ static void ace_bh(struct net_device *dev)
 #endif
                ace_load_jumbo_rx_ring(ap, RX_JUMBO_SIZE - cur_size);
        }
-       ap->bh_pending = 0;
+       ap->tasklet_pending = 0;
 }
 
 
@@ -1580,7 +1666,7 @@ static void ace_dump_trace(struct ace_private *ap)
  *
  * Loading rings is safe without holding the spin lock since this is
  * done only before the device is enabled, thus no interrupts are
- * generated and by the interrupt handler/bh handler.
+ * generated and by the interrupt handler/tasklet handler.
  */
 static void ace_load_std_rx_ring(struct ace_private *ap, int nr_bufs)
 {
@@ -1838,7 +1924,20 @@ static u32 ace_handle_event(struct net_device *dev, u32 evtcsm, u32 evtprd)
                                        ap->skb->rx_jumbo_skbuff[i].skb = NULL;
                                }
                        }
+
+                       if (ACE_IS_TIGON_I(ap)) {
+                               struct cmd cmd;
+                               cmd.evt = C_SET_RX_JUMBO_PRD_IDX;
+                               cmd.code = 0;
+                               cmd.idx = 0;
+                               ace_issue_cmd(ap->regs, &cmd);
+                       } else {
+                               writel(0, &((ap->regs)->RxJumboPrd));
+                               wmb();
+                       }
+
                        ap->jumbo = 0;
+                       ap->rx_jumbo_skbprd = 0;
                        printk(KERN_INFO "%s: Jumbo ring flushed\n",
                               dev->name);
                        if (!ap->tx_full)
@@ -2048,11 +2147,11 @@ static void ace_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
                         * caused by the NIC actually trying to access
                         * these incorrectly.
                         */
-#if (BITS_PER_LONG == 64)
-                       writel(0, &ap->tx_ring[idx].addr.addrhi);
+#ifdef ACE_64BIT_PTR
+                       ap->tx_ring[idx].addr.addrhi = 0;
 #endif
-                       writel(0, &ap->tx_ring[idx].addr.addrlo);
-                       writel(0, &ap->tx_ring[idx].flagsize);
+                       ap->tx_ring[idx].addr.addrlo = 0;
+                       ap->tx_ring[idx].flagsize = 0;
 
                        idx = (idx + 1) % TX_RING_ENTRIES;
                } while (idx != txcsm);
@@ -2069,7 +2168,7 @@ static void ace_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
                         * I've seen cases where it would fail otherwise ;-(
                         */
                        netif_wake_queue(dev);
-                       ace_mark_net_bh(NET_BH);
+                       ace_mark_net_bh();
 
                        /*
                         * TX ring is no longer full, aka the
@@ -2096,7 +2195,7 @@ static void ace_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
         */
        if (netif_running(dev)) {
                int cur_size;
-               int run_bh = 0;
+               int run_tasklet = 0;
 
                cur_size = atomic_read(&ap->cur_rx_bufs);
                if (cur_size < RX_LOW_STD_THRES) {
@@ -2108,7 +2207,7 @@ static void ace_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
                                ace_load_std_rx_ring(ap,
                                                     RX_RING_SIZE - cur_size);
                        } else
-                               run_bh = 1;
+                               run_tasklet = 1;
                }
 
                if (!ACE_IS_TIGON_I(ap)) {
@@ -2123,7 +2222,7 @@ static void ace_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
 #endif
                                        ace_load_mini_rx_ring(ap, RX_MINI_SIZE - cur_size);
                                } else
-                                       run_bh = 1;
+                                       run_tasklet = 1;
                        }
                }
 
@@ -2139,13 +2238,12 @@ static void ace_interrupt(int irq, void *dev_id, struct pt_regs *ptregs)
 #endif
                                        ace_load_jumbo_rx_ring(ap, RX_JUMBO_SIZE - cur_size);
                                } else
-                                       run_bh = 1;
+                                       run_tasklet = 1;
                        }
                }
-               if (run_bh && !ap->bh_pending) {
-                       ap->bh_pending = 1;
-                       queue_task(&ap->immediate, &tq_immediate);
-                       mark_bh(IMMEDIATE_BH);
+               if (run_tasklet && !ap->tasklet_pending) {
+                       ap->tasklet_pending = 1;
+                       tasklet_schedule(&ap->ace_tasklet);
                }
        }
 
@@ -2211,7 +2309,7 @@ static int ace_open(struct net_device *dev)
 
        netif_start_queue(dev);
 
-       MOD_INC_USE_COUNT;
+       ACE_MOD_INC_USE_COUNT;
 
        /*
         * Setup the timer
@@ -2223,11 +2321,7 @@ static int ace_open(struct net_device *dev)
        /*
         * Setup the bottom half rx ring refill handler
         */
-       INIT_LIST_HEAD(&ap->immediate.list);
-       ap->immediate.sync = 0;
-       ap->immediate.routine = (void *)(void *)ace_bh;
-       ap->immediate.data = dev;
-
+       tasklet_init(&ap->ace_tasklet, ace_tasklet, (unsigned long)dev);
        return 0;
 }
 
@@ -2261,6 +2355,8 @@ static int ace_close(struct net_device *dev)
        cmd.idx = 0;
        ace_issue_cmd(regs, &cmd);
 
+       tasklet_kill(&ap->ace_tasklet);
+
        /*
         * Make sure one CPU is not processing packets while
         * buffers are being released by another.
@@ -2275,9 +2371,7 @@ static int ace_close(struct net_device *dev)
                skb = ap->skb->tx_skbuff[i].skb;
                mapping = ap->skb->tx_skbuff[i].mapping;
                if (skb) {
-                       writel(0, &ap->tx_ring[i].addr.addrhi);
-                       writel(0, &ap->tx_ring[i].addr.addrlo);
-                       writel(0, &ap->tx_ring[i].flagsize);
+                       memset(&ap->tx_ring[i].addr, 0, sizeof(struct tx_desc));
                        pci_unmap_single(ap->pdev, mapping, skb->len,
                                         PCI_DMA_TODEVICE);
                        dev_kfree_skb(skb);
@@ -2294,7 +2388,7 @@ static int ace_close(struct net_device *dev)
 
        restore_flags(flags);
 
-       MOD_DEC_USE_COUNT;
+       ACE_MOD_DEC_USE_COUNT;
        return 0;
 }
 
@@ -2307,14 +2401,10 @@ static int ace_start_xmit(struct sk_buff *skb, struct net_device *dev)
        u32 idx, flagsize;
 
        /*
-        * ARGH, there is just no pretty way to do this
+        * This only happens with pre-softnet, ie. 2.2.x kernels.
         */
-#if (LINUX_VERSION_CODE < 0x02032b)
-       if (test_and_set_bit(0, &dev->tbusy))
+       if (early_stop_netif_stop_queue(dev))
                return 1;
-#else
-       netif_stop_queue(dev);
-#endif
 
        idx = ap->tx_prd;
 
@@ -2332,12 +2422,9 @@ static int ace_start_xmit(struct sk_buff *skb, struct net_device *dev)
                pci_map_single(ap->pdev, skb->data, skb->len,
                               PCI_DMA_TODEVICE);
        addr = (unsigned long) ap->skb->tx_skbuff[idx].mapping;
-#if (BITS_PER_LONG == 64)
-       writel(addr >> 32, &ap->tx_ring[idx].addr.addrhi);
-#endif
-       writel(addr & 0xffffffff, &ap->tx_ring[idx].addr.addrlo);
        flagsize = (skb->len << 16) | (BD_FLG_END) ;
-       writel(flagsize, &ap->tx_ring[idx].flagsize);
+       set_aceaddr(&ap->tx_ring[idx].addr, addr);
+       ap->tx_ring[idx].flagsize = flagsize;
        wmb();
        idx = (idx + 1) % TX_RING_ENTRIES;
 
@@ -2358,7 +2445,8 @@ static int ace_start_xmit(struct sk_buff *skb, struct net_device *dev)
                 */
                mod_timer(&ap->timer, jiffies + (3 * HZ));
 
-               /* The following check will fix a race between the interrupt
+               /*
+                * The following check will fix a race between the interrupt
                 * handler increasing the tx_ret_csm and testing for tx_full
                 * and this tx routine's testing the tx_ret_csm and setting
                 * the tx_full; note that this fix makes assumptions on the
@@ -2369,13 +2457,17 @@ static int ace_start_xmit(struct sk_buff *skb, struct net_device *dev)
                if (((idx + 2) % TX_RING_ENTRIES != ap->tx_ret_csm)
                        && xchg(&ap->tx_full, 0)) {
                        del_timer(&ap->timer);
+                       /*
+                        * We may not need this one in the post softnet era
+                        * in this case this can be changed to a
+                        * early_stop_netif_wake_queue(dev);
+                        */
                        netif_wake_queue(dev);
+               } else {
+                       late_stop_netif_stop_queue(dev);
                }
        } else {
-               /*
-                * No need for it to be atomic - seems it needs to be
-                */
-               netif_wake_queue(dev);
+               early_stop_netif_wake_queue(dev);
        }
 
        dev->trans_start = jiffies;
@@ -2408,7 +2500,7 @@ static int ace_change_mtu(struct net_device *dev, int new_mtu)
                while (test_and_set_bit(0, &ap->jumbo_refill_busy));
                synchronize_irq();
                ace_set_rxtx_parms(dev, 0);
-               if (ap->jumbo){
+               if (ap->jumbo) {
                        struct cmd cmd;
 
                        cmd.evt = C_RESET_JUMBO_RNG;
@@ -2424,6 +2516,7 @@ static int ace_change_mtu(struct net_device *dev, int new_mtu)
 
 static int ace_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
+#ifdef SIOCETHTOOL
        struct ace_private *ap = dev->priv;
        struct ace_regs *regs = ap->regs;
        struct ethtool_cmd ecmd;
@@ -2483,7 +2576,11 @@ static int ace_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                        ecmd.autoneg = AUTONEG_DISABLE;
 
 #if 0
+               /*
+                * Current struct ethtool_cmd is insufficient
+                */
                ecmd.trace = readl(&regs->TuneTrace);
+
                ecmd.txcoal = readl(&regs->TuneTxCoalTicks);
                ecmd.rxcoal = readl(&regs->TuneRxCoalTicks);
 #endif
@@ -2551,6 +2648,7 @@ static int ace_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                }
                return 0;
        }
+#endif
 
        return -EOPNOTSUPP;
 }
@@ -2563,7 +2661,7 @@ static int ace_set_mac_addr(struct net_device *dev, void *p)
 {
        struct sockaddr *addr=p;
        struct ace_regs *regs;
-       u16 *da;
+       u8 *da;
        struct cmd cmd;
 
        if(netif_running(dev))
@@ -2571,11 +2669,11 @@ static int ace_set_mac_addr(struct net_device *dev, void *p)
 
        memcpy(dev->dev_addr, addr->sa_data,dev->addr_len);
 
-       da = (u16 *)dev->dev_addr;
+       da = (u8 *)dev->dev_addr;
 
        regs = ((struct ace_private *)dev->priv)->regs;
-       writel(da[0], &regs->MacAddrHi);
-       writel((da[1] << 16) | da[2], &regs->MacAddrLo);
+       writel(da[0] << 8 | da[1], &regs->MacAddrHi);
+       writel((da[2] << 24) | (da[3] << 16) | (da[4] << 8) | da[5] , &regs->MacAddrLo);
 
        cmd.evt = C_SET_MAC_ADDR;
        cmd.code = 0;
@@ -3000,6 +3098,6 @@ static int __init read_eeprom_byte(struct net_device *dev,
 
 /*
  * Local variables:
- * compile-command: "gcc -D__KERNEL__ -DMODULE -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -DMODVERSIONS -include ../../include/linux/modversions.h   -c -o acenic.o acenic.c"
+ * compile-command: "gcc -D__SMP__ -D__KERNEL__ -DMODULE -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -DMODVERSIONS -include ../../include/linux/modversions.h   -c -o acenic.o acenic.c"
  * End:
  */
index b08695c93465b898133919bbac431229678c892b..c67121e4790db870979c44e2e1a2d8738fdaa499 100644 (file)
@@ -83,9 +83,9 @@ struct ace_regs {
        u32     Mb2Hi;
        u32     TxPrd;
        u32     Mb3Hi;
-       u32     RxStdPrd; /* RxStdPrd */
+       u32     RxStdPrd;
        u32     Mb4Hi;
-       u32     RxJumboPrd; /* RxJumboPrd */
+       u32     RxJumboPrd;
        u32     Mb5Hi;
        u32     RxMiniPrd;
        u32     Mb6Hi;
@@ -409,6 +409,7 @@ struct cmd {
 #define RCB_FLG_IP_SUM         0x02
 #define RCB_FLG_VLAN_ASSIST    0x10
 #define RCB_FLG_COAL_INT_ONLY  0x20
+#define RCB_FLG_TX_HOST_RING   0x40
 #define RCB_FLG_IEEE_SNAP_SUM  0x80
 #define RCB_FLG_EXT_RX_BD      0x100
 #define RCB_FLG_RNG_DISABLE    0x200
@@ -417,7 +418,7 @@ struct cmd {
 /*
  * TX ring
  */
-#define TX_RING_ENTRIES        128
+#define TX_RING_ENTRIES        256     
 #define TX_RING_SIZE   (TX_RING_ENTRIES * sizeof(struct tx_desc))
 #define TX_RING_BASE   0x3800
 
@@ -593,49 +594,56 @@ struct ace_skb
  */
 struct ace_private
 {
-       struct ace_skb          *skb;
+       struct ace_info         *info;
        struct ace_regs         *regs;          /* register base */
-       volatile int            fw_running;
-       int                     version, fw_up, link;
+       struct ace_skb          *skb;
+       dma_addr_t              info_dma;       /* 32/64 bit */
+
+       int                     version, link;
        int                     promisc, mcast_all;
+
        /*
-        * The send ring is located in the shared memory window
+        * TX elements
         */
-       struct ace_info         *info;
-       struct tx_desc          *tx_ring;
-       dma_addr_t              info_dma;
+       struct tx_desc          *tx_ring
+                               __attribute__ ((aligned (SMP_CACHE_BYTES)));
+       struct timer_list       timer;          /* used by TX handling only */
        u32                     tx_prd;
        volatile u32            tx_full, tx_ret_csm;
-       struct timer_list       timer;
 
+       /*
+        * RX elements
+        */
        unsigned long           std_refill_busy
                                __attribute__ ((aligned (SMP_CACHE_BYTES)));
        unsigned long           mini_refill_busy, jumbo_refill_busy;
-       atomic_t                cur_rx_bufs,
-                               cur_mini_bufs,
-                               cur_jumbo_bufs;
+       atomic_t                cur_rx_bufs;
+       atomic_t                cur_mini_bufs;
+       atomic_t                cur_jumbo_bufs;
        u32                     rx_std_skbprd, rx_mini_skbprd, rx_jumbo_skbprd;
        u32                     cur_rx;
-       struct tq_struct        immediate;
-       int                     bh_pending, jumbo;
-       /*
-        * These elements are allocated using consistent PCI dma memory.
-        */
+
        struct rx_desc          *rx_std_ring;
        struct rx_desc          *rx_jumbo_ring;
        struct rx_desc          *rx_mini_ring;
        struct rx_desc          *rx_return_ring;
-       dma_addr_t              rx_ring_base_dma;
+
+       int                     tasklet_pending, jumbo;
+       struct tasklet_struct   ace_tasklet;
 
        struct event            *evt_ring;
-       dma_addr_t              evt_ring_dma;
 
        volatile u32            *evt_prd, *rx_ret_prd, *tx_csm;
+
+       dma_addr_t              tx_ring_dma;    /* 32/64 bit */
+       dma_addr_t              rx_ring_base_dma;
+       dma_addr_t              evt_ring_dma;
        dma_addr_t              evt_prd_dma, rx_ret_prd_dma, tx_csm_dma;
 
        unsigned char           *trace_buf;
        struct pci_dev          *pdev;
        struct net_device       *next;
+       volatile int            fw_running;
        int                     board_idx;
        u16                     pci_command;
        u8                      pci_latency;
@@ -652,7 +660,7 @@ struct ace_private
 static inline void set_aceaddr(aceaddr *aa, dma_addr_t addr)
 {
        unsigned long baddr = (unsigned long) addr;
-#if (BITS_PER_LONG == 64)
+#ifdef ACE_64BIT_PTR
        aa->addrlo = baddr & 0xffffffff;
        aa->addrhi = baddr >> 32;
 #else
@@ -668,7 +676,7 @@ static inline void *get_aceaddr(aceaddr *aa)
 {
        unsigned long addr;
        mb();
-#if (BITS_PER_LONG == 64)
+#ifdef ACE_64BIT_PTR
        addr = (u64)aa->addrhi << 32 | aa->addrlo;
 #else
        addr = aa->addrlo;
@@ -710,7 +718,7 @@ static int ace_open(struct net_device *dev);
 static int ace_start_xmit(struct sk_buff *skb, struct net_device *dev);
 static int ace_close(struct net_device *dev);
 static void ace_timer(unsigned long data);
-static void ace_bh(struct net_device *dev);
+static void ace_tasklet(unsigned long dev);
 static void ace_dump_trace(struct ace_private *ap);
 static void ace_set_multicast_list(struct net_device *dev);
 static int ace_change_mtu(struct net_device *dev, int new_mtu);
index cb2441285a68a4bc1e77fa295088bf79998694a6..2a906a7bb350110da7f67c7c38dae2f3797b736d 100644 (file)
 #define tigonFwSbssLen 0x38
 #define tigonFwBssAddr 0x00015dd0
 #define tigonFwBssLen 0x2080
-u32 tigonFwText[];
-u32 tigonFwData[];
-u32 tigonFwRodata[];
 #ifndef CONFIG_ACENIC_OMIT_TIGON_I
+#define tigonFwText 0
+#define tigonFwData 0
+#define tigonFwRodata 0
+#else
 /* Generated by genfw.c */
 u32 tigonFwText[(MAX_TEXT_LEN/4) + 1] __initdata = {
 0x10000003, 
index 7a4c1a1d6e491b7d36fdd5e2235070c2b3de076f..a2eeef4ce9c400995538164bffceb7c4a8e02a80 100644 (file)
@@ -375,7 +375,7 @@ static int __init cops_irq (int ioaddr, int board)
         {
                 outb(0, ioaddr+DAYNA_RESET);
                 inb(ioaddr+DAYNA_RESET);
-                udelay(333333);
+                mdelay(333);
         }
         if(board==TANGENT)
         {
@@ -504,7 +504,7 @@ static void cops_reset(struct net_device *dev, int sleep)
                                 schedule();
                 }
                 else
-                        udelay(333333);
+                        mdelay(333);
         }
        netif_wake_queue(dev);
        return;
index aee2bcbddf0ebd909284a114b33cca9cf33acfe3..d172382a084a7f7d02482bf572c6a965ec7774ed 100644 (file)
@@ -1323,7 +1323,7 @@ static int arlan_open(struct net_device *dev)
        priv->timer.function = &arlan_registration_timer;       /* timer handler */
 
        arlan_command(dev, ARLAN_COMMAND_POWERUP | ARLAN_COMMAND_LONG_WAIT_NOW);
-       udelay(200000);
+       mdelay(200);
        add_timer(&priv->timer);
 
 #ifdef CONFIG_PROC_FS
index 5cdad9da4babb4b4c82de02550d6a091f8e137fe..ba550b23b4b20368894f7bc932d96b5eff6db1ca 100644 (file)
@@ -1,7 +1,7 @@
 /**********************************************************************
  * iph5526.c: IP/SCSI driver for the Interphase 5526 PCI Fibre Channel
  *                       Card.
- * Copyright (C) 1999 Vineet M Abraham <vma@iol.unh.edu>
+ * Copyright (C) 1999 Vineet M Abraham <vmabraham@hotmail.com>
  *
  * This program is free software; you can redistribute it and/or 
  * modify it under the terms of the GNU General Public License as 
@@ -33,10 +33,9 @@ Vineet M Abraham
 */     
 
 static const char *version =
-    "iph5526.c:v1.0 07.08.99 Vineet Abraham (vma@iol.unh.edu)\n";
+    "iph5526.c:v1.0 07.08.99 Vineet Abraham (vmabraham@hotmail.com)\n";
 
 #include <linux/module.h>
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/errno.h>
@@ -112,9 +111,16 @@ static const char *version =
 #define ALIGNED_ADDR(addr, len) ((((unsigned long)(addr) + (len - 1)) & ~(len - 1)) - (unsigned long)(addr))
 
 
+static struct pci_device_id iph5526_pci_tbl[] __initdata = {
+       { PCI_VENDOR_ID_INTERPHASE, PCI_DEVICE_ID_INTERPHASE_5526, PCI_ANY_ID, PCI_ANY_ID, },
+       { PCI_VENDOR_ID_INTERPHASE, PCI_DEVICE_ID_INTERPHASE_55x6, PCI_ANY_ID, PCI_ANY_ID, },
+       { }                     /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(pci, iph5526_pci_tbl);
+
 #define MAX_FC_CARDS 2
 static struct fc_info *fc[MAX_FC_CARDS+1];
-static unsigned int pci_irq_line = 0;
+static unsigned int pci_irq_line;
 static struct {
        unsigned short vendor_id;
        unsigned short device_id;
@@ -220,32 +226,23 @@ Scsi_Host_Template driver_template = IPH5526_SCSI_FC;
 
 static void iph5526_timeout(struct net_device *dev);
 
-#ifdef CONFIG_PCI
 static int iph5526_probe_pci(struct net_device *dev);
-#endif
-
 
 int __init iph5526_probe(struct net_device *dev)
 {
-#ifdef CONFIG_PCI
        if (pci_present() && (iph5526_probe_pci(dev) == 0))
                return 0;
-#endif
     return -ENODEV;
 }
 
-#ifdef CONFIG_PCI
 static int __init iph5526_probe_pci(struct net_device *dev)
 {
-#ifndef MODULE
-struct fc_info *fi;
-static int count = 0;
-#endif
 #ifdef MODULE
-struct fc_info *fi = (struct fc_info *)dev->priv;
-#endif
-
-#ifndef MODULE
+       struct fc_info *fi = (struct fc_info *)dev->priv;
+#else
+       struct fc_info *fi;
+       static int count = 0;
        if(fc[count] != NULL) {
                if (dev == NULL) {
                        dev = init_fcdev(NULL, 0);
@@ -277,7 +274,6 @@ struct fc_info *fi = (struct fc_info *)dev->priv;
        display_cache(fi);
        return 0;
 }
-#endif  /* CONFIG_PCI */
 
 static int __init fcdev_init(struct net_device *dev)
 {
@@ -2247,7 +2243,7 @@ static void reset_ichip(struct fc_info *fi)
        /* (i)chip reset */
        writel(ICHIP_HCR_RESET, fi->i_r.ptr_ichip_hw_control_reg);
        /*wait for chip to get reset */
-       udelay(10000);
+       mdelay(10);
        /*de-assert reset */
        writel(ICHIP_HCR_DERESET, fi->i_r.ptr_ichip_hw_control_reg);
        
@@ -3882,7 +3878,7 @@ struct pci_dev *pdev = NULL;
        /* This is to make sure that the ACC to the PRLI comes in 
         * for the last ALPA. 
         */
-       udelay(1000000); /* Ugly! Let the Gods forgive me */
+       mdelay(1000); /* Ugly! Let the Gods forgive me */
 
        DPRINTK1("leaving iph5526_detect\n");
        return no_of_hosts;
index 0b7bc011e244b2902e229f5706b4b0bce4765f3b..ef7ecd7dce8cc5418a9472430cc7f97cfe79a8eb 100644 (file)
@@ -1,7 +1,7 @@
 /**********************************************************************
  * iph5526.c: Structures for the Interphase 5526 PCI Fibre Channel 
  *                       IP/SCSI driver.
- * Copyright (C) 1999 Vineet M Abraham <vma@iol.unh.edu>
+ * Copyright (C) 1999 Vineet M Abraham <vmabraham@hotmail.com>
  **********************************************************************/
 
 #ifndef _TACH_STRUCT_H
index e12f65a7989fdc15fa7d4d6eb088c73937a3a76d..a840222afaa5adefd6831b763955c78f4a29d30b 100644 (file)
@@ -186,6 +186,7 @@ int __init hydra_probe(struct net_device *dev)
                        release_mem_region(board, 0x4000);
                        continue;
                }
+               SET_MODULE_OWNER(dev);
 
                for(j = 0; j < ETHER_ADDR_LEN; j++)
                        dev->dev_addr[j] = *((u8 *)ZTWO_VADDR(board + HYDRA_ADDRPROM + 2*j));
@@ -284,15 +285,11 @@ static int hydra_open(struct net_device *dev)
        /* take interface out of loopback */
        WRITE_REG(NIC_TCR, 0);
 
-       dev->tbusy = 0;
-       dev->interrupt = 0;
-       dev->start = 1;
+       netif_start_queue(dev);
     
-       if(request_irq(IRQ_AMIGA_PORTS, hydra_interrupt, SA_SHIRQ,
-                      "Hydra Ethernet", dev))
-               return(-EAGAIN);
-
-       MOD_INC_USE_COUNT;
+       i = request_irq(IRQ_AMIGA_PORTS, hydra_interrupt, SA_SHIRQ,
+                       dev->name, dev);
+       if (i) return i;
 
        return(0);
 }
@@ -304,8 +301,7 @@ static int hydra_close(struct net_device *dev)
        volatile u8 *nicbase = (u8 *)dev->base_addr;
        int n = 5000;
 
-       dev->start = 0;
-       dev->tbusy = 1;
+       netif_stop_queue(dev);
 
 #ifdef HYDRA_DEBUG
        printk("%s: Shutting down ethercard\n", dev->name);
@@ -319,8 +315,6 @@ static int hydra_close(struct net_device *dev)
     
        free_irq(IRQ_AMIGA_PORTS, dev);
 
-       MOD_DEC_USE_COUNT;
-
        return(0);
 }
 
@@ -668,20 +662,13 @@ static void set_multicast_list(struct net_device *dev, int num_addrs, void *addr
 
 
 #ifdef MODULE
-static char devicename[9] = { 0, };
-
-static struct net_device hydra_dev =
-{
-       devicename,                     /* filled in by register_netdev() */
-       0, 0, 0, 0,                     /* memory */
-       0, 0,                           /* base, irq */
-       0, 0, 0, NULL, hydra_probe,
-};
+static struct net_device hydra_dev;
 
 int init_module(void)
 {
        int err;
 
+       hydra_dev.init = hydra_probe;
        if ((err = register_netdev(&hydra_dev))) {
                if (err == -EIO)
                        printk("No Hydra board found. Module not loaded.\n");
index 4351442911fc61a2fbb69f7bea30d28b11aef4b8..963fb543ff903964e368c97fcc99c291b0c2e01c 100644 (file)
@@ -58,7 +58,7 @@ static const char *version = "lance.c:v1.15ac 1999/11/13 dplatt@3do.com, becker@
 
 static unsigned int lance_portlist[] __initdata = { 0x300, 0x320, 0x340, 0x360, 0};
 int lance_probe(struct net_device *dev);
-int lance_probe1(struct net_device *dev, int ioaddr, int irq, int options);
+static int lance_probe1(struct net_device *dev, int ioaddr, int irq, int options);
 
 #ifdef LANCE_DEBUG
 int lance_debug = LANCE_DEBUG;
@@ -287,21 +287,15 @@ static void lance_tx_timeout (struct net_device *dev);
 #ifdef MODULE
 #define MAX_CARDS              8       /* Max number of interfaces (cards) per module */
 
-static int io[MAX_CARDS] = { 0, };
-static int dma[MAX_CARDS] = { 0, };
-static int irq[MAX_CARDS]  = { 0, };
+static struct net_device dev_lance[MAX_CARDS];
+static int io[MAX_CARDS];
+static int dma[MAX_CARDS];
+static int irq[MAX_CARDS];
 
 MODULE_PARM(io, "1-" __MODULE_STRING(MAX_CARDS) "i");
 MODULE_PARM(dma, "1-" __MODULE_STRING(MAX_CARDS) "i");
 MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_CARDS) "i");
 
-static struct net_device dev_lance[MAX_CARDS] =
-{{
-    "", /* device name is inserted by linux/drivers/net/net_init.c */
-       0, 0, 0, 0,
-       0, 0,
-       0, 0, 0, NULL, NULL}};
-
 int init_module(void)
 {
        int this_dev, found = 0;
@@ -374,7 +368,7 @@ int lance_probe(struct net_device *dev)
        return -ENODEV;
 }
 
-int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int options)
+static int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int options)
 {
        struct lance_private *lp;
        short dma_channels;                                     /* Mark spuriously-busy DMA channels */
@@ -437,6 +431,9 @@ int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int options
        /* We can't use init_etherdev() to allocate dev->priv because it must
           a ISA DMA-able region. */
        dev = init_etherdev(dev, 0);
+       if (!dev)
+               return -ENOMEM;
+       SET_MODULE_OWNER(dev);
        dev->open = lance_open_fail;
        chipname = chip_table[lance_version].name;
        printk("%s: %s at %#3x,", dev->name, chipname, ioaddr);
@@ -653,8 +650,6 @@ lance_open(struct net_device *dev)
                return -EAGAIN;
        }
 
-       MOD_INC_USE_COUNT;
-
        /* We used to allocate DMA here, but that was silly.
           DMA lines can't be shared!  We now permanently allocate them. */
 
@@ -1143,7 +1138,6 @@ lance_close(struct net_device *dev)
 
        lance_purge_ring(dev);
 
-       MOD_DEC_USE_COUNT;
        return 0;
 }
 
index ac0771bd88e3f34b49228a2c7abd6e72943f771d..213b9b964394c306b6abe76df9af7807059bff67 100644 (file)
@@ -133,9 +133,11 @@ static void __init mace_probe1(struct device_node *mace)
        }
 
        dev = init_etherdev(0, PRIV_BYTES);
-       memset(dev->priv, 0, PRIV_BYTES);
+       if (!dev)
+               return;
+       SET_MODULE_OWNER(dev);
 
-       mp = (struct mace_data *) dev->priv;
+       mp = dev->priv;
        dev->base_addr = mace->addrs[0].address;
        mp->mace = (volatile struct mace *)
                ioremap(mace->addrs[0].address, 0x1000);
@@ -353,7 +355,6 @@ static int mace_open(struct net_device *dev)
     /* enable all interrupts except receive interrupts */
     out_8(&mb->imr, RCVINT);
 
-    MOD_INC_USE_COUNT;
     return 0;
 }
 
@@ -392,8 +393,6 @@ static int mace_close(struct net_device *dev)
 
     mace_clean_rings(mp);
 
-    MOD_DEC_USE_COUNT;
-
     return 0;
 }
 
index 7e11e54833817e0afbc3cd65d947fd2f436f0b4c..e7204dd3b02963ddba0706f67b2ccc7d31d6530d 100644 (file)
@@ -74,7 +74,7 @@ static char *version =
        "ni5010.c: v1.00 06/23/97 Jan-Pascal van Best and Andreas Mohr\n";
        
 /* bufsize_rcv == 0 means autoprobing */
-unsigned int bufsize_rcv = 0;
+static unsigned int bufsize_rcv = 0;
 
 #define jumpered_interrupts    /* IRQ line jumpered on board */
 #undef jumpered_dma            /* No DMA used */
@@ -114,8 +114,8 @@ static void reset_receiver(struct net_device *dev);
 
 static int     process_xmt_interrupt(struct net_device *dev);
 #define tx_done(dev) 1
-extern void    hardware_send_packet(struct net_device *dev, char *buf, int length);
-extern void    chipset_init(struct net_device *dev, int startp);
+static void    hardware_send_packet(struct net_device *dev, char *buf, int length);
+static void    chipset_init(struct net_device *dev, int startp);
 static void    dump_packet(void *buf, int len);
 static void    show_registers(struct net_device *dev);
 
@@ -123,11 +123,12 @@ static void       show_registers(struct net_device *dev);
 int __init ni5010_probe(struct net_device *dev)
 {
        int *port;
-
-       int base_addr = dev ? dev->base_addr : 0;
+       int base_addr = dev->base_addr;
 
         PRINTK2((KERN_DEBUG "%s: Entering ni5010_probe\n", dev->name));
-        
+
+       SET_MODULE_OWNER(dev);
+
        if (base_addr > 0x1ff)          /* Check a single specified location. */
                return ni5010_probe1(dev, base_addr);
        else if (base_addr != 0)        /* Don't probe at all. */
@@ -158,7 +159,7 @@ static inline int rd_port(int ioaddr)
        return inb(IE_SAPROM);
 }
 
-void __init trigger_irq(int ioaddr)
+static void __init trigger_irq(int ioaddr)
 {
                outb(0x00, EDLC_RESET); /* Clear EDLC hold RESET state */
                outb(0x00, IE_RESET);   /* Board reset */
@@ -398,7 +399,6 @@ static int ni5010_open(struct net_device *dev)
                
        if (NI5010_DEBUG) show_registers(dev); 
 
-       MOD_INC_USE_COUNT;
        PRINTK((KERN_DEBUG "%s: open successful\n", dev->name));
        return 0;
 }
@@ -612,7 +612,6 @@ static int ni5010_close(struct net_device *dev)
 
        netif_stop_queue(dev);
        
-       MOD_DEC_USE_COUNT;
        PRINTK((KERN_DEBUG "%s: %s closed down\n", dev->name, boardname));
        return 0;
 
@@ -662,7 +661,7 @@ static void ni5010_set_multicast_list(struct net_device *dev)
        }
 }
 
-extern void hardware_send_packet(struct net_device *dev, char *buf, int length)
+static void hardware_send_packet(struct net_device *dev, char *buf, int length)
 {
        struct ni5010_local *lp = (struct ni5010_local *)dev->priv;
        int ioaddr = dev->base_addr;
@@ -713,7 +712,7 @@ extern void hardware_send_packet(struct net_device *dev, char *buf, int length)
        if (NI5010_DEBUG) show_registers(dev);  
 }
 
-extern void chipset_init(struct net_device *dev, int startp)
+static void chipset_init(struct net_device *dev, int startp)
 {
        /* FIXME: Move some stuff here */
        PRINTK3((KERN_DEBUG "%s: doing NOTHING in chipset_init\n", dev->name));
@@ -733,7 +732,7 @@ static void show_registers(struct net_device *dev)
 }
 
 #ifdef MODULE
-static struct net_device dev_ni5010 = { init: ni5010_probe };
+static struct net_device dev_ni5010;
 static int io;
 static int irq;
 
@@ -759,6 +758,7 @@ int init_module(void)
        PRINTK2((KERN_DEBUG "%s: init_module irq=%#2x, io=%#3x\n", boardname, irq, io));
         dev_ni5010.irq=irq;
         dev_ni5010.base_addr=io;
+       dev_ni5010.init=ni5010_probe;
         if ((result = register_netdev(&dev_ni5010)) != 0) {
                PRINTK((KERN_WARNING "%s: register_netdev returned %d.\n", 
                        boardname, result));
index 9efbd33ef0552e7ce76734e500fac977e8a2f622..e05e75484e248cfac9fd3a82816c45a7584313cb 100644 (file)
@@ -160,9 +160,9 @@ sizeof(nop_cmd) = 8;
 /**************************************************************************/
 
 /* different DELAYs */
-#define DELAY(x) __delay((loops_per_sec>>5)*(x));
-#define DELAY_16(); { __delay( (loops_per_sec>>16)+1 ); }
-#define DELAY_18(); { __delay( (loops_per_sec>>18)+1 ); }
+#define DELAY(x) mdelay(32 * x);
+#define DELAY_16(); { udelay(16); }
+#define DELAY_18(); { udelay(4); }
 
 /* wait for command with timeout: */
 #define WAIT_4_SCB_CMD() \
@@ -219,7 +219,8 @@ struct priv
        struct net_device_stats stats;
        unsigned long base;
        char *memtop;
-       int lock,reseted;
+       long int lock;
+       int reseted;
        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 */
@@ -246,7 +247,6 @@ static int ni52_close(struct net_device *dev)
        ni_reset586(); /* the hard way to stop the receiver */
 
        netif_stop_queue(dev);
-       MOD_DEC_USE_COUNT;
 
        return 0;
 }
@@ -256,20 +256,22 @@ static int ni52_close(struct net_device *dev)
  */
 static int ni52_open(struct net_device *dev)
 {
+       int ret;
+
        ni_disint();
        alloc586(dev);
        init586(dev);
        startrecv586(dev);
        ni_enaint();
 
-       if(request_irq(dev->irq, &ni52_interrupt,0,"ni5210",dev))
+       ret = request_irq(dev->irq, &ni52_interrupt,0,dev->name,dev);
+       if (ret)
        {
                ni_reset586();
-               return -EAGAIN;
+               return ret;
        }
 
        netif_start_queue(dev);
-       MOD_INC_USE_COUNT;
 
        return 0; /* most done by init */
 }
@@ -319,7 +321,7 @@ static int check586(struct net_device *dev,char *where,unsigned size)
 /******************************************************************
  * set iscp at the right place, called by ni52_probe1 and open586.
  */
-void alloc586(struct net_device *dev)
+static void alloc586(struct net_device *dev)
 {
        struct priv *p =        (struct priv *) dev->priv;
 
@@ -362,6 +364,8 @@ int __init ni52_probe(struct net_device *dev)
 #endif
        int base_addr = dev->base_addr;
 
+       SET_MODULE_OWNER(dev);
+
        if (base_addr > 0x1ff)          /* Check a single specified location. */
                return ni52_probe1(dev, base_addr);
        else if (base_addr > 0)         /* Don't probe at all. */
@@ -393,8 +397,8 @@ static int __init ni52_probe1(struct net_device *dev,int ioaddr)
 {
        int i, size, retval;
 
-       if (!request_region(ioaddr, NI52_TOTAL_SIZE, "ni5210"))
-               return -ENODEV;
+       if (!request_region(ioaddr, NI52_TOTAL_SIZE, dev->name))
+               return -EBUSY;
 
        if( !(inb(ioaddr+NI52_MAGIC1) == NI52_MAGICVAL1) ||
            !(inb(ioaddr+NI52_MAGIC2) == NI52_MAGICVAL2)) {
@@ -1278,17 +1282,13 @@ static void set_multicast_list(struct net_device *dev)
 }
 
 #ifdef MODULE
-static struct net_device dev_ni52 = {
-       "",     /* device name inserted by net_init.c */
-       0, 0, 0, 0,
-       0x300, 9,        /* I/O address, IRQ */
-       0, 0, 0, NULL, ni52_probe };
+static struct net_device dev_ni52;
 
 /* set: io,irq,memstart,memend or set it when calling insmod */
-int irq=9;
-int io=0x300;
-long memstart=0; /* e.g 0xd0000 */
-long memend=0;  /* e.g 0xd4000 */
+static int irq=9;
+static int io=0x300;
+static long memstart=0; /* e.g 0xd0000 */
+static long memend=0;   /* e.g 0xd4000 */
 
 MODULE_PARM(io, "i");
 MODULE_PARM(irq, "i");
@@ -1301,6 +1301,7 @@ int init_module(void)
                printk("ni52: Autoprobing not allowed for modules.\nni52: Set symbols 'io' 'irq' 'memstart' and 'memend'\n");
                return -ENODEV;
        }
+       dev_ni52.init = ni52_probe;
        dev_ni52.irq = irq;
        dev_ni52.base_addr = io;
        dev_ni52.mem_end = memend;
index f3d2c5426a7277652f7ed291750938414081168a..9455d64539fef21dca2560990a3bad338e449235 100644 (file)
@@ -140,7 +140,7 @@ static int __init oaknet_init(void)
                dev->base_addr = 0;
 
                release_region(dev->base_addr, OAKNET_IO_SIZE);
-               return (ENODEV);
+               return (-ENODEV);
        }
 
        /*
@@ -148,7 +148,12 @@ static int __init oaknet_init(void)
         * our own device structure.
         */
 
-       dev = init_etherdev(0, 0);
+       dev = init_etherdev(NULL, 0);
+       if (!dev) {
+               release_region(dev->base_addr, OAKNET_IO_SIZE);
+               return (-ENOMEM);
+       }
+       SET_MODULE_OWNER(dev);
        oaknet_devs = dev;
 
        /*
@@ -238,7 +243,6 @@ static int
 oaknet_open(struct net_device *dev)
 {
        int status = ei_open(dev);
-       MOD_INC_USE_COUNT;
        return (status);
 }
 
@@ -265,7 +269,6 @@ static int
 oaknet_close(struct net_device *dev)
 {
        int status = ei_close(dev);
-       MOD_DEC_USE_COUNT;
        return (status);
 }
 
index 12c8b2b554a15c2b2300a7d3bfdce1928f3032bc..373e535fbe05a7c7ea664f7ea83c86b245f70d77 100644 (file)
@@ -95,18 +95,18 @@ STNIC_WRITE (int reg, byte val)
 \f
 int __init stnic_probe(void)
 {
-  struct net_device tmp, *dev = NULL;
+  struct net_device *dev;
   int i;
 
-  tmp.base_addr = 0x1000;
-  dev = &tmp;
-
   /* If we are not running on a SolutionEngine, give up now */
   if (! MACH_SE)
     return -ENODEV;
 
   /* New style probing API */
-  dev = init_etherdev (0, 0);
+  dev = init_etherdev (NULL, 0);
+  if (!dev)
+       return -ENOMEM;
+  SET_MODULE_OWNER(dev);
   stnic_dev = dev;
 
   /* Allocate dev->priv and fill in 8390 specific dev fields. */
@@ -127,15 +127,16 @@ int __init stnic_probe(void)
 
   /* Snarf the interrupt now.  There's no point in waiting since we cannot
      share and the board will usually be enabled. */
-  if (request_irq (dev->irq, ei_interrupt, 0, "DP83902A", dev))
-    {
+  i = request_irq (dev->irq, ei_interrupt, 0, dev->name, dev);
+  if (i)  {
       printk (" unable to get IRQ %d.\n", dev->irq);
+      unregister_netdev(dev);
       kfree(dev->priv);
-      dev->priv = NULL;
-      return -EAGAIN;
+      kfree(dev);
+      return i;
     }
 
-  ei_status.name = "eth0";
+  ei_status.name = dev->name;
   ei_status.word16 = 1;
   ei_status.tx_start_page = START_PG;
   ei_status.rx_start_page = START_PG + TX_PAGES;
@@ -159,7 +160,6 @@ stnic_open (struct net_device *dev)
   printk ("stnic open\n");
 #endif
   ei_open (dev);
-  MOD_INC_USE_COUNT;
   return 0;
 }
 
@@ -167,7 +167,6 @@ static int
 stnic_close (struct net_device *dev)
 {
   ei_close (dev);
-  MOD_DEC_USE_COUNT;
   return 0;
 }
 
index 6c37fb68326f1f52c87ae1e33ed9e0eac0ba49ba..dfac943492b922c15f043d00b8581009f142b07a 100644 (file)
@@ -1,3 +1,8 @@
+2000-12-17  Alan Cox <alan@redhat.com>
+
+       * merge support for the Davicom's quirks into the main tulip. Patch
+         by Tobias Ringstrom
+
 2000-11-02  Jeff Garzik  <jgarzik@mandrakesoft.com>
 
        * tulip_core.c (set_rx_mode): This is synchronized via
index e23ad7a6ec60ada83c7634bdc95503fa4c43c26c..49e8bf42e772a8e625119d8baddb5d7fb57b29e1 100644 (file)
@@ -198,12 +198,23 @@ subsequent_board:
                                if (p[1] == 0x05) {
                                        mtable->has_reset = i;
                                        leaf->media = p[2] & 0x0f;
+                               } else if (tp->chip_id == DM910X && p[1] == 0x80) {
+                                       /* Hack to ignore Davicom delay period block */
+                                       mtable->leafcount--;
+                                       count--;
+                                       i--;
+                                       leaf->leafdata = p + 2;
+                                       p += (p[0] & 0x3f) + 1;
+                                       continue;
                                } else if (p[1] & 1) {
                                        mtable->has_mii = 1;
                                        leaf->media = 11;
                                } else {
                                        mtable->has_nonmii = 1;
                                        leaf->media = p[2] & 0x0f;
+                                       /* Davicom's media number for 100BaseTX is strange */
+                                       if (tp->chip_id == DM910X && leaf->media == 1)
+                                               leaf->media = 3;
                                        switch (leaf->media) {
                                        case 0: new_advertise |= 0x0020; break;
                                        case 4: new_advertise |= 0x0040; break;
index 357454359d44794a39ecb820530087121555fe56..b0dcf1da1978b8467c2a7eaa07052726e2ecfd1a 100644 (file)
@@ -90,6 +90,7 @@ void tulip_timer(unsigned long data)
        case DC21142:
        case MX98713:
        case COMPEX9881:
+       case DM910X:
        default: {
                struct medialeaf *mleaf;
                unsigned char *p;
index d0668cf42d82ef190339ad50608d09eac0ce502c..dfe3b2a4f551f7cdd1eab28240bfc701759d0ff4 100644 (file)
@@ -76,6 +76,7 @@ enum chips {
        COMET,
        COMPEX9881,
        I21145,
+       DM910X,
 };
 
 
index f5d261027c1f8f11ce4b0369fbcd58e1eb605990..40fc7921dba590df437c2a84543abcc54e1ac0b9 100644 (file)
@@ -28,7 +28,7 @@
 #include <asm/unaligned.h>
 
 static char version[] __devinitdata =
-       "Linux Tulip driver version 0.9.11 (November 3, 2000)\n";
+       "Linux Tulip driver version 0.9.12 (December 17, 2000)\n";
 
 
 /* A few user-configurable values. */
@@ -147,6 +147,9 @@ struct tulip_chip_table tulip_tbl[] = {
   { "Intel DS21145 Tulip", 128, 0x0801fbff,
        HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI | HAS_NWAY,
        t21142_timer },
+  { "Davicom DM9102/DM9102A", 128, 0x0001ebef,
+       HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | HAS_ACPI,
+       tulip_timer },
   {0},
 };
 
@@ -171,8 +174,8 @@ static struct pci_device_id tulip_pci_tbl[] __devinitdata = {
        { 0x104A, 0x2774, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
        { 0x11F6, 0x9881, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMPEX9881 },
        { 0x8086, 0x0039, PCI_ANY_ID, PCI_ANY_ID, 0, 0, I21145 },
-       { 0x1282, 0x9100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21140 },
-       { 0x1282, 0x9102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21140 },
+       { 0x1282, 0x9100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DM910X },
+       { 0x1282, 0x9102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DM910X },
        { 0x1113, 0x1217, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98715 },
        {0, }
 };
@@ -467,7 +470,8 @@ static void tulip_tx_timeout(struct net_device *dev)
                        tulip_select_media(dev, 0);
                }
        } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142
-                          || tp->chip_id == MX98713 || tp->chip_id == COMPEX9881) {
+                          || tp->chip_id == MX98713 || tp->chip_id == COMPEX9881
+                          || tp->chip_id == DM910X) {
                printk(KERN_WARNING "%s: 21140 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),
@@ -1355,6 +1359,7 @@ static int __devinit tulip_init_one (struct pci_dev *pdev,
                outl(0x00000004, ioaddr + CSR13);
                break;
        case DC21140:
+       case DM910X:
        default:
                if (tp->mtable)
                        outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12);
index 2996fb6f9951efb4ac0eb67a5914e7185a3b5d3d..417e9e5d9b7bba36a70b2d3b4a453ed3926b5190 100644 (file)
        Do not change the version information unless an improvement has been made.
        Merely removing my name, as Compex has done in the past, does not count
        as an improvement.
+
+       Changelog:
+       * ported to 2.4
+               ???
+       * spin lock update, memory barriers, new style dma mappings
+               limit each tx buffer to < 1024 bytes
+               remove DescIntr from Rx descriptors (that's an Tx flag)
+               remove next pointer from Tx descriptors
+               synchronize tx_q_bytes
+               software reset in tx_timeout
+                       Copyright (C) 2000 Manfred Spraul
+
+       TODO:
+       * according to the documentation, the chip supports big endian
+               descriptors. Remove cpu_to_le32, enable BE descriptors.
 */
 
 /* These identify the driver base version and may not be removed. */
 static const char version1[] =
-"winbond-840.c:v1.01 5/15/2000  Donald Becker <becker@scyld.com>\n";
+"winbond-840.c:v1.01 (2.4 port) 5/15/2000  Donald Becker <becker@scyld.com>\n";
 static const char version2[] =
 "  http://www.scyld.com/network/drivers.html\n";
 
@@ -81,6 +96,8 @@ static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
 #define TX_FIFO_SIZE (2048)
 #define TX_BUG_FIFO_LIMIT (TX_FIFO_SIZE-1514-16)
 
+#define TX_BUFLIMIT    (1024-128)
+
 /* Operational parameters that usually are not changed. */
 /* Time in jiffies before concluding the transmitter is hung. */
 #define TX_TIMEOUT  (2*HZ)
@@ -113,12 +130,7 @@ static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
 #include <asm/processor.h>             /* Processor type for cache alignment. */
 #include <asm/bitops.h>
 #include <asm/io.h>
-
-/* Condensed operations for readability.
-   The compatibility defines are in kern_compat.h */
-
-#define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr))
-#define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr))
+#include <asm/delay.h>
 
 MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
 MODULE_DESCRIPTION("Winbond W89c840 Ethernet driver");
@@ -280,7 +292,7 @@ struct w840_rx_desc {
        s32 status;
        s32 length;
        u32 buffer1;
-       u32 next_desc;
+       u32 buffer2;
 };
 
 struct w840_tx_desc {
@@ -293,14 +305,21 @@ struct w840_tx_desc {
 enum desc_status_bits {
        DescOwn=0x80000000, DescEndRing=0x02000000, DescUseLink=0x01000000,
        DescWholePkt=0x60000000, DescStartPkt=0x20000000, DescEndPkt=0x40000000,
+};
+
+/* Bits in w840_tx_desc.length */
+enum desc_length_bits {
        DescIntr=0x80000000,
 };
 
 #define PRIV_ALIGN     15      /* Required alignment mask */
 struct netdev_private {
-       /* Descriptor rings first for alignment. */
-       struct w840_rx_desc rx_ring[RX_RING_SIZE];
-       struct w840_tx_desc tx_ring[TX_RING_SIZE];
+       struct w840_rx_desc *rx_ring;
+       dma_addr_t      rx_addr[RX_RING_SIZE];
+       struct w840_tx_desc *tx_ring;
+       dma_addr_t      tx_addr[RX_RING_SIZE];
+       dma_addr_t ring_dma_addr;
+       struct pci_dev *pdev;
        /* The addresses of receive-in-place skbuffs. */
        struct sk_buff* rx_skbuff[RX_RING_SIZE];
        /* The saved address of a sent-in-place packet/buffer, for later free(). */
@@ -334,13 +353,15 @@ static void mdio_write(struct net_device *dev, int phy_id, int location, int val
 static int  netdev_open(struct net_device *dev);
 static void check_duplex(struct net_device *dev);
 static void netdev_timer(unsigned long data);
+static void init_rxtx_rings(struct net_device *dev);
+static void free_rxtx_rings(struct netdev_private *np);
+static void init_registers(struct net_device *dev);
 static void tx_timeout(struct net_device *dev);
-static void init_ring(struct net_device *dev);
+static int alloc_ring(struct net_device *dev);
 static int  start_tx(struct sk_buff *skb, struct net_device *dev);
 static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs);
 static void netdev_error(struct net_device *dev, int intr_status);
 static int  netdev_rx(struct net_device *dev);
-static void netdev_error(struct net_device *dev, int intr_status);
 static inline unsigned ether_crc(int length, unsigned char *data);
 static void set_rx_mode(struct net_device *dev);
 static struct net_device_stats *get_stats(struct net_device *dev);
@@ -364,6 +385,11 @@ static int __devinit w840_probe1 (struct pci_dev *pdev,
                return -EIO;
        pci_set_master(pdev);
 
+       if(!pci_dma_supported(pdev,0xFFFFffff)) {
+               printk(KERN_WARNING "Winbond-840: Device %s disabled due to DMA limitations.\n",
+                               pdev->name);
+               return -EIO;
+       }
        dev = init_etherdev(NULL, sizeof(*np));
        if (!dev)
                return -ENOMEM;
@@ -403,6 +429,7 @@ static int __devinit w840_probe1 (struct pci_dev *pdev,
        np = dev->priv;
        np->chip_id = chip_idx;
        np->drv_flags = pci_id_tbl[chip_idx].drv_flags;
+       np->pdev = pdev;
        spin_lock_init(&np->lock);
        
        pdev->driver_data = dev;
@@ -632,60 +659,12 @@ static int netdev_open(struct net_device *dev)
                printk(KERN_DEBUG "%s: w89c840_open() irq %d.\n",
                           dev->name, dev->irq);
 
-       init_ring(dev);
-
-       writel(virt_to_bus(np->rx_ring), ioaddr + RxRingPtr);
-       writel(virt_to_bus(np->tx_ring), ioaddr + TxRingPtr);
-
-       for (i = 0; i < 6; i++)
-               writeb(dev->dev_addr[i], ioaddr + StationAddr + i);
-
-       /* Initialize other registers. */
-       /* Configure the PCI bus bursts and FIFO thresholds.
-          486: Set 8 longword cache alignment, 8 longword burst.
-          586: Set 16 longword cache alignment, no burst limit.
-          Cache alignment bits 15:14        Burst length 13:8
-               0000    <not allowed>           0000 align to cache     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. */
-#if defined(__powerpc__)               /* Big-endian */
-       writel(0x00100080 | 0xE010, ioaddr + PCIBusCfg);
-#elif defined(__alpha__)
-       writel(0xE010, ioaddr + PCIBusCfg);
-#elif defined(__i386__)
-#if defined(MODULE)
-       writel(0xE010, ioaddr + PCIBusCfg);
-#else
-       /* When not a module we can work around broken '486 PCI boards. */
-#define x86 boot_cpu_data.x86
-       writel((x86 <= 4 ? 0x4810 : 0xE010), ioaddr + PCIBusCfg);
-       if (x86 <= 4)
-               printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache "
-                          "alignment to %x.\n", dev->name,
-                          (x86 <= 4 ? 0x4810 : 0x8010));
-#endif
-#else
-       writel(0xE010, ioaddr + PCIBusCfg);
-#warning Processor architecture undefined!
-#endif
-
-       if (dev->if_port == 0)
-               dev->if_port = np->default_port;
+       if((i=alloc_ring(dev)))
+               return i;
 
-       writel(0, ioaddr + RxStartDemand);
-       np->csr6 = 0x20022002;
-       check_duplex(dev);
-       set_rx_mode(dev);
+       init_registers(dev);
 
        netif_start_queue(dev);
-
-       /* Clear and Enable interrupts by setting the interrupt mask. */
-       writel(0x1A0F5, ioaddr + IntrStatus);
-       writel(0x1A0F5, ioaddr + IntrEnable);
-
        if (debug > 2)
                printk(KERN_DEBUG "%s: Done netdev_open().\n", dev->name);
 
@@ -733,15 +712,149 @@ static void netdev_timer(unsigned long data)
                           "config %8.8x.\n",
                           dev->name, (int)readl(ioaddr + IntrStatus),
                           (int)readl(ioaddr + NetworkConfig));
+       spin_lock_irq(&np->lock);
        check_duplex(dev);
        if (np->csr6 != old_csr6) {
                writel(np->csr6 & ~0x0002, ioaddr + NetworkConfig);
                writel(np->csr6 | 0x2002, ioaddr + NetworkConfig);
        }
+       spin_unlock_irq(&np->lock);
        np->timer.expires = jiffies + next_tick;
        add_timer(&np->timer);
 }
 
+static void init_rxtx_rings(struct net_device *dev)
+{
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       int i;
+
+       np->rx_head_desc = &np->rx_ring[0];
+       np->tx_ring = (struct w840_tx_desc*)&np->rx_ring[RX_RING_SIZE];
+
+       /* Initial all Rx descriptors. */
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               np->rx_ring[i].length = cpu_to_le32(np->rx_buf_sz);
+               np->rx_ring[i].status = 0;
+               np->rx_skbuff[i] = 0;
+       }
+       /* Mark the last entry as wrapping the ring. */
+       np->rx_ring[i-1].length |= cpu_to_le32(DescEndRing);
+
+       /* Fill in the Rx buffers.  Handle allocation failure gracefully. */
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz);
+               np->rx_skbuff[i] = skb;
+               if (skb == NULL)
+                       break;
+               skb->dev = dev;                 /* Mark as being used by this device. */
+               np->rx_addr[i] = pci_map_single(np->pdev,skb->tail,
+                                       skb->len,PCI_DMA_FROMDEVICE);
+
+               np->rx_ring[i].buffer1 = cpu_to_le32(np->rx_addr[i]);
+               np->rx_ring[i].status = cpu_to_le32(DescOwn);
+       }
+
+       np->cur_rx = 0;
+       np->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
+
+       /* Initialize the Tx descriptors */
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               np->tx_skbuff[i] = 0;
+               np->tx_ring[i].status = 0;
+       }
+       np->tx_full = 0;
+       np->tx_q_bytes = np->dirty_tx = np->cur_tx = 0;
+
+       writel(np->ring_dma_addr, dev->base_addr + RxRingPtr);
+       writel(np->ring_dma_addr+sizeof(struct w840_rx_desc)*RX_RING_SIZE,
+               dev->base_addr + TxRingPtr);
+
+}
+
+static void free_rxtx_rings(struct netdev_private* np)
+{
+       int i;
+       /* Free all the skbuffs in the Rx queue. */
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               np->rx_ring[i].status = 0;
+               if (np->rx_skbuff[i]) {
+                       pci_unmap_single(np->pdev,
+                                               np->rx_addr[i],
+                                               np->rx_skbuff[i]->len,
+                                               PCI_DMA_FROMDEVICE);
+                       dev_kfree_skb(np->rx_skbuff[i]);
+               }
+               np->rx_skbuff[i] = 0;
+       }
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               if (np->tx_skbuff[i]) {
+                       pci_unmap_single(np->pdev,
+                                               np->tx_addr[i],
+                                               np->tx_skbuff[i]->len,
+                                               PCI_DMA_TODEVICE);
+                       dev_kfree_skb(np->tx_skbuff[i]);
+               }
+               np->tx_skbuff[i] = 0;
+       }
+}
+
+static void init_registers(struct net_device *dev)
+{
+       struct netdev_private *np = (struct netdev_private *)dev->priv;
+       long ioaddr = dev->base_addr;
+       int i;
+
+       for (i = 0; i < 6; i++)
+               writeb(dev->dev_addr[i], ioaddr + StationAddr + i);
+
+       /* Initialize other registers. */
+       /* Configure the PCI bus bursts and FIFO thresholds.
+          486: Set 8 longword cache alignment, 8 longword burst.
+          586: Set 16 longword cache alignment, no burst limit.
+          Cache alignment bits 15:14        Burst length 13:8
+               0000    <not allowed>           0000 align to cache     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. */
+#if defined(__powerpc__)               /* Big-endian */
+       writel(0x00100080 | 0xE010, ioaddr + PCIBusCfg);
+#elif defined(__alpha__)
+       writel(0xE010, ioaddr + PCIBusCfg);
+#elif defined(__i386__)
+#if defined(MODULE)
+       writel(0xE010, ioaddr + PCIBusCfg);
+#else
+       /* When not a module we can work around broken '486 PCI boards. */
+#define x86 boot_cpu_data.x86
+       writel((x86 <= 4 ? 0x4810 : 0xE010), ioaddr + PCIBusCfg);
+       if (x86 <= 4)
+               printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache "
+                          "alignment to %x.\n", dev->name,
+                          (x86 <= 4 ? 0x4810 : 0x8010));
+#endif
+#else
+       writel(0xE010, ioaddr + PCIBusCfg);
+#warning Processor architecture undefined!
+#endif
+
+       if (dev->if_port == 0)
+               dev->if_port = np->default_port;
+
+       /* Fast Ethernet; 128 byte Tx threshold; 
+               Transmit on; Receive on; */
+       np->csr6 = 0x20022002;
+       check_duplex(dev);
+       set_rx_mode(dev);
+       writel(0, ioaddr + RxStartDemand);
+
+       /* Clear and Enable interrupts by setting the interrupt mask. */
+       writel(0x1A0F5, ioaddr + IntrStatus);
+       writel(0x1A0F5, ioaddr + IntrEnable);
+
+}
+
 static void tx_timeout(struct net_device *dev)
 {
        struct netdev_private *np = (struct netdev_private *)dev->priv;
@@ -758,70 +871,60 @@ static void tx_timeout(struct net_device *dev)
                        printk(" %8.8x", (unsigned int)np->rx_ring[i].status);
                printk("\n"KERN_DEBUG"  Tx ring %8.8x: ", (int)np->tx_ring);
                for (i = 0; i < TX_RING_SIZE; i++)
-                       printk(" %4.4x", np->tx_ring[i].status);
+                       printk(" %8.8x", np->tx_ring[i].status);
                printk("\n");
        }
+       printk(KERN_DEBUG "Tx cur %d Tx dirty %d Tx Full %d, q bytes %d.\n",
+                               np->cur_tx, np->dirty_tx, np->tx_full,np->tx_q_bytes);
+       printk(KERN_DEBUG "Tx Descriptor addr %xh.\n",readl(ioaddr+0x4C));
+
 #endif
+       spin_lock_irq(&np->lock);
+       /*
+        * Under high load dirty_tx and the internal tx descriptor pointer
+        * come out of sync, thus perform a software reset and reinitialize
+        * everything.
+        */
+
+       writel(1, dev->base_addr+PCIBusCfg);
+       udelay(1);
+
+       free_rxtx_rings(np);
+       init_rxtx_rings(dev);
+       init_registers(dev);
+       set_rx_mode(dev);
 
-       /* Perhaps we should reinitialize the hardware here.  Just trigger a
-          Tx demand for now. */
-       writel(0, ioaddr + TxStartDemand);
-       dev->if_port = 0;
-       /* Stop and restart the chip's Tx processes . */
+       spin_unlock_irq(&np->lock);
 
+       netif_wake_queue(dev);
        dev->trans_start = jiffies;
        np->stats.tx_errors++;
        return;
 }
 
-
 /* Initialize the Rx and Tx rings, along with various 'dev' bits. */
-static void init_ring(struct net_device *dev)
+static int alloc_ring(struct net_device *dev)
 {
        struct netdev_private *np = (struct netdev_private *)dev->priv;
-       int i;
-
-       np->tx_full = 0;
-       np->tx_q_bytes = np->cur_rx = np->cur_tx = 0;
-       np->dirty_rx = np->dirty_tx = 0;
 
        np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
-       np->rx_head_desc = &np->rx_ring[0];
 
-       /* Initial all Rx descriptors. */
-       for (i = 0; i < RX_RING_SIZE; i++) {
-               np->rx_ring[i].length = cpu_to_le32(np->rx_buf_sz);
-               np->rx_ring[i].status = 0;
-               np->rx_ring[i].next_desc = virt_to_le32desc(&np->rx_ring[i+1]);
-               np->rx_skbuff[i] = 0;
-       }
-       /* Mark the last entry as wrapping the ring. */
-       np->rx_ring[i-1].length |= cpu_to_le32(DescEndRing);
-       np->rx_ring[i-1].next_desc = virt_to_le32desc(&np->rx_ring[0]);
-
-       /* Fill in the Rx buffers.  Handle allocation failure gracefully. */
-       for (i = 0; i < RX_RING_SIZE; i++) {
-               struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz);
-               np->rx_skbuff[i] = skb;
-               if (skb == NULL)
-                       break;
-               skb->dev = dev;                 /* Mark as being used by this device. */
-               np->rx_ring[i].buffer1 = virt_to_le32desc(skb->tail);
-               np->rx_ring[i].status = cpu_to_le32(DescOwn | DescIntr);
-       }
-       np->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
-
-       for (i = 0; i < TX_RING_SIZE; i++) {
-               np->tx_skbuff[i] = 0;
-               np->tx_ring[i].status = 0;
-       }
-       return;
+       np->rx_ring = pci_alloc_consistent(np->pdev,
+                       sizeof(struct w840_rx_desc)*RX_RING_SIZE +
+                       sizeof(struct w840_tx_desc)*TX_RING_SIZE,
+                       &np->ring_dma_addr);
+       if(!np->rx_ring)
+               return -ENOMEM;
+       init_rxtx_rings(dev);
+       return 0;
 }
 
+
 static int start_tx(struct sk_buff *skb, struct net_device *dev)
 {
        struct netdev_private *np = (struct netdev_private *)dev->priv;
        unsigned entry;
+       int len1, len2;
 
        /* Caution: the write order is important here, set the field
           with the "ownership" bits last. */
@@ -830,76 +933,35 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev)
        entry = np->cur_tx % TX_RING_SIZE;
 
        np->tx_skbuff[entry] = skb;
-       np->tx_ring[entry].buffer1 = virt_to_le32desc(skb->data);
-
-#define one_buffer
-#define BPT 1022
-#if defined(one_buffer)
-       np->tx_ring[entry].length = cpu_to_le32(DescWholePkt | skb->len);
+       np->tx_addr[entry] = pci_map_single(np->pdev,
+                               skb->data,skb->len, PCI_DMA_TODEVICE);
+       np->tx_ring[entry].buffer1 = cpu_to_le32(np->tx_addr[entry]);
+       len2 = 0;
+       len1 = skb->len;
+       if(len1 > TX_BUFLIMIT) {
+               len1 = TX_BUFLIMIT;
+               len2 = skb->len-len1;
+               np->tx_ring[entry].buffer2 = cpu_to_le32(np->tx_addr[entry]+TX_BUFLIMIT);
+       }
+       np->tx_ring[entry].length = cpu_to_le32(DescWholePkt | (len2 << 11) | len1);
        if (entry >= TX_RING_SIZE-1)             /* Wrap ring */
                np->tx_ring[entry].length |= cpu_to_le32(DescIntr | DescEndRing);
-       np->tx_ring[entry].status = cpu_to_le32(DescOwn);
        np->cur_tx++;
-#elif defined(two_buffer)
-       if (skb->len > BPT) {
-               unsigned int entry1 = ++np->cur_tx % TX_RING_SIZE;
-               np->tx_ring[entry].length = cpu_to_le32(DescStartPkt | BPT);
-               np->tx_ring[entry1].length = cpu_to_le32(DescEndPkt | (skb->len - BPT));
-               np->tx_ring[entry1].buffer1 = virt_to_le32desc((skb->data) + BPT);
-               np->tx_ring[entry1].status = cpu_to_le32(DescOwn);
-               np->tx_ring[entry].status = cpu_to_le32(DescOwn);
-               if (entry >= TX_RING_SIZE-1)
-                       np->tx_ring[entry].length |= cpu_to_le32(DescIntr|DescEndRing);
-               else if (entry1 >= TX_RING_SIZE-1)
-                       np->tx_ring[entry1].length |= cpu_to_le32(DescIntr|DescEndRing);
-               np->cur_tx++;
-       } else {
-               np->tx_ring[entry].length = cpu_to_le32(DescWholePkt | skb->len);
-               if (entry >= TX_RING_SIZE-1)             /* Wrap ring */
-                       np->tx_ring[entry].length |= cpu_to_le32(DescIntr | DescEndRing);
-               np->tx_ring[entry].status = cpu_to_le32(DescOwn);
-               np->cur_tx++;
-       }
-#elif defined(split_buffer)
-       {
-               /* Work around the Tx-FIFO-full bug by splitting our transmit packet
-                  into two pieces, the first which may be loaded without overflowing
-                  the FIFO, and the second which contains the remainder of the
-                  packet.  When we get a Tx-done interrupt that frees enough room
-                  in the FIFO we mark the remainder of the packet as loadable.
-
-                  This has the problem that the Tx descriptors are written both
-                  here and in the interrupt handler.
-               */
-
-               int buf1size = TX_FIFO_SIZE - np->tx_q_bytes;
-               int buf2size = skb->len - buf1size;
-
-               if (buf2size <= 0) {            /* We fit into one descriptor. */
-                       np->tx_ring[entry].length = cpu_to_le32(DescWholePkt | skb->len);
-               } else {                                /* We must use two descriptors. */
-                       unsigned int entry2;
-                       np->tx_ring[entry].length =
-                               cpu_to_le32(DescIntr | DescStartPkt | buf1size);
-                       if (entry >= TX_RING_SIZE-1) {           /* Wrap ring */
-                               np->tx_ring[entry].length |= cpu_to_le32(DescEndRing);
-                               entry2 = 0;
-                       } else
-                               entry2 = entry + 1;
-                       np->cur_tx++;
-                       np->tx_ring[entry2].buffer1 =
-                               virt_to_le32desc(skb->data + buf1size);
-                       np->tx_ring[entry2].length = cpu_to_le32(DescEndPkt | buf2size);
-                       if (entry2 >= TX_RING_SIZE-1)            /* Wrap ring */
-                               np->tx_ring[entry2].length |= cpu_to_le32(DescEndRing);
-               }
-               np->tx_ring[entry].status = cpu_to_le32(DescOwn);
-               np->cur_tx++;
-       }
-#endif
-       np->tx_q_bytes += skb->len;
+
+       /* The spinlock protects against 2 races:
+        * - tx_q_bytes is updated by this function and intr_handler
+        * - our hardware is extremely fast and finishes the packet between
+        *      our check for "queue full" and netif_stop_queue.
+        *      Thus setting DescOwn and netif_stop_queue must be atomic.
+        */
+       spin_lock_irq(&np->lock);
+
+       wmb(); /* flush length, buffer1, buffer2 */
+       np->tx_ring[entry].status = cpu_to_le32(DescOwn);
+       wmb(); /* flush status and kick the hardware */
        writel(0, dev->base_addr + TxStartDemand);
 
+       np->tx_q_bytes += skb->len;
        /* Work around horrible bug in the chip by marking the queue as full
           when we do not have FIFO room for a maximum sized packet. */
        if (np->cur_tx - np->dirty_tx > TX_QUEUE_LEN)
@@ -911,6 +973,7 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev)
                netif_stop_queue(dev);
 
        dev->trans_start = jiffies;
+       spin_unlock_irq(&np->lock);
 
        if (debug > 4) {
                printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n",
@@ -977,6 +1040,9 @@ static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs)
                                np->stats.tx_packets++;
                        }
                        /* Free the original skb. */
+                       pci_unmap_single(np->pdev,np->tx_addr[entry],
+                                               np->tx_skbuff[entry]->len,
+                                               PCI_DMA_TODEVICE);
                        np->tx_q_bytes -= np->tx_skbuff[entry]->len;
                        dev_kfree_skb_irq(np->tx_skbuff[entry]);
                        np->tx_skbuff[entry] = 0;
@@ -1070,6 +1136,9 @@ static int netdev_rx(struct net_device *dev)
                                && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
                                skb->dev = dev;
                                skb_reserve(skb, 2);    /* 16 byte align the IP header */
+                               pci_dma_sync_single(np->pdev,np->rx_addr[entry],
+                                                       np->rx_skbuff[entry]->len,
+                                                       PCI_DMA_FROMDEVICE);
                                /* Call copy + cksum if available. */
 #if HAS_IP_COPYSUM
                                eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0);
@@ -1079,15 +1148,11 @@ static int netdev_rx(struct net_device *dev)
                                           pkt_len);
 #endif
                        } else {
-                               char *temp = skb_put(skb = np->rx_skbuff[entry], pkt_len);
+                               pci_unmap_single(np->pdev,np->rx_addr[entry],
+                                                       np->rx_skbuff[entry]->len,
+                                                       PCI_DMA_FROMDEVICE);
+                               skb_put(skb = np->rx_skbuff[entry], pkt_len);
                                np->rx_skbuff[entry] = NULL;
-#ifndef final_version                          /* Remove after testing. */
-                               if (le32desc_to_virt(desc->buffer1) != temp)
-                                       printk(KERN_ERR "%s: Internal fault: The skbuff addresses "
-                                                  "do not match in netdev_rx: %p vs. %p / %p.\n",
-                                                  dev->name, le32desc_to_virt(desc->buffer1),
-                                                  skb->head, temp);
-#endif
                        }
 #ifndef final_version                          /* Remove after testing. */
                        /* You will want this info for the initial debug. */
@@ -1122,8 +1187,12 @@ static int netdev_rx(struct net_device *dev)
                        if (skb == NULL)
                                break;                  /* Better luck next round. */
                        skb->dev = dev;                 /* Mark as being used by this device. */
-                       np->rx_ring[entry].buffer1 = virt_to_le32desc(skb->tail);
+                       np->rx_addr[entry] = pci_map_single(np->pdev,
+                                                       skb->tail,
+                                                       skb->len, PCI_DMA_FROMDEVICE);
+                       np->rx_ring[entry].buffer1 = cpu_to_le32(np->rx_addr[entry]);
                }
+               wmb();
                np->rx_ring[entry].status = cpu_to_le32(DescOwn);
        }
 
@@ -1141,7 +1210,21 @@ static void netdev_error(struct net_device *dev, int intr_status)
        if (intr_status == 0xffffffff)
                return;
        if (intr_status & TxFIFOUnderflow) {
-               np->csr6 += 0x4000;     /* Bump up the Tx threshold */
+               /* Bump up the Tx threshold */
+#if 0
+               /* This causes lots of dropped packets,
+                * and under high load even tx_timeouts
+                */
+               np->csr6 += 0x4000;
+#else
+               int cur = (np->csr6 >> 14)&0x7f;
+               if (cur < 64)
+                       cur *= 2;
+                else
+                       cur = 0; /* load full packet before starting */
+               np->csr6 &= ~(0x7F << 14);
+               np->csr6 |= cur<<14;
+#endif
                printk(KERN_DEBUG "%s: Tx underflow, increasing threshold to %8.8x.\n",
                           dev->name, np->csr6);
                writel(np->csr6, ioaddr + NetworkConfig);
@@ -1270,13 +1353,13 @@ static int netdev_close(struct net_device *dev)
 #ifdef __i386__
        if (debug > 2) {
                printk("\n"KERN_DEBUG"  Tx ring at %8.8x:\n",
-                          (int)virt_to_le32desc(np->tx_ring));
+                          (int)np->tx_ring);
                for (i = 0; i < TX_RING_SIZE; i++)
                        printk(" #%d desc. %4.4x %4.4x %8.8x.\n",
                                   i, np->tx_ring[i].length,
                                   np->tx_ring[i].status, np->tx_ring[i].buffer1);
                printk("\n"KERN_DEBUG "  Rx ring %8.8x:\n",
-                          (int)virt_to_le32desc(np->rx_ring));
+                          (int)np->rx_ring);
                for (i = 0; i < RX_RING_SIZE; i++) {
                        printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x\n",
                                   i, np->rx_ring[i].length,
@@ -1289,19 +1372,7 @@ static int netdev_close(struct net_device *dev)
 
        del_timer_sync(&np->timer);
 
-       /* Free all the skbuffs in the Rx queue. */
-       for (i = 0; i < RX_RING_SIZE; i++) {
-               np->rx_ring[i].status = 0;
-               if (np->rx_skbuff[i]) {
-                       dev_kfree_skb(np->rx_skbuff[i]);
-               }
-               np->rx_skbuff[i] = 0;
-       }
-       for (i = 0; i < TX_RING_SIZE; i++) {
-               if (np->tx_skbuff[i])
-                       dev_kfree_skb(np->tx_skbuff[i]);
-               np->tx_skbuff[i] = 0;
-       }
+       free_rxtx_rings(np);
 
        return 0;
 }
@@ -1311,7 +1382,7 @@ static void __devexit w840_remove1 (struct pci_dev *pdev)
        struct net_device *dev = pdev->driver_data;
        
        /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
-       while (dev) {
+       if (dev) {
                struct netdev_private *np = (void *)(dev->priv);
                unregister_netdev(dev);
 #ifdef USE_IO_OPS
index 9913c1998ce91ea4fcb14bae10c8faa70adfe2cb..6ee9208e9bd677c7860d56915dac9e861f0d4af9 100644 (file)
@@ -84,6 +84,19 @@ static void __init quirk_triton(struct pci_dev *dev)
        }
 }
 
+/*
+ *     VIA Apollo VP3 needs ETBF on BT848/878
+ */
+static void __init quirk_viaetbf(struct pci_dev *dev)
+{
+       if((pci_pci_problems&PCIPCI_VIAETBF)==0)
+       {
+               printk(KERN_INFO "Limiting direct PCI/PCI transfers.\n");
+               pci_pci_problems|=PCIPCI_VIAETBF;
+       }
+}
+
 /*
  *     Natoma has some interesting boundary conditions with Zoran stuff
  *     at least
@@ -262,6 +275,7 @@ static struct pci_fixup pci_fixups[] __initdata = {
        { PCI_FIXUP_FINAL,      PCI_VENDOR_ID_INTEL,    PCI_DEVICE_ID_INTEL_82443BX_2,  quirk_natoma },
        { PCI_FIXUP_FINAL,      PCI_VENDOR_ID_SI,       PCI_DEVICE_ID_SI_5597,          quirk_nopcipci },
        { PCI_FIXUP_FINAL,      PCI_VENDOR_ID_SI,       PCI_DEVICE_ID_SI_496,           quirk_nopcipci },
+       { PCI_FIXUP_FINAL,      PCI_VENDOR_ID_VIA,      PCI_DEVICE_ID_VIA_82C597_0,     quirk_viaetbf },
        { PCI_FIXUP_HEADER,     PCI_VENDOR_ID_VIA,      PCI_DEVICE_ID_VIA_82C597_0,     quirk_vt82c598_id },
        { PCI_FIXUP_HEADER,     PCI_VENDOR_ID_VIA,      PCI_DEVICE_ID_VIA_82C586_3,     quirk_vt82c586_acpi },
        { PCI_FIXUP_HEADER,     PCI_VENDOR_ID_VIA,      PCI_DEVICE_ID_VIA_82C686_4,     quirk_vt82c686_acpi },
index 36ef8ea7fe0c4efa751eb36abd86a8e7526942cc..ae06b7bfa4bdb28bdb3bc523f0795e98c4453274 100644 (file)
@@ -113,6 +113,7 @@ obj-$(CONFIG_SCSI_DEBUG)    += scsi_debug.o
 obj-$(CONFIG_SCSI_FCAL)                += fcal.o
 
 obj-$(CONFIG_CHR_DEV_ST)       += st.o
+obj-$(CONFIG_CHR_DEV_OSST)     += osst.o
 obj-$(CONFIG_BLK_DEV_SD)       += sd_mod.o
 obj-$(CONFIG_BLK_DEV_SR)       += sr_mod.o
 obj-$(CONFIG_CHR_DEV_SG)       += sg.o
diff --git a/drivers/scsi/README.osst b/drivers/scsi/README.osst
new file mode 100644 (file)
index 0000000..74f9b35
--- /dev/null
@@ -0,0 +1,219 @@
+README file for the osst driver
+===============================
+(w) Kurt Garloff <garloff@suse.de> 12/2000
+
+This file describes the osst driver as of version 0.8.x/0.9.x, the released
+version of the osst driver.
+It is intended to help advanced users to understand the role of osst and to
+get them started using (and maybe debugging) it.
+It won't address issues like "How do I compile a kernel?" or "How do I load
+a module?", as these are too basic.
+Once the OnStream got merged into the official kernel, the distro makers
+will provide the OnStream support for those who are not familiar with
+hacking their kernels.
+
+
+Purpose
+-------
+The osst driver was developed, because the standard SCSI tape driver in
+Linux, st, does not support the OnStream SC-x0 SCSI tape. The st is not to
+blame for that, as the OnStream tape drives do not support the standard SCSI
+command set for Serial Access Storage Devices (SASDs), which basically
+corresponds to the QIC-157 spec.
+Nevertheless, the OnStream tapes are nice pieces of hardware and therefore
+the osst driver has been written to make these tape devs supported by Linux.
+The driver is free software. It's released under the GNU GPL and planned to
+be integrated into the mainstream kernel.
+
+
+Implementation
+--------------
+The osst is a new high-level SCSI driver, just like st, sr, sd and sg. It
+can be compiled into the kernel or loaded as a module.
+As it represents a new device, it got assigned a new device node: /dev/osstX
+are character devices with major no 206 and minor numbers like the /dev/stX
+devices. If those are not present, you may create them by calling
+Makedevs.sh as root (see below).
+The driver started being a copy of st and as such, the osst devices'
+behavior looks very much the same as st to the userspace applications.
+
+
+History
+-------
+In the first place, osst shared it's identity very much with st. That meant
+that it used the same kernel structures and the same device node as st.
+So you could only have either of them being present in the kernel. This has
+been fixed by registering an own device, now.
+st and osst can coexist, each only accessing the devices it can support by
+themselves.
+
+
+Installation
+------------
+osst got integrated into the linux kernel. Select it during kernel
+configuration as module or compile statically into the kernel.
+Compile your kernel and install the modules.
+
+Now, your osst driver is inside the kernel or available as a module,
+depending on your choice during kernel config. You may still need to create
+the device nodes by calling the Makedevs.sh script (see below) manually,
+unless you use a devfs kernel, where this won't be needed.
+
+To load your module, you may use the command 
+modprobe osst
+as root. dmesg should show you, whether your OnStream tapes have been
+recognized.
+
+If you want to have the module autoloaded on access to /dev/osst, you may
+add something like
+alias char-major-206 osst
+to your /etc/modules.conf (old name: conf.modules).
+
+You may find it convenient to create a symbolic link 
+ln -s nosst0 /dev/tape
+to make programs assuming a default name of /dev/tape more convenient to
+use.
+
+The device nodes for osst have to be created. Use the Makedevs.sh script
+attached to this file.
+
+
+Using it
+--------
+You may use the OnStream tape driver with your standard backup software,
+which may be tar, cpio, amanda, arkeia, BRU, Lone Tar, ...
+by specifying /dev/(n)osst0 as the tape device to use or using the above
+symlink trick. The IOCTLs to control tape operation are also mostly
+supported and you may try the mt (or mt_st) program to jump between
+filemarks, eject the tape, ...
+
+There's one limitation: You need to use a block size of 32kB.
+
+(This limitation is worked on and will be fixed in version 0.8.8 of
+ this driver.)
+
+If you just want to get started with standard software, here is an example
+for creating and restoring a full backup:
+# Backup
+tar cvf - / --exclude /proc | buffer -s 32k -m 24M -B -t -o /dev/nosst0
+# Restore
+buffer -s 32k -m 8M -B -t -i /dev/osst0 | tar xvf - -C /
+
+The buffer command has been used to buffer the data before it goes to the
+tape (or the file system) in order to smooth out the data stream and prevent
+the tape from needing to stop and rewind. The OnStream does have an internal
+buffer and a variable speed which help this, but especially on writing, the
+buffering still proves useful in most cases. It also pads the data to
+guarantees the block size of 32k. (Otherwise you may pass the -b64 option to
+tar.)
+Expect something like 1.8MB/s for the SC-x0 drives and 0.9MB/s for the DI-30.
+The USB drive will give you about 0.7MB/s.
+On a fast machine, you may profit from software data compression (z flag for
+tar).
+
+
+USB and IDE
+-----------
+Via the SCSI emulation layers usb-storage and ide-scsi, you can also use the
+osst driver to drive the USB-30 and the DI-30 drives. (Unfortunately, there
+is no such layer for the parallel port, otherwise the DP-30 would work as
+well.) For the USB support, you need the latest 2.4.0-test kernels and the 
+latest usb-storage driver from 
+http://www.linux-usb.org/
+http://sourceforge.net/cvs/?group_id=3581
+
+Note that the ide-tape driver as of 1.16f uses a slightly outdated on-tape
+format and therefore is not completely interoperable with osst tapes.
+
+The ADR-x0 line is fully SCSI-2 compliant and is supported by st, not osst.
+The on-tape format is supposed to be compatible with the one used by osst.
+
+
+Feedback and updates
+--------------------
+The driver development is coordinated through a mailing list
+<osst@linux1.onstream.nl>
+a CVS repository and some web pages. 
+The tester's pages which contain recent news and updated drivers to download
+can be found on
+http://linux1.onstream.nl/test/
+
+If you find any problems, please have a look at the tester's page in order
+to see whether the problem is already known and solved. Otherwise, please
+report it to the mailing list. Your feedback is welcome. (This holds also
+for reports of successful usage, of course.) 
+In case of trouble, please do always provide the following info:
+* driver and kernel version used (see syslog)
+* driver messages (syslog)
+* SCSI config and OnStream Firmware (/proc/scsi/scsi)
+* description of error. Is it reproducible?
+* software and commands used
+
+You may subscribe to the mailing list, BTW, it's a majordomo list.
+
+
+Status
+------
+0.8.0 was the first widespread BETA release. Since then a lot of reports
+have been sent, but mostly reported success or only minor trouble.
+All the issues have been addressed.
+Check the web pages for more info about the current developments.
+0.9.x is the tree for the 2.3/2.4 kernel.
+
+
+Acknowledgments
+----------------
+The driver has been started by making a copy of Kai Makisara's st driver.
+Most of the development has been done by Willem Riede. The presence of the
+userspace program osg (onstreamsg) from Terry Hardie has been rather
+helpful. The same holds for Gadi Oxman's ide-tape support for the DI-30.
+I did add some patches to those drivers as well and coordinated things a
+little bit. 
+Note that most of them did mostly spend their spare time for the creation of
+this driver.
+The people from OnStream, especially Jack Bombeeck did support this project
+and always tried to answer HW or FW related questions. Furthermore, he
+pushed the FW developers to do the right things.
+SuSE did support this project by allowing me to work on it during my working
+time for them and by integrating the driver into their distro.
+
+More people did help by sending useful comments. Sorry to those who have
+been forgotten. Thanks to all the GNU/FSF and Linux developers who made this
+platform such an interesting, nice and stable platform.
+Thanks go to those who tested the drivers and did send useful reports. Your
+help is needed!
+
+
+Makedevs.sh
+-----------
+#!/bin/sh
+# Script to create OnStream SC-x0 device nodes (major 206)
+# Usage: Makedevs.sh [nos [path to dev]]
+# $Id: README.osst.kernel,v 1.4 2000/12/20 14:13:15 garloff Exp $
+major=206
+nrs=4
+dir=/dev
+test -z "$1" || nrs=$1
+test -z "$2" || dir=$2
+declare -i nr
+nr=0
+test -d $dir || mkdir -p $dir
+while test $nr -lt $nrs; do
+  mknod $dir/osst$nr c $major $nr
+  chown 0.disk $dir/osst$nr; chmod 660 $dir/osst$nr;
+  mknod $dir/nosst$nr c $major $[nr+128]
+  chown 0.disk $dir/nosst$nr; chmod 660 $dir/nosst$nr;
+  mknod $dir/osst${nr}l c $major $[nr+32]
+  chown 0.disk $dir/osst${nr}l; chmod 660 $dir/osst${nr}l;
+  mknod $dir/nosst${nr}l c $major $[nr+160]
+  chown 0.disk $dir/nosst${nr}l; chmod 660 $dir/nosst${nr}l;
+  mknod $dir/osst${nr}m c $major $[nr+64]
+  chown 0.disk $dir/osst${nr}m; chmod 660 $dir/osst${nr}m;
+  mknod $dir/nosst${nr}m c $major $[nr+192]
+  chown 0.disk $dir/nosst${nr}m; chmod 660 $dir/nosst${nr}m;
+  mknod $dir/osst${nr}a c $major $[nr+96]
+  chown 0.disk $dir/osst${nr}a; chmod 660 $dir/osst${nr}a;
+  mknod $dir/nosst${nr}a c $major $[nr+224]
+  chown 0.disk $dir/nosst${nr}a; chmod 660 $dir/nosst${nr}a;
+  let nr+=1
+done
diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c
new file mode 100644 (file)
index 0000000..2eca1b6
--- /dev/null
@@ -0,0 +1,5306 @@
+/*
+  SCSI Tape Driver for Linux version 1.1 and newer. See the accompanying
+  file README.st for more information.
+
+  History:
+
+  OnStream SCSI Tape support (osst) cloned from st.c by
+  Willem Riede (osst@riede.org) Feb 2000
+  Fixes ... Kurt Garloff <garloff@suse.de> Mar 2000
+
+  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, Steve Hirsch, Andreas Koppenh"ofer,
+  Michael Leodolter, Eyal Lebedinsky, J"org Weule, and Eric Youngdale.
+
+  Copyright 1992 - 2000 Kai Makisara
+                email Kai.Makisara@metla.fi
+
+  $Header: /home/cvsroot/Driver/osst.c,v 1.49 2000/12/20 02:56:01 garloff Exp $
+
+  Last modified: Wed Feb  2 22:04:05 2000 by makisara@kai.makisara.local
+  Some small formal changes - aeb, 950809
+*/
+
+static const char * cvsid = "$Id: osst.c,v 1.49 2000/12/20 02:56:01 garloff Exp $";
+const char * osst_version = "0.9.4.2";
+
+/* The "failure to reconnect" firmware bug */
+#define OSST_FW_NEED_POLL_MIN 10602 /*(107A)*/
+#define OSST_FW_NEED_POLL_MAX 10708 /*(108D)*/
+#define OSST_FW_NEED_POLL(x,d) ((x) >= OSST_FW_NEED_POLL_MIN && (x) <= OSST_FW_NEED_POLL_MAX && d->host->this_id != 7)
+
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mtio.h>
+#include <linux/ioctl.h>
+#include <linux/fcntl.h>
+#include <linux/spinlock.h>
+#include <linux/vmalloc.h>
+#include <asm/uaccess.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+
+/* The driver prints some debugging information on the console if DEBUG
+   is defined and non-zero. */
+#define DEBUG 0
+
+/* The message level for the debug messages is currently set to KERN_NOTICE
+   so that people can easily see the messages. Later when the debugging messages
+   in the drivers are more widely classified, this may be changed to KERN_DEBUG. */
+#define OSST_DEB_MSG  KERN_NOTICE
+
+#define MAJOR_NR OSST_MAJOR
+#include <linux/blk.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include <scsi/scsi_ioctl.h>
+
+#define ST_KILOBYTE 1024
+
+#include "st.h"
+#include "osst.h"
+#include "osst_options.h"
+#include "osst_detect.h"
+
+#include "constants.h"
+
+#ifdef MODULE
+MODULE_AUTHOR("Willem Riede");
+MODULE_DESCRIPTION("OnStream SCSI Tape Driver");
+MODULE_PARM(buffer_kbs, "i");
+MODULE_PARM(write_threshold_kbs, "i");
+MODULE_PARM(max_buffers, "i");
+MODULE_PARM(max_sg_segs, "i");
+static int buffer_kbs = 0;
+static int write_threshold_kbs = 0;
+static int max_buffers = 0;
+static int max_sg_segs = 0;
+#else
+static struct osst_dev_parm {
+       char   *name;
+       int    *val;
+} parms[] __initdata = {
+       { "buffer_kbs",          &buffer_kbs          },
+       { "write_threshold_kbs", &write_threshold_kbs },
+       { "max_buffers",         &max_buffers         },
+       { "max_sg_segs",         &max_sg_segs         };
+#endif
+
+/* Some default definitions have been moved to osst_options.h */
+#define OSST_BUFFER_SIZE (OSST_BUFFER_BLOCKS * ST_KILOBYTE)
+#define OSST_WRITE_THRESHOLD (OSST_WRITE_THRESHOLD_BLOCKS * ST_KILOBYTE)
+
+/* The buffer size should fit into the 24 bits for length in the
+   6-byte SCSI read and write commands. */
+#if OSST_BUFFER_SIZE >= (2 << 24 - 1)
+#error "Buffer size should not exceed (2 << 24 - 1) bytes!"
+#endif
+
+#if DEBUG
+static int debugging = 1;
+#endif
+
+#define MAX_RETRIES 0
+#define MAX_WRITE_RETRIES 0
+#define MAX_READY_RETRIES 5
+#define NO_TAPE  NOT_READY
+
+#define OSST_TIMEOUT (200 * HZ)
+#define OSST_LONG_TIMEOUT (1800 * HZ)
+
+#define TAPE_NR(x) (MINOR(x) & ~(128 | ST_MODE_MASK))
+#define TAPE_MODE(x) ((MINOR(x) & ST_MODE_MASK) >> ST_MODE_SHIFT)
+
+/* Internal ioctl to set both density (uppermost 8 bits) and blocksize (lower
+   24 bits) */
+#define SET_DENS_AND_BLK 0x10001
+
+static int osst_nbr_buffers;
+static int osst_buffer_size       = OSST_BUFFER_SIZE;
+static int osst_write_threshold   = OSST_WRITE_THRESHOLD;
+static int osst_max_buffers       = OSST_MAX_BUFFERS;
+static int osst_max_sg_segs       = OSST_MAX_SG;
+
+static OS_Scsi_Tape **os_scsi_tapes = NULL;
+static OSST_buffer  **osst_buffers  = NULL;
+
+static int modes_defined = FALSE;
+
+static OSST_buffer *new_tape_buffer(int, int);
+static int enlarge_buffer(OSST_buffer *, int, int);
+static void normalize_buffer(OSST_buffer *);
+static int append_to_buffer(const char *, OSST_buffer *, int);
+static int from_buffer(OSST_buffer *, char *, int);
+static int osst_zero_buffer_tail(OSST_buffer *);
+static int osst_copy_to_buffer(OSST_buffer *, unsigned char *);
+static int osst_copy_from_buffer(OSST_buffer *, unsigned char *);
+
+static int osst_init(void);
+static int osst_attach(Scsi_Device *);
+static int osst_detect(Scsi_Device *);
+static void osst_detach(Scsi_Device *);
+
+struct Scsi_Device_Template osst_template =
+{
+       name:           "OnStream tape",
+       tag:            "osst",
+       scsi_type:      TYPE_TAPE,
+       major:          OSST_MAJOR,
+       detect:         osst_detect,
+       init:           osst_init,
+       attach:         osst_attach,
+       detach:         osst_detach
+};
+
+static int osst_int_ioctl(OS_Scsi_Tape *STp, Scsi_Request ** aSRpnt, unsigned int cmd_in,unsigned long arg);
+
+static int osst_set_frame_position(OS_Scsi_Tape *STp, Scsi_Request ** aSRpnt, int frame, int skip);
+
+static int osst_get_frame_position(OS_Scsi_Tape *STp, Scsi_Request ** aSRpnt);
+
+static int osst_flush_write_buffer(OS_Scsi_Tape *STp, Scsi_Request ** aSRpnt, int file_blk);
+
+static int osst_write_error_recovery(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, int pending);
+
+\f
+/* Routines that handle the interaction with mid-layer SCSI routines */
+
+/* Convert the result to success code */
+static int osst_chk_result(OS_Scsi_Tape * STp, Scsi_Request * SRpnt)
+{
+       int dev = TAPE_NR(STp->devt);
+       int result = SRpnt->sr_result;
+       unsigned char * sense = SRpnt->sr_sense_buffer, scode;
+#if DEBUG
+       const char *stp;
+#endif
+
+       if (!result) {
+               sense[0] = 0;    /* We don't have sense data if this byte is zero */
+               return 0;
+       }
+       if (driver_byte(result) & DRIVER_SENSE)
+               scode = sense[2] & 0x0f;
+       else {
+               sense[0] = 0;    /* We don't have sense data if this byte is zero */
+               scode = 0;
+       }
+
+#if DEBUG
+       if (debugging) {
+               printk(OSST_DEB_MSG "osst%d: Error: %x, cmd: %x %x %x %x %x %x Len: %d\n",
+                  dev, result,
+                  SRpnt->sr_cmnd[0], SRpnt->sr_cmnd[1], SRpnt->sr_cmnd[2],
+                  SRpnt->sr_cmnd[3], SRpnt->sr_cmnd[4], SRpnt->sr_cmnd[5],
+                  SRpnt->sr_bufflen);
+               if (driver_byte(result) & DRIVER_SENSE)
+                       print_req_sense("osst", SRpnt);
+       }
+       else
+#endif
+       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 &&
+                SRpnt->sr_cmnd[0] != MODE_SENSE &&
+                SRpnt->sr_cmnd[0] != TEST_UNIT_READY)) { /* Abnormal conditions for tape */
+               if (driver_byte(result) & DRIVER_SENSE) {
+                       printk(KERN_WARNING "osst%d: Error with sense data: ", dev);
+                       print_req_sense("osst", SRpnt);
+               }
+               else
+                       printk(KERN_WARNING
+                            "osst%d: Error %x (sugg. bt 0x%x, driver bt 0x%x, host bt 0x%x).\n",
+                            dev, result, suggestion(result), driver_byte(result) & DRIVER_MASK,
+                            host_byte(result));
+       }
+
+       if ((sense[0] & 0x70) == 0x70 &&
+            scode == RECOVERED_ERROR) {
+               STp->recover_count++;
+               STp->recover_erreg++;
+#if DEBUG
+               if (debugging) {
+                       if (SRpnt->sr_cmnd[0] == READ_6)
+                               stp = "read";
+                       else if (SRpnt->sr_cmnd[0] == WRITE_6)
+                               stp = "write";
+                       else
+                               stp = "ioctl";
+                       printk(OSST_DEB_MSG "osst%d: Recovered %s error (%d).\n", dev, stp,
+                                            os_scsi_tapes[dev]->recover_count);
+               }
+#endif
+               if ((sense[2] & 0xe0) == 0)
+                       return 0;
+       }
+       return (-EIO);
+}
+
+
+/* Wakeup from interrupt */
+static void osst_sleep_done (Scsi_Cmnd * SCpnt)
+{
+       unsigned int dev;
+       int remainder;
+       OS_Scsi_Tape * STp;
+
+       if ((dev = TAPE_NR(SCpnt->request.rq_dev)) < osst_template.nr_dev) {
+               STp = os_scsi_tapes[dev];
+               if ((STp->buffer)->writing &&
+                   (SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
+                   (SCpnt->sense_buffer[2] & 0x40)) {
+                       /* EOM at write-behind, has all been written? */
+                       if ((SCpnt->sense_buffer[0] & 0x80) != 0)
+                               remainder = (SCpnt->sense_buffer[3] << 24) |
+                                           (SCpnt->sense_buffer[4] << 16) |
+                                           (SCpnt->sense_buffer[5] << 8 ) |
+                                            SCpnt->sense_buffer[6];
+                       else
+                               remainder = 0;
+                       if ((SCpnt->sense_buffer[2] & 0x0f) == VOLUME_OVERFLOW ||
+                           remainder > 0)
+                               (STp->buffer)->midlevel_result = SCpnt->result; /* Error */
+                       else
+                               (STp->buffer)->midlevel_result = INT_MAX;       /* OK */
+               }
+               else
+                       (STp->buffer)->midlevel_result = SCpnt->result;
+               SCpnt->request.rq_status = RQ_SCSI_DONE;
+               (STp->buffer)->last_SRpnt = SCpnt->sc_request;
+
+#if DEBUG
+               STp->write_pending = 0;
+#endif
+               up(SCpnt->request.sem);
+       }
+#if DEBUG
+       else if (debugging)
+               printk(KERN_ERR "osst?: Illegal interrupt device %x\n", dev);
+#endif
+}
+
+
+/* Do the scsi command. Waits until command performed if do_wait is true.
+   Otherwise osst_write_behind_check() is used to check that the command
+   has finished. */
+static Scsi_Request * osst_do_scsi(Scsi_Request *SRpnt, OS_Scsi_Tape *STp, 
+       unsigned char *cmd, int bytes, int direction, int timeout, int retries, int do_wait)
+{
+       unsigned char *bp;
+//static int inject = 0; /* FIXME - take out inject occasional read errors */
+//static int repeat = 0;
+       if (SRpnt == NULL) {
+               if ((SRpnt = scsi_allocate_request(STp->device)) == NULL) {
+                       printk(KERN_ERR "osst%d: Can't get SCSI request.\n", TAPE_NR(STp->devt));
+                       if (signal_pending(current))
+                               (STp->buffer)->syscall_result = (-EINTR);
+                       else
+                               (STp->buffer)->syscall_result = (-EBUSY);
+                       return NULL;
+               }
+       }
+
+       cmd[1] |= (SRpnt->sr_device->lun << 5) & 0xe0;
+       init_MUTEX_LOCKED(&STp->sem);
+       SRpnt->sr_use_sg = (bytes > (STp->buffer)->sg[0].length) ?
+                                   (STp->buffer)->use_sg : 0;
+       if (SRpnt->sr_use_sg) {
+               bp = (char *)&(STp->buffer->sg[0]);
+               if (STp->buffer->sg_segs < SRpnt->sr_use_sg)
+                       SRpnt->sr_use_sg = STp->buffer->sg_segs;
+       }
+       else
+               bp = (STp->buffer)->b_data;
+       SRpnt->sr_data_direction = direction;
+       SRpnt->sr_cmd_len = 0;
+       SRpnt->sr_request.sem = &(STp->sem);
+       SRpnt->sr_request.rq_status = RQ_SCSI_BUSY;
+       SRpnt->sr_request.rq_dev = STp->devt;
+
+       scsi_do_req(SRpnt, (void *)cmd, bp, bytes, osst_sleep_done, timeout, retries);
+
+       if (do_wait) {
+               down(SRpnt->sr_request.sem);
+               SRpnt->sr_request.sem = NULL;
+               STp->buffer->syscall_result = osst_chk_result(STp, SRpnt);
+//if ((STp->buffer)->syscall_result == 0 &&
+//    cmd[0] == READ_6 && cmd[4] && ( /* (++ inject % 83) == 29  || */
+//     (STp->first_frame_position == 240 /* or STp->read_error_frame to fail again on the block calculated above */ && ++repeat < 3))) {
+//     printk(OSST_DEB_MSG "osst%d: injecting read error\n", TAPE_NR(STp->devt));
+//     STp->buffer->last_result_fatal = 1; /* FIXME - take out inject occasional read errors */
+//}
+       }
+       return SRpnt;
+}
+
+
+/* Handle the write-behind checking (downs the semaphore) */
+static void osst_write_behind_check(OS_Scsi_Tape *STp)
+{
+       OSST_buffer * STbuffer;
+       ST_partstat * STps;
+
+       STbuffer = STp->buffer;
+
+#if DEBUG
+       if (STp->write_pending)
+               STp->nbr_waits++;
+       else
+               STp->nbr_finished++;
+#endif
+
+       down(&(STp->sem));
+       (STp->buffer)->last_SRpnt->sr_request.sem = NULL;
+
+       STp->buffer->syscall_result = osst_chk_result(STp, STp->buffer->last_SRpnt);
+
+       if ((STp->buffer)->syscall_result)
+               (STp->buffer)->syscall_result =
+                       osst_write_error_recovery(STp, &((STp->buffer)->last_SRpnt), 1);
+       else
+               STp->first_frame_position++;
+
+       scsi_release_request((STp->buffer)->last_SRpnt);
+
+       if (STbuffer->writing < STbuffer->buffer_bytes)
+#if 0
+       memcpy(STbuffer->b_data,
+          STbuffer->b_data + STbuffer->writing,
+          STbuffer->buffer_bytes - STbuffer->writing);
+#else
+       printk(KERN_WARNING "osst: write_behind_check: something left in buffer!\n");
+#endif
+       STbuffer->buffer_bytes -= STbuffer->writing;
+       STps = &(STp->ps[STp->partition]);
+       if (STps->drv_block >= 0) {
+               if (STp->block_size == 0)
+                       STps->drv_block++;
+               else
+                       STps->drv_block += STbuffer->writing / STp->block_size;
+       }
+       STbuffer->writing = 0;
+
+       return;
+}
+
+
+\f
+/* Onstream specific Routines */
+/*
+ * Initialize the OnStream AUX
+ */
+static void osst_init_aux(OS_Scsi_Tape * STp, int frame_type, int logical_blk_num)
+{
+       os_aux_t       *aux = STp->buffer->aux;
+       os_partition_t *par = &aux->partition;
+       os_dat_t       *dat = &aux->dat;
+
+       if (STp->raw) return;
+
+       memset(aux, 0, sizeof(*aux));
+       aux->format_id = htonl(0);
+       memcpy(aux->application_sig, "LIN4", 4);
+       aux->hdwr = htonl(0);
+       aux->frame_type = frame_type;
+
+       switch (frame_type) {
+         case  OS_FRAME_TYPE_HEADER:
+               aux->update_frame_cntr    = htonl(STp->update_frame_cntr);
+               par->partition_num        = OS_CONFIG_PARTITION;
+               par->par_desc_ver         = OS_PARTITION_VERSION;
+               par->wrt_pass_cntr        = htons(0xffff);
+               /* 0-4 = reserved, 5-9 = header, 2990-2994 = header, 2995-2999 = reserved */
+               par->first_frame_ppos     = htonl(0);
+               par->last_frame_ppos      = htonl(0xbb7);
+               aux->frame_seq_num        = htonl(0);
+               aux->logical_blk_num_high = htonl(0);
+               aux->logical_blk_num      = htonl(0);
+               aux->next_mark_ppos       = htonl(STp->first_mark_ppos);
+               break;
+         case  OS_FRAME_TYPE_DATA:
+         case  OS_FRAME_TYPE_MARKER:
+               dat->dat_sz = 8;
+               dat->reserved1 = 0;
+               dat->entry_cnt = 1;
+               dat->reserved3 = 0;
+               dat->dat_list[0].blk_sz   = htonl(frame_type==OS_FRAME_TYPE_DATA?STp->block_size:0);
+               dat->dat_list[0].blk_cnt  = htons(1);
+               dat->dat_list[0].flags    = frame_type==OS_FRAME_TYPE_MARKER?OS_DAT_FLAGS_MARK:OS_DAT_FLAGS_DATA;
+               dat->dat_list[0].reserved = 0;
+         case  OS_FRAME_TYPE_EOD:
+               aux->update_frame_cntr    = htonl(0);
+               par->partition_num        = OS_DATA_PARTITION;
+               par->par_desc_ver         = OS_PARTITION_VERSION;
+               par->wrt_pass_cntr        = htons(STp->wrt_pass_cntr);
+               par->first_frame_ppos     = htonl(STp->first_data_ppos);
+               par->last_frame_ppos      = htonl(STp->capacity);
+               aux->frame_seq_num        = htonl(logical_blk_num);
+               aux->logical_blk_num_high = htonl(0);
+               aux->logical_blk_num      = htonl(logical_blk_num);
+               break;
+         default: ; /* probably FILL */
+       }
+       aux->filemark_cnt = ntohl(STp->filemark_cnt);        /* FIXME -- violates ADR spec */
+       aux->phys_fm = ntohl(0xffffffff);
+       aux->last_mark_ppos = ntohl(STp->last_mark_ppos);
+}
+
+/*
+ * Verify that we have the correct tape frame
+ */
+static int osst_verify_frame(OS_Scsi_Tape * STp, int logical_blk_num, int quiet)
+{
+       os_aux_t       * aux  = STp->buffer->aux;
+       os_partition_t * par  = &(aux->partition);
+       ST_partstat    * STps = &(STp->ps[STp->partition]);
+       int              i;
+       int              dev  = TAPE_NR(STp->devt);
+
+       if (STp->raw) {
+               if (STp->buffer->syscall_result) {
+                       for (i=0; i < STp->buffer->sg_segs; i++)
+                               memset(STp->buffer->sg[i].address, 0, STp->buffer->sg[i].length);
+                       strcpy(STp->buffer->b_data, "READ ERROR ON FRAME");
+               }
+               return 1;
+       }
+       if (STp->buffer->syscall_result) {
+               printk(KERN_INFO "osst%d: Skipping frame, read error\n", dev);
+               return 0;
+       }
+       if (ntohl(aux->format_id) != 0) {
+               printk(KERN_INFO "osst%d: Skipping frame, format_id %u\n", dev, ntohl(aux->format_id));
+               return 0;
+       }
+       if (memcmp(aux->application_sig, STp->application_sig, 4) != 0 &&
+           (memcmp(aux->application_sig, "LIN3", 4) != 0 || STp->linux_media_version != 4)) {
+               printk(KERN_INFO "osst%d: Skipping frame, incorrect application signature\n", dev);
+               return 0;
+       }
+       if (par->partition_num != OS_DATA_PARTITION) {
+               if (!STp->linux_media || STp->linux_media_version != 2) {
+                       printk(KERN_INFO "osst%d: Skipping frame, partition num %d\n", dev, par->partition_num);                            return 0;
+               }
+       }
+       if (par->par_desc_ver != OS_PARTITION_VERSION) {
+               printk(KERN_INFO "osst%d: Skipping frame, partition version %d\n", dev, par->par_desc_ver);
+               return 0;
+       }
+       if (ntohs(par->wrt_pass_cntr) != STp->wrt_pass_cntr) {
+               printk(KERN_INFO "osst%d: Skipping frame, wrt_pass_cntr %d (expected %d)\n", 
+                                dev, ntohs(par->wrt_pass_cntr), STp->wrt_pass_cntr);
+               return 0;
+       }
+       if (aux->frame_seq_num != aux->logical_blk_num) {
+               printk(KERN_INFO "osst%d: Skipping frame, seq != logical\n", dev);
+               return 0;
+       }
+       if (aux->frame_type != OS_FRAME_TYPE_DATA &&
+           aux->frame_type != OS_FRAME_TYPE_EOD &&
+           aux->frame_type != OS_FRAME_TYPE_MARKER) {
+               if (!quiet)
+                       printk(KERN_INFO "osst%d: Skipping frame, frame type %x\n", dev, aux->frame_type);
+               return 0;
+       }
+       if (aux->frame_type == OS_FRAME_TYPE_EOD &&
+           STp->first_frame_position < STp->eod_frame_ppos) {
+               printk(KERN_INFO "osst%d: Skipping premature EOD frame %d\n", dev, STp->first_frame_position);
+               return 0;
+       }
+       STp->logical_blk_in_buffer = 1;
+
+       if (logical_blk_num != -1 && ntohl(aux->logical_blk_num) != logical_blk_num) {
+               if (!quiet)
+                       printk(KERN_INFO "osst%d: Skipping frame, logical_blk_num %u (expected %d)\n", 
+                                        dev, ntohl(aux->logical_blk_num), logical_blk_num);
+               return 0;
+       }
+       if (aux->frame_type == OS_FRAME_TYPE_MARKER) {
+               STps->eof = ST_FM_HIT;
+
+               i = ntohl(aux->filemark_cnt);
+               if (STp->header_cache != NULL && i < OS_FM_TAB_MAX && (i > STp->filemark_cnt ||
+                   STp->first_frame_position - 1 != ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[i]))) {
+#if 1 //DEBUG
+                       printk(OSST_DEB_MSG "osst%i: %s filemark %d at frame %d\n", dev,
+                                 STp->header_cache->dat_fm_tab.fm_tab_ent[i] == 0?"Learned":"Corrected",
+                                 i, STp->first_frame_position - 1);
+#endif
+                       STp->header_cache->dat_fm_tab.fm_tab_ent[i] = htonl(STp->first_frame_position - 1);
+                       if (i >= STp->filemark_cnt)
+                                STp->filemark_cnt = i+1;
+               }
+       }
+       if (aux->frame_type == OS_FRAME_TYPE_EOD) {
+               STps->eof = ST_EOD_1;
+       }
+       if (aux->frame_type == OS_FRAME_TYPE_DATA) {
+               STps->eof = ST_NOEOF;
+       }
+       return 1;
+}
+
+/*
+ * Wait for the unit to become Ready
+ */
+static int osst_wait_ready(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, unsigned timeout)
+{
+       unsigned char   cmd[MAX_COMMAND_SIZE];
+       Scsi_Request  * SRpnt;
+       long            startwait = jiffies;
+#if DEBUG
+       int             dbg = debugging;
+       int             dev  = TAPE_NR(STp->devt);
+
+       printk(OSST_DEB_MSG "osst%d: Reached onstream wait ready\n", dev);
+#endif
+
+       memset(cmd, 0, MAX_COMMAND_SIZE);
+       cmd[0] = TEST_UNIT_READY;
+
+       SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, 0, SCSI_DATA_NONE, STp->timeout, MAX_READY_RETRIES, TRUE);
+       *aSRpnt = SRpnt;
+       if (!SRpnt) return (-EBUSY);
+
+       while ( STp->buffer->syscall_result && time_before(jiffies, startwait + timeout*HZ) &&
+               SRpnt->sr_sense_buffer[2]  == 2 && SRpnt->sr_sense_buffer[12] == 4          &&
+              (SRpnt->sr_sense_buffer[13] == 1 || SRpnt->sr_sense_buffer[13] == 8)         ) {
+#if DEBUG
+           if (debugging) {
+               printk(OSST_DEB_MSG "osst%d: Sleeping in onstream wait ready\n", dev);
+               printk(OSST_DEB_MSG "osst%d: Turning off debugging for a while\n", dev);
+               debugging = 0;
+           }
+#endif
+           current->state = TASK_INTERRUPTIBLE;
+           schedule_timeout(HZ / 10);
+
+           memset(cmd, 0, MAX_COMMAND_SIZE);
+           cmd[0] = TEST_UNIT_READY;
+
+           SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, SCSI_DATA_NONE, STp->timeout, MAX_READY_RETRIES, TRUE);
+       }
+       *aSRpnt = SRpnt;
+#if DEBUG
+       debugging = dbg;
+#endif
+       if ( STp->buffer->syscall_result &&
+            osst_write_error_recovery(STp, aSRpnt, 0) ) {
+#if DEBUG
+           printk(OSST_DEB_MSG "osst%d: Abnormal exit from onstream wait ready\n", dev);
+printk(OSST_DEB_MSG "osst%d: Result = %d, Sense: 0=%02x, 2=%02x, 12=%02x, 13=%02x\n", dev,
+STp->buffer->syscall_result, SRpnt->sr_sense_buffer[0], SRpnt->sr_sense_buffer[2],
+SRpnt->sr_sense_buffer[12], SRpnt->sr_sense_buffer[13]);
+#endif
+           return (-EIO);
+       }
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%d: Normal exit from onstream wait ready\n", dev);
+#endif
+       return 0;
+}
+
+static int osst_position_tape_and_confirm(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, int frame)
+{
+       int     retval;
+
+       osst_wait_ready(STp, aSRpnt, 15 * 60);                  /* TODO - can this catch a write error? */
+       retval = osst_set_frame_position(STp, aSRpnt, frame, 0);
+       if (retval) return (retval);
+       osst_wait_ready(STp, aSRpnt, 15 * 60);
+       return (osst_get_frame_position(STp, aSRpnt));
+}
+
+/*
+ * Wait for write(s) to complete
+ */
+static int osst_flush_drive_buffer(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt)
+{
+       unsigned char   cmd[MAX_COMMAND_SIZE];
+       Scsi_Request  * SRpnt;
+
+       int             result = 0;
+#if DEBUG
+       int             dev  = TAPE_NR(STp->devt);
+
+       printk(OSST_DEB_MSG "osst%d: Reached onstream flush drive buffer (write filemark)\n", dev);
+#endif
+
+       memset(cmd, 0, MAX_COMMAND_SIZE);
+       cmd[0] = WRITE_FILEMARKS;
+       cmd[1] = 1;
+
+       SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, 0, SCSI_DATA_NONE, STp->timeout, MAX_WRITE_RETRIES, TRUE);
+       *aSRpnt = SRpnt;
+       if (!SRpnt) return (-EBUSY);
+
+       if ((STp->buffer)->syscall_result)
+               result = osst_write_error_recovery(STp, aSRpnt, 0);
+
+       result |= osst_wait_ready(STp, aSRpnt, 5 * 60);
+       STp->ps[STp->partition].rw = ST_IDLE;
+       return (result);
+}
+
+#define OSST_POLL_PER_SEC 10
+static int osst_wait_frame(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, int curr, int minlast, int to)
+{
+       long    startwait     = jiffies;
+       int     dev           = TAPE_NR(STp->devt);
+#if DEBUG
+       char    notyetprinted = 1;
+#endif
+       if ((minlast >= 0 && STp->ps[STp->partition].rw != ST_READING) ||
+           (minlast <  0 && STp->ps[STp->partition].rw != ST_WRITING) )
+               printk(KERN_ERR "osst%i: waiting for frame without having initialized %s!\n",
+                               dev, minlast<0?"write":"read");
+
+       while (time_before (jiffies, startwait + to*HZ))
+       { 
+               int result;
+               result = osst_get_frame_position (STp, aSRpnt);
+               if (result == -EIO)
+                       if ((result = osst_write_error_recovery(STp, aSRpnt, 0)) == 0)
+                               return 0;       /* successfull recovery leaves drive ready for frame */
+               if (result < 0) break;
+               if (STp->first_frame_position == curr &&
+                   ((minlast < 0 &&
+                     (signed)STp->last_frame_position > (signed)curr + minlast) ||
+                    (minlast >= 0 && STp->cur_frames > minlast)
+                   ) && result >= 0)
+               {
+#if DEBUG                      
+                       if (debugging || jiffies - startwait >= 2*HZ/OSST_POLL_PER_SEC)
+                               printk (OSST_DEB_MSG
+                                       "osst%i: Succ wait f fr %i (>%i): %i-%i %i (%i): %3li.%li s\n",
+                                       dev, curr, curr+minlast, STp->first_frame_position,
+                                       STp->last_frame_position, STp->cur_frames,
+                                       result, (jiffies-startwait)/HZ, 
+                                       (((jiffies-startwait)%HZ)*10)/HZ);
+#endif
+                       return 0;
+               }
+#if DEBUG
+               if (jiffies - startwait >= 2*HZ/OSST_POLL_PER_SEC && notyetprinted)
+               {
+                       printk (OSST_DEB_MSG "osst%i: Wait for frame %i (>%i): %i-%i %i (%i)\n",
+                               dev, curr, curr+minlast, STp->first_frame_position,
+                               STp->last_frame_position, STp->cur_frames, result);
+                       notyetprinted--;
+               }
+#endif
+               current->state = TASK_INTERRUPTIBLE;
+               schedule_timeout (HZ / OSST_POLL_PER_SEC);
+       }
+#if DEBUG
+       printk (OSST_DEB_MSG "osst%i: Fail wait f fr %i (>%i): %i-%i %i: %3li.%li s\n",
+               dev, curr, curr+minlast, STp->first_frame_position,
+               STp->last_frame_position, STp->cur_frames,
+               (jiffies-startwait)/HZ, (((jiffies-startwait)%HZ)*10)/HZ);
+#endif 
+       return -EBUSY;
+}
+
+/*
+ * Read the next OnStream tape block at the current location
+ */
+static int osst_read_block(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, int timeout)
+{
+       unsigned char   cmd[MAX_COMMAND_SIZE];
+       Scsi_Request  * SRpnt;
+       int             retval = 0;
+#if DEBUG
+       os_aux_t      * aux    = STp->buffer->aux;
+       int             dev    = TAPE_NR(STp->devt);
+#endif
+
+       /* TODO: Error handling */
+       if (STp->poll)
+               retval = osst_wait_frame (STp, aSRpnt, STp->first_frame_position, 0, timeout);
+#if 0// DEBUG
+       printk ("osst_read: wait for frame returned %i\n", retval);
+#endif
+       
+       memset(cmd, 0, MAX_COMMAND_SIZE);
+       cmd[0] = READ_6;
+       cmd[1] = 1;
+       cmd[4] = 1;
+
+#if DEBUG
+       if (debugging)
+           printk(OSST_DEB_MSG "osst%i: Reading block from OnStream tape\n", dev);
+#endif
+       SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, OS_FRAME_SIZE, SCSI_DATA_READ,
+                                     STp->timeout, MAX_RETRIES, TRUE);
+       *aSRpnt = SRpnt;
+       if (!SRpnt)
+           return (-EBUSY);
+
+       if ((STp->buffer)->syscall_result) {
+           retval = 1;
+           if (STp->read_error_frame == 0) {
+               STp->read_error_frame = STp->first_frame_position;
+               printk(OSST_DEB_MSG "osst: recording read error at %d\n", STp->read_error_frame);/*FIXME*/
+           }
+#if DEBUG
+           if (debugging)
+               printk(OSST_DEB_MSG "osst%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n",
+                  dev,
+                  SRpnt->sr_sense_buffer[0], SRpnt->sr_sense_buffer[1],
+                  SRpnt->sr_sense_buffer[2], SRpnt->sr_sense_buffer[3],
+                  SRpnt->sr_sense_buffer[4], SRpnt->sr_sense_buffer[5],
+                  SRpnt->sr_sense_buffer[6], SRpnt->sr_sense_buffer[7]);
+#endif
+       }
+       else
+           STp->first_frame_position++;
+#if DEBUG
+       if (debugging) {
+          printk(OSST_DEB_MSG "osst%i: AUX: %c%c%c%c UpdFrCt#%d %s FrSeq#%d LogBlk#%d\n", dev,
+                       aux->application_sig[0], aux->application_sig[1],
+                       aux->application_sig[2], aux->application_sig[3], ntohl(aux->update_frame_cntr),
+                       aux->frame_type==1?"EOD":aux->frame_type==2?"MARK":
+                       aux->frame_type==8?"HEADR":aux->frame_type==0x80?"DATA":"FILL", 
+                       ntohl(aux->frame_seq_num), ntohl(aux->logical_blk_num) );
+          if (aux->frame_type==2)
+               printk(OSST_DEB_MSG "osst%i: mark_cnt=%d, last_mark=%d, next_mark=%d\n", dev,
+                       ntohl(aux->filemark_cnt), ntohl(aux->last_mark_ppos), ntohl(aux->next_mark_ppos));
+          printk(OSST_DEB_MSG "osst%i: Exit read block from OnStream tape with code %d\n", dev, retval);
+       }
+#endif
+       return (retval);
+}
+
+static int osst_initiate_read(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt)
+{
+       ST_partstat   * STps   = &(STp->ps[STp->partition]);
+       Scsi_Request  * SRpnt  ;
+       unsigned char   cmd[MAX_COMMAND_SIZE];
+       int             retval = 0;
+#if DEBUG
+       int             dev    = TAPE_NR(STp->devt);
+#endif
+
+       if (STps->rw != ST_READING) {         /* Initialize read operation */
+               if (STps->rw == ST_WRITING) {
+                       osst_flush_write_buffer(STp, aSRpnt, 1);
+                       osst_flush_drive_buffer(STp, aSRpnt);
+               }
+               STps->rw = ST_READING;
+               STp->logical_blk_in_buffer = 0;
+
+               /*
+                *      Issue a read 0 command to get the OnStream drive
+                *      read blocks into its buffer.
+                */
+               memset(cmd, 0, MAX_COMMAND_SIZE);
+               cmd[0] = READ_6;
+               cmd[1] = 1;
+
+#if DEBUG
+               printk(OSST_DEB_MSG "osst%i: Start Read Ahead on OnStream tape\n", dev);
+#endif
+               SRpnt   = osst_do_scsi(*aSRpnt, STp, cmd, 0, SCSI_DATA_NONE, STp->timeout, MAX_RETRIES, TRUE);
+               *aSRpnt = SRpnt;
+               retval  = STp->buffer->syscall_result;
+       }
+
+       return retval;
+}
+
+static int osst_get_logical_blk(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, int logical_blk_num, int quiet)
+{
+       ST_partstat * STps  = &(STp->ps[STp->partition]);
+       int           dev   = TAPE_NR(STp->devt);
+       int           cnt   = 0,
+                     bad   = 0,
+                     past  = 0,
+                     x,
+                     position;
+
+       /*
+        * Search and wait for the next logical tape block
+        */
+       while (1) {
+               if (cnt++ > 400) {
+                       printk(KERN_WARNING "osst%d: Couldn't find logical block %d, aborting\n",
+                                           dev, logical_blk_num);
+                       if (STp->read_error_frame) {
+                               osst_set_frame_position(STp, aSRpnt, STp->read_error_frame, 0);
+#if 1 //DEBUG
+                               printk(OSST_DEB_MSG "osst%d: Repositioning tape to bad block %d\n",
+                                                   dev, STp->read_error_frame);
+#endif
+                               STp->read_error_frame = 0;
+                       }
+                       return (-EIO);
+               }
+#if DEBUG
+               if (debugging)
+                       printk(OSST_DEB_MSG "osst%d: Looking for block %d, attempt %d\n",
+                                         dev, logical_blk_num, cnt);
+#endif
+               if ( osst_initiate_read(STp, aSRpnt)
+               || ( (!STp->logical_blk_in_buffer) && osst_read_block(STp, aSRpnt, 30) ) ) {
+                       position = osst_get_frame_position(STp, aSRpnt);
+                       if (position >= 0xbae && position < 0xbb8)
+                               position = 0xbb8;
+                       else if (position > STp->eod_frame_ppos || ++bad == 10) {
+printk(OSST_DEB_MSG "osst%d: start again from pos %d, eod %d, bad %d\n", dev, position, STp->eod_frame_ppos, bad); /*FIXME*/
+                               position = STp->read_error_frame - 1;
+                       }
+                       else {
+                               position += 39;
+                               cnt += 20;
+                       }
+#if DEBUG
+                       printk(OSST_DEB_MSG "osst%d: Bad block detected, positioning tape to block %d\n",
+                                        dev, position);
+#endif
+                       osst_set_frame_position(STp, aSRpnt, position, 0);
+                       continue;
+               }
+               if (osst_verify_frame(STp, logical_blk_num, quiet))
+                       break;
+               if (osst_verify_frame(STp, -1, quiet)) {
+                       x = ntohl(STp->buffer->aux->logical_blk_num);
+                       if (STp->fast_open) {
+#if 1 //DEBUG
+                               printk(OSST_DEB_MSG
+                                      "osst%d: Found logical block %d instead of %d after fast open\n",
+                                      dev, x, logical_blk_num);
+#endif
+                               STp->header_ok = 0;
+                               STp->read_error_frame = 0;
+                               return (-EIO);
+                       }
+                       if (x > logical_blk_num) {
+                               if (++past > 3) {
+                                       /* positioning backwards did not bring us to the desired block */
+                                       position = STp->read_error_frame - 1;
+                               }
+                               else
+                                       position = osst_get_frame_position(STp, aSRpnt)
+                                                + logical_blk_num - x - 1;
+#if 1 //DEBUG
+                                printk(OSST_DEB_MSG
+                                      "osst%d: Found logical block %d while looking for %d: back up %d\n",
+                                               dev, x, logical_blk_num,
+                                               STp->first_frame_position - position);
+#endif
+                               osst_set_frame_position(STp, aSRpnt, position, 0);
+                               cnt += 10;
+                       }
+                       else
+                               past = 0;
+               }
+               if (osst_get_frame_position(STp, aSRpnt) == 0xbaf) {
+#if DEBUG
+                       printk(OSST_DEB_MSG "osst%d: Skipping config partition\n", dev);
+#endif
+                       osst_set_frame_position(STp, aSRpnt, 0xbb8, 0);
+                       cnt--;
+               }
+               STp->logical_blk_in_buffer = 0;
+       }
+       if (cnt > 1) {
+               STp->recover_count++;
+               STp->recover_erreg++;
+       }
+       STp->logical_blk_num = ntohl(STp->buffer->aux->logical_blk_num);
+
+#if DEBUG
+       if (debugging || STps->eof)
+               printk(OSST_DEB_MSG "osst%i: Exit get logical block (%d=>%d) from OnStream tape with code %d\n",                                                         dev, logical_blk_num, STp->logical_blk_num, STps->eof);
+#endif
+       STp->fast_open = FALSE;
+       STp->read_error_frame = 0;
+       return (STps->eof);
+}
+
+static int osst_seek_logical_blk(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, int logical_blk_num)
+{
+       int  estimate;
+       int  retries = 0;
+       int  dev     = TAPE_NR(STp->devt);
+
+       if (logical_blk_num < 0) logical_blk_num = 0;
+       /* FIXME -- this may not be valid for foreign formats */
+       if (logical_blk_num < 2980) estimate  = logical_blk_num + 10;
+       else                        estimate  = logical_blk_num + 20;
+
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%d: Seeking logical block %d (now at %d)\n",
+                         dev, logical_blk_num, STp->logical_blk_num);
+#endif
+       while (++retries < 10) {
+          osst_set_frame_position(STp, aSRpnt, estimate, 0);
+          if (osst_get_logical_blk(STp, aSRpnt, logical_blk_num, 1) >= 0)
+             return 0;
+          if (osst_get_logical_blk(STp, aSRpnt, -1, 1) < 0)
+             goto error;
+          if (STp->logical_blk_num != logical_blk_num)
+             estimate += logical_blk_num - STp->logical_blk_num;
+          else
+             break;
+       }
+error:
+       printk(KERN_WARNING "osst%d: Couldn't seek to logical block %d (at %d), %d retries\n", 
+                           dev, logical_blk_num, STp->logical_blk_num, retries);
+       return (-EIO);
+}
+
+static int osst_seek_frame(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, int frame)
+{
+       ST_partstat   * STps = &(STp->ps[STp->partition]);
+       int             r;
+
+       if (frame < 0 || frame >= STp->capacity) return (-ENXIO);
+
+       if (frame <= STp->first_data_ppos) {
+               STp->logical_blk_num = STps->drv_file = STps->drv_block = 0;
+               return (osst_set_frame_position(STp, aSRpnt, frame, 0));
+       }
+       r = osst_set_frame_position(STp, aSRpnt, frame-1, 0);
+       if (r < 0) return r;
+
+       r = osst_get_logical_blk(STp, aSRpnt, -1, 1);
+       if (r < 0) return r;
+
+       if (osst_get_frame_position(STp, aSRpnt) != frame) return (-EIO);
+
+       STp->logical_blk_num++;
+       STp->logical_blk_in_buffer = 0;
+       STps->drv_file  = htonl(STp->buffer->aux->filemark_cnt);
+       STps->drv_block = -1;
+       STps->eof       = ST_NOEOF;
+       return 0;
+}
+
+/*
+ * Read back the drive's internal buffer contents, as a part
+ * of the write error recovery mechanism for old OnStream
+ * firmware revisions.
+ */
+static int osst_read_back_buffer_and_rewrite(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt,
+                                       unsigned int block, unsigned int skip, int pending)
+{
+       Scsi_Request  * SRpnt = * aSRpnt;
+       unsigned char * buffer, * p;
+       unsigned char   cmd[MAX_COMMAND_SIZE];
+       int             frames, flag, new_block, i, logical_blk_num;
+       int             dev  = TAPE_NR(STp->devt);
+       long            startwait = jiffies;
+#if DEBUG
+       int             dbg = debugging;
+#endif
+
+       frames = STp->cur_frames;
+       if ((buffer = (unsigned char *)vmalloc((frames + pending) * OS_DATA_SIZE)) == NULL)
+               return (-EIO);
+
+       logical_blk_num = STp->logical_blk_num - frames - pending;
+       printk(KERN_INFO "osst%d: Reading back %d frames from drive buffer%s\n",
+                        dev, frames, pending?" and one that was pending":"");
+
+       if (pending) {
+               osst_copy_from_buffer(STp->buffer, (p = &buffer[frames * OS_DATA_SIZE]));
+//             memcpy((p = &buffer[frames * OS_DATA_SIZE]), STp->buffer->b_data, OS_DATA_SIZE);
+#if DEBUG
+               if (debugging)
+                       printk(OSST_DEB_MSG "osst%d: Pending logical block %d, data %x %x %x %x\n",
+                                         dev, logical_blk_num + frames, p[0], p[1], p[2], p[3]);
+#endif
+       }
+       for (i = 0, p = buffer; i < frames; i++, p += OS_DATA_SIZE) {
+
+               memset(cmd, 0, MAX_COMMAND_SIZE);
+               cmd[0] = 0x3C;          /* Buffer Read           */
+               cmd[1] = 6;             /* Retrieve Faulty Block */
+               cmd[7] = 32768 >> 8;
+               cmd[8] = 32768 & 0xff;
+
+               SRpnt = osst_do_scsi(SRpnt, STp, cmd, OS_FRAME_SIZE, SCSI_DATA_READ,
+                                           STp->timeout, MAX_RETRIES, TRUE);
+       
+               if ((STp->buffer)->syscall_result) {
+                       printk(KERN_ERR "osst%d: Failed to read block back from OnStream buffer\n", dev);
+                       vfree((void *)buffer);
+                       *aSRpnt = SRpnt;
+                       return (-EIO);
+               }
+               osst_copy_from_buffer(STp->buffer, p);
+//             memcpy(p, STp->buffer->b_data, OS_DATA_SIZE);
+#if DEBUG
+               if (debugging)
+                       printk(OSST_DEB_MSG "osst%d: Read back logical block %d, data %x %x %x %x\n",
+                                         dev, logical_blk_num + i, p[0], p[1], p[2], p[3]);
+#endif
+       }
+       *aSRpnt = SRpnt;
+       osst_get_frame_position(STp, aSRpnt);
+
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%d: Frames left in buffer: %d\n", dev, STp->cur_frames);
+#endif
+       /* Write synchronously so we can be sure we're OK again and don't have to recover recursively */
+       /* In the header we don't actually re-write the blocks that fail, just the ones after them */
+
+       for (flag=1, new_block=block, p=buffer, i=0; i < frames + pending; ) {
+
+               if (flag) {
+                       if (STp->write_type == OS_WRITE_HEADER) {
+                               i += skip;
+                               p += skip * OS_DATA_SIZE;
+                       }
+                       else if (new_block < 2990 && new_block+skip+frames+pending >= 2990)
+                               new_block = 3000-i;
+                       else
+                               new_block += skip;
+#if DEBUG
+                       printk(OSST_DEB_MSG "osst%d: Position to frame %d, write lblk %d\n",
+                                               dev, new_block+i, logical_blk_num+i); /* FIXME var blk sz */
+#endif
+                       osst_set_frame_position(STp, aSRpnt, new_block + i, 0);
+                       osst_wait_ready(STp, aSRpnt, 60);
+                       osst_get_frame_position(STp, aSRpnt);
+                       SRpnt = * aSRpnt;
+
+                       if (new_block > block + 1000) {
+                               printk(KERN_ERR "osst%d: Failed to find valid tape media\n", dev);
+                               vfree((void *)buffer);
+                               return (-EIO);
+                       }
+                       flag = 0;
+                       if ( i >= frames + pending ) break;
+               }
+               osst_copy_to_buffer(STp->buffer, p);
+//             memcpy(STp->buffer->b_data, p, OS_DATA_SIZE);
+               /*
+                * IMPORTANT: for error recovery to work, _never_ queue frames with mixed frame type!
+                */
+               osst_init_aux(STp, STp->buffer->aux->frame_type, logical_blk_num+i );
+               memset(cmd, 0, MAX_COMMAND_SIZE);
+               cmd[0] = WRITE_6;
+               cmd[1] = 1;
+               cmd[4] = 1;
+
+#if DEBUG
+               if (debugging)
+                       printk(OSST_DEB_MSG "osst%d: About to attempt to write to frame %d\n", dev, new_block+i);
+#endif
+               SRpnt = osst_do_scsi(SRpnt, STp, cmd, OS_FRAME_SIZE, SCSI_DATA_WRITE,
+                                           STp->timeout, MAX_WRITE_RETRIES, TRUE);
+
+               if (STp->buffer->syscall_result)
+                       flag = 1;
+               else {
+                       p += OS_DATA_SIZE; i++;
+
+                       /* if we just sent the last frame, wait till all successfully written */
+                       if ( i == frames + pending ) {
+#if DEBUG
+                               printk(OSST_DEB_MSG "osst%d: Check re-write successful\n", dev);
+#endif
+                               memset(cmd, 0, MAX_COMMAND_SIZE);
+                               cmd[0] = WRITE_FILEMARKS;
+                               cmd[1] = 1;
+                               SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, SCSI_DATA_NONE,
+                                                           STp->timeout, MAX_WRITE_RETRIES, TRUE);
+#if DEBUG
+                               if (debugging) {
+                                       printk(OSST_DEB_MSG "osst%d: Sleeping in re-write wait ready\n", dev);
+                                       printk(OSST_DEB_MSG "osst%d: Turning off debugging for a while\n", dev);
+                                       debugging = 0;
+                               }
+#endif
+                               flag = STp->buffer->syscall_result;
+                               while ( !flag && time_before(jiffies, startwait + 60*HZ) ) {
+
+                                       memset(cmd, 0, MAX_COMMAND_SIZE);
+                                       cmd[0] = TEST_UNIT_READY;
+
+                                       SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, SCSI_DATA_NONE, STp->timeout,
+                                                                        MAX_READY_RETRIES, TRUE);
+
+                                       if (SRpnt->sr_sense_buffer[2] == 2 && SRpnt->sr_sense_buffer[12] == 4 &&
+                                           (SRpnt->sr_sense_buffer[13] == 1 || SRpnt->sr_sense_buffer[13] == 8)) {
+                                               /* in the process of becoming ready */
+                                               schedule_timeout(HZ / 10);
+                                               continue;
+                                       }
+                                       if (STp->buffer->syscall_result)
+                                               flag = 1;
+                                       break;
+                               }
+#if DEBUG
+                               debugging = dbg;
+                               printk(OSST_DEB_MSG "osst%d: Wait re-write finished\n", dev);
+#endif
+                       }
+               }
+               if (flag) {
+                       if ((SRpnt->sr_sense_buffer[ 2] & 0x0f) == 13 &&
+                            SRpnt->sr_sense_buffer[12]         ==  0 &&
+                            SRpnt->sr_sense_buffer[13]         ==  2) {
+                               printk(KERN_ERR "osst%d: Volume overflow in write error recovery\n", dev);
+                               vfree((void *)buffer);
+                               return (-EIO);                  /* hit end of tape = fail */
+                       }
+                       i = ((SRpnt->sr_sense_buffer[3] << 24) |
+                            (SRpnt->sr_sense_buffer[4] << 16) |
+                            (SRpnt->sr_sense_buffer[5] <<  8) |
+                             SRpnt->sr_sense_buffer[6]        ) - new_block;
+                       p = &buffer[i * OS_DATA_SIZE];
+#if DEBUG
+                       printk(OSST_DEB_MSG "osst%d: Additional write error at %d\n", dev, new_block+i);
+#endif
+                       osst_get_frame_position(STp, aSRpnt);
+#if DEBUG
+                       printk(OSST_DEB_MSG "osst%d: reported frame positions: host = %d, tape = %d\n",
+                                         dev, STp->first_frame_position, STp->last_frame_position);
+#endif
+               }
+               *aSRpnt = SRpnt;
+       }    
+       vfree((void *)buffer);
+       return 0;
+}
+
+static int osst_reposition_and_retry(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt,
+                                       unsigned int block, unsigned int skip, int pending)
+{
+       unsigned char   cmd[MAX_COMMAND_SIZE];
+       Scsi_Request  * SRpnt     = * aSRpnt;
+       int             dev       = TAPE_NR(STp->devt);
+       int             attempts  = 1000 / skip;
+       int             flag      = 1;
+       long            startwait = jiffies;
+#if DEBUG
+       int             dbg       = debugging;
+#endif
+
+       while (attempts && time_before(jiffies, startwait + 60*HZ)) {
+               if (flag) {
+#if DEBUG
+                       debugging = dbg;
+#endif
+                       if (block < 2990 && block+skip+STp->cur_frames+pending >= 2990)
+                               block = 3000-skip;
+#if DEBUG
+                       printk(OSST_DEB_MSG "osst%d: Position to frame %d, re-write from lblk %d\n",
+                                         dev, block+skip, STp->logical_blk_num-STp->cur_frames-pending);
+#endif
+                       osst_set_frame_position(STp, aSRpnt, block + skip, 1);
+                       flag = 0;
+                       attempts--;
+               }
+               if (osst_get_frame_position(STp, aSRpnt) < 0) {         /* additional write error */
+#if DEBUG
+                       printk(OSST_DEB_MSG "osst%d: Addl error, host %d, tape %d, buffer %d\n",
+                                         dev, STp->first_frame_position,
+                                         STp->last_frame_position, STp->cur_frames);
+#endif
+                       block = STp->last_frame_position;
+                       flag = 1;
+                       continue;
+               }
+               if (pending && STp->cur_frames < 50) {
+
+                       memset(cmd, 0, MAX_COMMAND_SIZE);
+                       cmd[0] = WRITE_6;
+                       cmd[1] = 1;
+                       cmd[4] = 1;
+#if DEBUG
+                       printk(OSST_DEB_MSG "osst%d: About to write pending lblk %d at frame %d\n",
+                                         dev, STp->logical_blk_num-1, STp->first_frame_position);
+#endif
+                       SRpnt = osst_do_scsi(SRpnt, STp, cmd, OS_FRAME_SIZE, SCSI_DATA_WRITE,
+                                                   STp->timeout, MAX_WRITE_RETRIES, TRUE);
+                       *aSRpnt = SRpnt;
+
+                       if (STp->buffer->syscall_result) {              /* additional write error */
+                               if ((SRpnt->sr_sense_buffer[ 2] & 0x0f) == 13 &&
+                                    SRpnt->sr_sense_buffer[12]         ==  0 &&
+                                    SRpnt->sr_sense_buffer[13]         ==  2) {
+                                       printk(OSST_DEB_MSG
+                                              "osst%d: Volume overflow in write error recovery\n",
+                                              dev);
+                                       break;                          /* hit end of tape = fail */
+                               }
+                               flag = 1;
+                       }
+                       else
+                               pending = 0;
+
+                       continue;
+               }
+               if (STp->cur_frames == 0) {
+#if DEBUG
+                       debugging = dbg;
+                       printk(OSST_DEB_MSG "osst%d: Wait re-write finished\n", dev);
+#endif
+                       return 0;
+               }
+#if DEBUG
+               if (debugging) {
+                       printk(OSST_DEB_MSG "osst%d: Sleeping in re-write wait ready\n", dev);
+                       printk(OSST_DEB_MSG "osst%d: Turning off debugging for a while\n", dev);
+                       debugging = 0;
+               }
+#endif
+               schedule_timeout(HZ / 10);
+       }
+       printk(KERN_ERR "osst%d: Failed to find valid tape media\n", dev);
+#if DEBUG
+       debugging = dbg;
+#endif
+       return (-EIO);
+}
+
+/*
+ * Error recovery algorithm for the OnStream tape.
+ */
+
+static int osst_write_error_recovery(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, int pending)
+{
+       Scsi_Request * SRpnt  = * aSRpnt;
+       ST_partstat  * STps   = & STp->ps[STp->partition];
+       int            dev    = TAPE_NR(STp->devt);
+       int            retval = 0;
+       int            rw_state;
+       unsigned int   block, skip;
+
+       rw_state = STps->rw;
+
+       if ((SRpnt->sr_sense_buffer[ 2] & 0x0f) != 3
+         || SRpnt->sr_sense_buffer[12]         != 12
+         || SRpnt->sr_sense_buffer[13]         != 0) {
+#if DEBUG
+               printk(OSST_DEB_MSG "osst%d: Write error recovery cannot handle %02x:%02x:%02x\n",
+                       dev, SRpnt->sr_sense_buffer[2], SRpnt->sr_sense_buffer[12], SRpnt->sr_sense_buffer[13]);
+#endif
+               return (-EIO);
+       }
+       block = (SRpnt->sr_sense_buffer[3] << 24) |
+               (SRpnt->sr_sense_buffer[4] << 16) |
+               (SRpnt->sr_sense_buffer[5] <<  8) |
+                SRpnt->sr_sense_buffer[6];
+       skip  =  SRpnt->sr_sense_buffer[9];
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%d: Detected physical bad block at %u, advised to skip %d\n", dev, block, skip);
+#endif
+       osst_get_frame_position(STp, aSRpnt);
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%d: reported frame positions: host = %d, tape = %d\n",
+                       dev, STp->first_frame_position, STp->last_frame_position);
+#endif
+       switch (STp->write_type) {
+          case OS_WRITE_DATA:
+          case OS_WRITE_EOD:
+          case OS_WRITE_NEW_MARK:
+               printk(KERN_WARNING "osst%d: Relocating %d buffered logical blocks to physical block %u\n",
+                       dev, STp->cur_frames, block + skip);
+               if (STp->os_fw_rev >= 10600)
+                       retval = osst_reposition_and_retry(STp, aSRpnt, block, skip, pending);
+               else
+                       retval = osst_read_back_buffer_and_rewrite(STp, aSRpnt, block, skip, pending);
+               break;
+          case OS_WRITE_LAST_MARK:
+               printk(KERN_ERR "osst%d: Bad block in update last marker, fatal\n", dev);
+               osst_set_frame_position(STp, aSRpnt, block + STp->cur_frames + pending, 0);
+               retval = -EIO;
+               break;
+          case OS_WRITE_HEADER:
+               printk(KERN_WARNING "osst%d: Bad block in header partition, skipped\n", dev);
+               retval = osst_read_back_buffer_and_rewrite(STp, aSRpnt, block, 1, pending);
+               break;
+          default:
+               printk(KERN_WARNING "osst%d: Bad block in filler, ignored\n", dev);
+               osst_set_frame_position(STp, aSRpnt, block + STp->cur_frames + pending, 0);
+       }
+       osst_get_frame_position(STp, aSRpnt);
+#if DEBUG
+       printk(KERN_ERR "osst%d: Positioning complete, cur_frames %d, pos %d, tape pos %d\n", 
+                       dev, STp->cur_frames, STp->first_frame_position, STp->last_frame_position);
+       printk(OSST_DEB_MSG "osst%d: next logical block to write: %d\n", dev, STp->logical_blk_num);
+#endif
+       if (retval == 0) {
+               STp->recover_count++;
+               STp->recover_erreg++;
+       }
+       STps->rw = rw_state;
+       return retval;
+}
+
+static int osst_space_over_filemarks_backward(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt,
+                                                                int mt_op, int mt_count)
+{
+       int     dev = TAPE_NR(STp->devt);
+       int     cnt;
+       int     last_mark_ppos = -1;
+
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%d: Reached space_over_filemarks_backwards %d %d\n", dev, mt_op, mt_count);
+#endif
+       if (osst_get_logical_blk(STp, aSRpnt, -1, 0) < 0) {
+               printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks_bwd\n", dev);
+               return -EIO;
+       }
+       if (STp->linux_media_version >= 4) {
+               /*
+                * direct lookup in header filemark list
+                */
+               cnt = ntohl(STp->buffer->aux->filemark_cnt);
+               if (STp->header_ok                         && 
+                   STp->header_cache != NULL              &&
+                   (cnt - mt_count)  >= 0                 &&
+                   (cnt - mt_count)   < OS_FM_TAB_MAX     &&
+                   (cnt - mt_count)   < STp->filemark_cnt &&
+                   STp->header_cache->dat_fm_tab.fm_tab_ent[cnt-1] == STp->buffer->aux->last_mark_ppos)
+
+                       last_mark_ppos = ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[cnt - mt_count]);
+#if 1 //DEBUG
+               if (STp->header_cache == NULL || (cnt - mt_count) < 0 || (cnt - mt_count) >= OS_FM_TAB_MAX)
+                       printk(OSST_DEB_MSG "osst%i: Filemark lookup fail due to %s\n", dev,
+                              STp->header_cache == NULL?"lack of header cache":"count out of range");
+               else
+                       printk(OSST_DEB_MSG "osst%i: Filemark lookup: prev mark %d (%s), skip %d to %d\n",
+                               dev, cnt,
+                               ((cnt == -1 && ntohl(STp->buffer->aux->last_mark_ppos) == -1) ||
+                                (STp->header_cache->dat_fm_tab.fm_tab_ent[cnt-1] ==
+                                        STp->buffer->aux->last_mark_ppos))?"match":"error",
+                              mt_count, last_mark_ppos);
+#endif
+               if (last_mark_ppos > 10 && last_mark_ppos < STp->eod_frame_ppos) {
+                       osst_set_frame_position(STp, aSRpnt, last_mark_ppos, 0);
+                       if (osst_get_logical_blk(STp, aSRpnt, -1, 0) < 0) {
+                               printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks\n", dev);
+                               return (-EIO);
+                       }
+                       if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) {
+                               printk(KERN_INFO "osst%i: Expected to find marker at block %d, not found\n",
+                                                dev, last_mark_ppos);
+                               return (-EIO);
+                       }
+                       if (mt_op == MTBSFM) {
+                               STp->logical_blk_num++;
+                               STp->logical_blk_in_buffer = 0;
+                       }
+                       return 0;
+               }
+               printk(KERN_INFO "osst%i: Reverting to scan filemark backwards\n", dev);
+       }
+       cnt = 0;
+       while (cnt != mt_count) {
+               last_mark_ppos = ntohl(STp->buffer->aux->last_mark_ppos);
+               if (last_mark_ppos == -1)
+                       return (-EIO);
+#if DEBUG
+               printk(OSST_DEB_MSG "osst%i: Positioning to last mark at %d\n", dev, last_mark_ppos);
+#endif
+               osst_set_frame_position(STp, aSRpnt, last_mark_ppos, 0);
+               cnt++;
+               if (osst_get_logical_blk(STp, aSRpnt, -1, 0) < 0) {
+                       printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks\n", dev);
+                       return (-EIO);
+               }
+               if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) {
+                       printk(KERN_INFO "osst%i: Expected to find marker at block %d, not found\n", dev, last_mark_ppos);
+                       return (-EIO);
+               }
+       }
+       if (mt_op == MTBSFM) {
+               STp->logical_blk_num++;
+               STp->logical_blk_in_buffer = 0;
+       }
+       return 0;
+}
+
+/*
+ * ADRL 1.1 compatible "slow" space filemarks fwd version
+ *
+ * Just scans for the filemark sequentially.
+ */
+static int osst_space_over_filemarks_forward_slow(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt,
+                                                                    int mt_op, int mt_count)
+{
+       int     dev = TAPE_NR(STp->devt);
+       int     cnt = 0;
+
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%d: Reached space_over_filemarks_forward_slow %d %d\n", dev, mt_op, mt_count);
+#endif
+       if (osst_get_logical_blk(STp, aSRpnt, -1, 0) < 0) {
+               printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks_fwd\n", dev);
+               return (-EIO);
+       }
+       while (1) {
+               if (osst_get_logical_blk(STp, aSRpnt, -1, 0) < 0) {
+                       printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks\n", dev);
+                       return (-EIO);
+               }
+               if (STp->buffer->aux->frame_type == OS_FRAME_TYPE_MARKER)
+                       cnt++;
+               if (STp->buffer->aux->frame_type == OS_FRAME_TYPE_EOD) {
+#if DEBUG
+                       printk(OSST_DEB_MSG "osst%i: space_fwd: EOD reached\n", dev);
+#endif
+                       if (STp->first_frame_position > STp->eod_frame_ppos+1) {
+#if DEBUG
+                               printk(OSST_DEB_MSG "osst%i: EOD position corrected (%d=>%d)\n",
+                                               dev, STp->eod_frame_ppos, STp->first_frame_position-1);
+#endif
+                               STp->eod_frame_ppos = STp->first_frame_position-1;
+                       }
+                       return (-EIO);
+               }
+               if (cnt == mt_count)
+                       break;
+               STp->logical_blk_in_buffer = 0;
+       }
+       if (mt_op == MTFSF) {
+               STp->logical_blk_num++;
+               STp->logical_blk_in_buffer = 0;
+       }
+       return 0;
+}
+
+/*
+ * Fast linux specific version of OnStream FSF
+ */
+static int osst_space_over_filemarks_forward_fast(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt,
+                                                                    int mt_op, int mt_count)
+{
+       int     dev = TAPE_NR(STp->devt);
+       int     cnt = 0,
+               next_mark_ppos = -1;
+
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%d: Reached space_over_filemarks_forward_fast %d %d\n", dev, mt_op, mt_count);
+#endif
+       if (osst_get_logical_blk(STp, aSRpnt, -1, 0) < 0) {
+               printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks_fwd\n", dev);
+               return (-EIO);
+       }
+
+       if (STp->linux_media_version >= 4) {
+               /*
+                * direct lookup in header filemark list
+                */
+               cnt = ntohl(STp->buffer->aux->filemark_cnt) - 1;
+               if (STp->header_ok                         && 
+                   STp->header_cache != NULL              &&
+                   (cnt + mt_count)   < OS_FM_TAB_MAX     &&
+                   (cnt + mt_count)   < STp->filemark_cnt &&
+                   ((cnt == -1 && ntohl(STp->buffer->aux->last_mark_ppos) == -1) ||
+                    (STp->header_cache->dat_fm_tab.fm_tab_ent[cnt] == STp->buffer->aux->last_mark_ppos)))
+
+                       next_mark_ppos = ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[cnt + mt_count]);
+#if 1 //DEBUG
+               if (STp->header_cache == NULL || (cnt + mt_count) >= OS_FM_TAB_MAX)
+                       printk(OSST_DEB_MSG "osst%i: Filemark lookup fail due to %s\n", dev,
+                              STp->header_cache == NULL?"lack of header cache":"count out of range");
+               else
+                       printk(OSST_DEB_MSG "osst%i: Filemark lookup: prev mark %d (%s), skip %d to %d\n", dev, cnt,
+                              ((cnt == -1 && ntohl(STp->buffer->aux->last_mark_ppos) == -1) ||
+                               (STp->header_cache->dat_fm_tab.fm_tab_ent[cnt] ==
+                                        STp->buffer->aux->last_mark_ppos))?"match":"error",
+                              mt_count, next_mark_ppos);
+#endif
+               if (next_mark_ppos <= 10 || next_mark_ppos > STp->eod_frame_ppos) {
+                       printk(KERN_INFO "osst%i: Reverting to slow filemark space\n", dev);
+                       return osst_space_over_filemarks_forward_slow(STp, aSRpnt, mt_op, mt_count);
+               } else {
+                       osst_set_frame_position(STp, aSRpnt, next_mark_ppos, 0);
+                       if (osst_get_logical_blk(STp, aSRpnt, -1, 0) < 0) {
+                               printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks\n", dev);
+                               return (-EIO);
+                       }
+                       if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) {
+                               printk(KERN_INFO "osst%i: Expected to find marker at block %d, not found\n",
+                                                dev, next_mark_ppos);
+                               return (-EIO);
+                       }
+                       if (ntohl(STp->buffer->aux->filemark_cnt) != cnt + mt_count) {
+                               printk(KERN_INFO "osst%i: Expected to find marker %d at block %d, not %d\n",
+                                                dev, cnt+mt_count, next_mark_ppos,
+                                                ntohl(STp->buffer->aux->filemark_cnt));
+                                       return (-EIO);
+                       }
+               }
+       } else {
+               /*
+                * Find nearest (usually previous) marker, then jump from marker to marker
+                */
+               while (1) {
+                       if (STp->buffer->aux->frame_type == OS_FRAME_TYPE_MARKER)
+                               break;
+                       if (STp->buffer->aux->frame_type == OS_FRAME_TYPE_EOD) {
+#if DEBUG
+                               printk(OSST_DEB_MSG "osst%i: space_fwd: EOD reached\n", dev);
+#endif
+                               return (-EIO);
+                       }
+                       if (ntohl(STp->buffer->aux->filemark_cnt) == 0) {
+                               if (STp->first_mark_ppos == -1) {
+                                       printk(KERN_INFO "osst%i: Reverting to slow filemark space\n", dev);
+                                       return osst_space_over_filemarks_forward_slow(STp, aSRpnt, mt_op, mt_count);
+                               }
+                               osst_set_frame_position(STp, aSRpnt, STp->first_mark_ppos, 0);
+                               if (osst_get_logical_blk(STp, aSRpnt, -1, 0) < 0) {
+                                       printk(KERN_INFO
+                                              "osst%i: Couldn't get logical blk num in space_filemarks_fwd_fast\n",
+                                              dev);
+                                       return (-EIO);
+                               }
+                               if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) {
+                                       printk(KERN_INFO "osst%i: Expected to find filemark at %d\n",
+                                                        dev, STp->first_mark_ppos);
+                                       return (-EIO);
+                               }
+                       } else {
+                               if (osst_space_over_filemarks_backward(STp, aSRpnt, MTBSF, 1) < 0)
+                                       return (-EIO);
+                               mt_count++;
+                       }
+               }
+               cnt++;
+               while (cnt != mt_count) {
+                       next_mark_ppos = ntohl(STp->buffer->aux->next_mark_ppos);
+                       if (!next_mark_ppos || next_mark_ppos > STp->eod_frame_ppos) {
+                               printk(KERN_INFO "osst%i: Reverting to slow filemark space\n", dev);
+                               return osst_space_over_filemarks_forward_slow(STp, aSRpnt, mt_op, mt_count - cnt);
+                       }
+#if DEBUG
+                       else printk(OSST_DEB_MSG "osst%i: Positioning to next mark at %d\n", dev, next_mark_ppos);
+#endif
+                       osst_set_frame_position(STp, aSRpnt, next_mark_ppos, 0);
+                       cnt++;
+                       if (osst_get_logical_blk(STp, aSRpnt, -1, 0) < 0) {
+                               printk(KERN_INFO "osst%i: Couldn't get logical blk num in space_filemarks\n", dev);
+                               return (-EIO);
+                       }
+                       if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_MARKER) {
+                               printk(KERN_INFO "osst%i: Expected to find marker at block %d, not found\n",
+                                                dev, next_mark_ppos);
+                               return (-EIO);
+                       }
+               }
+       }
+       if (mt_op == MTFSF) 
+               STp->logical_blk_num++;
+               STp->logical_blk_in_buffer = 0;
+       return 0;
+}
+
+/*
+ * In debug mode, we want to see as many errors as possible
+ * to test the error recovery mechanism.
+ */
+#if DEBUG
+static void osst_set_retries(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, int retries)
+{
+       unsigned char   cmd[MAX_COMMAND_SIZE];
+       Scsi_Request     * SRpnt  = * aSRpnt;
+       int             dev  = TAPE_NR(STp->devt);
+
+       memset(cmd, 0, MAX_COMMAND_SIZE);
+       cmd[0] = MODE_SELECT;
+       cmd[1] = 0x10;
+       cmd[4] = NUMBER_RETRIES_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+       (STp->buffer)->b_data[0] = cmd[4] - 1;
+       (STp->buffer)->b_data[1] = 0;                   /* Medium Type - ignoring */
+       (STp->buffer)->b_data[2] = 0;                   /* Reserved */
+       (STp->buffer)->b_data[3] = 0;                   /* Block Descriptor Length */
+       (STp->buffer)->b_data[MODE_HEADER_LENGTH + 0] = NUMBER_RETRIES_PAGE | (1 << 7);
+       (STp->buffer)->b_data[MODE_HEADER_LENGTH + 1] = 2;
+       (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] = 4;
+       (STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] = retries;
+
+       if (debugging)
+           printk(OSST_DEB_MSG "osst%i: Setting number of retries on OnStream tape to %d\n", dev, retries);
+
+       SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_WRITE, STp->timeout, 0, TRUE);
+       *aSRpnt = SRpnt;
+
+       if ((STp->buffer)->syscall_result)
+           printk (KERN_ERR "osst%d: Couldn't set retries to %d\n", dev, retries);
+}
+#endif
+
+#if 0
+static void osst_update_markers(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, int last_mark_ppos, int this_mark_ppos)
+{
+       int           dev = TAPE_NR(STp->devt);
+       int           frame,
+                     reslt;
+
+       if (STp->raw) return;
+
+       STp->last_mark_ppos = this_mark_ppos;
+       if (STp->header_cache != NULL && STp->filemark_cnt < OS_FM_TAB_MAX)
+               STp->header_cache->dat_fm_tab.fm_tab_ent[STp->filemark_cnt] = htonl(this_mark_ppos);
+       if (STp->filemark_cnt++ == 0)
+               STp->first_mark_ppos = this_mark_ppos;
+
+       if (STp->linux_media_version >= 4) return;
+       if (last_mark_ppos == -1)          return;
+
+       STp->write_type = OS_WRITE_LAST_MARK;
+       frame = osst_get_frame_position(STp, aSRpnt);
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%i: Update last_marker at frame %d\n", dev, last_mark_addr);
+       printk(OSST_DEB_MSG "osst%i: current position %d, lblk %d, tape blk %d\n",
+                         dev, frame, STp->logical_blk_num, STp->last_frame_position);
+#endif
+       osst_set_frame_position(STp, aSRpnt, last_mark_ppos, 0);
+       osst_initiate_read (STp, aSRpnt);
+       reslt = osst_read_block(STp, aSRpnt, 180);
+
+       if (reslt) {
+               printk(KERN_WARNING "osst%i: couldn't read last marker\n", dev);
+               osst_set_frame_position(STp, aSRpnt, frame, 0);
+               return;
+       }
+       if (STp->buffer->aux->frame_type  != OS_FRAME_TYPE_MARKER) {
+               printk(KERN_WARNING "osst%i: expected marker at addr %d\n", dev, last_mark_ppos);
+               osst_set_frame_position(STp, aSRpnt, frame, 0);
+               return;
+       }
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%i: writing back marker\n", dev);
+#endif
+       STp->buffer->aux->next_mark_ppos = htonl(this_mark_ppos);
+       osst_set_frame_position(STp, aSRpnt, last_mark_ppos, 0);
+       STp->dirty = 1;
+       if (osst_flush_write_buffer(STp, aSRpnt, 0) ||
+           osst_flush_drive_buffer(STp, aSRpnt)     ) {
+               printk(KERN_WARNING "osst%i: couldn't write marker back at addr %d\n", dev, last_mark_ppos);
+       }
+       osst_set_frame_position(STp, aSRpnt, frame, 0); 
+
+       return; /* FIXME -- errors should go back to user space */
+}
+#endif
+
+static int osst_write_filemark(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt)
+{
+       int     result;
+       int     this_mark_ppos;
+#if DEBUG
+       int     dev = TAPE_NR(STp->devt);
+#endif
+
+       if (STp->raw) return 0;
+
+       STp->write_type = OS_WRITE_NEW_MARK;
+       this_mark_ppos = osst_get_frame_position(STp, aSRpnt);
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%i: Writing Filemark %i at frame %d (lblk %d)\n", 
+              dev, STp->filemark_cnt, this_mark_ppos, STp->logical_blk_num);
+#endif
+       osst_init_aux(STp, OS_FRAME_TYPE_MARKER, STp->logical_blk_num++);
+       STp->ps[STp->partition].rw = ST_WRITING;
+       STp->dirty = 1;
+       result  = osst_flush_write_buffer(STp, aSRpnt, 0);
+       result |= osst_flush_drive_buffer(STp, aSRpnt);
+       STp->last_mark_ppos = this_mark_ppos;
+       if (STp->header_cache != NULL && STp->filemark_cnt < OS_FM_TAB_MAX)
+               STp->header_cache->dat_fm_tab.fm_tab_ent[STp->filemark_cnt] = htonl(this_mark_ppos);
+       if (STp->filemark_cnt++ == 0)
+               STp->first_mark_ppos = this_mark_ppos;
+//     osst_update_markers(STp, aSRpnt, STp->last_mark_addr, this_mark_addr);
+       return result;
+}
+
+static int osst_write_eod(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt)
+{
+       int     result;
+#if DEBUG
+       int     dev = TAPE_NR(STp->devt);
+#endif
+
+       if (STp->raw) return 0;
+
+       STp->write_type = OS_WRITE_EOD;
+       STp->eod_frame_ppos = osst_get_frame_position(STp, aSRpnt);
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%i: Writing EOD at %d=>%d\n", dev, STp->logical_blk_num, STp->eod_frame_ppos);
+#endif
+       osst_init_aux(STp, OS_FRAME_TYPE_EOD, STp->logical_blk_num++);
+       STp->ps[STp->partition].rw = ST_WRITING;
+       STp->dirty = 1;
+
+       result  = osst_flush_write_buffer(STp, aSRpnt, 0);      
+       result |= osst_flush_drive_buffer(STp, aSRpnt);
+       STp->eod_frame_lfa = --(STp->logical_blk_num);
+       return result;
+}
+
+static int osst_write_filler(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, int block, int count)
+{
+       int     dev = TAPE_NR(STp->devt);
+
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%i: Reached onstream write filler group %d\n", dev, block);
+#endif
+       osst_wait_ready(STp, aSRpnt, 60 * 5);
+       osst_set_frame_position(STp, aSRpnt, block, 0);
+       STp->write_type = OS_WRITE_FILLER;
+       osst_init_aux(STp, OS_FRAME_TYPE_FILL, 0);
+       while (count--) {
+               memcpy(STp->buffer->b_data, "Filler", 6);
+               STp->buffer->buffer_bytes = 6;
+               STp->dirty = 1;
+               if (osst_flush_write_buffer(STp, aSRpnt, 0)) {
+                       printk(KERN_INFO "osst%i: Couldn't write filler frame\n", dev);
+                       return (-EIO);
+               }
+       }
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%i: Exiting onstream write filler group\n", dev);
+#endif
+       return osst_flush_drive_buffer(STp, aSRpnt);
+}
+
+static int __osst_write_header(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, int block, int count)
+{
+       int     dev   = TAPE_NR(STp->devt);
+       int     result;
+
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%i: Reached onstream write header group %d\n", dev, block);
+#endif
+       osst_wait_ready(STp, aSRpnt, 60 * 5);
+       osst_set_frame_position(STp, aSRpnt, block, 0);
+       STp->write_type = OS_WRITE_HEADER;
+       STp->ps[STp->partition].rw = ST_WRITING;
+       osst_init_aux(STp, OS_FRAME_TYPE_HEADER, STp->logical_blk_num);
+       while (count--) {
+               osst_copy_to_buffer(STp->buffer, (unsigned char *)STp->header_cache);
+               STp->buffer->buffer_bytes = sizeof(os_header_t);
+               STp->dirty = 1;
+               if (osst_flush_write_buffer(STp, aSRpnt, 0)) {
+                       printk(KERN_INFO "osst%i: Couldn't write header frame\n", dev);
+                       return (-EIO);
+               }
+       }
+       result = osst_flush_drive_buffer(STp, aSRpnt);
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%i: Write onstream header group %s\n", dev, result?"failed":"done");
+#endif
+       return result;
+}
+
+static int osst_write_header(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, int locate_eod)
+{
+       os_header_t * header;
+       int           result;
+       int           dev   = TAPE_NR(STp->devt);
+
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%i: Writing tape header\n", dev);
+#endif
+       if (STp->raw) return 0;
+
+       if (STp->header_cache == NULL) {
+               if ((STp->header_cache = (os_header_t *)vmalloc(sizeof(os_header_t))) == NULL) {
+                       printk(KERN_ERR "osst%i: Failed to allocate header cache\n", dev);
+                       return (-ENOMEM);
+               }
+               memset(STp->header_cache, 0, sizeof(os_header_t));
+#if DEBUG
+               printk(OSST_DEB_MSG "osst%d: Allocated and cleared memory for header cache\n", dev);
+#endif
+       }
+       if (STp->header_ok) STp->update_frame_cntr++;
+       else                STp->update_frame_cntr = 0;
+
+       header = STp->header_cache;
+       strcpy(header->ident_str, "ADR_SEQ");
+       header->major_rev      = 1;
+       header->minor_rev      = 4;
+       header->ext_trk_tb_off = htons(17192);
+       header->pt_par_num     = 1;
+       header->partition[0].partition_num              = OS_DATA_PARTITION;
+       header->partition[0].par_desc_ver               = OS_PARTITION_VERSION;
+       header->partition[0].wrt_pass_cntr              = htons(STp->wrt_pass_cntr);
+       header->partition[0].first_frame_ppos           = htonl(STp->first_data_ppos);
+       header->partition[0].last_frame_ppos            = htonl(STp->capacity);
+       header->partition[0].eod_frame_ppos             = htonl(STp->eod_frame_ppos);
+       header->cfg_col_width                           = htonl(20);
+       header->dat_col_width                           = htonl(1500);
+       header->qfa_col_width                           = htonl(0);
+       header->ext_track_tb.nr_stream_part             = 1;
+       header->ext_track_tb.et_ent_sz                  = 32;
+       header->ext_track_tb.dat_ext_trk_ey.et_part_num = 0;
+       header->ext_track_tb.dat_ext_trk_ey.fmt         = 1;
+       header->ext_track_tb.dat_ext_trk_ey.fm_tab_off  = htons(17736);
+       header->ext_track_tb.dat_ext_trk_ey.last_hlb_hi = 0;
+       header->ext_track_tb.dat_ext_trk_ey.last_hlb    = htonl(STp->eod_frame_lfa);
+       header->ext_track_tb.dat_ext_trk_ey.last_pp     = htonl(STp->eod_frame_ppos);
+       header->dat_fm_tab.fm_part_num                  = 0;
+       header->dat_fm_tab.fm_tab_ent_sz                = 4;
+       header->dat_fm_tab.fm_tab_ent_cnt               = htons(STp->filemark_cnt<OS_FM_TAB_MAX?
+                                                               STp->filemark_cnt:OS_FM_TAB_MAX);
+
+       result  = __osst_write_header(STp, aSRpnt, 0xbae, 5);
+       if (STp->update_frame_cntr == 0)
+                   osst_write_filler(STp, aSRpnt, 0xbb3, 5);
+       result &= __osst_write_header(STp, aSRpnt,     5, 5);
+
+       if (locate_eod) {
+#if DEBUG
+               printk(OSST_DEB_MSG "osst%i: locating back to eod frame addr %d\n", dev, STp->eod_frame_ppos);
+#endif
+               osst_set_frame_position(STp, aSRpnt, STp->eod_frame_ppos, 0);
+       }
+       if (result)
+               printk(KERN_WARNING "osst%i: write header failed\n", dev);
+       else {
+               memcpy(STp->application_sig, "LIN4", 4);
+               STp->linux_media         = 1;
+               STp->linux_media_version = 4;
+               STp->header_ok           = 1;
+       }
+       return result;
+}
+
+static int osst_reset_header(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt)
+{
+       if (STp->header_cache != NULL)
+               memset(STp->header_cache, 0, sizeof(os_header_t));
+
+       STp->logical_blk_num = 0;
+       STp->logical_blk_in_buffer = 0;
+       STp->eod_frame_ppos = STp->first_data_ppos = 0x0000000A;
+       STp->filemark_cnt = 0;
+       STp->first_mark_ppos = STp->last_mark_ppos = -1;
+       return osst_write_header(STp, aSRpnt, 1);
+}
+
+static int __osst_analyze_headers(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, int block)
+{
+       int           dev = TAPE_NR(STp->devt);
+       os_header_t * header;
+       os_aux_t    * aux;
+       char          id_string[8];
+       int           linux_media_version,
+                     update_frame_cntr;
+
+       if (STp->raw)
+               return 1;
+
+       if (block == 5 || block == 0xbae || STp->buffer->syscall_result) {
+               if (osst_set_frame_position(STp, aSRpnt, block, 0))
+                       printk(KERN_WARNING "osst%i: Couldn't position tape\n", dev);
+               if (osst_initiate_read (STp, aSRpnt)) {
+                       printk(KERN_WARNING "osst%i: Couldn't initiate read\n", dev);
+                       return 0;
+               }
+       }
+       if (osst_read_block(STp, aSRpnt, 180)) {
+#if DEBUG
+               printk(OSST_DEB_MSG "osst%i: Couldn't read header frame\n", dev);
+#endif
+               return 0;
+       }
+       header = (os_header_t *) STp->buffer->b_data;   /* warning: only first segment addressable */
+       aux = STp->buffer->aux;
+       if (aux->frame_type != OS_FRAME_TYPE_HEADER) {
+#if DEBUG
+               printk(OSST_DEB_MSG "osst%i: Skipping non-header frame (%d)\n", dev, block);
+#endif
+               return 0;
+       }
+       if (strncmp(header->ident_str, "ADR_SEQ", 7) != 0 &&
+           strncmp(header->ident_str, "ADR-SEQ", 7) != 0) {
+               strncpy(id_string, header->ident_str, 7);
+               id_string[7] = 0;
+               printk(KERN_INFO "osst%i: Invalid header identification string %s\n", dev, id_string);
+               return 0;
+       }
+       update_frame_cntr = ntohl(aux->update_frame_cntr);
+       if (update_frame_cntr < STp->update_frame_cntr) {
+#if DEBUG
+               printk(OSST_DEB_MSG "osst%i: Skipping frame %d with update_frame_counter %d<%d\n",
+                                  dev, block, update_frame_cntr, STp->update_frame_cntr);
+#endif
+               return 0;
+       }
+       if (header->major_rev != 1 || header->minor_rev != 4 ) {
+               printk(KERN_INFO "osst%i: %s revision %d.%d detected (1.4 supported)\n", 
+                                dev, (header->major_rev != 1 || header->minor_rev < 2 || 
+                                      header->minor_rev  > 4 )? "Invalid" : "Warning:",
+                                header->major_rev, header->minor_rev);
+               if (header->major_rev != 1 || header->minor_rev < 2 || header->minor_rev > 4)
+                       return 0;
+       }
+       if (header->pt_par_num != 1)
+               printk(KERN_INFO "osst%i: Warning: %d partitions defined, only one supported\n", 
+                                dev, header->pt_par_num);
+       memcpy(id_string, aux->application_sig, 4);
+       id_string[4] = 0;
+       if (memcmp(id_string, "LIN", 3) == 0) {
+               STp->linux_media = 1;
+               linux_media_version = id_string[3] - '0';
+               if (linux_media_version != 4)
+                       printk(KERN_INFO "osst%i: Linux media version %d detected (current 4)\n",
+                                        dev, linux_media_version);
+       } else {
+               printk(KERN_WARNING "osst%i: non Linux media detected (%s)\n", dev, id_string);
+               return 0;
+       }
+       if (linux_media_version < STp->linux_media_version) {
+#if DEBUG
+               printk(OSST_DEB_MSG "osst%i: Skipping frame %d with linux_media_version %d\n",
+                                 dev, block, linux_media_version);
+#endif
+               return 0;
+       }
+       if (linux_media_version > STp->linux_media_version) {
+#if DEBUG
+               printk(OSST_DEB_MSG "osst%i: Frame %d sets linux_media_version to %d\n",
+                                  dev, block, linux_media_version);
+#endif
+               memcpy(STp->application_sig, id_string, 5);
+               STp->linux_media_version = linux_media_version;
+               STp->update_frame_cntr = -1;
+       }
+       if (update_frame_cntr > STp->update_frame_cntr) {
+#if DEBUG
+               printk(OSST_DEB_MSG "osst%i: Frame %d sets update_frame_counter to %d\n",
+                                  dev, block, update_frame_cntr);
+#endif
+               if (STp->header_cache == NULL) {
+                       if ((STp->header_cache = (os_header_t *)vmalloc(sizeof(os_header_t))) == NULL) {
+                               printk(KERN_ERR "osst%i: Failed to allocate header cache\n", dev);
+                               return 0;
+                       }
+#if DEBUG
+                       printk(OSST_DEB_MSG "osst%d: Allocated memory for header cache\n", dev);
+#endif
+               }
+               osst_copy_from_buffer(STp->buffer, (unsigned char *)STp->header_cache);
+               header = STp->header_cache;     /* further accesses from cached (full) copy */
+
+               STp->wrt_pass_cntr     = ntohs(header->partition[0].wrt_pass_cntr);
+               STp->first_data_ppos   = ntohl(header->partition[0].first_frame_ppos);
+               STp->eod_frame_ppos    = ntohl(header->partition[0].eod_frame_ppos);
+               STp->eod_frame_lfa     = ntohl(header->ext_track_tb.dat_ext_trk_ey.last_hlb);
+               STp->filemark_cnt      = ntohl(aux->filemark_cnt);
+               STp->first_mark_ppos   = ntohl(aux->next_mark_ppos);
+               STp->last_mark_ppos    = ntohl(aux->last_mark_ppos);
+               STp->update_frame_cntr = update_frame_cntr;
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%i: detected write pass %d, update frame counter %d, filemark counter %d\n",
+                         dev, STp->wrt_pass_cntr, STp->update_frame_cntr, STp->filemark_cnt);
+       printk(OSST_DEB_MSG "osst%i: first data frame on tape = %d, last = %d, eod frame = %d\n", dev,
+                         STp->first_data_ppos,
+                         ntohl(header->partition[0].last_frame_ppos),
+                         ntohl(header->partition[0].eod_frame_ppos));
+       printk(OSST_DEB_MSG "osst%i: first mark on tape = %d, last = %d, eod frame = %d\n", 
+                         dev, STp->first_mark_ppos, STp->last_mark_ppos, STp->eod_frame_ppos);
+#endif
+               if (header->minor_rev < 4 && STp->linux_media_version == 4) {
+                       printk(OSST_DEB_MSG "osst%i: Moving filemark list to ADR 1.4 location\n", dev);
+                       memcpy((void *)header->dat_fm_tab.fm_tab_ent, 
+                              (void *)header->old_filemark_list, sizeof(header->dat_fm_tab.fm_tab_ent));
+                       memset((void *)header->old_filemark_list, 0, sizeof(header->old_filemark_list));
+               }
+               if (header->minor_rev == 4   &&
+                   (header->ext_trk_tb_off                          != htons(17192)               ||
+                    header->partition[0].partition_num              != OS_DATA_PARTITION          ||
+                    header->partition[0].par_desc_ver               != OS_PARTITION_VERSION       ||
+                    header->partition[0].last_frame_ppos            != htonl(STp->capacity)       ||
+                    header->cfg_col_width                           != htonl(20)                  ||
+                    header->dat_col_width                           != htonl(1500)                ||
+                    header->qfa_col_width                           != htonl(0)                   ||
+                    header->ext_track_tb.nr_stream_part             != 1                          ||
+                    header->ext_track_tb.et_ent_sz                  != 32                         ||
+                    header->ext_track_tb.dat_ext_trk_ey.et_part_num != OS_DATA_PARTITION          ||
+                    header->ext_track_tb.dat_ext_trk_ey.fmt         != 1                          ||
+                    header->ext_track_tb.dat_ext_trk_ey.fm_tab_off  != htons(17736)               ||
+                    header->ext_track_tb.dat_ext_trk_ey.last_hlb_hi != 0                          ||
+                    header->ext_track_tb.dat_ext_trk_ey.last_pp     != htonl(STp->eod_frame_ppos) ||
+                    header->dat_fm_tab.fm_part_num                  != OS_DATA_PARTITION          ||
+                    header->dat_fm_tab.fm_tab_ent_sz                != 4                          ||
+                    header->dat_fm_tab.fm_tab_ent_cnt               !=
+                            htons(STp->filemark_cnt<OS_FM_TAB_MAX?STp->filemark_cnt:OS_FM_TAB_MAX)))
+                       printk(KERN_WARNING "osst%i: Failed consistency check ADR 1.4 format\n", dev);
+
+//             memcpy(STp->header_cache, header, sizeof(os_header_t));
+       }
+
+       return 1;
+}
+
+static int osst_analyze_headers(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt)
+{
+       int     position, block;
+       int     first, last;
+       int     valid = 0;
+       int     dev = TAPE_NR(STp->devt);
+
+       position = osst_get_frame_position(STp, aSRpnt);
+
+       if (STp->raw) {
+               STp->header_ok = STp->linux_media = 1;
+               STp->linux_media_version = 0;
+               return 1;
+       }
+       STp->header_ok = STp->linux_media = STp->linux_media_version = 0;
+       STp->wrt_pass_cntr = STp->update_frame_cntr = -1;
+       STp->eod_frame_ppos = STp->first_data_ppos = -1;
+       STp->first_mark_ppos = STp->last_mark_ppos = -1;
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%i: Reading header\n", dev);
+#endif
+
+       /* optimization for speed - if we are positioned at block 10, read second group first */        
+       /* TODO try the ADR 1.1 locations for the second group if we have no valid one yet... */
+
+       first = position==10?0xbae: 5;
+       last  = position==10?0xbb3:10;
+
+       for (block = first; block < last; block++)
+               if (__osst_analyze_headers(STp, aSRpnt, block))
+                       valid = 1;
+
+       first = position==10? 5:0xbae;
+       last  = position==10?10:0xbb3;
+
+       for (block = first; block < last; block++)
+               if (__osst_analyze_headers(STp, aSRpnt, block))
+                       valid = 1;
+
+       if (!valid) {
+               printk(KERN_ERR "osst%i: Failed to find valid ADRL header, new media?\n", dev);
+               STp->eod_frame_ppos = STp->first_data_ppos = 0;
+               osst_set_frame_position(STp, aSRpnt, 10, 0);
+               return 0;
+       }
+       if (position <= STp->first_data_ppos) {
+               position = STp->first_data_ppos;
+               STp->ps[0].drv_file = STp->ps[0].drv_block = STp->logical_blk_num = 0;
+       }
+       osst_set_frame_position(STp, aSRpnt, position, 0);
+       STp->header_ok = 1;
+
+       return 1;
+}
+
+static int osst_verify_position(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt)
+{
+       int     frame_position  = STp->first_frame_position;
+       int     logical_blk_num = STp->logical_blk_num;
+       int     prev_mark_ppos  = -1;
+       int     actual_mark_ppos, i, n;
+#if 1 //DEBUG
+       int     dev = TAPE_NR(STp->devt);
+
+       printk(OSST_DEB_MSG "osst%i: Verify that the tape is really the one we think before writing\n", dev);
+#endif
+       osst_set_frame_position(STp, aSRpnt, frame_position - 1, 0);
+       if (osst_get_logical_blk(STp, aSRpnt, -1, 0) < 0) {
+#if DEBUG
+               printk(OSST_DEB_MSG "osst%i: Couldn't get logical blk num in verify_position\n", dev);
+#endif
+               return (-EIO);
+       }
+       if (STp->linux_media_version >= 4) {
+               for (i=0; i<STp->filemark_cnt; i++)
+                       if ((n=ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[i])) < frame_position)
+                               prev_mark_ppos = n;
+       } else
+               prev_mark_ppos = frame_position - 1;  /* usually - we don't really know */
+       actual_mark_ppos = STp->buffer->aux->frame_type == OS_FRAME_TYPE_MARKER ?
+                               frame_position - 1 : ntohl(STp->buffer->aux->last_mark_ppos);
+       if (frame_position  != STp->first_frame_position ||
+           logical_blk_num != STp->logical_blk_num + 1  ||
+           prev_mark_ppos  != actual_mark_ppos           ) {
+#if 1 //DEBUG
+               printk(OSST_DEB_MSG "osst%i: Block mismatch: frame %d-%d, lblk %d-%d, mark %d-%d\n", dev,
+                                 STp->first_frame_position, frame_position, STp->logical_blk_num + 1,
+                                 logical_blk_num, actual_mark_ppos, prev_mark_ppos);
+#endif
+               return (-EIO);
+       }
+       STp->logical_blk_in_buffer = 0;
+       STp->logical_blk_num = logical_blk_num;
+       return 0;
+}
+
+/* Acc. to OnStream, the vers. numbering is the following:
+ * X.XX for released versions (X=digit), 
+ * XXXY for unreleased versions (Y=letter)
+ * Ordering 1.05 < 106A < 106a < 106B < ... < 1.06
+ * This fn makes monoton numbers out of this scheme ...
+ */
+static unsigned int osst_parse_firmware_rev (const char * str)
+{
+       unsigned int rev;
+       if (str[1] == '.') {
+               rev = (str[0]-0x30)*10000
+                       +(str[2]-0x30)*1000
+                       +(str[3]-0x30)*100;
+       } else {
+               rev = (str[0]-0x30)*10000
+                       +(str[1]-0x30)*1000
+                       +(str[2]-0x30)*100 - 100;
+               rev += 2*(str[3] & 0x1f)
+                       +(str[3] >= 0x60? 1: 0);
+       }
+       return rev;
+}
+
+/*
+ * Configure the OnStream SCII tape drive for default operation
+ */
+static int osst_configure_onstream(OS_Scsi_Tape *STp, Scsi_Request ** aSRpnt)
+{
+       int                            dev   = TAPE_NR(STp->devt);
+       unsigned char                  cmd[MAX_COMMAND_SIZE];
+       Scsi_Request                 * SRpnt = * aSRpnt;
+       osst_mode_parameter_header_t * header;
+       osst_block_size_page_t       * bs;
+       osst_capabilities_page_t     * cp;
+       osst_tape_paramtr_page_t     * prm;
+       int                            drive_buffer_size;
+
+       if (STp->ready != ST_READY) {
+#if DEBUG
+           printk(OSST_DEB_MSG "osst%i: Not Ready\n", dev);
+#endif
+           return (-EIO);
+       }
+       
+       if (STp->os_fw_rev < 10600) {
+           printk("osst%i: Old OnStream firmware revision detected (%s)\n", 
+                      dev, STp->device->rev);
+           printk("osst%i: An upgrade to version 1.06 or above is recommended\n",
+                      dev);
+       }
+
+       /*
+        * Configure 32.5KB (data+aux) frame size.
+        * Get the current block size from the block size mode page
+        */
+       memset(cmd, 0, MAX_COMMAND_SIZE);
+       cmd[0] = MODE_SENSE;
+       cmd[1] = 8;
+       cmd[2] = BLOCK_SIZE_PAGE;
+       cmd[4] = BLOCK_SIZE_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+       SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_READ, STp->timeout, 0, TRUE);
+       if (SRpnt == NULL) {
+#if DEBUG
+           printk(OSST_DEB_MSG "osst: Busy\n");
+#endif
+           return (-EBUSY);
+       }
+       *aSRpnt = SRpnt;
+       if ((STp->buffer)->syscall_result != 0) {
+           printk (KERN_ERR "osst%i: Can't get tape block size mode page\n", dev);
+           return (-EIO);
+       }
+
+       header = (osst_mode_parameter_header_t *) (STp->buffer)->b_data;
+       bs = (osst_block_size_page_t *) ((STp->buffer)->b_data + sizeof(osst_mode_parameter_header_t) + header->bdl);
+
+#if DEBUG
+       printk(KERN_INFO "osst%i: 32KB play back: %s\n",   dev, bs->play32     ? "Yes" : "No");
+       printk(KERN_INFO "osst%i: 32.5KB play back: %s\n", dev, bs->play32_5   ? "Yes" : "No");
+       printk(KERN_INFO "osst%i: 32KB record: %s\n",      dev, bs->record32   ? "Yes" : "No");
+       printk(KERN_INFO "osst%i: 32.5KB record: %s\n",    dev, bs->record32_5 ? "Yes" : "No");
+#endif
+
+       /*
+        * Configure default auto columns mode, 32.5KB transfer mode
+        */ 
+       bs->one = 1;
+       bs->play32 = 0;
+       bs->play32_5 = 1;
+       bs->record32 = 0;
+       bs->record32_5 = 1;
+
+       memset(cmd, 0, MAX_COMMAND_SIZE);
+       cmd[0] = MODE_SELECT;
+       cmd[1] = 0x10;
+       cmd[4] = BLOCK_SIZE_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+       SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_WRITE, STp->timeout, 0, TRUE);
+       *aSRpnt = SRpnt;
+       if ((STp->buffer)->syscall_result != 0) {
+           printk (KERN_ERR "osst%i: Couldn't set tape block size mode page\n", dev);
+           return (-EIO);
+       }
+
+       STp->block_size = (STp->raw) ? OS_FRAME_SIZE : OS_DATA_SIZE;
+       STp->min_block  = OS_FRAME_SIZE; /* FIXME */
+       STp->max_block  = STp->block_size;
+
+#if DEBUG
+       printk(KERN_INFO "osst%i: Block Size changed to 32.5K\n", dev);
+        /*
+        * In debug mode, we want to see as many errors as possible
+        * to test the error recovery mechanism.
+        */
+       osst_set_retries(STp, aSRpnt, 0);
+       SRpnt = * aSRpnt;
+#endif
+
+       /*
+        * Set vendor name to 'LIN4' for "Linux support version 4".
+        */
+
+       memset(cmd, 0, MAX_COMMAND_SIZE);
+       cmd[0] = MODE_SELECT;
+       cmd[1] = 0x10;
+       cmd[4] = VENDOR_IDENT_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+       header->mode_data_length = VENDOR_IDENT_PAGE_LENGTH + MODE_HEADER_LENGTH - 1;
+       header->medium_type      = 0;   /* Medium Type - ignoring */
+       header->dsp              = 0;   /* Reserved */
+       header->bdl              = 0;   /* Block Descriptor Length */
+       
+       (STp->buffer)->b_data[MODE_HEADER_LENGTH + 0] = VENDOR_IDENT_PAGE | (1 << 7);
+       (STp->buffer)->b_data[MODE_HEADER_LENGTH + 1] = 6;
+       (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] = 'L';
+       (STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] = 'I';
+       (STp->buffer)->b_data[MODE_HEADER_LENGTH + 4] = 'N';
+       (STp->buffer)->b_data[MODE_HEADER_LENGTH + 5] = '4';
+       (STp->buffer)->b_data[MODE_HEADER_LENGTH + 6] = 0;
+       (STp->buffer)->b_data[MODE_HEADER_LENGTH + 7] = 0;
+
+       SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_WRITE, STp->timeout, 0, TRUE);
+       *aSRpnt = SRpnt;
+
+       if ((STp->buffer)->syscall_result != 0) {
+           printk (KERN_ERR "osst%i: Couldn't set vendor name to %s\n", dev, 
+                       (char *) ((STp->buffer)->b_data + MODE_HEADER_LENGTH + 2));
+           return (-EIO);
+       }
+
+       memset(cmd, 0, MAX_COMMAND_SIZE);
+       cmd[0] = MODE_SENSE;
+       cmd[1] = 8;
+       cmd[2] = CAPABILITIES_PAGE;
+       cmd[4] = CAPABILITIES_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+       SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_READ, STp->timeout, 0, TRUE);
+       *aSRpnt = SRpnt;
+
+       if ((STp->buffer)->syscall_result != 0) {
+           printk (KERN_ERR "osst%i: can't get capabilities page\n", dev);
+           return (-EIO);
+       }
+
+       header = (osst_mode_parameter_header_t *) (STp->buffer)->b_data;
+       cp     = (osst_capabilities_page_t    *) ((STp->buffer)->b_data +
+                sizeof(osst_mode_parameter_header_t) + header->bdl);
+
+       drive_buffer_size = ntohs(cp->buffer_size) / 2;
+
+       memset(cmd, 0, MAX_COMMAND_SIZE);
+       cmd[0] = MODE_SENSE;
+       cmd[1] = 8;
+       cmd[2] = TAPE_PARAMTR_PAGE;
+       cmd[4] = TAPE_PARAMTR_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+       SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_READ, STp->timeout, 0, TRUE);
+       *aSRpnt = SRpnt;
+
+       if ((STp->buffer)->syscall_result != 0) {
+           printk (KERN_ERR "osst%i: can't get tape parameter page\n", dev);
+           return (-EIO);
+       }
+
+       header = (osst_mode_parameter_header_t *) (STp->buffer)->b_data;
+       prm    = (osst_tape_paramtr_page_t    *) ((STp->buffer)->b_data +
+                sizeof(osst_mode_parameter_header_t) + header->bdl);
+
+       STp->density  = prm->density;
+       STp->capacity = ntohs(prm->segtrk) * ntohs(prm->trks);
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%d: Density %d, tape length: %dMB, drive buffer size: %dKB\n",
+                         dev, STp->density, STp->capacity / 32, drive_buffer_size);
+#endif
+
+       return 0;
+       
+}
+
+
+/* Step over EOF if it has been inadvertently crossed (ioctl not used because
+   it messes up the block number). */
+static int cross_eof(OS_Scsi_Tape *STp, Scsi_Request ** aSRpnt, int forward)
+{
+       int     result;
+       int     dev   = TAPE_NR(STp->devt);
+
+#if DEBUG
+       if (debugging)
+               printk(OSST_DEB_MSG "osst%d: Stepping over filemark %s.\n",
+                                 dev, forward ? "forward" : "backward");
+#endif
+
+       if (forward) {
+          /* assumes that the filemark is already read by the drive, so this is low cost */
+          result = osst_space_over_filemarks_forward_slow(STp, aSRpnt, MTFSF, 1);
+       }
+       else
+          /* assumes this is only called if we just read the filemark! */
+          result = osst_seek_logical_blk(STp, aSRpnt, STp->logical_blk_num - 1);
+
+       if (result < 0)
+          printk(KERN_ERR "osst%d: Stepping over filemark %s failed.\n",
+                               dev, forward ? "forward" : "backward");
+
+       return result;
+}
+
+
+/* Get the tape position. */
+
+static int osst_get_frame_position(OS_Scsi_Tape *STp, Scsi_Request ** aSRpnt)
+{
+       unsigned char   scmd[MAX_COMMAND_SIZE];
+       Scsi_Request  * SRpnt;
+       int             result = 0;
+
+       /* KG: We want to be able to use it for checking Write Buffer availability
+        *  and thus don't want to risk to overwrite anything. Exchange buffers ... */
+       char            mybuf[24];
+       char          * olddata = STp->buffer->b_data;
+       int             oldsize = STp->buffer->buffer_size;
+       int             dev     = TAPE_NR(STp->devt);
+
+       if (STp->ready != ST_READY) return (-EIO);
+
+       memset (scmd, 0, MAX_COMMAND_SIZE);
+       scmd[0] = READ_POSITION;
+
+       STp->buffer->b_data = mybuf; STp->buffer->buffer_size = 24;
+       SRpnt = osst_do_scsi(*aSRpnt, STp, scmd, 20, SCSI_DATA_READ,
+                                     STp->timeout, MAX_READY_RETRIES, TRUE);
+       if (!SRpnt) {
+               STp->buffer->b_data = olddata; STp->buffer->buffer_size = oldsize;
+               return (-EBUSY);
+       }
+       *aSRpnt = SRpnt;
+
+       if (STp->buffer->syscall_result)
+               result = ((SRpnt->sr_sense_buffer[2] & 0x0f) == 3) ? -EIO : -EINVAL;
+
+       if (result == -EINVAL)
+               printk(KERN_ERR "osst%d: Can't read tape position.\n", dev);
+       else {
+
+               if (result == -EIO) {   /* re-read position */
+                       unsigned char mysense[16];
+                       memcpy (mysense, SRpnt->sr_sense_buffer, 16);
+                       memset (scmd, 0, MAX_COMMAND_SIZE);
+                       scmd[0] = READ_POSITION;
+                       STp->buffer->b_data = mybuf; STp->buffer->buffer_size = 24;
+                       SRpnt = osst_do_scsi(SRpnt, STp, scmd, 20, SCSI_DATA_READ,
+                                                   STp->timeout, MAX_READY_RETRIES, TRUE);
+                       if (!STp->buffer->syscall_result)
+                               memcpy (SRpnt->sr_sense_buffer, mysense, 16);
+               }
+               STp->first_frame_position = ((STp->buffer)->b_data[4] << 24)
+                                         + ((STp->buffer)->b_data[5] << 16)
+                                         + ((STp->buffer)->b_data[6] << 8)
+                                         +  (STp->buffer)->b_data[7];
+               STp->last_frame_position  = ((STp->buffer)->b_data[ 8] << 24)
+                                         + ((STp->buffer)->b_data[ 9] << 16)
+                                         + ((STp->buffer)->b_data[10] <<  8)
+                                         +  (STp->buffer)->b_data[11];
+               STp->cur_frames           =  (STp->buffer)->b_data[15];
+#if DEBUG
+               if (debugging) {
+                       printk(OSST_DEB_MSG "osst%d: Drive Positions: host %d, tape %d%s, buffer %d\n", dev,
+                                           STp->first_frame_position, STp->last_frame_position,
+                                           ((STp->buffer)->b_data[0]&0x80)?" (BOP)":
+                                           ((STp->buffer)->b_data[0]&0x40)?" (EOP)":"",
+                                           STp->cur_frames);
+               }
+#endif
+               if (STp->cur_frames == 0 && STp->first_frame_position != STp->last_frame_position) {
+#if DEBUG
+                       printk(KERN_WARNING "osst%d: Correcting read position %d, %d, %d\n", dev,
+                                       STp->first_frame_position, STp->last_frame_position, STp->cur_frames);
+#endif
+                       STp->first_frame_position = STp->last_frame_position;
+               }
+       }
+       STp->buffer->b_data = olddata; STp->buffer->buffer_size = oldsize;
+
+       return (result == 0 ? STp->first_frame_position : result);
+}
+
+
+/* Set the tape block */
+static int osst_set_frame_position(OS_Scsi_Tape *STp, Scsi_Request ** aSRpnt, int block, int skip)
+{
+       unsigned char   scmd[MAX_COMMAND_SIZE];
+       Scsi_Request  * SRpnt;
+       ST_partstat   * STps;
+       int             result = 0;
+       int             timeout;
+       int             dev = TAPE_NR(STp->devt);
+
+       if (STp->ready != ST_READY) return (-EIO);
+
+       timeout = STp->long_timeout;
+       STps = &(STp->ps[STp->partition]);
+
+       if (block < 0 || block > STp->capacity) {
+               printk(KERN_ERR "osst%d: Reposition request %d out of range\n", dev, block);
+               block = block < 0 ? 0 : (STp->capacity - 1);
+               result = (-EINVAL);
+       }
+#if DEBUG
+       if (debugging)
+               printk(OSST_DEB_MSG "osst%d: Setting block to %d.\n", dev, block);
+#endif
+       memset (scmd, 0, MAX_COMMAND_SIZE);
+       scmd[0] = SEEK_10;
+       scmd[1] = 1;
+       scmd[3] = (block >> 24);
+       scmd[4] = (block >> 16);
+       scmd[5] = (block >> 8);
+       scmd[6] = block;
+       if (skip)
+               scmd[9] = 0x80;
+
+       SRpnt = osst_do_scsi(*aSRpnt, STp, scmd, 0, SCSI_DATA_NONE, timeout, MAX_READY_RETRIES, TRUE);
+       if (!SRpnt)
+               return (-EBUSY);
+       *aSRpnt  = SRpnt;
+
+       STp->first_frame_position = STp->last_frame_position = block;
+       STps->eof = ST_NOEOF;
+       if ((STp->buffer)->syscall_result != 0) {
+#if DEBUG
+               printk(OSST_DEB_MSG "osst%d: SEEK command failed.\n", dev);
+#endif
+               result = (-EIO);
+       }
+       STps->at_sm = 0;
+       STps->rw = ST_IDLE;
+       STp->logical_blk_in_buffer = 0;
+       return result;
+}
+
+
+\f
+/* osst versions of st functions - augmented and stripped to suit OnStream only */
+
+/* Flush the write buffer (never need to write if variable blocksize). */
+static int osst_flush_write_buffer(OS_Scsi_Tape *STp, Scsi_Request ** aSRpnt, int file_blk)
+{
+       int offset, transfer, blks = 0;
+       int result = 0;
+       unsigned char cmd[MAX_COMMAND_SIZE];
+       Scsi_Request * SRpnt = *aSRpnt;
+       ST_partstat * STps;
+       int dev = TAPE_NR(STp->devt);
+
+       if ((STp->buffer)->writing) {
+               if (SRpnt == (STp->buffer)->last_SRpnt)
+#if DEBUG
+                       { printk(OSST_DEB_MSG
+        "osst%d: aSRpnt points to Scsi_Request that write_behind_check will release -- cleared\n", dev);
+#endif
+                       *aSRpnt = SRpnt = NULL;
+#if DEBUG
+                       } else if (SRpnt)
+                               printk(OSST_DEB_MSG
+        "osst%d: aSRpnt does not point to Scsi_Request that write_behind_check will release -- strange\n", dev);
+#endif 
+               osst_write_behind_check(STp);
+               if ((STp->buffer)->syscall_result) {
+#if DEBUG
+                       if (debugging)
+                               printk(OSST_DEB_MSG "osst%d: Async write error (flush) %x.\n",
+                                      dev, (STp->buffer)->midlevel_result);
+#endif
+                       if ((STp->buffer)->midlevel_result == INT_MAX)
+                               return (-ENOSPC);
+                       return (-EIO);
+               }
+       }
+
+       result = 0;
+       if (STp->dirty == 1) {
+
+               offset   = STp->buffer->buffer_bytes;
+               transfer = OS_FRAME_SIZE;
+               blks     = 1;
+               
+#if DEBUG
+               if (debugging)
+                       printk(OSST_DEB_MSG "osst%d: Flushing %d bytes, Tranfering %d bytes in %d blocks.\n",
+                                dev, offset, transfer, blks);
+#endif
+               if (offset < OS_DATA_SIZE)
+                       osst_zero_buffer_tail(STp->buffer);
+
+               /* TODO: Error handling! */
+               if (STp->poll)
+                       result = osst_wait_frame (STp, aSRpnt, STp->first_frame_position, -50, 120);
+
+               memset(cmd, 0, MAX_COMMAND_SIZE);
+               cmd[0] = WRITE_6;
+               cmd[1] = 1;
+               cmd[4] = blks;
+
+               SRpnt = osst_do_scsi(*aSRpnt, STp, cmd, transfer, SCSI_DATA_WRITE,
+                                         STp->timeout, MAX_WRITE_RETRIES, TRUE);
+               *aSRpnt = SRpnt;
+               if (!SRpnt)
+                       return (-EBUSY);
+
+               STps = &(STp->ps[STp->partition]);
+               if ((STp->buffer)->syscall_result != 0) {
+                       printk(OSST_DEB_MSG
+                               "osst%d: write sense [0]=0x%02x [2]=%02x [12]=%02x [13]=%02x\n",
+                               dev, SRpnt->sr_sense_buffer[0], SRpnt->sr_sense_buffer[2],
+                               SRpnt->sr_sense_buffer[12], SRpnt->sr_sense_buffer[13]);
+                       if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 &&
+                           (SRpnt->sr_sense_buffer[2] & 0x40) && /* FIXME - SC-30 drive doesn't assert EOM bit */
+                           (SRpnt->sr_sense_buffer[2] & 0x0f) == NO_SENSE) {
+                               STp->dirty = 0;
+                               (STp->buffer)->buffer_bytes = 0;
+                               result = (-ENOSPC);
+                       }
+                       else {
+                               if (osst_write_error_recovery(STp, aSRpnt, 1)) {
+                                       printk(KERN_ERR "osst%d: Error on flush.\n", dev);
+                                       result = (-EIO);
+                               }
+                       }
+                       STps->drv_block = (-1);
+               }
+               else {
+                       if (file_blk && STps->drv_block >= 0)
+                               STps->drv_block += blks;
+                       STp->first_frame_position += blks;
+                       STp->dirty = 0;
+                       (STp->buffer)->buffer_bytes = 0;
+               }
+       }
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%d: Exit flush write buffer with code %d\n", dev, result);
+#endif
+       return result;
+}
+
+
+/* Flush the tape buffer. The tape will be positioned correctly unless
+   seek_next is true. */
+static int osst_flush_buffer(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, int seek_next)
+{
+       int backspace, result;
+       OSST_buffer    * STbuffer;
+       ST_partstat  * STps;
+#if DEBUG
+       int dev = TAPE_NR(STp->devt);
+#endif
+
+       STbuffer = STp->buffer;
+
+       /*
+        * If there was a bus reset, block further access
+        * to this device.
+        */
+       if( STp->device->was_reset )
+               return (-EIO);
+
+       if (STp->ready != ST_READY)
+               return 0;
+
+       STps = &(STp->ps[STp->partition]);
+       if (STps->rw == ST_WRITING)  /* Writing */
+               return osst_flush_write_buffer(STp, aSRpnt, 1);
+
+       if (STp->block_size == 0)
+               return 0;
+
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%i: Reached flush (read) buffer\n", dev);
+#endif
+       backspace = ((STp->buffer)->buffer_bytes + (STp->buffer)->read_pointer) / STp->block_size -
+                   ((STp->buffer)->read_pointer + STp->block_size - 1        ) / STp->block_size ;
+       (STp->buffer)->buffer_bytes = 0;
+       (STp->buffer)->read_pointer = 0;
+       result = 0;
+       if (!seek_next) {
+               if (STps->eof == ST_FM_HIT) {
+                       result = cross_eof(STp, aSRpnt, FALSE); /* Back over the EOF hit */
+                       if (!result)
+                               STps->eof = ST_NOEOF;
+                       else {
+                               if (STps->drv_file >= 0)
+                                       STps->drv_file++;
+                               STps->drv_block = 0;
+                       }
+               }
+               if (!result && backspace > 0)   /* TODO -- design and run a test case for this */
+                       result = osst_seek_logical_blk(STp, aSRpnt, STp->logical_blk_num - backspace);
+       }
+       else if (STps->eof == ST_FM_HIT) {
+               if (STps->drv_file >= 0)
+                       STps->drv_file++;
+               STps->drv_block = 0;
+               STps->eof = ST_NOEOF;
+       }
+
+       return result;
+}
+
+\f
+/* Entry points to osst */
+
+/* Write command */
+static ssize_t osst_write(struct file * filp, const char * buf, size_t count, loff_t *ppos)
+{
+       struct inode *inode = filp->f_dentry->d_inode;
+       ssize_t total, retval = 0;
+       ssize_t i, do_count, blks, transfer;
+       int write_threshold;
+       int doing_write = 0;
+       unsigned char cmd[MAX_COMMAND_SIZE];
+       const char *b_point;
+       Scsi_Request * SRpnt = NULL;
+       OS_Scsi_Tape * STp;
+       ST_mode * STm;
+       ST_partstat * STps;
+       int dev = TAPE_NR(inode->i_rdev);
+
+       STp = os_scsi_tapes[dev];
+
+       if (down_interruptible(&STp->lock))
+               return (-ERESTARTSYS);
+
+       /*
+        * If we are in the middle of error recovery, don't let anyone
+        * else try and use this device.  Also, if error recovery fails, it
+        * may try and take the device offline, in which case all further
+        * access to the device is prohibited.
+        */
+       if( !scsi_block_when_processing_errors(STp->device) ) {
+               retval = (-ENXIO);
+               goto out;
+       }
+       
+       if (ppos != &filp->f_pos) {
+               /* "A request was outside the capabilities of the device." */
+               retval = (-ENXIO);
+               goto out;
+       }
+
+       if (STp->ready != ST_READY) {
+               if (STp->ready == ST_NO_TAPE)
+                       retval = (-ENOMEDIUM);
+               else
+                       retval = (-EIO);
+               goto out;
+       }
+       STm = &(STp->modes[STp->current_mode]);
+       if (!STm->defined) {
+               retval = (-ENXIO);
+               goto out;
+       }
+       if (count == 0)
+               goto out;
+
+       /*
+        * If there was a bus reset, block further access
+        * to this device.
+        */
+       if (STp->device->was_reset) {
+               retval = (-EIO);
+               goto out;
+       }
+
+#if DEBUG
+       if (!STp->in_use) {
+               printk(OSST_DEB_MSG "osst%d: Incorrect device.\n", dev);
+               retval = (-EIO);
+               goto out;
+       }
+#endif
+
+       if (STp->write_prot) {
+               retval = (-EACCES);
+               goto out;
+       }
+
+       /* Write must be integral number of blocks */
+       if (STp->block_size != 0 && (count % STp->block_size) != 0) {
+               printk(KERN_WARNING "osst%d: Write (%ld bytes) not multiple of tape block size (32k).\n",
+                                      dev, (unsigned long)count);
+               retval = (-EINVAL);
+               goto out;
+       }
+
+       if (STp->first_frame_position >= STp->capacity - 164) {
+               printk(KERN_WARNING "osst%d: Write truncated at EOM early warning (frame %d).\n",
+                                      dev, STp->first_frame_position);
+               retval = (-ENOSPC);
+               goto out;
+       }
+
+       STps = &(STp->ps[STp->partition]);
+
+       if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED &&
+           !osst_int_ioctl(STp, &SRpnt, MTLOCK, 0))
+               STp->door_locked = ST_LOCKED_AUTO;
+
+
+       if (STps->rw == ST_READING) {
+               retval = osst_flush_buffer(STp, &SRpnt, 0);
+               if (retval)
+                       goto out;
+               STps->rw = ST_IDLE;
+       }
+       else if (STps->rw != ST_WRITING) {
+               /* Are we totally rewriting this tape? */
+               if (!STp->header_ok || STp->first_frame_position == STp->first_data_ppos ||
+                           (STps->drv_file == 0 && STps->drv_block == 0)) {
+                       STp->wrt_pass_cntr++;
+#if DEBUG
+                       printk(OSST_DEB_MSG "osst%d: Allocating next write pass counter: %d\n",
+                                                 dev, STp->wrt_pass_cntr);
+#endif
+                       osst_reset_header(STp, &SRpnt);
+                       STps->drv_file = STps->drv_block = STp->logical_blk_num = 0;
+               }
+               /* Do we know where we'll be writing on the tape? */
+               else {
+                       if ((STp->fast_open && osst_verify_position(STp, &SRpnt)) ||
+                                       STps->drv_file < 0 || STps->drv_block < 0) {
+                               if (STp->first_frame_position == STp->eod_frame_ppos) {
+                                       STps->drv_file = STp->filemark_cnt;
+                                       STps->drv_block = 0;
+                               }
+                               else {
+                                       /* We have no idea where the tape is positioned - give up */
+#if DEBUG
+                                       printk(OSST_DEB_MSG "osst%d: Cannot write at indeterminate position.\n", dev);
+#endif
+                                       retval = (-EIO);
+                                       goto out;
+                               }
+                       }         
+                       if (STps->drv_file > 0 && STps->drv_file < STp->filemark_cnt) {
+                               STp->filemark_cnt = STps->drv_file;
+                               STp->last_mark_ppos = ntohl(STp->header_cache->dat_fm_tab.fm_tab_ent[STp->filemark_cnt-1]);
+                               printk(KERN_WARNING
+                                       "osst%d: Overwriting file %d with old write pass counter %d\n",
+                                               dev, STps->drv_file, STp->wrt_pass_cntr);
+                               printk(KERN_WARNING
+                                       "osst%d: may lead to stale data being accepted on reading back!\n",
+                                               dev);
+#if DEBUG
+                               printk(OSST_DEB_MSG
+                                       "osst%d: resetting filemark count to %d and last mark ppos to %d\n",
+                                               dev, STp->filemark_cnt, STp->last_mark_ppos);
+#endif
+                       }
+               }
+               STp->fast_open = FALSE;
+       }
+       if (!STp->header_ok) {
+#if DEBUG
+               printk(OSST_DEB_MSG "osst%d: Write cannot proceed without valid headers\n", dev);
+#endif
+               retval = (-EIO);
+               goto out;
+       }
+
+       if ((STp->buffer)->writing) {
+if (SRpnt) printk(KERN_ERR "osst%d: Not supposed to have SRpnt at line %d\n", dev, __LINE__);
+               osst_write_behind_check(STp);
+               if ((STp->buffer)->syscall_result) {
+#if DEBUG
+               if (debugging)
+                       printk(OSST_DEB_MSG "osst%d: Async write error (write) %x.\n", dev,
+                                                (STp->buffer)->midlevel_result);
+#endif
+               if ((STp->buffer)->midlevel_result == INT_MAX)
+                       STps->eof = ST_EOM_OK;
+               else
+                       STps->eof = ST_EOM_ERROR;
+               }
+       }
+       if (STps->eof == ST_EOM_OK) {
+               retval = (-ENOSPC);
+               goto out;
+       }
+       else if (STps->eof == ST_EOM_ERROR) {
+               retval = (-EIO);
+               goto out;
+       }
+
+       /* Check the buffer readability in cases where copy_user might catch
+                the problems after some tape movement. */
+       if ((copy_from_user(&i, buf, 1) != 0 ||
+            copy_from_user(&i, buf + count - 1, 1) != 0)) {
+               retval = (-EFAULT);
+               goto out;
+       }
+
+       if (!STm->do_buffer_writes) {
+#if 0
+               if (STp->block_size != 0 && (count % STp->block_size) != 0)
+                       {retval=(-EIO);goto out;}   /* Write must be integral number of blocks */
+#endif
+               write_threshold = 1;
+       }
+       else
+               write_threshold = (STp->buffer)->buffer_blocks * STp->block_size;
+       if (!STm->do_async_writes)
+               write_threshold--;
+
+       total = count;
+
+       if ((!STp-> raw) && (STp->first_frame_position == 0xbae)) {
+#if DEBUG
+               printk(OSST_DEB_MSG "osst%d: Skipping over config partition.\n", dev);
+#endif
+               if (osst_flush_drive_buffer(STp, &SRpnt) < 0) {
+                       retval = (-EIO);
+                       goto out;
+               }
+               /* error recovery may have bumped us past the header partition */
+               if (osst_get_frame_position(STp, &SRpnt) < 0xbb8)
+                       osst_position_tape_and_confirm(STp, &SRpnt, 0xbb8);
+       }
+       
+       if (STp->poll)
+               retval = osst_wait_frame (STp, &SRpnt, STp->first_frame_position, -50, 60);
+       /* TODO: Check for an error ! */
+       
+       memset(cmd, 0, MAX_COMMAND_SIZE);
+       cmd[0] = WRITE_6;
+       cmd[1] = 1;
+
+       STps->rw = ST_WRITING;
+       STp->write_type = OS_WRITE_DATA;
+
+#if DEBUG
+       printk(OSST_DEB_MSG "osst%d: Writing %d bytes to file %d block %d lblk %d frame %d\n",
+                         dev, count, STps->drv_file, STps->drv_block,
+                         STp->logical_blk_num, STp->first_frame_position);
+#endif
+
+       b_point = buf;
+       while ((STp->buffer)->buffer_bytes + count > write_threshold)
+       {
+               doing_write = 1;
+               do_count = (STp->buffer)->buffer_blocks * STp->block_size -
+                          (STp->buffer)->buffer_bytes;
+               if (do_count > count)
+                       do_count = count;
+
+               i = append_to_buffer(b_point, STp->buffer, do_count);
+               if (i) {
+                       retval = i;
+                       goto out;
+               }
+
+               transfer = OS_FRAME_SIZE;
+               blks     = 1;
+
+               osst_init_aux(STp, OS_FRAME_TYPE_DATA, STp->logical_blk_num++ );
+
+               cmd[2] = blks >> 16;
+               cmd[3] = blks >> 8;
+               cmd[4] = blks;
+
+               SRpnt = osst_do_scsi(SRpnt, STp, cmd, transfer, SCSI_DATA_WRITE,
+                                 STp->timeout, MAX_WRITE_RETRIES, TRUE);
+               if (!SRpnt) {
+                       retval = (STp->buffer)->syscall_result;
+                       goto out;
+               }
+
+               if ((STp->buffer)->syscall_result != 0) {
+#if DEBUG
+                       if (debugging)
+                               printk(OSST_DEB_MSG "osst%d: Error on write:\n", dev);
+#endif
+                       if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 &&
+                           (SRpnt->sr_sense_buffer[2] & 0x40)) {
+                               if ((SRpnt->sr_sense_buffer[0] & 0x80) != 0)
+                                       transfer = (SRpnt->sr_sense_buffer[3] << 24) |
+                                                  (SRpnt->sr_sense_buffer[4] << 16) |
+                                                  (SRpnt->sr_sense_buffer[5] <<  8) |
+                                                   SRpnt->sr_sense_buffer[6];
+                               else
+                                       transfer = 0;
+                               transfer *= STp->block_size;
+                               if (transfer <= do_count) {
+                                       filp->f_pos += do_count - transfer;
+                                       count -= do_count - transfer;
+                                       if (STps->drv_block >= 0) {
+                                               STps->drv_block += (do_count - transfer) / STp->block_size;
+                                       }
+                                       STps->eof = ST_EOM_OK;
+                                       retval = (-ENOSPC); /* EOM within current request */
+#if DEBUG
+                                       if (debugging)
+                                             printk(OSST_DEB_MSG "osst%d: EOM with %d bytes unwritten.\n",
+                                                                    dev, transfer);
+#endif
+                               }
+                               else {
+                                       STps->eof = ST_EOM_ERROR;
+                                       STps->drv_block = (-1);    /* Too cautious? */
+                                       retval = (-EIO); /* EOM for old data */
+#if DEBUG
+                                       if (debugging)
+                                             printk(OSST_DEB_MSG "osst%d: EOM with lost data.\n", dev);
+#endif
+                               }
+                       }
+                       else {
+                               if (osst_write_error_recovery(STp, &SRpnt, 1) == 0) goto ok;
+                               STps->drv_block = (-1);    /* Too cautious? */
+                               retval = (-EIO);
+                       }
+
+                       (STp->buffer)->buffer_bytes = 0;
+                       STp->dirty = 0;
+                       if (count < total)
+                               retval = total - count;
+                       goto out;
+               }
+               STp->first_frame_position++;
+ok:
+               filp->f_pos += do_count;
+               b_point += do_count;
+               count -= do_count;
+               if (STps->drv_block >= 0) {
+                       STps->drv_block += blks;
+               }
+               STp->first_frame_position += blks;
+               (STp->buffer)->buffer_bytes = 0;
+               STp->dirty = 0;
+       }
+       if (count != 0) {
+               STp->dirty = 1;
+               i = append_to_buffer(b_point, STp->buffer, count);
+               if (i) {
+                       retval = i;
+                       goto out;
+               }
+               filp->f_pos += count;
+               count = 0;
+       }
+
+       if (doing_write && (STp->buffer)->syscall_result != 0) {
+               retval = (STp->buffer)->syscall_result;
+               goto out;
+       }
+
+       if (STm->do_async_writes &&
+           ((STp->buffer)->buffer_bytes >= STp->write_threshold &&
+            (STp->buffer)->buffer_bytes >= OS_DATA_SIZE)      ) { 
+               /* Schedule an asynchronous write */
+               (STp->buffer)->writing = ((STp->buffer)->buffer_bytes /
+                                          STp->block_size) * STp->block_size;
+               STp->dirty = !((STp->buffer)->writing ==
+                                         (STp->buffer)->buffer_bytes);
+
+               transfer = OS_FRAME_SIZE;
+               blks     = 1;
+
+               osst_init_aux(STp, OS_FRAME_TYPE_DATA, STp->logical_blk_num++ );
+
+               cmd[2] = blks >> 16;
+               cmd[3] = blks >> 8;
+               cmd[4] = blks;
+#if DEBUG
+               STp->write_pending = 1;
+#endif
+
+               SRpnt = osst_do_scsi(SRpnt, STp, cmd, transfer, SCSI_DATA_WRITE,
+                                 STp->timeout, MAX_WRITE_RETRIES, FALSE);
+               if (SRpnt == NULL) {
+                       retval = (STp->buffer)->syscall_result;
+                       goto out;
+               }
+       }
+//    else if (SRpnt != NULL) {
+//     scsi_release_request(SRpnt);    /* FIXME -- this relesae no longer in st - why? */
+       SRpnt = NULL;                   /* Prevent releasing this request! */
+//    }
+       STps->at_sm &= (total == 0);
+       if (total > 0)
+       STps->eof = ST_NOEOF;
+
+       retval = total;
+
+out:
+       if (SRpnt != NULL) scsi_release_request(SRpnt);
+
+       up(&STp->lock);
+
+       return retval;
+}
+
+
+/* Read command */
+static ssize_t osst_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
+{
+       struct inode * inode = filp->f_dentry->d_inode;
+       ssize_t total, retval = 0;
+       ssize_t i, transfer;
+       int special;
+       OS_Scsi_Tape * STp;
+       ST_mode * STm;
+       ST_partstat * STps;
+       Scsi_Request *SRpnt = NULL;
+       int dev = TAPE_NR(inode->i_rdev);
+
+       STp = os_scsi_tapes[dev];
+
+       if (down_interruptible(&STp->lock))
+               return (-ERESTARTSYS);
+
+       /*
+        * If we are in the middle of error recovery, don't let anyone
+        * else try and use this device.  Also, if error recovery fails, it
+        * may try and take the device offline, in which case all further
+        * access to the device is prohibited.
+        */
+       if( !scsi_block_when_processing_errors(STp->device) ) {
+               retval = (-ENXIO);
+               goto out;
+       }
+       
+       if (ppos != &filp->f_pos) {
+               /* "A request was outside the capabilities of the device." */
+               retval = (-ENXIO);
+               goto out;
+       }
+
+       if (STp->ready != ST_READY) {
+               if (STp->ready == ST_NO_TAPE)
+                       retval = (-ENOMEDIUM);
+               else
+                       retval = (-EIO);
+               goto out;
+       }
+       STm = &(STp->modes[STp->current_mode]);
+       if (!STm->defined) {
+               retval = (-ENXIO);
+               goto out;
+       }
+#if DEBUG
+       if (!STp->in_use) {
+               printk(OSST_DEB_MSG "osst%d: Incorrect device.\n", dev);
+               retval = (-EIO);
+               goto out;
+       }
+#endif
+       /* Must have initialized medium */
+       if (!STp->header_ok) {
+               retval = (-EIO);
+               goto out;
+       }
+
+       if ((count % STp->block_size) != 0) {
+               printk(KERN_WARNING "osst%d: Use multiple of %d bytes as block size (%ld requested)\n",
+                         dev, STp->block_size, (unsigned long) count);
+               retval = (-EINVAL);     /* Read must be integral number of blocks */
+               goto out;
+       }
+
+       if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED &&
+       !osst_int_ioctl(STp, &SRpnt, MTLOCK, 0))
+               STp->door_locked = ST_LOCKED_AUTO;
+
+       STps = &(STp->ps[STp->partition]);
+       if (STps->rw == ST_WRITING) {
+               retval = osst_flush_buffer(STp, &SRpnt, 0);
+               if (retval)
+                       goto out;
+               STps->rw = ST_IDLE;
+       }
+
+#if DEBUG
+       if (debugging && STps->eof != ST_NOEOF)
+               printk(OSST_DEB_MSG "osst%d: EOF/EOM flag up (%d). Bytes %d\n", dev,
+                                    STps->eof, (STp->buffer)->buffer_bytes);
+#endif
+       if ((STp->buffer)->buffer_bytes == 0 &&
+            STps->eof >= ST_EOD_1) {
+               if (STps->eof < ST_EOD) {
+                       STps->eof += 1;
+                       retval = 0;
+                       goto out;
+               }
+               retval = (-EIO);  /* EOM or Blank Check */
+               goto out;
+       }
+
+       /* Check the buffer writability before any tape movement. Don't alter
+                buffer data. */
+       if (copy_from_user(&i, buf, 1)             != 0 ||
+           copy_to_user  (buf, &i, 1)             != 0 ||
+           copy_from_user(&i, buf + count - 1, 1) != 0 ||
+           copy_to_user  (buf + count - 1, &i, 1) != 0) {
+               retval = (-EFAULT);
+               goto out;
+       }
+
+       /* Loop until enough data in buffer or a special condition found */
+       for (total = 0, special = 0; total < count && !special; ) {
+
+               /* Get new data if the buffer is empty */
+               if ((STp->buffer)->buffer_bytes == 0) {
+                       special = osst_get_logical_blk(STp, &SRpnt, STp->logical_blk_num, 0);
+                       STp->buffer->buffer_bytes = special ? 0 : OS_DATA_SIZE; 
+                       STp->buffer->read_pointer = 0;
+                       STp->logical_blk_num++;         /* block to look for next time */
+                       STp->logical_blk_in_buffer = 0;
+                       if (special < 0) {                      /* No need to continue read */
+                               retval = special;
+                               goto out;
+                       }
+                       STps->drv_block++;
+               }
+
+               /* Move the data from driver buffer to user buffer */
+               if ((STp->buffer)->buffer_bytes > 0) {
+#if DEBUG
+                       if (debugging && STps->eof != ST_NOEOF)
+                           printk(OSST_DEB_MSG "osst%d: EOF up (%d). Left %d, needed %d.\n", dev,
+                                                STps->eof, (STp->buffer)->buffer_bytes, count - total);
+#endif
+                       transfer = (STp->buffer)->buffer_bytes < count - total ?
+                                  (STp->buffer)->buffer_bytes : count - total;
+                       i = from_buffer(STp->buffer, buf, transfer);
+                       if (i)  {
+                               retval = i;
+                               goto out;
+                       }
+                       filp->f_pos += transfer;
+                       buf += transfer;
+                       total += transfer;
+               }
+
+       } /* for (total = 0, special = 0; total < count && !special; ) */
+
+       /* Change the eof state if no data from tape or buffer */
+       if (total == 0) {
+               if (STps->eof == ST_FM_HIT) {
+                       STps->eof = (STp->first_frame_position >= STp->eod_frame_ppos)?ST_EOD:ST_FM;
+                       STps->drv_block = 0;
+                       if (STps->drv_file >= 0)
+                               STps->drv_file++;
+               }
+               else if (STps->eof == ST_EOD_1) {
+                       STps->eof = ST_EOD_2;
+                       if (STps->drv_block > 0 && STps->drv_file >= 0)
+                               STps->drv_file++;
+                       STps->drv_block = 0;
+               }
+               else if (STps->eof == ST_EOD_2)
+                       STps->eof = ST_EOD;
+       }
+       else if (STps->eof == ST_FM)
+               STps->eof = ST_NOEOF;
+
+       retval = total;
+
+out:
+       if (SRpnt != NULL) scsi_release_request(SRpnt);
+
+       up(&STp->lock);
+
+       return retval;
+}
+
+
+/* Set the driver options */
+static void osst_log_options(OS_Scsi_Tape *STp, ST_mode *STm, int dev)
+{
+  printk(KERN_INFO
+"osst%d: Mode %d options: buffer writes: %d, async writes: %d, read ahead: %d\n",
+        dev, STp->current_mode, STm->do_buffer_writes, STm->do_async_writes,
+        STm->do_read_ahead);
+  printk(KERN_INFO
+"osst%d:    can bsr: %d, two FMs: %d, fast mteom: %d, auto lock: %d,\n",
+        dev, STp->can_bsr, STp->two_fm, STp->fast_mteom, STp->do_auto_lock);
+  printk(KERN_INFO
+"osst%d:    defs for wr: %d, no block limits: %d, partitions: %d, s2 log: %d\n",
+        dev, STm->defaults_for_writes, STp->omit_blklims, STp->can_partitions,
+        STp->scsi2_logical);
+  printk(KERN_INFO
+"osst%d:    sysv: %d\n", dev, STm->sysv);
+#if DEBUG
+  printk(KERN_INFO
+        "osst%d:    debugging: %d\n",
+        dev, debugging);
+#endif
+}
+
+
+static int osst_set_options(OS_Scsi_Tape *STp, long options)
+{
+       int value;
+       long code;
+       ST_mode *STm;
+       int dev = TAPE_NR(STp->devt);
+
+       STm = &(STp->modes[STp->current_mode]);
+       if (!STm->defined) {
+               memcpy(STm, &(STp->modes[0]), sizeof(ST_mode));
+               modes_defined = TRUE;
+#if DEBUG
+               if (debugging)
+                       printk(OSST_DEB_MSG "osst%d: Initialized mode %d definition from mode 0\n",
+                                            dev, STp->current_mode);
+#endif
+       }
+
+       code = options & MT_ST_OPTIONS;
+       if (code == MT_ST_BOOLEANS) {
+               STm->do_buffer_writes = (options & MT_ST_BUFFER_WRITES) != 0;
+               STm->do_async_writes  = (options & MT_ST_ASYNC_WRITES) != 0;
+               STm->defaults_for_writes = (options & MT_ST_DEF_WRITES) != 0;
+               STm->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;
+               STp->do_auto_lock     = (options & MT_ST_AUTO_LOCK) != 0;
+               STp->can_bsr          = (options & MT_ST_CAN_BSR) != 0;
+               STp->omit_blklims     = (options & MT_ST_NO_BLKLIMS) != 0;
+               if ((STp->device)->scsi_level >= SCSI_2)
+                       STp->can_partitions = (options & MT_ST_CAN_PARTITIONS) != 0;
+               STp->scsi2_logical    = (options & MT_ST_SCSI2LOGICAL) != 0;
+               STm->sysv             = (options & MT_ST_SYSV) != 0;
+#if DEBUG
+               debugging = (options & MT_ST_DEBUGGING) != 0;
+#endif
+               osst_log_options(STp, STm, dev);
+       }
+       else if (code == MT_ST_SETBOOLEANS || code == MT_ST_CLEARBOOLEANS) {
+               value = (code == MT_ST_SETBOOLEANS);
+               if ((options & MT_ST_BUFFER_WRITES) != 0)
+                       STm->do_buffer_writes = value;
+               if ((options & MT_ST_ASYNC_WRITES) != 0)
+                       STm->do_async_writes = value;
+               if ((options & MT_ST_DEF_WRITES) != 0)
+                       STm->defaults_for_writes = value;
+               if ((options & MT_ST_READ_AHEAD) != 0)
+                       STm->do_read_ahead = value;
+               if ((options & MT_ST_TWO_FM) != 0)
+                       STp->two_fm = value;
+               if ((options & MT_ST_FAST_MTEOM) != 0)
+                       STp->fast_mteom = value;
+               if ((options & MT_ST_AUTO_LOCK) != 0)
+                       STp->do_auto_lock = value;
+               if ((options & MT_ST_CAN_BSR) != 0)
+                       STp->can_bsr = value;
+               if ((options & MT_ST_NO_BLKLIMS) != 0)
+                       STp->omit_blklims = value;
+               if ((STp->device)->scsi_level >= SCSI_2 &&
+                   (options & MT_ST_CAN_PARTITIONS) != 0)
+                       STp->can_partitions = value;
+               if ((options & MT_ST_SCSI2LOGICAL) != 0)
+                       STp->scsi2_logical = value;
+               if ((options & MT_ST_SYSV) != 0)
+                       STm->sysv = value;
+#if DEBUG
+               if ((options & MT_ST_DEBUGGING) != 0)
+                       debugging = value;
+#endif
+               osst_log_options(STp, STm, dev);
+       }
+       else if (code == MT_ST_WRITE_THRESHOLD) {
+               value = (options & ~MT_ST_OPTIONS) * ST_KILOBYTE;
+               if (value < 1 || value > osst_buffer_size) {
+                       printk(KERN_WARNING "osst%d: Write threshold %d too small or too large.\n",
+                                            dev, value);
+                       return (-EIO);
+               }
+               STp->write_threshold = value;
+               printk(KERN_INFO "osst%d: Write threshold set to %d bytes.\n",
+                                 dev, value);
+       }
+       else if (code == MT_ST_DEF_BLKSIZE) {
+               value = (options & ~MT_ST_OPTIONS);
+               if (value == ~MT_ST_OPTIONS) {
+                       STm->default_blksize = (-1);
+                       printk(KERN_INFO "osst%d: Default block size disabled.\n", dev);
+               }
+               else {
+                       STm->default_blksize = value;
+                       printk(KERN_INFO "osst%d: Default block size set to %d bytes.\n",
+                                         dev, STm->default_blksize);
+               }
+       }
+       else if (code == MT_ST_TIMEOUTS) {
+               value = (options & ~MT_ST_OPTIONS);
+               if ((value & MT_ST_SET_LONG_TIMEOUT) != 0) {
+                       STp->long_timeout = (value & ~MT_ST_SET_LONG_TIMEOUT) * HZ;
+                       printk(KERN_INFO "osst%d: Long timeout set to %d seconds.\n", dev,
+                                            (value & ~MT_ST_SET_LONG_TIMEOUT));
+               }
+               else {
+                       STp->timeout = value * HZ;
+                       printk(KERN_INFO "osst%d: Normal timeout set to %d seconds.\n", dev, value);
+               }
+       }
+       else if (code == MT_ST_DEF_OPTIONS) {
+               code = (options & ~MT_ST_CLEAR_DEFAULT);
+               value = (options & MT_ST_CLEAR_DEFAULT);
+               if (code == MT_ST_DEF_DENSITY) {
+                       if (value == MT_ST_CLEAR_DEFAULT) {
+                               STm->default_density = (-1);
+                               printk(KERN_INFO "osst%d: Density default disabled.\n", dev);
+                       }
+                       else {
+                               STm->default_density = value & 0xff;
+                               printk(KERN_INFO "osst%d: Density default set to %x\n",
+                                                 dev, STm->default_density);
+                       }
+               }
+               else if (code == MT_ST_DEF_DRVBUFFER) {
+                       if (value == MT_ST_CLEAR_DEFAULT) {
+                               STp->default_drvbuffer = 0xff;
+                               printk(KERN_INFO "osst%d: Drive buffer default disabled.\n", dev);
+                       }
+                       else {
+                               STp->default_drvbuffer = value & 7;
+                               printk(KERN_INFO "osst%d: Drive buffer default set to %x\n",
+                                                 dev, STp->default_drvbuffer);
+                       }
+               }
+               else if (code == MT_ST_DEF_COMPRESSION) {
+                       if (value == MT_ST_CLEAR_DEFAULT) {
+                               STm->default_compression = ST_DONT_TOUCH;
+                               printk(KERN_INFO "osst%d: Compression default disabled.\n", dev);
+                       }
+                       else {
+                               STm->default_compression = (value & 1 ? ST_YES : ST_NO);
+                               printk(KERN_INFO "osst%d: Compression default set to %x\n",
+                                                 dev, (value & 1));
+                       }
+               }
+       }
+       else
+               return (-EIO);
+
+       return 0;
+}
+
+
+/* Internal ioctl function */
+static int osst_int_ioctl(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, unsigned int cmd_in, unsigned long arg)
+{
+       int timeout;
+       long ltmp;
+       int i, ioctl_result;
+       int chg_eof = TRUE;
+       unsigned char cmd[MAX_COMMAND_SIZE];
+       Scsi_Request * SRpnt = * aSRpnt;
+       ST_partstat * STps;
+       int fileno, blkno, at_sm, logical_blk_num;
+       int datalen = 0, direction = SCSI_DATA_NONE;
+       int dev = TAPE_NR(STp->devt);
+
+       if (STp->ready != ST_READY && cmd_in != MTLOAD) {
+               if (STp->ready == ST_NO_TAPE)
+                       return (-ENOMEDIUM);
+               else
+                       return (-EIO);
+       }
+       timeout = STp->long_timeout;
+       STps = &(STp->ps[STp->partition]);
+       fileno = STps->drv_file;
+       blkno = STps->drv_block;
+       at_sm = STps->at_sm;
+       logical_blk_num = STp->logical_blk_num;
+
+       memset(cmd, 0, MAX_COMMAND_SIZE);
+       switch (cmd_in) {
+        case MTFSFM:
+               chg_eof = FALSE; /* Changed from the FSF after this */
+        case MTFSF:
+               if (STp->raw)
+                  return (-EIO);
+               if (STp->linux_media)
+                  ioctl_result = osst_space_over_filemarks_forward_fast(STp, &SRpnt, cmd_in, arg);
+               else
+                  ioctl_result = osst_space_over_filemarks_forward_slow(STp, &SRpnt, cmd_in, arg);
+               logical_blk_num = STp->logical_blk_num;
+               if (fileno >= 0)
+                  fileno += arg;
+               blkno = 0;
+               at_sm &= (arg == 0);
+               goto os_bypass;
+
+        case MTBSF:
+               chg_eof = FALSE; /* Changed from the FSF after this */
+        case MTBSFM:
+               if (STp->raw)
+                  return (-EIO);
+               ioctl_result = osst_space_over_filemarks_backward(STp, &SRpnt, cmd_in, arg);
+               logical_blk_num = STp->logical_blk_num;
+               if (fileno >= 0)
+                  fileno -= arg;
+               blkno = (-1);  /* We can't know the block number */
+               at_sm &= (arg == 0);
+               goto os_bypass;
+
+        case MTFSR:
+        case MTBSR:
+#if DEBUG
+               if (debugging)
+                  printk(OSST_DEB_MSG "osst%i: Skipping %lu blocks %s from logical block %d\n",
+                               dev, arg, cmd_in==MTFSR?"forward":"backward", logical_blk_num);
+#endif
+               if (cmd_in == MTFSR) {
+                  logical_blk_num += arg;
+                  if (blkno >= 0) blkno += arg;
+               }
+               else {
+                  logical_blk_num -= arg;
+                  if (blkno >= 0) blkno -= arg;
+               }
+               ioctl_result = osst_seek_logical_blk(STp, &SRpnt, logical_blk_num-1);
+               STp->logical_blk_in_buffer = 0;
+               at_sm &= (arg == 0);
+               goto os_bypass;
+
+        case MTFSS:
+               cmd[0] = SPACE;
+               cmd[1] = 0x04; /* Space Setmarks */   /* FIXME -- OS can't do this? */
+               cmd[2] = (arg >> 16);
+               cmd[3] = (arg >> 8);
+               cmd[4] = arg;
+#if DEBUG
+               if (debugging)
+                       printk(OSST_DEB_MSG "osst%d: Spacing tape forward %d setmarks.\n", dev,
+               cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+#endif
+               if (arg != 0) {
+                       blkno = fileno = (-1);
+                       at_sm = 1;
+               }
+               break;
+        case MTBSS:
+               cmd[0] = SPACE;
+               cmd[1] = 0x04; /* Space Setmarks */   /* FIXME -- OS can't do this? */
+               ltmp = (-arg);
+               cmd[2] = (ltmp >> 16);
+               cmd[3] = (ltmp >> 8);
+               cmd[4] = ltmp;
+#if DEBUG
+               if (debugging) {
+                       if (cmd[2] & 0x80)
+                          ltmp = 0xff000000;
+                        ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
+                        printk(OSST_DEB_MSG "osst%d: Spacing tape backward %ld setmarks.\n",
+                                               dev, (-ltmp));
+                }
+#endif
+                if (arg != 0) {
+                       blkno = fileno = (-1);
+                        at_sm = 1;
+                }
+                break;
+        case MTWEOF:
+                if ( STps->rw == ST_WRITING && !(STp->device)->was_reset)
+                        ioctl_result = osst_flush_write_buffer(STp, &SRpnt, 1);
+                else
+                        ioctl_result = 0;
+                for (i=0; i<arg; i++)
+                        ioctl_result |= osst_write_filemark(STp, &SRpnt);
+                logical_blk_num = STp->logical_blk_num;
+                if (fileno >= 0) fileno += arg;
+                if (blkno  >= 0) blkno   = 0;
+                goto os_bypass;
+
+        case MTWSM:
+                if (STp->write_prot)
+                        return (-EACCES);
+                if (!STp->raw)
+                        return 0;
+                cmd[0] = WRITE_FILEMARKS;   /* FIXME -- need OS version */
+                if (cmd_in == MTWSM)
+                        cmd[1] = 2;
+                cmd[2] = (arg >> 16);
+                cmd[3] = (arg >> 8);
+                cmd[4] = arg;
+                timeout = STp->timeout;
+#if DEBUG
+                if (debugging) {
+                        if (cmd_in == MTWEOF)
+                          printk(OSST_DEB_MSG "osst%d: Writing %d filemarks.\n", dev,
+                                 cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+                        else
+                          printk(OSST_DEB_MSG "osst%d: Writing %d setmarks.\n", dev,
+                                 cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+                }
+#endif
+                if (fileno >= 0)
+                        fileno += arg;
+                blkno = 0;
+                at_sm = (cmd_in == MTWSM);
+                break;
+        case MTOFFL:
+        case MTLOAD:
+        case MTUNLOAD:
+        case MTRETEN:
+                cmd[0] = START_STOP;
+                cmd[1] = 1;                    /* Don't wait for completion */
+                if (cmd_in == MTLOAD)
+                        cmd[4] = 1;            /* load */
+                if (cmd_in == MTRETEN)
+                        cmd[4] = 3;            /* retension then mount */
+                if (cmd_in == MTOFFL)
+                        cmd[4] = 4;            /* rewind then eject */
+                timeout = STp->timeout;
+#if DEBUG
+                if (debugging) {
+                        switch (cmd_in) {
+                                case MTUNLOAD:
+                                        printk(OSST_DEB_MSG "osst%d: Unloading tape.\n", dev);
+                                        break;
+                                case MTLOAD:
+                                        printk(OSST_DEB_MSG "osst%d: Loading tape.\n", dev);
+                                        break;
+                                case MTRETEN:
+                                        printk(OSST_DEB_MSG "osst%d: Retensioning tape.\n", dev);
+                                        break;
+                                case MTOFFL:
+                                        printk(OSST_DEB_MSG "osst%d: Ejecting tape.\n", dev);
+                                        break;
+                        }
+                }
+#endif
+                fileno = blkno = at_sm = logical_blk_num = 0 ;
+                break;
+        case MTNOP:
+#if DEBUG
+                if (debugging)
+                        printk(OSST_DEB_MSG "osst%d: No op on tape.\n", dev);
+#endif
+                return 0;  /* Should do something ? */
+                break;
+        case MTEOM:
+#if DEBUG
+               if (debugging)
+                  printk(OSST_DEB_MSG "osst%d: Spacing to end of recorded medium.\n", dev);
+#endif
+               osst_set_frame_position(STp, &SRpnt, STp->eod_frame_ppos, 0);
+               if (osst_get_logical_blk(STp, &SRpnt, -1, 0) < 0) {
+                  ioctl_result = -EIO;
+                  goto os_bypass;
+               }
+               if (STp->buffer->aux->frame_type != OS_FRAME_TYPE_EOD) {
+#if DEBUG
+                  printk(OSST_DEB_MSG "osst%d: No EOD frame found where expected.\n", dev);
+#endif
+                  ioctl_result = -EIO;
+                  goto os_bypass;
+               }
+               ioctl_result = osst_set_frame_position(STp, &SRpnt, STp->eod_frame_ppos, 0);
+               logical_blk_num = STp->logical_blk_num;
+               fileno          = STp->filemark_cnt;
+               blkno  = at_sm  = 0;
+               goto os_bypass;
+
+        case MTERASE:
+               if (STp->write_prot)
+                  return (-EACCES);
+               ioctl_result = osst_reset_header(STp, &SRpnt);
+               i = osst_write_eod(STp, &SRpnt);
+               if (i < ioctl_result) ioctl_result = i;
+               i = osst_position_tape_and_confirm(STp, &SRpnt, STp->eod_frame_ppos);
+               if (i < ioctl_result) ioctl_result = i;
+               fileno = blkno = at_sm = logical_blk_num = 0 ;
+               goto os_bypass;
+
+        case MTREW:
+               cmd[0] = REZERO_UNIT; /* rewind */
+               cmd[1] = 1;
+#if DEBUG
+               if (debugging)
+                  printk(OSST_DEB_MSG "osst%d: Rewinding tape, Immed=%d.\n", dev, cmd[1]);
+#endif
+               fileno = blkno = at_sm = logical_blk_num = 0 ;
+               break;
+
+        case MTLOCK:
+               chg_eof = FALSE;
+               cmd[0] = ALLOW_MEDIUM_REMOVAL;
+               cmd[4] = SCSI_REMOVAL_PREVENT;
+#if DEBUG
+               if (debugging)
+                   printk(OSST_DEB_MSG "osst%d: Locking drive door.\n", dev);
+#endif;
+               break;
+
+        case MTUNLOCK:
+               chg_eof = FALSE;
+               cmd[0] = ALLOW_MEDIUM_REMOVAL;
+               cmd[4] = SCSI_REMOVAL_ALLOW;
+#if DEBUG
+               if (debugging)
+                  printk(OSST_DEB_MSG "osst%d: Unlocking drive door.\n", dev);
+#endif;
+       break;
+
+        case MTSETBLK:           /* Set block length */
+        case MTSETDENSITY:       /* Set tape density */
+        case MTSETDRVBUFFER:     /* Set drive buffering */
+        case SET_DENS_AND_BLK:   /* Set density and block size */
+                chg_eof = FALSE;
+                if (STp->dirty || (STp->buffer)->buffer_bytes != 0)
+                        return (-EIO);       /* Not allowed if data in buffer */
+                if ((cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) &&
+                  (arg & MT_ST_BLKSIZE_MASK) != 0 &&
+                  ((arg & MT_ST_BLKSIZE_MASK) < STp->min_block ||
+                   (arg & MT_ST_BLKSIZE_MASK) > STp->max_block ||
+                   (arg & MT_ST_BLKSIZE_MASK) > osst_buffer_size)) {
+                        printk(KERN_WARNING "osst%d: Illegal block size.\n", dev);
+                        return (-EINVAL);
+                }
+                return 0;  /* silently ignore if block size didn't change */
+
+        default:
+               return (-ENOSYS);
+       }
+
+       SRpnt = osst_do_scsi(SRpnt, STp, cmd, datalen, direction, timeout, MAX_RETRIES, TRUE);
+
+       ioctl_result = (STp->buffer)->syscall_result;
+
+       if (!SRpnt) {
+#if DEBUG
+               printk(OSST_DEB_MSG "osst%d: Couldn't exec scsi cmd for IOCTL\n", dev);
+#endif
+               return ioctl_result;
+       }
+
+os_bypass:
+#if DEBUG
+       if (debugging)
+               printk(OSST_DEB_MSG "osst%d: IOCTL (%d) Result=%d\n", dev, cmd_in, ioctl_result);
+#endif
+
+       if (!ioctl_result) {  /* SCSI command successful */
+
+               if (cmd_in == MTFSFM) {
+                        fileno--;
+                        blkno--;
+               }
+               if (cmd_in == MTBSFM) {
+                        fileno++;
+                        blkno++;
+               }
+               STps->drv_block = blkno;
+               STps->drv_file = fileno;
+               STps->at_sm = at_sm;
+               STp->logical_blk_num = logical_blk_num;
+
+               if (cmd_in == MTLOCK)
+                        STp->door_locked = ST_LOCKED_EXPLICIT;
+               else if (cmd_in == MTUNLOCK)
+                       STp->door_locked = ST_UNLOCKED;
+
+               if (cmd_in == MTEOM)
+                       STps->eof = ST_EOD;
+               else if (cmd_in == MTFSF)
+                       STps->eof = (STp->first_frame_position >= STp->eod_frame_ppos)?ST_EOD:ST_FM;
+               else if (chg_eof)
+                       STps->eof = ST_NOEOF;
+
+               if (cmd_in == MTOFFL || cmd_in == MTUNLOAD)
+                       STp->rew_at_close = 0;
+               else if (cmd_in == MTLOAD) {
+/*                     STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0;  FIXME */
+                       for (i=0; i < ST_NBR_PARTITIONS; i++) {
+                           STp->ps[i].rw = ST_IDLE;
+                           STp->ps[i].last_block_valid = FALSE;
+                       }
+                       STp->partition = 0;
+               }
+
+               if (cmd_in == MTREW) {
+                       ioctl_result = osst_position_tape_and_confirm(STp, &SRpnt, STp->first_data_ppos); 
+                       if (ioctl_result > 0)
+                               ioctl_result = 0;
+               }
+
+       } else if (cmd_in == MTBSF || cmd_in == MTBSFM ) {
+               if (osst_position_tape_and_confirm(STp, &SRpnt, STp->first_data_ppos) < 0)
+                       STps->drv_file = STps->drv_block = -1;
+               else
+                       STps->drv_file = STps->drv_block = 0;
+               STps->eof = ST_NOEOF;
+       } else if (cmd_in == MTFSF || cmd_in == MTFSFM) {
+               if (osst_position_tape_and_confirm(STp, &SRpnt, STp->eod_frame_ppos) < 0)
+                       STps->drv_file = STps->drv_block = -1;
+               else {
+                       STps->drv_file  = STp->filemark_cnt;
+                       STps->drv_block = 0;
+               }
+               STps->eof = ST_EOD;
+       } else if (cmd_in == MTBSR || cmd_in == MTFSR || cmd_in == MTWEOF || cmd_in == MTEOM) {
+               STps->drv_file = STps->drv_block = (-1);
+               STps->eof = ST_NOEOF;
+               STp->header_ok = 0;
+       } else if (cmd_in == MTERASE) {
+               STp->header_ok = 0;
+       } else if (SRpnt) {  /* SCSI command was not completely successful. */
+               if (SRpnt->sr_sense_buffer[2] & 0x40) {
+                       STps->eof = ST_EOM_OK;
+                       STps->drv_block = 0;
+               }
+               if (chg_eof)
+                       STps->eof = ST_NOEOF;
+
+               if ((SRpnt->sr_sense_buffer[2] & 0x0f) == BLANK_CHECK)
+                       STps->eof = ST_EOD;
+
+               if (cmd_in == MTLOCK)
+                       STp->door_locked = ST_LOCK_FAILS;
+
+       }
+       *aSRpnt = SRpnt;
+
+       return ioctl_result;
+}
+
+
+/* Open the device */
+static int os_scsi_tape_open(struct inode * inode, struct file * filp)
+{
+       unsigned short flags;
+       int i, b_size, need_dma_buffer, new_session = FALSE, retval = 0;
+       unsigned char cmd[MAX_COMMAND_SIZE];
+       Scsi_Request * SRpnt;
+       OS_Scsi_Tape * STp;
+       ST_mode * STm;
+       ST_partstat * STps;
+       int dev = TAPE_NR(inode->i_rdev);
+       int mode = TAPE_MODE(inode->i_rdev);
+
+       if (dev >= osst_template.dev_max || (STp = os_scsi_tapes[dev]) == NULL || !STp->device)
+               return (-ENXIO);
+
+       if( !scsi_block_when_processing_errors(STp->device) ) {
+               return -ENXIO;
+       }
+
+       if (STp->in_use) {
+#if DEBUG
+               printk(OSST_DEB_MSG "osst%d: Device already in use.\n", dev);
+#endif
+               return (-EBUSY);
+       }
+       STp->in_use       = 1;
+       STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0;
+
+       if (STp->device->host->hostt->module)
+                __MOD_INC_USE_COUNT(STp->device->host->hostt->module);
+       if (osst_template.module)
+                __MOD_INC_USE_COUNT(osst_template.module);
+
+       if (mode != STp->current_mode) {
+#if DEBUG
+               if (debugging)
+                       printk(OSST_DEB_MSG "osst%d: Mode change from %d to %d.\n",
+                                              dev, STp->current_mode, mode);
+#endif
+               new_session = TRUE;
+               STp->current_mode = mode;
+       }
+       STm = &(STp->modes[STp->current_mode]);
+
+       flags = filp->f_flags;
+       STp->write_prot = ((flags & O_ACCMODE) == O_RDONLY);
+
+       STp->raw = (MINOR(inode->i_rdev) & 0x40) != 0;
+
+       /* Allocate a buffer for this user */
+       need_dma_buffer = STp->restr_dma;
+       for (i=0; i < osst_nbr_buffers; i++)
+               if (!osst_buffers[i]->in_use &&
+                  (!need_dma_buffer || osst_buffers[i]->dma))
+                       break;
+       if (i >= osst_nbr_buffers) {
+               STp->buffer = new_tape_buffer(FALSE, need_dma_buffer);
+               if (STp->buffer == NULL) {
+                       printk(KERN_WARNING "osst%d: Can't allocate tape buffer.\n", dev);
+                       retval = (-EBUSY);
+                       goto err_out;
+               }
+       }
+       else
+               STp->buffer = osst_buffers[i];
+       (STp->buffer)->in_use = 1;
+       (STp->buffer)->writing = 0;
+       (STp->buffer)->syscall_result = 0;
+       (STp->buffer)->use_sg = STp->device->host->sg_tablesize;
+
+       /* Compute the usable buffer size for this SCSI adapter */
+       if (!(STp->buffer)->use_sg)
+       (STp->buffer)->buffer_size = (STp->buffer)->sg[0].length;
+       else {
+               for (i=0, (STp->buffer)->buffer_size = 0; i < (STp->buffer)->use_sg &&
+                    i < (STp->buffer)->sg_segs; i++)
+                       (STp->buffer)->buffer_size += (STp->buffer)->sg[i].length;
+       }
+
+       STp->dirty = 0;
+       for (i=0; i < ST_NBR_PARTITIONS; i++) {
+               STps = &(STp->ps[i]);
+               STps->rw = ST_IDLE;
+       }
+       STp->ready = ST_READY;
+       STp->recover_count = 0;
+#if DEBUG
+       STp->nbr_waits = STp->nbr_finished = 0;
+#endif
+
+       memset (cmd, 0, MAX_COMMAND_SIZE);
+       cmd[0] = TEST_UNIT_READY;
+
+       SRpnt = osst_do_scsi(NULL, STp, cmd, 0, SCSI_DATA_NONE, STp->timeout, MAX_READY_RETRIES, TRUE);
+       if (!SRpnt) {
+               retval = (STp->buffer)->syscall_result;
+               goto err_out;
+       }
+       if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70      &&
+           (SRpnt->sr_sense_buffer[2] & 0x0f) == NOT_READY &&
+            SRpnt->sr_sense_buffer[12]        == 4         ) {
+#if DEBUG
+               printk(OSST_DEB_MSG "osst%d: Unit not ready, cause %x\n", dev, SRpnt->sr_sense_buffer[13]);
+#endif
+               if (SRpnt->sr_sense_buffer[13] == 2) {  /* initialize command required (LOAD) */
+                       memset (cmd, 0, MAX_COMMAND_SIZE);
+                       cmd[0] = START_STOP;
+                       cmd[1] = 1;
+                       cmd[4] = 1;
+                       SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, SCSI_DATA_NONE,
+                                            STp->timeout, MAX_READY_RETRIES, TRUE);
+               }
+               osst_wait_ready(STp, &SRpnt, (SRpnt->sr_sense_buffer[13]==1?15:3) * 60);
+       }
+       if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 &&
+           (SRpnt->sr_sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */
+#if DEBUG
+               printk(OSST_DEB_MSG "osst%d: Unit wants attention\n", dev);
+#endif
+               STp->header_ok = 0;
+
+               for (i=0; i < 10; i++) {
+
+                       memset (cmd, 0, MAX_COMMAND_SIZE);
+                       cmd[0] = TEST_UNIT_READY;
+
+                       SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, SCSI_DATA_NONE,
+                                            STp->timeout, MAX_READY_RETRIES, TRUE);
+                       if ((SRpnt->sr_sense_buffer[0] & 0x70) != 0x70 ||
+                           (SRpnt->sr_sense_buffer[2] & 0x0f) != UNIT_ATTENTION)
+                               break;
+               }
+
+               STp->device->was_reset = 0;
+               STp->partition = STp->new_partition = 0;
+               if (STp->can_partitions)
+                       STp->nbr_partitions = 1;  /* This guess will be updated later if necessary */
+               for (i=0; i < ST_NBR_PARTITIONS; i++) {
+                       STps = &(STp->ps[i]);
+                       STps->rw = ST_IDLE;
+                       STps->eof = ST_NOEOF;
+                       STps->at_sm = 0;
+                       STps->last_block_valid = FALSE;
+                       STps->drv_block = 0;
+                       STps->drv_file = 0 ;
+               }
+               new_session = TRUE;
+       }
+       /*
+        * if we have valid headers from before, and the drive/tape seem untouched,
+        * open without reconfiguring and re-reading the headers
+        */
+       if (!STp->buffer->syscall_result && STp->header_ok &&
+           !SRpnt->sr_result && SRpnt->sr_sense_buffer[0] == 0) {
+
+               memset(cmd, 0, MAX_COMMAND_SIZE);
+               cmd[0] = MODE_SENSE;
+               cmd[1] = 8;
+               cmd[2] = VENDOR_IDENT_PAGE;
+               cmd[4] = VENDOR_IDENT_PAGE_LENGTH + MODE_HEADER_LENGTH;
+
+               SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_READ, STp->timeout, 0, TRUE);
+
+               if (STp->buffer->syscall_result                     ||
+                   STp->buffer->b_data[MODE_HEADER_LENGTH + 2] != 'L' ||
+                   STp->buffer->b_data[MODE_HEADER_LENGTH + 3] != 'I' ||
+                   STp->buffer->b_data[MODE_HEADER_LENGTH + 4] != 'N' ||
+                   STp->buffer->b_data[MODE_HEADER_LENGTH + 5] != '4'  ) {
+#if DEBUG
+                       printk(OSST_DEB_MSG "osst%d: signature was changed to %c%c%c%c\n", dev,
+                         STp->buffer->b_data[MODE_HEADER_LENGTH + 2],
+                         STp->buffer->b_data[MODE_HEADER_LENGTH + 3],
+                         STp->buffer->b_data[MODE_HEADER_LENGTH + 4],
+                         STp->buffer->b_data[MODE_HEADER_LENGTH + 5]);
+#endif
+                       STp->header_ok = 0;
+               }
+               i = STp->first_frame_position;
+               if (STp->header_ok && i == osst_get_frame_position(STp, &SRpnt)) {
+                       if (STp->door_locked == ST_UNLOCKED) {
+                               if (osst_int_ioctl(STp, &SRpnt, MTLOCK, 0))
+                                       printk(KERN_WARNING "osst%d: Can't lock drive door\n", dev);
+                               else
+                                       STp->door_locked = ST_LOCKED_AUTO;
+                       }
+                       STp->fast_open = TRUE;
+                       scsi_release_request(SRpnt);
+                       return 0;
+               }
+#if DEBUG
+               if (i != STp->first_frame_position)
+                       printk(OSST_DEB_MSG "osst%d: tape position changed from %d to %d\n",
+                                               dev, i, STp->first_frame_position);
+#endif
+               STp->header_ok = 0;
+       }
+       STp->fast_open = FALSE;
+
+       if ((STp->buffer)->syscall_result != 0 &&   /* in all error conditions except no medium */ 
+           (SRpnt->sr_sense_buffer[2] != 2 || SRpnt->sr_sense_buffer[12] != 0x3A) ) {
+
+               memset(cmd, 0, MAX_COMMAND_SIZE);
+               cmd[0] = MODE_SELECT;
+               cmd[1] = 0x10;
+               cmd[4] = 4 + MODE_HEADER_LENGTH;
+
+               (STp->buffer)->b_data[0] = cmd[4] - 1;
+               (STp->buffer)->b_data[1] = 0;                   /* Medium Type - ignoring */
+               (STp->buffer)->b_data[2] = 0;                   /* Reserved */
+               (STp->buffer)->b_data[3] = 0;                   /* Block Descriptor Length */
+               (STp->buffer)->b_data[MODE_HEADER_LENGTH + 0] = 0x3f;
+               (STp->buffer)->b_data[MODE_HEADER_LENGTH + 1] = 1;
+               (STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] = 2;
+               (STp->buffer)->b_data[MODE_HEADER_LENGTH + 3] = 3;
+
+#if 1 //DEBUG
+               printk(OSST_DEB_MSG "osst%i: Applying soft reset\n", dev);
+#endif
+               SRpnt = osst_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_WRITE, STp->timeout, 0, TRUE);
+
+               STp->header_ok = 0;
+
+               for (i=0; i < 10; i++) {
+
+                       memset (cmd, 0, MAX_COMMAND_SIZE);
+                       cmd[0] = TEST_UNIT_READY;
+
+                       SRpnt = osst_do_scsi(SRpnt, STp, cmd, 0, SCSI_DATA_NONE,
+                                            STp->timeout, MAX_READY_RETRIES, TRUE);
+                       if ((SRpnt->sr_sense_buffer[0] & 0x70) != 0x70 ||
+                           (SRpnt->sr_sense_buffer[2] & 0x0f) == NOT_READY)
+                       break;
+
+                       if ((SRpnt->sr_sense_buffer[2] & 0x0f) == UNIT_ATTENTION) {
+                               STp->device->was_reset = 0;
+                               STp->partition = STp->new_partition = 0;
+                               if (STp->can_partitions)
+                                       STp->nbr_partitions = 1;  /* This guess will be updated later if necessary */
+                               for (i=0; i < ST_NBR_PARTITIONS; i++) {
+                                       STps = &(STp->ps[i]);
+                                       STps->rw = ST_IDLE;
+                                       STps->eof = ST_NOEOF;
+                                       STps->at_sm = 0;
+                                       STps->last_block_valid = FALSE;
+                                       STps->drv_block = 0;
+                                       STps->drv_file = 0 ;
+                               }
+                               new_session = TRUE;
+                       }
+               }
+       }
+
+       if (osst_wait_ready(STp, &SRpnt, 15 * 60))              /* FIXME - not allowed with NOBLOCK */
+                printk(KERN_WARNING "osst%i: Device did not become Ready in open\n",dev);
+
+       if ((STp->buffer)->syscall_result != 0) {
+               if ((STp->device)->scsi_level >= SCSI_2 &&
+                   (SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 &&
+                   (SRpnt->sr_sense_buffer[2] & 0x0f) == NOT_READY &&
+                    SRpnt->sr_sense_buffer[12] == 0x3a) { /* Check ASC */
+                       STp->ready = ST_NO_TAPE;
+               } else
+                       STp->ready = ST_NOT_READY;
+               scsi_release_request(SRpnt);
+               SRpnt = NULL;
+               STp->density = 0;       /* Clear the erroneous "residue" */
+               STp->write_prot = 0;
+               STp->block_size = 0;
+               STp->ps[0].drv_file = STp->ps[0].drv_block = (-1);
+               STp->partition = STp->new_partition = 0;
+               STp->door_locked = ST_UNLOCKED;
+               return 0;
+       }
+
+       STp->min_block = STp->max_block = (-1);
+
+       osst_configure_onstream(STp, &SRpnt);
+
+/*     STp->drv_write_prot = ((STp->buffer)->b_data[2] & 0x80) != 0; FIXME */
+
+       if (OS_FRAME_SIZE > (STp->buffer)->buffer_size &&
+           !enlarge_buffer(STp->buffer, OS_FRAME_SIZE, STp->restr_dma)) {
+               printk(KERN_NOTICE "osst%d: Framesize %d too large for buffer.\n", dev,
+                                    OS_FRAME_SIZE);
+               retval = (-EIO);
+               goto err_out;
+       }
+
+       if ((STp->buffer)->buffer_size >= OS_FRAME_SIZE) {
+               for (i = 0, b_size = 0; 
+                    i < STp->buffer->sg_segs && (b_size + STp->buffer->sg[i].length) <= OS_DATA_SIZE; 
+                    b_size += STp->buffer->sg[i++].length);
+               STp->buffer->aux = (os_aux_t *) (STp->buffer->sg[i].address + OS_DATA_SIZE - b_size);
+#if DEBUG
+               printk(OSST_DEB_MSG "osst%d: b_data points to %p in segment 0 at %p\n", dev,
+                       STp->buffer->b_data, STp->buffer->sg[0].address);
+               printk(OSST_DEB_MSG "osst%d: AUX points to %p in segment %d at %p\n", dev,
+                        STp->buffer->aux, i, STp->buffer->sg[i].address);
+#endif
+       } else
+               STp->buffer->aux = NULL; /* this had better never happen! */
+
+       (STp->buffer)->buffer_blocks = 1;
+       (STp->buffer)->buffer_bytes  =
+       (STp->buffer)->read_pointer  =
+       STp->logical_blk_in_buffer   = 0;
+
+#if DEBUG
+       if (debugging)
+               printk(OSST_DEB_MSG "osst%d: Block size: %d, frame size: %d, buffer size: %d (%d blocks).\n",
+                    dev, STp->block_size, OS_FRAME_SIZE, (STp->buffer)->buffer_size,
+                    (STp->buffer)->buffer_blocks);
+#endif
+
+       if (STp->drv_write_prot) {
+               STp->write_prot = 1;
+#if DEBUG
+               if (debugging)
+                       printk(OSST_DEB_MSG "osst%d: Write protected\n", dev);
+#endif
+               if ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR) {
+                       retval = (-EROFS);
+                       goto err_out;
+               }
+       }
+
+       if (new_session) {  /* Change the drive parameters for the new mode */
+#if DEBUG
+               if (debugging)
+       printk(OSST_DEB_MSG "osst%d: New Session\n", dev);
+#endif
+               STp->density_changed = STp->blksize_changed = FALSE;
+               STp->compression_changed = FALSE;
+       }
+
+       /*
+        * properly position the tape and check the ADR headers
+        */
+       if (STp->door_locked == ST_UNLOCKED) {
+                if (osst_int_ioctl(STp, &SRpnt, MTLOCK, 0))
+                       printk(KERN_WARNING "osst%d: Can't lock drive door\n", dev);
+                else
+                       STp->door_locked = ST_LOCKED_AUTO;
+       }
+
+       osst_analyze_headers(STp, &SRpnt);
+
+       scsi_release_request(SRpnt);
+       SRpnt = NULL;
+
+       return 0;
+
+err_out:
+       if (SRpnt != NULL)
+               scsi_release_request(SRpnt);
+       if (STp->buffer != NULL) {
+               STp->buffer->in_use = 0;
+               STp->buffer = NULL;
+       }
+       STp->in_use = 0;
+
+       if (STp->device->host->hostt->module)
+           __MOD_DEC_USE_COUNT(STp->device->host->hostt->module);
+       if (osst_template.module)
+           __MOD_DEC_USE_COUNT(osst_template.module);
+
+       return retval;
+}
+
+
+/* Flush the tape buffer before close */
+static int os_scsi_tape_flush(struct file * filp)
+{
+       int result = 0, result2;
+       OS_Scsi_Tape * STp;
+       ST_mode * STm;
+       ST_partstat * STps;
+       Scsi_Request *SRpnt = NULL;
+
+       struct inode *inode = filp->f_dentry->d_inode;
+       kdev_t devt = inode->i_rdev;
+       int dev;
+
+       if (file_count(filp) > 1)
+       return 0;
+
+       dev = TAPE_NR(devt);
+       STp = os_scsi_tapes[dev];
+       STm = &(STp->modes[STp->current_mode]);
+       STps = &(STp->ps[STp->partition]);
+
+       if ( STps->rw == ST_WRITING && !(STp->device)->was_reset) {
+               result = osst_flush_write_buffer(STp, &SRpnt, 1);
+               if (result != 0 && result != (-ENOSPC))
+         goto out;
+       }
+
+       if ( STps->rw == ST_WRITING && !(STp->device)->was_reset) {
+
+#if DEBUG
+               if (debugging) {
+                       printk(OSST_DEB_MSG "osst%d: File length %ld bytes.\n",
+                                              dev, (long)(filp->f_pos));
+                       printk(OSST_DEB_MSG "osst%d: Async write waits %d, finished %d.\n",
+                                              dev, STp->nbr_waits, STp->nbr_finished);
+               }
+#endif
+
+               result = osst_flush_drive_buffer(STp, &SRpnt);
+               if (result < 0) goto out;
+               result = osst_write_filemark(STp, &SRpnt);
+               if (result < 0) goto out;
+
+               if (STps->drv_file >= 0)
+                       STps->drv_file++ ;
+               STps->drv_block = 0;
+
+               result = osst_write_eod(STp, &SRpnt);
+               osst_write_header(STp, &SRpnt, !(STp->rew_at_close));
+
+               STps->eof = ST_FM;
+
+#if DEBUG
+               if (debugging)
+                       printk(OSST_DEB_MSG "osst%d: Buffer flushed, %d EOF(s) written\n",
+                                              dev, 1+STp->two_fm);
+#endif
+       }
+       else if (!STp->rew_at_close) {
+               STps = &(STp->ps[STp->partition]);
+               if (!STm->sysv || STps->rw != ST_READING) {
+                       if (STp->can_bsr)
+                               result = osst_flush_buffer(STp, &SRpnt, 0);
+                       else if (STps->eof == ST_FM_HIT) {
+                               result = cross_eof(STp, &SRpnt, FALSE);
+                                       if (result) {
+                                               if (STps->drv_file >= 0)
+                                                       STps->drv_file++;
+                                               STps->drv_block = 0;
+                                               STps->eof = ST_FM;
+                                       }
+                                       else
+                                               STps->eof = ST_NOEOF;
+                       }
+               }
+               else if ((STps->eof == ST_NOEOF &&
+                         !(result = cross_eof(STp, &SRpnt, TRUE))) ||
+                         STps->eof == ST_FM_HIT) {
+                       if (STps->drv_file >= 0)
+                               STps->drv_file++;
+                       STps->drv_block = 0;
+                       STps->eof = ST_FM;
+               }
+       }
+
+out:
+       if (STp->rew_at_close) {
+               result2 = osst_position_tape_and_confirm(STp, &SRpnt, STp->first_data_ppos);
+               STps->drv_file = STps->drv_block = STp->logical_blk_num = 0;
+               if (result == 0)
+                       result = result2;
+       }
+       if (SRpnt) scsi_release_request(SRpnt);
+
+       return result;
+}
+
+
+/* Close the device and release it */
+static int os_scsi_tape_close(struct inode * inode, struct file * filp)
+{
+       int result = 0;
+       OS_Scsi_Tape * STp;
+       Scsi_Request * SRpnt = NULL;
+
+       kdev_t devt = inode->i_rdev;
+       int dev;
+
+       dev = TAPE_NR(devt);
+       STp = os_scsi_tapes[dev];
+
+       if (STp->door_locked == ST_LOCKED_AUTO)
+               osst_int_ioctl(STp, &SRpnt, MTUNLOCK, 0);
+       if (SRpnt) scsi_release_request(SRpnt);
+
+       if (STp->buffer != NULL)
+               STp->buffer->in_use = 0;
+
+       STp->in_use = 0;
+       if (STp->device->host->hostt->module)
+               __MOD_DEC_USE_COUNT(STp->device->host->hostt->module);
+       if(osst_template.module)
+               __MOD_DEC_USE_COUNT(osst_template.module);
+
+       return result;
+}
+
+
+/* The ioctl command */
+static int osst_ioctl(struct inode * inode,struct file * file,
+        unsigned int cmd_in, unsigned long arg)
+{
+       int i, cmd_nr, cmd_type, retval = 0;
+       unsigned int blk;
+       OS_Scsi_Tape *STp;
+       ST_mode *STm;
+       ST_partstat *STps;
+       Scsi_Request *SRpnt = NULL;
+       int dev = TAPE_NR(inode->i_rdev);
+
+       STp = os_scsi_tapes[dev];
+
+       if (down_interruptible(&STp->lock))
+               return -ERESTARTSYS;
+
+#if DEBUG
+       if (debugging && !STp->in_use) {
+               printk(OSST_DEB_MSG "osst%d: Incorrect device.\n", dev);
+               retval = (-EIO);
+               goto out;
+       }
+#endif
+       STm = &(STp->modes[STp->current_mode]);
+       STps = &(STp->ps[STp->partition]);
+
+       /*
+        * If we are in the middle of error recovery, don't let anyone
+        * else try and use this device.  Also, if error recovery fails, it
+        * may try and take the device offline, in which case all further
+        * access to the device is prohibited.
+        */
+       if( !scsi_block_when_processing_errors(STp->device) ) {
+               retval = (-ENXIO);
+               goto out;
+       }
+
+       cmd_type = _IOC_TYPE(cmd_in);
+       cmd_nr   = _IOC_NR(cmd_in);
+
+       if (cmd_type == _IOC_TYPE(MTIOCTOP) && cmd_nr == _IOC_NR(MTIOCTOP)) {
+               struct mtop mtc;
+
+               if (_IOC_SIZE(cmd_in) != sizeof(mtc)) {
+                       retval = (-EINVAL);
+                       goto out;
+               }
+
+               i = copy_from_user((char *) &mtc, (char *)arg, sizeof(struct mtop));
+               if (i) {
+                       retval = (-EFAULT);
+                       goto out;
+               }
+
+               if (mtc.mt_op == MTSETDRVBUFFER && !capable(CAP_SYS_ADMIN)) {
+                       printk(KERN_WARNING "osst%d: MTSETDRVBUFFER only allowed for root.\n", dev);
+                       retval = (-EPERM);
+                       goto out;
+               }
+
+               if (!STm->defined && (mtc.mt_op != MTSETDRVBUFFER && (mtc.mt_count & MT_ST_OPTIONS) == 0)) {
+                       retval = (-ENXIO);
+                       goto out;
+               }
+
+               if (!(STp->device)->was_reset) {
+
+                       if (STps->eof == ST_FM_HIT) {
+                               if (mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM|| mtc.mt_op == MTEOM) {
+                                       mtc.mt_count -= 1;
+                                       if (STps->drv_file >= 0)
+                                               STps->drv_file += 1;
+                               }
+                               else if (mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM) {
+                                       mtc.mt_count += 1;
+                                       if (STps->drv_file >= 0)
+                                               STps->drv_file += 1;
+                               }
+                       }
+
+                       if (mtc.mt_op == MTSEEK) {
+                               /* Old position must be restored if partition will be changed */
+                               i = !STp->can_partitions || (STp->new_partition != STp->partition);
+                       }
+                       else {
+                               i = mtc.mt_op == MTREW   || mtc.mt_op == MTOFFL ||
+                                   mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM  ||
+                                   mtc.mt_op == MTLOCK  || mtc.mt_op == MTLOAD ||
+                                   mtc.mt_op == MTCOMPRESSION;
+                       }
+                       i = osst_flush_buffer(STp, &SRpnt, i);
+                       if (i < 0) {
+                               retval = i;
+                               goto out;
+                       }
+               }
+               else {
+                       /*
+                        * If there was a bus reset, block further access
+                        * to this device.  If the user wants to rewind the tape,
+                        * then reset the flag and allow access again.
+                        */
+                       if(mtc.mt_op != MTREW   &&
+                          mtc.mt_op != MTOFFL  &&
+                          mtc.mt_op != MTRETEN &&
+                          mtc.mt_op != MTERASE &&
+                          mtc.mt_op != MTSEEK  &&
+                          mtc.mt_op != MTEOM)   {
+                               retval = (-EIO);
+                               goto out;
+                       }
+                       STp->device->was_reset = 0;
+                       if (STp->door_locked != ST_UNLOCKED &&
+                           STp->door_locked != ST_LOCK_FAILS) {
+                               if (osst_int_ioctl(STp, &SRpnt, MTLOCK, 0)) {
+                                       printk(KERN_NOTICE "osst%d: Could not relock door after bus reset.\n",
+                                                                 dev);
+                                       STp->door_locked = ST_UNLOCKED;
+                               }
+                       }
+               }
+
+               if (mtc.mt_op != MTNOP && mtc.mt_op != MTSETBLK &&
+                   mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTWSM &&
+                   mtc.mt_op != MTSETDRVBUFFER && mtc.mt_op != MTSETPART)
+                       STps->rw = ST_IDLE;  /* Prevent automatic WEOF and fsf */
+
+               if (mtc.mt_op == MTOFFL && STp->door_locked != ST_UNLOCKED)
+                       osst_int_ioctl(STp, &SRpnt, MTUNLOCK, 0);  /* Ignore result! */
+
+               if (mtc.mt_op == MTSETDRVBUFFER &&
+                  (mtc.mt_count & MT_ST_OPTIONS) != 0) {
+                       retval = osst_set_options(STp, mtc.mt_count);
+                       goto out;
+               }
+
+               if (mtc.mt_op == MTSETPART) {
+/*     if (!STp->can_partitions ||
+          mtc.mt_count < 0 || mtc.mt_count >= ST_NBR_PARTITIONS)
+        return (-EINVAL);
+                if (mtc.mt_count >= STp->nbr_partitions &&
+          (STp->nbr_partitions = nbr_partitions(inode)) < 0)
+        return (-EIO);*/
+                       if (mtc.mt_count >= STp->nbr_partitions)
+                               retval = -EINVAL;
+                       else {
+                               STp->new_partition = mtc.mt_count;
+                               retval = 0;
+                       }
+                       goto out;
+               }
+
+               if (mtc.mt_op == MTMKPART) {
+                       if (!STp->can_partitions) {
+                               retval = (-EINVAL);
+                               goto out;
+                       }
+                       if ((i = osst_int_ioctl(STp, &SRpnt, MTREW, 0)) < 0 /*||
+                           (i = partition_tape(inode, mtc.mt_count)) < 0*/) {
+                               retval = i;
+                               goto out;
+                       }
+                       for (i=0; i < ST_NBR_PARTITIONS; i++) {
+                               STp->ps[i].rw = ST_IDLE;
+                               STp->ps[i].at_sm = 0;
+                               STp->ps[i].last_block_valid = FALSE;
+                       }
+                       STp->partition = STp->new_partition = 0;
+                       STp->nbr_partitions = 1;  /* Bad guess ?-) */
+                       STps->drv_block = STps->drv_file = 0;
+                       retval = 0;
+                       goto out;
+               }
+
+               if (mtc.mt_op == MTSEEK) {
+                       i = osst_seek_frame(STp, &SRpnt, mtc.mt_count);
+                       if (!STp->can_partitions)
+                               STp->ps[0].rw = ST_IDLE;
+                       retval = i;
+                       goto out;
+               }
+
+/*   if (STp->can_partitions && STp->ready == ST_READY &&
+        (i = update_partition(inode)) < 0)
+                {retval=i;goto out;}*/
+
+               if (mtc.mt_op == MTCOMPRESSION)
+                       retval = -EINVAL /*osst_compression(STp, (mtc.mt_count & 1))*/;
+               else
+
+                       retval = osst_int_ioctl(STp, &SRpnt, mtc.mt_op, mtc.mt_count);
+               goto out;
+       }
+
+       if (!STm->defined) {
+               retval = (-ENXIO);
+               goto out;
+       }
+
+       if ((i = osst_flush_buffer(STp, &SRpnt, FALSE)) < 0) {
+               retval = i;
+               goto out;
+       }
+
+/* if (STp->can_partitions &&
+                (i = update_partition(inode)) < 0)
+        {retval=i;goto out;}*/
+
+       if (cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET)) {
+               struct mtget mt_status;
+
+               if (_IOC_SIZE(cmd_in) != sizeof(struct mtget)) {
+                        retval = (-EINVAL);
+                        goto out;
+               }
+
+               mt_status.mt_type = MT_ISONSTREAM_SC;
+               mt_status.mt_erreg = STp->recover_erreg << MT_ST_SOFTERR_SHIFT;
+               mt_status.mt_dsreg =
+                       ((STp->block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK) |
+                       ((STp->density    << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK);
+               mt_status.mt_blkno = STps->drv_block;
+               mt_status.mt_fileno = STps->drv_file;
+               if (STp->block_size != 0) {
+                       if (STps->rw == ST_WRITING)
+                               mt_status.mt_blkno += (STp->buffer)->buffer_bytes / STp->block_size;
+                       else if (STps->rw == ST_READING)
+                               mt_status.mt_blkno -= ((STp->buffer)->buffer_bytes +
+                                                       STp->block_size - 1) / STp->block_size;
+               }
+
+               mt_status.mt_gstat = 0;
+               if (STp->drv_write_prot)
+                       mt_status.mt_gstat |= GMT_WR_PROT(0xffffffff);
+               if (mt_status.mt_blkno == 0) {
+                       if (mt_status.mt_fileno == 0)
+                               mt_status.mt_gstat |= GMT_BOT(0xffffffff);
+                       else
+                               mt_status.mt_gstat |= GMT_EOF(0xffffffff);
+               }
+               mt_status.mt_resid = STp->partition;
+               if (STps->eof == ST_EOM_OK || STps->eof == ST_EOM_ERROR)
+                       mt_status.mt_gstat |= GMT_EOT(0xffffffff);
+               else if (STps->eof >= ST_EOM_OK)
+                       mt_status.mt_gstat |= GMT_EOD(0xffffffff);
+               if (STp->density == 1)
+                       mt_status.mt_gstat |= GMT_D_800(0xffffffff);
+               else if (STp->density == 2)
+                       mt_status.mt_gstat |= GMT_D_1600(0xffffffff);
+               else if (STp->density == 3)
+                       mt_status.mt_gstat |= GMT_D_6250(0xffffffff);
+               if (STp->ready == ST_READY)
+                       mt_status.mt_gstat |= GMT_ONLINE(0xffffffff);
+               if (STp->ready == ST_NO_TAPE)
+                       mt_status.mt_gstat |= GMT_DR_OPEN(0xffffffff);
+               if (STps->at_sm)
+                       mt_status.mt_gstat |= GMT_SM(0xffffffff);
+               if (STm->do_async_writes || (STm->do_buffer_writes && STp->block_size != 0) ||
+                   STp->drv_buffer != 0)
+                       mt_status.mt_gstat |= GMT_IM_REP_EN(0xffffffff);
+
+               i = copy_to_user((char *)arg, (char *)&mt_status,
+                     sizeof(struct mtget));
+               if (i) {
+                       retval = (-EFAULT);
+                       goto out;
+               }
+
+               STp->recover_erreg = 0;  /* Clear after read */
+               retval = 0;
+               goto out;
+       } /* End of MTIOCGET */
+
+       if (cmd_type == _IOC_TYPE(MTIOCPOS) && cmd_nr == _IOC_NR(MTIOCPOS)) {
+               struct mtpos mt_pos;
+
+               if (_IOC_SIZE(cmd_in) != sizeof(struct mtpos)) {
+                       retval = (-EINVAL);
+                       goto out;
+               }
+               blk = osst_get_frame_position(STp, &SRpnt);
+               if (blk < 0) {
+                       retval = blk;
+                       goto out;
+               }
+               mt_pos.mt_blkno = blk;
+               i = copy_to_user((char *)arg, (char *) (&mt_pos), sizeof(struct mtpos));
+               if (i)
+                       retval = -EFAULT;
+               goto out;
+       }
+       if (SRpnt) scsi_release_request(SRpnt);
+
+       up(&STp->lock);
+
+       return scsi_ioctl(STp->device, cmd_in, (void *) arg);
+
+out:
+       if (SRpnt) scsi_release_request(SRpnt);
+
+       up(&STp->lock);
+
+       return retval;
+}
+
+\f
+/* Memory handling routines */
+
+/* Try to allocate a new tape buffer */
+static OSST_buffer * new_tape_buffer( int from_initialization, int need_dma )
+{
+       int i, priority, b_size, order, got = 0, segs = 0;
+       OSST_buffer *tb;
+
+       if (osst_nbr_buffers >= osst_template.dev_max)
+               return NULL;  /* Should never happen */
+
+       if (from_initialization)
+               priority = GFP_ATOMIC;
+       else
+               priority = GFP_KERNEL;
+
+       i = sizeof(OSST_buffer) + (osst_max_sg_segs - 1) * sizeof(struct scatterlist);
+       tb = (OSST_buffer *)kmalloc(i, priority);
+       if (tb) {
+//    tb->this_size = i;
+               if (need_dma)
+                       priority |= GFP_DMA;
+
+               /* Try to allocate the first segment up to OSST_FIRST_ORDER and the
+                others big enough to reach the goal */
+               for (b_size = PAGE_SIZE,          order = 0;
+                    b_size < osst_buffer_size && order < OSST_FIRST_ORDER;
+                    b_size *= 2,                 order++ );
+
+               for ( ; b_size >= PAGE_SIZE; order--, b_size /= 2) {
+                       tb->sg[0].address =
+                           (unsigned char *)__get_free_pages(priority, order);
+                       if (tb->sg[0].address != NULL) {
+                           tb->sg[0].alt_address = NULL;
+                           tb->sg[0].length = b_size;
+                           break;
+                       }
+               }
+               if (tb->sg[segs].address == NULL) {
+                       kfree(tb);
+                       tb = NULL;
+               }
+               else {  /* Got something, continue */
+
+                       for (b_size = PAGE_SIZE, order = 0;
+                            osst_buffer_size > tb->sg[0].length + (OSST_FIRST_SG - 1) * b_size;
+                            b_size *= 2, order++ );
+
+                       for (segs=1, got=tb->sg[0].length;
+                            got < osst_buffer_size && segs < OSST_FIRST_SG; ) {
+                           tb->sg[segs].address =
+                               (unsigned char *)__get_free_pages(priority, order);
+                           if (tb->sg[segs].address == NULL) {
+                               if (osst_buffer_size - got <=
+                                   (OSST_FIRST_SG - segs) * b_size / 2) {
+                                   b_size /= 2; /* Large enough for the rest of the buffers */
+                                   order--;
+                                   continue;
+                               }
+                               tb->sg_segs = segs;
+                               tb->orig_sg_segs = 0;
+#if DEBUG
+                               tb->buffer_size = got;
+#endif
+                               normalize_buffer(tb);
+                               kfree(tb);
+                               tb = NULL;
+                               break;
+                           }
+                           tb->sg[segs].alt_address = NULL;
+                           tb->sg[segs].length = b_size;
+                           got += b_size;
+                           segs++;
+                       }
+               }
+       }
+       if (!tb) {
+               printk(KERN_NOTICE "osst: Can't allocate new tape buffer (nbr %d).\n",
+                                  osst_nbr_buffers);
+               return NULL;
+       }
+       tb->sg_segs = tb->orig_sg_segs = segs;
+       tb->b_data = tb->sg[0].address;
+
+#if DEBUG
+       if (debugging) {
+               printk(OSST_DEB_MSG
+                       "osst: Allocated tape buffer %d (%d bytes, %d segments, dma: %d, a: %p).\n",
+                          osst_nbr_buffers, got, tb->sg_segs, need_dma, tb->b_data);
+               printk(OSST_DEB_MSG
+                       "osst: segment sizes: first %d, last %d bytes.\n",
+                          tb->sg[0].length, tb->sg[segs-1].length);
+       }
+#endif
+       tb->in_use = 0;
+       tb->dma = need_dma;
+       tb->buffer_size = got;
+       tb->writing = 0;
+       osst_buffers[osst_nbr_buffers++] = tb;
+
+       return tb;
+}
+
+
+/* Try to allocate a temporary enlarged tape buffer */
+static int enlarge_buffer(OSST_buffer *STbuffer, int new_size, int need_dma)
+{
+       int segs, nbr, max_segs, b_size, priority, order, got;
+
+       normalize_buffer(STbuffer);
+
+       max_segs = STbuffer->use_sg;
+       if (max_segs > osst_max_sg_segs)
+               max_segs = osst_max_sg_segs;
+       nbr = max_segs - STbuffer->sg_segs;
+       if (nbr <= 0)
+               return FALSE;
+
+       priority = GFP_KERNEL;
+       if (need_dma)
+               priority |= GFP_DMA;
+       for (b_size = PAGE_SIZE, order = 0;
+                b_size * nbr < new_size - STbuffer->buffer_size;
+                b_size *= 2, order++);
+
+       for (segs=STbuffer->sg_segs, got=STbuffer->buffer_size;
+            segs < max_segs && got < new_size; ) {
+               STbuffer->sg[segs].address =
+                         (unsigned char *)__get_free_pages(priority, order);
+               if (STbuffer->sg[segs].address == NULL) {
+                       if (new_size - got <= (max_segs - segs) * b_size / 2) {
+                               b_size /= 2;  /* Large enough for the rest of the buffers */
+                               order--;
+                               continue;
+                       }
+                       printk(KERN_NOTICE "osst: Failed to enlarge buffer to %d bytes.\n",
+                                               new_size);
+#if DEBUG
+                       STbuffer->buffer_size = got;
+#endif
+                       normalize_buffer(STbuffer);
+                       return FALSE;
+               }
+               STbuffer->sg[segs].alt_address = NULL;
+               STbuffer->sg[segs].length = b_size;
+               STbuffer->sg_segs += 1;
+               got += b_size;
+               STbuffer->buffer_size = got;
+               segs++;
+       }
+#if DEBUG
+       if (debugging) {
+               for (nbr=0; osst_buffers[nbr] != STbuffer && nbr < osst_nbr_buffers; nbr++);
+                       printk(OSST_DEB_MSG
+       "osst: Expanded tape buffer %d (%d bytes, %d->%d segments, dma: %d, a: %p).\n",
+                          nbr, got, STbuffer->orig_sg_segs, STbuffer->sg_segs, need_dma, STbuffer->b_data);
+                       printk(OSST_DEB_MSG
+                          "osst: segment sizes: first %d, last %d bytes.\n",
+                          STbuffer->sg[0].length, STbuffer->sg[segs-1].length);
+       }
+#endif
+
+       return TRUE;
+}
+
+
+/* Release the extra buffer */
+static void normalize_buffer(OSST_buffer *STbuffer)
+{
+  int i, order, b_size;
+
+       for (i=STbuffer->orig_sg_segs; i < STbuffer->sg_segs; i++) {
+
+               for (b_size = PAGE_SIZE, order = 0;
+                    b_size < STbuffer->sg[i].length;
+                    b_size *= 2, order++);
+
+               free_pages((unsigned long)STbuffer->sg[i].address, order);
+               STbuffer->buffer_size -= STbuffer->sg[i].length;
+       }
+#if DEBUG
+       if (debugging && STbuffer->orig_sg_segs < STbuffer->sg_segs)
+               printk(OSST_DEB_MSG "osst: Buffer at %p normalized to %d bytes (segs %d).\n",
+                            STbuffer->b_data, STbuffer->buffer_size, STbuffer->sg_segs);
+#endif
+       STbuffer->sg_segs = STbuffer->orig_sg_segs;
+}
+
+
+/* Move data from the user buffer to the tape buffer. Returns zero (success) or
+   negative error code. */
+static int append_to_buffer(const char *ubp, OSST_buffer *st_bp, int do_count)
+{
+       int i, cnt, res, offset;
+
+       for (i=0, offset=st_bp->buffer_bytes;
+            i < st_bp->sg_segs && offset >= st_bp->sg[i].length; i++)
+       offset -= st_bp->sg[i].length;
+       if (i == st_bp->sg_segs) {  /* Should never happen */
+               printk(KERN_WARNING "osst: Append_to_buffer offset overflow.\n");
+               return (-EIO);
+       }
+       for ( ; i < st_bp->sg_segs && do_count > 0; i++) {
+               cnt = st_bp->sg[i].length - offset < do_count ?
+                     st_bp->sg[i].length - offset : do_count;
+               res = copy_from_user(st_bp->sg[i].address + offset, ubp, cnt);
+               if (res)
+                       return (-EFAULT);
+               do_count -= cnt;
+               st_bp->buffer_bytes += cnt;
+               ubp += cnt;
+               offset = 0;
+       }
+       if (do_count) {  /* Should never happen */
+               printk(KERN_WARNING "osst: Append_to_buffer overflow (left %d).\n",
+                      do_count);
+               return (-EIO);
+       }
+       return 0;
+}
+
+
+/* Move data from the tape buffer to the user buffer. Returns zero (success) or
+   negative error code. */
+static int from_buffer(OSST_buffer *st_bp, char *ubp, int do_count)
+{
+       int i, cnt, res, offset;
+
+       for (i=0, offset=st_bp->read_pointer;
+            i < st_bp->sg_segs && offset >= st_bp->sg[i].length; i++)
+               offset -= st_bp->sg[i].length;
+       if (i == st_bp->sg_segs) {  /* Should never happen */
+               printk(KERN_WARNING "osst: From_buffer offset overflow.\n");
+               return (-EIO);
+       }
+       for ( ; i < st_bp->sg_segs && do_count > 0; i++) {
+               cnt = st_bp->sg[i].length - offset < do_count ?
+                     st_bp->sg[i].length - offset : do_count;
+               res = copy_to_user(ubp, st_bp->sg[i].address + offset, cnt);
+               if (res)
+                       return (-EFAULT);
+               do_count -= cnt;
+               st_bp->buffer_bytes -= cnt;
+               st_bp->read_pointer += cnt;
+               ubp += cnt;
+               offset = 0;
+       }
+       if (do_count) {  /* Should never happen */
+               printk(KERN_WARNING "osst: From_buffer overflow (left %d).\n", do_count);
+               return (-EIO);
+       }
+       return 0;
+}
+
+/* Sets the tail of the buffer after fill point to zero.
+   Returns zero (success) or negative error code.        */
+static int osst_zero_buffer_tail(OSST_buffer *st_bp)
+{
+       int     i, offset, do_count, cnt;
+
+       for (i = 0, offset = st_bp->buffer_bytes;
+            i < st_bp->sg_segs && offset >= st_bp->sg[i].length; i++)
+               offset -= st_bp->sg[i].length;
+       if (i == st_bp->sg_segs) {  /* Should never happen */
+               printk(KERN_WARNING "osst: Zero_buffer offset overflow.\n");
+               return (-EIO);
+       }
+       for (do_count = OS_DATA_SIZE - st_bp->read_pointer;
+            i < st_bp->sg_segs && do_count > 0; i++) {
+               cnt = st_bp->sg[i].length - offset < do_count ?
+                     st_bp->sg[i].length - offset : do_count ;
+               memset(st_bp->sg[i].address + offset, 0, cnt);
+               do_count -= cnt;
+               offset = 0;
+       }
+       if (do_count) {  /* Should never happen */
+               printk(KERN_WARNING "osst: Zero_buffer overflow (left %d).\n", do_count);
+               return (-EIO);
+       }
+       return 0;
+}
+
+/* Copy a osst 32K block of memory into the buffer.
+   Returns zero (success) or negative error code.  */
+static int osst_copy_to_buffer(OSST_buffer *st_bp, unsigned char *ptr)
+{
+       int     i, cnt, do_count = OS_DATA_SIZE;
+
+       for (i = 0; i < st_bp->sg_segs && do_count > 0; i++) {
+               cnt = st_bp->sg[i].length < do_count ?
+                     st_bp->sg[i].length : do_count ;
+               memcpy(st_bp->sg[i].address, ptr, cnt);
+               do_count -= cnt;
+               ptr      += cnt;
+       }
+       if (do_count || i != st_bp->sg_segs-1) {  /* Should never happen */
+               printk(KERN_WARNING "osst: Copy_to_buffer overflow (left %d at sg %d).\n",
+                                        do_count, i);
+               return (-EIO);
+       }
+       return 0;
+}
+
+/* Copy a osst 32K block of memory from the buffer.
+   Returns zero (success) or negative error code.  */
+static int osst_copy_from_buffer(OSST_buffer *st_bp, unsigned char *ptr)
+{
+       int     i, cnt, do_count = OS_DATA_SIZE;
+
+       for (i = 0; i < st_bp->sg_segs && do_count > 0; i++) {
+               cnt = st_bp->sg[i].length < do_count ?
+                     st_bp->sg[i].length : do_count ;
+               memcpy(ptr, st_bp->sg[i].address, cnt);
+               do_count -= cnt;
+               ptr      += cnt;
+       }
+       if (do_count || i != st_bp->sg_segs-1) {  /* Should never happen */
+               printk(KERN_WARNING "osst: Copy_from_buffer overflow (left %d at sg %d).\n",
+                                        do_count, i);
+               return (-EIO);
+       }
+       return 0;
+}
+
+\f
+/* Module housekeeping */
+
+#ifndef MODULE
+/* Set the boot options. Syntax: st=xxx,yyy
+   where xxx is buffer size in 1024 byte blocks and yyy is write threshold
+   in 1024 byte blocks. */
+__initfunc( void osst_setup(char *str, int *ints))
+{
+  if (ints[0] > 0 && ints[1] > 0)
+       osst_buffer_size = ints[1] * ST_KILOBYTE;
+  if (ints[0] > 1 && ints[2] > 0) {
+       osst_write_threshold = ints[2] * ST_KILOBYTE;
+       if (osst_write_threshold > osst_buffer_size)
+               osst_write_threshold = osst_buffer_size;
+  }
+  if (ints[0] > 2 && ints[3] > 0)
+       osst_max_buffers = ints[3];
+}
+#endif
+
+
+static struct file_operations osst_fops = {
+       read:           osst_read,
+       write:          osst_write,
+       ioctl:          osst_ioctl,
+       open:           os_scsi_tape_open,
+       flush:          os_scsi_tape_flush,
+       release:        os_scsi_tape_close,
+};
+
+static int osst_supports(Scsi_Device * SDp)
+{
+       struct  osst_support_data {
+               char *vendor;
+               char *model;
+               char *rev;
+               char *driver_hint; /* Name of the correct driver, NULL if unknown */
+       };
+
+static struct  osst_support_data support_list[] = {
+               /* {"XXX", "Yy-", "", NULL},  example */
+               SIGS_FROM_OSST,
+               {NULL, }};
+
+       struct  osst_support_data *rp;
+
+       /* We are willing to drive OnStream SC-x0 as well as the
+        *       * IDE, ParPort, FireWire, USB variants, if accessible by
+        *               * emulation layer (ide-scsi, usb-storage, ...) */
+
+       for (rp=&(support_list[0]); rp->vendor != NULL; rp++)
+               if (!strncmp(rp->vendor, SDp->vendor, strlen(rp->vendor)) &&
+                   !strncmp(rp->model, SDp->model, strlen(rp->model)) &&
+                   !strncmp(rp->rev, SDp->rev, strlen(rp->rev))) 
+                       return 1;
+       return 0;
+}
+
+static int osst_attach(Scsi_Device * SDp)
+{
+       OS_Scsi_Tape * tpnt;
+       ST_mode * STm;
+       ST_partstat * STps;
+       int i;
+#ifdef CONFIG_DEVFS_FS
+       int mode;
+#endif
+
+       if (SDp->type != TYPE_TAPE || !osst_supports(SDp))
+                return 1;
+
+       if (osst_template.nr_dev >= osst_template.dev_max) {
+                SDp->attached--;
+                return 1;
+       }
+       
+       /* find a free minor number */
+       for (i=0; os_scsi_tapes[i] && i<osst_template.dev_max; i++);
+       if(i >= osst_template.dev_max) panic ("Scsi_devices corrupt (osst)");
+
+       /* allocate a OS_Scsi_Tape for this device */
+       tpnt = (OS_Scsi_Tape *)kmalloc(sizeof(OS_Scsi_Tape), GFP_ATOMIC);
+       if (tpnt == NULL) {
+                SDp->attached--;
+                printk(KERN_ERR "osst: Can't allocate device descriptor.\n");
+                return 1;
+       }
+       memset(tpnt, 0, sizeof(OS_Scsi_Tape));
+       os_scsi_tapes[i] = tpnt;
+       tpnt->capacity = 0xfffff;
+
+       /* allocate a buffer for this device */
+       if (!new_tape_buffer(TRUE, TRUE)) 
+                printk(KERN_ERR "osst: Unable to allocate a tape buffer.\n");
+
+#ifdef CONFIG_DEVFS_FS
+       for (mode = 0; mode < ST_NBR_MODES; ++mode) {
+                char name[8];
+                static char *formats[ST_NBR_MODES] ={"", "l", "m", "a"};
+
+                /*  Rewind entry  */
+                sprintf (name, "mt%s", formats[mode]);
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+                tpnt->de_r[mode] =
+                       devfs_register (SDp->de, name, DEVFS_FL_DEFAULT,
+                          MAJOR_NR, i + (mode << 5),
+                          S_IFCHR | S_IRUGO | S_IWUGO,
+                          &osst_fops, NULL);
+# else
+                tpnt->de_r[mode] =
+                       devfs_register (SDp->de, name, 0, DEVFS_FL_DEFAULT,
+                          MAJOR_NR, i + (mode << 5),
+                          S_IFCHR | S_IRUGO | S_IWUGO,
+                          0, 0, &osst_fops, NULL);
+# endif                
+                /*  No-rewind entry  */
+                sprintf (name, "mt%sn", formats[mode]);
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+                tpnt->de_n[mode] =
+                       devfs_register (SDp->de, name, DEVFS_FL_DEFAULT,
+                          MAJOR_NR, i + (mode << 5) + 128,
+                          S_IFCHR | S_IRUGO | S_IWUGO,
+                          &osst_fops, NULL);
+# else         
+                tpnt->de_n[mode] =
+                       devfs_register (SDp->de, name, 0, DEVFS_FL_DEFAULT,
+                          MAJOR_NR, i + (mode << 5) + 128,
+                          S_IFCHR | S_IRUGO | S_IWUGO,
+                          0, 0, &osst_fops, NULL);
+# endif
+       }
+       devfs_register_tape (tpnt->de_r[0]);
+#endif
+
+       tpnt->device = SDp;
+       tpnt->devt = MKDEV(MAJOR_NR, i);
+       tpnt->dirty = 0;
+       tpnt->in_use = 0;
+       tpnt->drv_buffer = 1;  /* Try buffering if no mode sense */
+       tpnt->restr_dma = (SDp->host)->unchecked_isa_dma;
+       tpnt->density = 0;
+       tpnt->do_auto_lock = OSST_AUTO_LOCK;
+       tpnt->can_bsr = OSST_IN_FILE_POS;
+       tpnt->can_partitions = 0;
+       tpnt->two_fm = OSST_TWO_FM;
+       tpnt->fast_mteom = OSST_FAST_MTEOM;
+       tpnt->scsi2_logical = OSST_SCSI2LOGICAL; /* FIXME */
+       tpnt->write_threshold = osst_write_threshold;
+       tpnt->default_drvbuffer = 0xff; /* No forced buffering */
+       tpnt->partition = 0;
+       tpnt->new_partition = 0;
+       tpnt->nbr_partitions = 0;
+       tpnt->timeout = OSST_TIMEOUT;
+       tpnt->long_timeout = OSST_LONG_TIMEOUT;
+
+       /* Recognize OnStream tapes */
+       printk ("osst%i: Tape driver with OnStream support osst %s\nosst%i: %s\n",
+              i, osst_version, i, cvsid);
+       /* We don't need to test for OnStream, as this has been done in detect () */
+       tpnt->os_fw_rev = osst_parse_firmware_rev (SDp->rev);
+#if DEBUG
+       printk ("osst%i: OnStream tape drive recognized, Model %s\n", i, SDp->model);
+#endif
+       tpnt->omit_blklims = 1;
+
+       tpnt->poll = (strncmp(SDp->model, "DI-", 3) == 0) || OSST_FW_NEED_POLL(tpnt->os_fw_rev,SDp);
+       tpnt->logical_blk_in_buffer = 0;
+       tpnt->header_ok = 0;
+       tpnt->linux_media = 0;
+       tpnt->header_cache = NULL;
+
+       for (i=0; i < ST_NBR_MODES; i++) {
+               STm = &(tpnt->modes[i]);
+               STm->defined = FALSE;
+               STm->sysv = OSST_SYSV;
+               STm->defaults_for_writes = 0;
+               STm->do_async_writes = OSST_ASYNC_WRITES;
+               STm->do_buffer_writes = OSST_BUFFER_WRITES;
+               STm->do_read_ahead = OSST_READ_AHEAD;
+               STm->default_compression = ST_DONT_TOUCH;
+               STm->default_blksize = 32 * ST_KILOBYTE;  /* No forced size */
+               STm->default_density = (-1);  /* No forced density */
+       }
+
+       for (i=0; i < ST_NBR_PARTITIONS; i++) {
+               STps = &(tpnt->ps[i]);
+               STps->rw = ST_IDLE;
+               STps->eof = ST_NOEOF;
+               STps->at_sm = 0;
+               STps->last_block_valid = FALSE;
+               STps->drv_block = (-1);
+               STps->drv_file = (-1);
+       }
+
+       tpnt->current_mode = 0;
+       tpnt->modes[0].defined = TRUE;
+       tpnt->density_changed = tpnt->compression_changed = tpnt->blksize_changed = FALSE;
+       init_MUTEX(&tpnt->lock);
+
+       osst_template.nr_dev++;
+       return 0;
+};
+
+static int osst_detect(Scsi_Device * SDp)
+{
+       if (SDp->type != TYPE_TAPE) return 0;
+       if ( ! osst_supports(SDp) ) return 0;
+       
+       printk(KERN_WARNING
+               "Detected OnStream scsi tape osst%d at scsi%d, channel %d, id %d, lun %d\n",
+               osst_template.dev_noticed++,
+               SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+       return 1;
+}
+
+static int osst_registered = 0;
+
+/* Driver initialization (not __initfunc because may be called later) */
+static int osst_init()
+{
+  int i;
+
+  if (osst_template.dev_noticed == 0) return 0;
+
+  if(!osst_registered) {
+#ifdef CONFIG_DEVFS_FS
+       if (devfs_register_chrdev(MAJOR_NR,"osst",&osst_fops)) {
+#else
+       if (register_chrdev(MAJOR_NR,"osst",&osst_fops)) {
+#endif
+               printk(KERN_ERR "osst: Unable to get major %d for OnStream tapes\n",MAJOR_NR);
+               return 1;
+       }
+       osst_registered++;
+  }
+  
+  if (os_scsi_tapes) return 0;
+  osst_template.dev_max = OSST_MAX_TAPES;
+  if (osst_template.dev_max > 128 / ST_NBR_MODES)
+       printk(KERN_INFO "osst: Only %d tapes accessible.\n", 128 / ST_NBR_MODES);
+  os_scsi_tapes =
+       (OS_Scsi_Tape **)kmalloc(osst_template.dev_max * sizeof(OS_Scsi_Tape *),
+                                  GFP_ATOMIC);
+  if (os_scsi_tapes == NULL) {
+       printk(KERN_ERR "osst: Unable to allocate array for OnStream SCSI tapes.\n");
+#ifdef CONFIG_DEVFS_FS
+       devfs_unregister_chrdev(MAJOR_NR, "osst");
+#else
+       unregister_chrdev(MAJOR_NR, "osst");
+#endif
+       return 1;
+  }
+
+  for (i=0; i < osst_template.dev_max; ++i) os_scsi_tapes[i] = NULL;
+
+  /* Allocate the buffer pointers */
+  osst_buffers =
+       (OSST_buffer **)kmalloc(osst_template.dev_max * sizeof(OSST_buffer *),
+                                   GFP_ATOMIC);
+  if (osst_buffers == NULL) {
+       printk(KERN_ERR "osst: Unable to allocate tape buffer pointers.\n");
+#ifdef CONFIG_DEVFS_FS
+       devfs_unregister_chrdev(MAJOR_NR, "osst");
+#else
+       unregister_chrdev(MAJOR_NR, "osst");
+#endif
+       kfree(os_scsi_tapes);
+       return 1;
+  }
+  osst_nbr_buffers = 0;
+
+#if DEBUG
+  printk(OSST_DEB_MSG "osst: Buffer size %d bytes, write threshold %d bytes.\n",
+        osst_buffer_size, osst_write_threshold);
+#endif
+  return 0;
+}
+
+
+static void osst_detach(Scsi_Device * SDp)
+{
+  OS_Scsi_Tape * tpnt;
+  int i;
+#ifdef CONFIG_DEVFS_FS
+  int mode;
+#endif
+
+  for(i=0; i<osst_template.dev_max; i++) {
+       tpnt = os_scsi_tapes[i];
+       if(tpnt != NULL && tpnt->device == SDp) {
+               tpnt->device = NULL;
+#ifdef CONFIG_DEVFS_FS
+               for (mode = 0; mode < ST_NBR_MODES; ++mode) {
+         devfs_unregister (tpnt->de_r[mode]);
+         tpnt->de_r[mode] = NULL;
+         devfs_unregister (tpnt->de_n[mode]);
+         tpnt->de_n[mode] = NULL;
+               }
+#endif
+               kfree(tpnt);
+               os_scsi_tapes[i] = NULL;
+               SDp->attached--;
+               osst_template.nr_dev--;
+               osst_template.dev_noticed--;
+               return;
+       }
+  }
+  return;
+}
+
+
+static int __init init_osst(void) 
+{
+  int result;
+
+  if (buffer_kbs > 0)
+               osst_buffer_size = buffer_kbs * ST_KILOBYTE;
+  if (write_threshold_kbs > 0)
+               osst_write_threshold = write_threshold_kbs * ST_KILOBYTE;
+  if (osst_write_threshold > osst_buffer_size)
+               osst_write_threshold = osst_buffer_size;
+  if (max_buffers > 0)
+               osst_max_buffers = max_buffers;
+  if (max_sg_segs >= OSST_FIRST_SG)
+               osst_max_sg_segs = max_sg_segs;
+  printk(KERN_INFO "osst: bufsize %d, wrt %d, max buffers %d, s/g segs %d.\n",
+        osst_buffer_size, osst_write_threshold, osst_max_buffers, osst_max_sg_segs);
+//printk(OSST_DEB_MSG "osst: sizeof(header) = %d (%s)\n",sizeof(os_header_t),sizeof(os_header_t)==OS_DATA_SIZE?"ok":"error");
+  osst_template.module = THIS_MODULE;
+  result = scsi_register_module(MODULE_SCSI_DEV, &osst_template);
+  if (result)
+               return result;
+
+  return 0;
+}
+
+static void __exit exit_osst (void)
+{
+  int i;
+  OS_Scsi_Tape * STp;
+
+  scsi_unregister_module(MODULE_SCSI_DEV, &osst_template);
+#ifdef CONFIG_DEVFS_FS
+  devfs_unregister_chrdev(MAJOR_NR, "osst");
+#else
+  unregister_chrdev(MAJOR_NR, "osst");
+#endif
+  osst_registered--;
+  if(os_scsi_tapes != NULL) {
+       for (i=0; i < osst_template.dev_max; ++i) {
+               if ((STp = os_scsi_tapes[i])) {
+       if (STp->header_cache != NULL) vfree(STp->header_cache);
+       kfree(STp);
+               }
+       }
+       kfree(os_scsi_tapes);
+
+       if (osst_buffers != NULL) {
+               for (i=0; i < osst_nbr_buffers; i++)
+       if (osst_buffers[i] != NULL) {
+         osst_buffers[i]->orig_sg_segs = 0;
+         normalize_buffer(osst_buffers[i]);
+         kfree(osst_buffers[i]);
+       }
+
+               kfree(osst_buffers);
+       }
+  }
+  osst_template.dev_max = 0;
+  printk(KERN_INFO "osst: Unloaded.\n");
+}
+
+module_init(init_osst);
+module_exit(exit_osst);
diff --git a/drivers/scsi/osst.h b/drivers/scsi/osst.h
new file mode 100644 (file)
index 0000000..97d9f43
--- /dev/null
@@ -0,0 +1,634 @@
+/*
+ *     $Header: /home/cvsroot/Driver/osst.h,v 1.9 2000/10/08 03:09:43 riede Exp $
+ */
+
+#include <asm/byteorder.h>
+#include <linux/config.h>
+#ifdef CONFIG_DEVFS_FS
+#include <linux/devfs_fs_kernel.h>
+#endif
+
+/*     FIXME - rename and use the following two types or delete them!
+ *              and the types really should go to st.h anyway...
+ *     INQUIRY packet command - Data Format (From Table 6-8 of QIC-157C)
+ */
+typedef struct {
+       unsigned        device_type     :5;     /* Peripheral Device Type */
+       unsigned        reserved0_765   :3;     /* Peripheral Qualifier - Reserved */
+       unsigned        reserved1_6t0   :7;     /* Reserved */
+       unsigned        rmb             :1;     /* Removable Medium Bit */
+       unsigned        ansi_version    :3;     /* ANSI Version */
+       unsigned        ecma_version    :3;     /* ECMA Version */
+       unsigned        iso_version     :2;     /* ISO Version */
+       unsigned        response_format :4;     /* Response Data Format */
+       unsigned        reserved3_45    :2;     /* Reserved */
+       unsigned        reserved3_6     :1;     /* TrmIOP - Reserved */
+       unsigned        reserved3_7     :1;     /* AENC - Reserved */
+       u8              additional_length;      /* Additional Length (total_length-4) */
+       u8              rsv5, rsv6, rsv7;       /* Reserved */
+       u8              vendor_id[8];           /* Vendor Identification */
+       u8              product_id[16];         /* Product Identification */
+       u8              revision_level[4];      /* Revision Level */
+       u8              vendor_specific[20];    /* Vendor Specific - Optional */
+       u8              reserved56t95[40];      /* Reserved - Optional */
+                                               /* Additional information may be returned */
+} idetape_inquiry_result_t;
+
+/*
+ *     READ POSITION packet command - Data Format (From Table 6-57)
+ */
+typedef struct {
+       unsigned        reserved0_10    :2;     /* Reserved */
+       unsigned        bpu             :1;     /* Block Position Unknown */    
+       unsigned        reserved0_543   :3;     /* Reserved */
+       unsigned        eop             :1;     /* End Of Partition */
+       unsigned        bop             :1;     /* Beginning Of Partition */
+       u8              partition;              /* Partition Number */
+       u8              reserved2, reserved3;   /* Reserved */
+       u32             first_block;            /* First Block Location */
+       u32             last_block;             /* Last Block Location (Optional) */
+       u8              reserved12;             /* Reserved */
+       u8              blocks_in_buffer[3];    /* Blocks In Buffer - (Optional) */
+       u32             bytes_in_buffer;        /* Bytes In Buffer (Optional) */
+} idetape_read_position_result_t;
+
+/*
+ *      Follows structures which are related to the SELECT SENSE / MODE SENSE
+ *      packet commands. 
+ */
+#define COMPRESSION_PAGE           0x0f
+#define COMPRESSION_PAGE_LENGTH    16
+
+#define CAPABILITIES_PAGE          0x2a
+#define CAPABILITIES_PAGE_LENGTH   20
+
+#define TAPE_PARAMTR_PAGE          0x2b
+#define TAPE_PARAMTR_PAGE_LENGTH   16
+
+#define NUMBER_RETRIES_PAGE        0x2f
+#define NUMBER_RETRIES_PAGE_LENGTH 4
+
+#define BLOCK_SIZE_PAGE            0x30
+#define BLOCK_SIZE_PAGE_LENGTH     4
+
+#define BUFFER_FILLING_PAGE        0x33
+#define BUFFER_FILLING_PAGE_LENGTH 
+
+#define VENDOR_IDENT_PAGE          0x36
+#define VENDOR_IDENT_PAGE_LENGTH   8
+
+#define LOCATE_STATUS_PAGE         0x37
+#define LOCATE_STATUS_PAGE_LENGTH  0
+
+#define MODE_HEADER_LENGTH         4
+
+
+/*
+ *     REQUEST SENSE packet command result - Data Format.
+ */
+typedef struct {
+       unsigned        error_code      :7;     /* Current of deferred errors */
+       unsigned        valid           :1;     /* The information field conforms to QIC-157C */
+       u8              reserved1       :8;     /* Segment Number - Reserved */
+       unsigned        sense_key       :4;     /* Sense Key */
+       unsigned        reserved2_4     :1;     /* Reserved */
+       unsigned        ili             :1;     /* Incorrect Length Indicator */
+       unsigned        eom             :1;     /* End Of Medium */
+       unsigned        filemark        :1;     /* Filemark */
+       u32             information __attribute__ ((packed));
+       u8              asl;                    /* Additional sense length (n-7) */
+       u32             command_specific;       /* Additional command specific information */
+       u8              asc;                    /* Additional Sense Code */
+       u8              ascq;                   /* Additional Sense Code Qualifier */
+       u8              replaceable_unit_code;  /* Field Replaceable Unit Code */
+       unsigned        sk_specific1    :7;     /* Sense Key Specific */
+       unsigned        sksv            :1;     /* Sense Key Specific information is valid */
+       u8              sk_specific2;           /* Sense Key Specific */
+       u8              sk_specific3;           /* Sense Key Specific */
+       u8              pad[2];                 /* Padding to 20 bytes */
+} idetape_request_sense_result_t;
+
+/*
+ *      Mode Parameter Header for the MODE SENSE packet command
+ */
+typedef struct {
+        u8              mode_data_length;       /* Length of the following data transfer */
+        u8              medium_type;            /* Medium Type */
+        u8              dsp;                    /* Device Specific Parameter */
+        u8              bdl;                    /* Block Descriptor Length */
+} osst_mode_parameter_header_t;
+
+/*
+ *      Mode Parameter Block Descriptor the MODE SENSE packet command
+ *
+ *      Support for block descriptors is optional.
+ */
+typedef struct {
+        u8              density_code;           /* Medium density code */
+        u8              blocks[3];              /* Number of blocks */
+        u8              reserved4;              /* Reserved */
+        u8              length[3];              /* Block Length */
+} osst_parameter_block_descriptor_t;
+
+/*
+ *      The Data Compression Page, as returned by the MODE SENSE packet command.
+ */
+typedef struct {
+#if   defined(__BIG_ENDIAN_BITFIELD)
+        unsigned        ps              :1;
+        unsigned        reserved0       :1;     /* Reserved */
+       unsigned        page_code       :6;     /* Page Code - Should be 0xf */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+       unsigned        page_code       :6;     /* Page Code - Should be 0xf */
+        unsigned        reserved0       :1;     /* Reserved */
+        unsigned        ps              :1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+        u8              page_length;            /* Page Length - Should be 14 */
+#if   defined(__BIG_ENDIAN_BITFIELD)
+        unsigned        dce             :1;     /* Data Compression Enable */
+        unsigned        dcc             :1;     /* Data Compression Capable */
+       unsigned        reserved2       :6;     /* Reserved */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+       unsigned        reserved2       :6;     /* Reserved */
+        unsigned        dcc             :1;     /* Data Compression Capable */
+        unsigned        dce             :1;     /* Data Compression Enable */
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+#if   defined(__BIG_ENDIAN_BITFIELD)
+        unsigned        dde             :1;     /* Data Decompression Enable */
+        unsigned        red             :2;     /* Report Exception on Decompression */
+       unsigned        reserved3       :5;     /* Reserved */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+       unsigned        reserved3       :5;     /* Reserved */
+        unsigned        red             :2;     /* Report Exception on Decompression */
+        unsigned        dde             :1;     /* Data Decompression Enable */
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+        u32             ca;                     /* Compression Algorithm */
+        u32             da;                     /* Decompression Algorithm */
+        u8              reserved[4];            /* Reserved */
+} osst_data_compression_page_t;
+
+/*
+ *      The Medium Partition Page, as returned by the MODE SENSE packet command.
+ */
+typedef struct {
+#if   defined(__BIG_ENDIAN_BITFIELD)
+        unsigned        ps              :1;
+        unsigned        reserved1_6     :1;     /* Reserved */
+       unsigned        page_code       :6;     /* Page Code - Should be 0x11 */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+       unsigned        page_code       :6;     /* Page Code - Should be 0x11 */
+        unsigned        reserved1_6     :1;     /* Reserved */
+        unsigned        ps              :1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+        u8              page_length;            /* Page Length - Should be 6 */
+        u8              map;                    /* Maximum Additional Partitions - Should be 0 */
+        u8              apd;                    /* Additional Partitions Defined - Should be 0 */
+#if   defined(__BIG_ENDIAN_BITFIELD)
+        unsigned        fdp             :1;     /* Fixed Data Partitions */
+        unsigned        sdp             :1;     /* Should be 0 */
+        unsigned        idp             :1;     /* Should be 0 */
+        unsigned        psum            :2;     /* Should be 0 */
+       unsigned        reserved4_012   :3;     /* Reserved */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+       unsigned        reserved4_012   :3;     /* Reserved */
+        unsigned        psum            :2;     /* Should be 0 */
+        unsigned        idp             :1;     /* Should be 0 */
+        unsigned        sdp             :1;     /* Should be 0 */
+        unsigned        fdp             :1;     /* Fixed Data Partitions */
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+        u8              mfr;                    /* Medium Format Recognition */
+        u8              reserved[2];            /* Reserved */
+} osst_medium_partition_page_t;
+
+/*
+ *      Capabilities and Mechanical Status Page
+ */
+typedef struct {
+#if   defined(__BIG_ENDIAN_BITFIELD)
+        unsigned        reserved1_67    :2;
+       unsigned        page_code       :6;     /* Page code - Should be 0x2a */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+       unsigned        page_code       :6;     /* Page code - Should be 0x2a */
+        unsigned        reserved1_67    :2;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+        u8              page_length;            /* Page Length - Should be 0x12 */
+        u8              reserved2, reserved3;
+#if   defined(__BIG_ENDIAN_BITFIELD)
+        unsigned        reserved4_67    :2;
+        unsigned        sprev           :1;     /* Supports SPACE in the reverse direction */
+        unsigned        reserved4_1234  :4;
+       unsigned        ro              :1;     /* Read Only Mode */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+       unsigned        ro              :1;     /* Read Only Mode */
+        unsigned        reserved4_1234  :4;
+        unsigned        sprev           :1;     /* Supports SPACE in the reverse direction */
+        unsigned        reserved4_67    :2;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+#if   defined(__BIG_ENDIAN_BITFIELD)
+        unsigned        reserved5_67    :2;
+        unsigned        qfa             :1;     /* Supports the QFA two partition formats */
+        unsigned        reserved5_4     :1;
+        unsigned        efmt            :1;     /* Supports ERASE command initiated formatting */
+       unsigned        reserved5_012   :3;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+       unsigned        reserved5_012   :3;
+        unsigned        efmt            :1;     /* Supports ERASE command initiated formatting */
+        unsigned        reserved5_4     :1;
+        unsigned        qfa             :1;     /* Supports the QFA two partition formats */
+        unsigned        reserved5_67    :2;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+#if   defined(__BIG_ENDIAN_BITFIELD)
+        unsigned        cmprs           :1;     /* Supports data compression */
+        unsigned        ecc             :1;     /* Supports error correction */
+       unsigned        reserved6_45    :2;     /* Reserved */  
+        unsigned        eject           :1;     /* The device can eject the volume */
+        unsigned        prevent         :1;     /* The device defaults in the prevent state after power up */
+        unsigned        locked          :1;     /* The volume is locked */
+       unsigned        lock            :1;     /* Supports locking the volume */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+       unsigned        lock            :1;     /* Supports locking the volume */
+        unsigned        locked          :1;     /* The volume is locked */
+        unsigned        prevent         :1;     /* The device defaults in the prevent state after power up */
+        unsigned        eject           :1;     /* The device can eject the volume */
+       unsigned        reserved6_45    :2;     /* Reserved */  
+        unsigned        ecc             :1;     /* Supports error correction */
+        unsigned        cmprs           :1;     /* Supports data compression */
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+#if   defined(__BIG_ENDIAN_BITFIELD)
+        unsigned        blk32768        :1;     /* slowb - the device restricts the byte count for PIO */
+                                                /* transfers for slow buffer memory ??? */
+                                                /* Also 32768 block size in some cases */
+        unsigned        reserved7_3_6   :4;
+        unsigned        blk1024         :1;     /* Supports 1024 bytes block size */
+        unsigned        blk512          :1;     /* Supports 512 bytes block size */
+       unsigned        reserved7_0     :1;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+       unsigned        reserved7_0     :1;
+        unsigned        blk512          :1;     /* Supports 512 bytes block size */
+        unsigned        blk1024         :1;     /* Supports 1024 bytes block size */
+        unsigned        reserved7_3_6   :4;
+        unsigned        blk32768        :1;     /* slowb - the device restricts the byte count for PIO */
+                                                /* transfers for slow buffer memory ??? */
+                                                /* Also 32768 block size in some cases */
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+        u16             max_speed;              /* Maximum speed supported in KBps */
+        u8              reserved10, reserved11;
+        u16             ctl;                    /* Continuous Transfer Limit in blocks */
+        u16             speed;                  /* Current Speed, in KBps */
+        u16             buffer_size;            /* Buffer Size, in 512 bytes */
+        u8              reserved18, reserved19;
+} osst_capabilities_page_t;
+
+/*
+ *      Block Size Page
+ */
+typedef struct {
+#if   defined(__BIG_ENDIAN_BITFIELD)
+        unsigned        ps              :1;
+        unsigned        reserved1_6     :1;
+       unsigned        page_code       :6;     /* Page code - Should be 0x30 */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+       unsigned        page_code       :6;     /* Page code - Should be 0x30 */
+        unsigned        reserved1_6     :1;
+        unsigned        ps              :1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+        u8              page_length;            /* Page Length - Should be 2 */
+        u8              reserved2;
+#if   defined(__BIG_ENDIAN_BITFIELD)
+        unsigned        one             :1;
+        unsigned        reserved2_6     :1;
+        unsigned        record32_5      :1;
+        unsigned        record32        :1;
+        unsigned        reserved2_23    :2;
+        unsigned        play32_5        :1;
+       unsigned        play32          :1;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+       unsigned        play32          :1;
+        unsigned        play32_5        :1;
+        unsigned        reserved2_23    :2;
+        unsigned        record32        :1;
+        unsigned        record32_5      :1;
+        unsigned        reserved2_6     :1;
+        unsigned        one             :1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+} osst_block_size_page_t;
+
+/*
+ *     Tape Parameters Page
+ */
+typedef struct {
+#if   defined(__BIG_ENDIAN_BITFIELD)
+        unsigned        ps              :1;
+        unsigned        reserved1_6     :1;
+       unsigned        page_code       :6;     /* Page code - Should be 0x2b */
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+       unsigned        page_code       :6;     /* Page code - Should be 0x2b */
+        unsigned        reserved1_6     :1;
+        unsigned        ps              :1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+       u8              reserved2;
+       u8              density;
+       u8              reserved3,reserved4;
+       u16             segtrk;
+       u16             trks;
+       u8              reserved5,reserved6,reserved7,reserved8,reserved9,reserved10;
+} osst_tape_paramtr_page_t;
+
+/* OnStream definitions */
+
+#define OS_CONFIG_PARTITION     (0xff)
+#define OS_DATA_PARTITION       (0)
+#define OS_PARTITION_VERSION    (1)
+
+/*
+ * partition
+ */
+typedef struct os_partition_s {
+        __u8    partition_num;
+        __u8    par_desc_ver;
+        __u16   wrt_pass_cntr;
+        __u32   first_frame_ppos;
+        __u32   last_frame_ppos;
+        __u32   eod_frame_ppos;
+} os_partition_t;
+
+/*
+ * DAT entry
+ */
+typedef struct os_dat_entry_s {
+        __u32   blk_sz;
+        __u16   blk_cnt;
+        __u8    flags;
+        __u8    reserved;
+} os_dat_entry_t;
+
+/*
+ * DAT
+ */
+#define OS_DAT_FLAGS_DATA       (0xc)
+#define OS_DAT_FLAGS_MARK       (0x1)
+
+typedef struct os_dat_s {
+        __u8            dat_sz;
+        __u8            reserved1;
+        __u8            entry_cnt;
+        __u8            reserved3;
+        os_dat_entry_t  dat_list[16];
+} os_dat_t;
+
+/*
+ * Frame types
+ */
+#define OS_FRAME_TYPE_FILL      (0)
+#define OS_FRAME_TYPE_EOD       (1 << 0)
+#define OS_FRAME_TYPE_MARKER    (1 << 1)
+#define OS_FRAME_TYPE_HEADER    (1 << 3)
+#define OS_FRAME_TYPE_DATA      (1 << 7)
+
+/*
+ * AUX
+ */
+typedef struct os_aux_s {
+        __u32           format_id;              /* hardware compability AUX is based on */
+        char            application_sig[4];     /* driver used to write this media */
+        __u32           hdwr;                   /* reserved */
+        __u32           update_frame_cntr;      /* for configuration frame */
+        __u8            frame_type;
+        __u8            frame_type_reserved;
+        __u8            reserved_18_19[2];
+        os_partition_t  partition;
+        __u8            reserved_36_43[8];
+        __u32           frame_seq_num;
+        __u32           logical_blk_num_high;
+        __u32           logical_blk_num;
+        os_dat_t        dat;
+        __u8            reserved188_191[4];
+        __u32           filemark_cnt;
+        __u32           phys_fm;
+        __u32           last_mark_ppos;
+        __u8            reserved204_223[20];
+
+        /*
+         * __u8         app_specific[32];
+         *
+         * Linux specific fields:
+         */
+         __u32          next_mark_ppos;         /* when known, points to next marker */
+         __u8           linux_specific[28];
+
+        __u8            reserved_256_511[256];
+} os_aux_t;
+
+#define OS_FM_TAB_MAX 1024
+
+typedef struct os_fm_tab_s {
+       __u8            fm_part_num;
+       __u8            reserved_1;
+       __u8            fm_tab_ent_sz;
+       __u8            reserved_3;
+       __u16           fm_tab_ent_cnt;
+       __u8            reserved6_15[10];
+       __u32           fm_tab_ent[OS_FM_TAB_MAX];
+} os_fm_tab_t;
+
+typedef struct os_ext_trk_ey_s {
+       __u8            et_part_num;
+       __u8            fmt;
+       __u16           fm_tab_off;
+       __u8            reserved4_7[4];
+       __u32           last_hlb_hi;
+       __u32           last_hlb;
+       __u32           last_pp;
+       __u8            reserved20_31[12];
+} os_ext_trk_ey_t;
+
+typedef struct os_ext_trk_tb_s {
+       __u8            nr_stream_part;
+       __u8            reserved_1;
+       __u8            et_ent_sz;
+       __u8            reserved3_15[13];
+       os_ext_trk_ey_t dat_ext_trk_ey;
+       os_ext_trk_ey_t qfa_ext_trk_ey;
+} os_ext_trk_tb_t;
+
+typedef struct os_header_s {
+        char            ident_str[8];
+        __u8            major_rev;
+        __u8            minor_rev;
+       __u16           ext_trk_tb_off;
+        __u8            reserved12_15[4];
+        __u8            pt_par_num;
+        __u8            pt_reserved1_3[3];
+        os_partition_t  partition[16];
+       __u32           cfg_col_width;
+       __u32           dat_col_width;
+       __u32           qfa_col_width;
+       __u8            cartridge[16];
+       __u8            reserved304_511[208];
+       __u32           old_filemark_list[16680/4];             /* in ADR 1.4 __u8 track_table[16680] */
+       os_ext_trk_tb_t ext_track_tb;
+       __u8            reserved17272_17735[464];
+       os_fm_tab_t     dat_fm_tab;
+       os_fm_tab_t     qfa_fm_tab;
+       __u8            reserved25960_32767[6808];
+} os_header_t;
+
+
+/*
+ * OnStream ADRL frame
+ */
+#define OS_FRAME_SIZE   (32 * 1024 + 512)
+#define OS_DATA_SIZE    (32 * 1024)
+#define OS_AUX_SIZE     (512)
+//#define OSST_MAX_SG      2
+
+/* The tape buffer descriptor. */
+typedef struct {
+  unsigned char in_use;
+  unsigned char dma;   /* DMA-able buffer */
+  int buffer_size;
+  int buffer_blocks;
+  int buffer_bytes;
+  int read_pointer;
+  int writing;
+  int midlevel_result;
+  int syscall_result;
+  Scsi_Request *last_SRpnt;
+  unsigned char *b_data;
+  os_aux_t *aux;               /* onstream AUX structure at end of each block */
+  unsigned short use_sg;       /* zero or number of segments for this adapter */
+  unsigned short sg_segs;      /* total number of allocated segments */
+  unsigned short orig_sg_segs; /* number of segments allocated at first try */
+  struct scatterlist sg[1];    /* MUST BE last item */
+} OSST_buffer;
+
+/* The tape drive descriptor */
+typedef struct {
+  kdev_t devt;
+  unsigned capacity;
+  Scsi_Device* device;
+  struct semaphore lock;       /* for serialization */
+  struct semaphore sem;        /* for SCSI commands */
+  OSST_buffer * buffer;
+
+  /* Drive characteristics */
+  unsigned char omit_blklims;
+  unsigned char do_auto_lock;
+  unsigned char can_bsr;
+  unsigned char can_partitions;
+  unsigned char two_fm;
+  unsigned char fast_mteom;
+  unsigned char restr_dma;
+  unsigned char scsi2_logical;
+  unsigned char default_drvbuffer;  /* 0xff = don't touch, value 3 bits */
+  int write_threshold;
+  int timeout;                 /* timeout for normal commands */
+  int long_timeout;            /* timeout for commands known to take long time*/
+
+  /* Mode characteristics */
+  ST_mode modes[ST_NBR_MODES];
+  int current_mode;
+#ifdef CONFIG_DEVFS_FS
+  devfs_handle_t de_r[ST_NBR_MODES];  /*  Rewind entries     */
+  devfs_handle_t de_n[ST_NBR_MODES];  /*  No-rewind entries  */
+#endif
+
+  /* Status variables */
+  int partition;
+  int new_partition;
+  int nbr_partitions;    /* zero until partition support enabled */
+  ST_partstat ps[ST_NBR_PARTITIONS];
+  unsigned char dirty;
+  unsigned char ready;
+  unsigned char write_prot;
+  unsigned char drv_write_prot;
+  unsigned char in_use;
+  unsigned char blksize_changed;
+  unsigned char density_changed;
+  unsigned char compression_changed;
+  unsigned char drv_buffer;
+  unsigned char density;
+  unsigned char door_locked;
+  unsigned char rew_at_close;
+  unsigned char inited;
+  int block_size;
+  int min_block;
+  int max_block;
+  int recover_count;            /* from tape opening */
+  int recover_erreg;            /* from last status call */
+  /*
+   * OnStream specific data
+   */
+  int     os_fw_rev;                          /* the firmware revision * 10000 */
+  unsigned char  raw;                          /* flag OnStream raw access (32.5KB block size) */
+  unsigned char  poll;                         /* flag that this drive needs polling (IDE|firmware) */
+  unsigned char  logical_blk_in_buffer;               /* flag that the block as per logical_blk_num
+                                               * has been read into STp->buffer and is valid */
+  int      logical_blk_num;                    /* logical block number */
+  unsigned first_frame_position;               /* physical frame to be transfered to/from host */
+  unsigned last_frame_position;                /* physical frame to be transferd to/from tape */
+  int      cur_frames;                         /* current number of frames in internal buffer */
+  int      max_frames;                         /* max number of frames in internal buffer */
+  char     application_sig[5];                 /* application signature */
+  unsigned char  fast_open;                    /* flag that reminds us we didn't check headers at open */
+  unsigned short wrt_pass_cntr;                /* write pass counter */
+  int      update_frame_cntr;                  /* update frame counter */
+  int      onstream_write_error;               /* write error recovery active */
+  int      header_ok;                          /* header frame verified ok */
+  int      linux_media;                        /* reading linux-specifc media */
+  int      linux_media_version;
+  os_header_t * header_cache;                 /* cache is kept for filemark positions */
+  int      filemark_cnt;
+  int      first_mark_ppos;
+  int      last_mark_ppos;
+  int      first_data_ppos;
+  int      eod_frame_ppos;
+  int      eod_frame_lfa;
+  int      write_type;                         /* used in write error recovery */
+  int      read_error_frame;                   /* used in read error recovery */
+  unsigned long cmd_start_time;
+  unsigned long max_cmd_time;
+
+#if DEBUG
+  unsigned char write_pending;
+  int nbr_finished;
+  int nbr_waits;
+  unsigned char last_cmnd[6];
+  unsigned char last_sense[16];
+#endif
+} OS_Scsi_Tape;
+
+/* Values of write_type */
+#define OS_WRITE_DATA      0
+#define OS_WRITE_EOD       1
+#define OS_WRITE_NEW_MARK  2
+#define OS_WRITE_LAST_MARK 3
+#define OS_WRITE_HEADER    4
+#define OS_WRITE_FILLER    5
+
diff --git a/drivers/scsi/osst_detect.h b/drivers/scsi/osst_detect.h
new file mode 100644 (file)
index 0000000..21717d0
--- /dev/null
@@ -0,0 +1,6 @@
+#define SIGS_FROM_OSST \
+       {"OnStream", "SC-", "", "osst"}, \
+       {"OnStream", "DI-", "", "osst"}, \
+       {"OnStream", "DP-", "", "osst"}, \
+       {"OnStream", "FW-", "", "osst"}, \
+       {"OnStream", "USB", "", "osst"}
diff --git a/drivers/scsi/osst_options.h b/drivers/scsi/osst_options.h
new file mode 100644 (file)
index 0000000..7b48e10
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+   The compile-time configurable defaults for the Linux SCSI tape driver.
+
+   Copyright 1995 Kai Makisara.
+   
+   Last modified: Wed Sep  2 21:24:07 1998 by root@home
+   
+   Changed (and renamed) for OnStream SCSI drives garloff@suse.de
+   2000-06-21
+
+   $Header: /home/cvsroot/Driver/osst_options.h,v 1.4 2000/06/26 01:44:01 riede Exp $
+*/
+
+#ifndef _OSST_OPTIONS_H
+#define _OSST_OPTIONS_H
+
+/* The minimum limit for the number of SCSI tape devices is determined by
+   OSST_MAX_TAPES. If the number of tape devices and the "slack" defined by
+   OSST_EXTRA_DEVS exceeds OSST_MAX_TAPES, the large number is used. */
+#define OSST_MAX_TAPES 4
+
+/* If OSST_IN_FILE_POS is nonzero, the driver positions the tape after the
+   record been read by the user program even if the tape has moved further
+   because of buffered reads. Should be set to zero to support also drives
+   that can't space backwards over records. NOTE: The tape will be
+   spaced backwards over an "accidentally" crossed filemark in any case. */
+#define OSST_IN_FILE_POS 0
+
+/* The tape driver buffer size in kilobytes. */
+/* Don't change, as this is the HW blocksize */
+#define OSST_BUFFER_BLOCKS 32
+
+/* The number of kilobytes of data in the buffer that triggers an
+   asynchronous write in fixed block mode. See also OSST_ASYNC_WRITES
+   below. */
+#define OSST_WRITE_THRESHOLD_BLOCKS 30
+
+/* The maximum number of tape buffers the driver allocates. The number
+   is also constrained by the number of drives detected. Determines the
+   maximum number of concurrently active tape drives. */
+#define OSST_MAX_BUFFERS OSST_MAX_TAPES 
+
+/* Maximum number of scatter/gather segments */
+/* Fit one buffer in pages and add one for the AUX header */
+#define OSST_MAX_SG      (((OSST_BUFFER_BLOCKS*1024) / PAGE_SIZE) + 1)
+
+/* The number of scatter/gather segments to allocate at first try (must be
+   smaller or equal to the maximum). */
+#define OSST_FIRST_SG    ((OSST_BUFFER_BLOCKS*1024) / PAGE_SIZE)
+
+/* The size of the first scatter/gather segments (determines the maximum block
+   size for SCSI adapters not supporting scatter/gather). The default is set
+   to try to allocate the buffer as one chunk. */
+#define OSST_FIRST_ORDER  5
+
+
+/* The following lines define defaults for properties that can be set
+   separately for each drive using the MTSTOPTIONS ioctl. */
+
+/* If OSST_TWO_FM is non-zero, the driver writes two filemarks after a
+   file being written. Some drives can't handle two filemarks at the
+   end of data. */
+#define OSST_TWO_FM 0
+
+/* If OSST_BUFFER_WRITES is non-zero, writes in fixed block mode are
+   buffered until the driver buffer is full or asynchronous write is
+   triggered. May make detection of End-Of-Medium early enough fail. */
+#define OSST_BUFFER_WRITES 1
+
+/* If OSST_ASYNC_WRITES is non-zero, the SCSI write command may be started
+   without waiting for it to finish. May cause problems in multiple
+   tape backups. */
+#define OSST_ASYNC_WRITES 1
+
+/* If OSST_READ_AHEAD is non-zero, blocks are read ahead in fixed block
+   mode. */
+#define OSST_READ_AHEAD 1
+
+/* If OSST_AUTO_LOCK is non-zero, the drive door is locked at the first
+   read or write command after the device is opened. The door is opened
+   when the device is closed. */
+#define OSST_AUTO_LOCK 0
+
+/* If OSST_FAST_MTEOM is non-zero, the MTEOM ioctl is done using the
+   direct SCSI command. The file number status is lost but this method
+   is fast with some drives. Otherwise MTEOM is done by spacing over
+   files and the file number status is retained. */
+#define OSST_FAST_MTEOM 0
+
+/* If OSST_SCSI2LOGICAL is nonzero, the logical block addresses are used for
+   MTIOCPOS and MTSEEK by default. Vendor addresses are used if OSST_SCSI2LOGICAL
+   is zero. */
+#define OSST_SCSI2LOGICAL 0
+
+/* If OSST_SYSV is non-zero, the tape behaves according to the SYS V semantics.
+   The default is BSD semantics. */
+#define OSST_SYSV 0
+
+
+#endif
index 88608c5f54a8a52dd7364bfa6dcdcac764ad1eeb..2e4dbf1e38f7c8a62159545059d052929e7a0593 100644 (file)
@@ -175,7 +175,7 @@ static int update_partition(Scsi_Tape *);
 static int st_int_ioctl(Scsi_Tape *, unsigned int, unsigned long);
 
 \f
-/* #include "osst_detect.h" */
+#include "osst_detect.h"
 #ifndef SIGS_FROM_OSST
 #define SIGS_FROM_OSST \
        {"OnStream", "SC-", "", "osst"}, \
index c4d2c50cec90f300dc108438f79e6c737f29ceed..20403413190cfbb3bad1e9bb9cecf72630c8a67c 100644 (file)
@@ -507,21 +507,19 @@ static void i810_rec_setup(struct i810_state *state)
 extern __inline__ unsigned i810_get_dma_addr(struct i810_state *state)
 {
        struct dmabuf *dmabuf = &state->dmabuf;
-       u32 offset;
+       unsigned int civ, offset;
        struct i810_channel *c = dmabuf->channel;
        
        if (!dmabuf->enable)
                return 0;
-       offset = inb(state->card->iobase+c->port+OFF_CIV);
-       offset++;
-       offset&=31;
-       /* Offset has to compensate for the fact we finished the segment
-          on the IRQ so we are at next_segment,0 */
-//     printk("BANK%d ", offset);
-       offset *= (dmabuf->dmasize/SG_LEN);
-//     printk("DMASZ=%d", dmabuf->dmasize);
-//     offset += 1024-(4*inw(state->card->iobase+c->port+OFF_PICB));
-//     printk("OFF%d ", offset);
+       do {
+               civ = inb(state->card->iobase+c->port+OFF_CIV);
+               offset = (civ + 1) * (dmabuf->dmasize/SG_LEN) -
+                             2 * inw(state->card->iobase+c->port+OFF_PICB);
+               /* CIV changed before we read PICB (very seldom) ?
+                * then PICB was rubbish, so try again */
+       } while (civ != inb(state->card->iobase+c->port+OFF_CIV));
+                
        return offset;
 }
 
@@ -730,10 +728,13 @@ static int prog_dmabuf(struct i810_state *state, unsigned rec)
                sg->control|=CON_IOC;
                sg++;
        }
+
        spin_lock_irqsave(&state->card->lock, flags);
+       outb(2, state->card->iobase+dmabuf->channel->port+OFF_CR);   /* reset DMA machine */
        outl(virt_to_bus(&dmabuf->channel->sg[0]), state->card->iobase+dmabuf->channel->port+OFF_BDBAR);
        outb(16, state->card->iobase+dmabuf->channel->port+OFF_LVI);
        outb(0, state->card->iobase+dmabuf->channel->port+OFF_CIV);
+
        if (rec) {
                i810_rec_setup(state);
        } else {
@@ -753,14 +754,10 @@ static int prog_dmabuf(struct i810_state *state, unsigned rec)
 
        return 0;
 }
-
-/* we are doing quantum mechanics here, the buffer can only be empty, half or full filled i.e.
-   |------------|------------|   or   |xxxxxxxxxxxx|------------|   or   |xxxxxxxxxxxx|xxxxxxxxxxxx|
-   but we almost always get this
-   |xxxxxx------|------------|   or   |xxxxxxxxxxxx|xxxxx-------|
-   so we have to clear the tail space to "silence"
-   |xxxxxx000000|------------|   or   |xxxxxxxxxxxx|xxxxxx000000|
-*/
+/*
+ * Clear the rest of the last i810 dma buffer, normally there is no rest
+ * because the OSS fragment size is the same as the size of this buffer.
+ */
 static void i810_clear_tail(struct i810_state *state)
 {
        struct dmabuf *dmabuf = &state->dmabuf;
@@ -773,14 +770,8 @@ static void i810_clear_tail(struct i810_state *state)
        swptr = dmabuf->swptr;
        spin_unlock_irqrestore(&state->card->lock, flags);
 
-       if (swptr == 0 || swptr == dmabuf->dmasize / 2 || swptr == dmabuf->dmasize)
-               return;
-
-       if (swptr < dmabuf->dmasize/2)
-               len = dmabuf->dmasize/2 - swptr;
-       else
-               len = dmabuf->dmasize - swptr;
-
+       len = swptr % (dmabuf->dmasize/SG_LEN);
+       
        memset(dmabuf->rawbuf + swptr, silence, len);
 
        spin_lock_irqsave(&state->card->lock, flags);
@@ -1188,11 +1179,16 @@ static unsigned int i810_poll(struct file *file, struct poll_table_struct *wait)
        unsigned long flags;
        unsigned int mask = 0;
 
-       if (file->f_mode & FMODE_WRITE)
+       if (file->f_mode & FMODE_WRITE) {
+               if (!dmabuf->ready && prog_dmabuf(state, 0))
+                       return 0;
                poll_wait(file, &dmabuf->wait, wait);
-       if (file->f_mode & FMODE_READ)
+       }
+       if (file->f_mode & FMODE_READ) {
+               if (!dmabuf->ready && prog_dmabuf(state, 1))
+                       return 0;
                poll_wait(file, &dmabuf->wait, wait);
-
+       }
        spin_lock_irqsave(&state->card->lock, flags);
        i810_update_ptr(state);
        if (file->f_mode & FMODE_READ) {
@@ -1531,7 +1527,6 @@ static int i810_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 static int i810_open(struct inode *inode, struct file *file)
 {
        int i = 0;
-       int minor = MINOR(inode->i_rdev);
        struct i810_card *card = devs;
        struct i810_state *state = NULL;
        struct dmabuf *dmabuf = NULL;
@@ -1779,18 +1774,42 @@ static int __init i810_ac97_init(struct i810_card *card)
                
                card->ac97_features = eid;
                                
+               /* Now check the codec for useful features to make up for
+                  the dumbness of the 810 hardware engine */
+
                if(!(eid&0x0001))
                        printk(KERN_WARNING "i810_audio: only 48Khz playback available.\n");
-                       
+               else
+               {
+                       /* Enable variable rate mode */
+                       i810_ac97_set(codec, AC97_EXTENDED_STATUS, 9);
+                       i810_ac97_set(codec,AC97_EXTENDED_STATUS,
+                               i810_ac97_get(codec, AC97_EXTENDED_STATUS)|0xE800);
+                       /* power up everything, modify this when implementing power saving */
+                       i810_ac97_set(codec, AC97_POWER_CONTROL,
+                               i810_ac97_get(codec, AC97_POWER_CONTROL) & ~0x7f00);
+                       /* wait for analog ready */
+                       for (i=10;
+                            i && ((i810_ac97_get(codec, AC97_POWER_CONTROL) & 0xf) != 0xf);
+                            i--)
+                       {
+                               current->state = TASK_UNINTERRUPTIBLE;
+                               schedule_timeout(HZ/20);
+                       }
+
+                       if(!(i810_ac97_get(codec, AC97_EXTENDED_STATUS)&1))
+                       {
+                               printk(KERN_WARNING "i810_audio: Codec refused to allow VRA, using 48Khz only.\n");
+                                       card->ac97_features&=~1;
+                       }
+               }
+               
                if ((codec->dev_mixer = register_sound_mixer(&i810_mixer_fops, -1)) < 0) {
                        printk(KERN_ERR "i810_audio: couldn't register mixer!\n");
                        kfree(codec);
                        break;
                }
 
-               /* Now check the codec for useful features to make up for 
-                  the dumbness of the 810 hardware engine */
-                  
                card->ac97_codec[num_ac97] = codec;
 
                /* if there is no secondary codec at all, don't probe any more */
index 31ec19d10e978745fb792a8e166289512d4a7630..11fe4201263d921951a4ccd424284b30f5f85614 100644 (file)
  * issues of the card, using the OTI-605 chip, have an MPU-401 compatable Midi
  * port. This port is configured differently to that of the OPTi audio chips.
  *
- * NOTE! If you want to set CD-ROM address and/or joystick enable, define
- *       MAD16_CONF in local.h as combination of the following bits:
- *
- *      0x01    - joystick disabled
- *
- *      CD-ROM type selection (select just one):
- *      0x00    - none
- *      0x02    - Sony 31A
- *      0x04    - Mitsumi
- *      0x06    - Panasonic (type "LaserMate", not "Sound Blaster")
- *      0x08    - Secondary IDE (address 0x170)
- *      0x0a    - Primary IDE (address 0x1F0)
- *      
- *      For example Mitsumi with joystick disabled = 0x04|0x01 = 0x05
- *      For example LaserMate (for use with sbpcd) plus joystick = 0x06
- *      
- *    MAD16_CDSEL:
- *      This defaults to CD I/O 0x340, no IRQ and DMA3 
- *      (DMA5 with Mitsumi or IDE). If you like to change these, define
- *      MAD16_CDSEL with the following bits:
- *
- *      CD-ROM port: 0x00=340, 0x40=330, 0x80=360 or 0xc0=320
- *      OPL4 select: 0x20=OPL4, 0x00=OPL3
- *      CD-ROM irq: 0x00=disabled, 0x04=IRQ5, 0x08=IRQ7, 0x0c=IRQ3, 0x10=IRQ9,
- *                  0x14=IRQ10 and 0x18=IRQ11.
- *
- *      CD-ROM DMA (Sony or Panasonic): 0x00=DMA3, 0x01=DMA2, 0x02=DMA1 or 0x03=disabled
- *   or
- *      CD-ROM DMA (Mitsumi or IDE):    0x00=DMA5, 0x01=DMA6, 0x02=DMA7 or 0x03=disabled
- *
- *      For use with sbpcd, address 0x340, set MAD16_CDSEL to 0x03 or 0x23.
- *
  *     Changes
  *     
  *     Alan Cox                Clean up, added module selections.
@@ -67,6 +35,8 @@
  *                                                             25-Nov-1999
  *     Christoph Hellwig       Adapted to module_init/module_exit.
  *     Arnaldo C. de Melo      got rid of attach_uart401       21-Sep-2000
+ *
+ *     Pavel Rabel             Clean up                           Nov-2000
  */
 
 #include <linux/config.h>
@@ -395,10 +365,6 @@ static int __init init_c930(struct address_info *hw_config)
 {
        unsigned char cfg = 0;
 
-#ifdef MAD16_CONF
-       cfg |= (0x0f & MAD16_CONF);
-#endif
-
        if(c931_detected)
        {
                /* Bit 0 has reversd meaning. Bits 1 and 2 sese
@@ -435,14 +401,9 @@ static int __init init_c930(struct address_info *hw_config)
        /* bit 2 of MC4 reverses it's meaning between the C930
           and the C931. */
        cfg = c931_detected ? 0x04 : 0x00;
-#ifdef MAD16_CDSEL
-       if(MAD16_CDSEL & 0x20)
-               mad_write(MC4_PORT, 0x62|cfg);  /* opl4 */
-       else
-               mad_write(MC4_PORT, 0x52|cfg);  /* opl3 */
-#else
+
        mad_write(MC4_PORT, 0x52|cfg);
-#endif
+
        mad_write(MC5_PORT, 0x3C);      /* Init it into mode2 */
        mad_write(MC6_PORT, 0x02);      /* Enable WSS, Disable MPU and SB */
        mad_write(MC7_PORT, 0xCB);
@@ -590,20 +551,9 @@ static int __init probe_mad16(struct address_info *hw_config)
         */
 
        tmp &= ~0x0f;
-#if defined(MAD16_CONF)
-       tmp |= ((MAD16_CONF) & 0x0f);   /* CD-ROM and joystick bits */
-#endif
        mad_write(MC1_PORT, tmp);
 
-#if defined(MAD16_CONF) && defined(MAD16_CDSEL)
-       tmp = MAD16_CDSEL;
-#else
        tmp = mad_read(MC2_PORT);
-#endif
-
-#ifdef MAD16_OPL4
-       tmp |= 0x20;            /* Enable OPL4 access */
-#endif
 
        mad_write(MC2_PORT, tmp);
        mad_write(MC3_PORT, 0xf0);      /* Disable SB */
@@ -718,13 +668,7 @@ static void __init attach_mad16(struct address_info *hw_config)
 static int __init probe_mad16_mpu(struct address_info *hw_config)
 {
        static int mpu_attached = 0;
-       static int valid_ports[] = {
-               0x330, 0x320, 0x310, 0x300
-       };
-       
-       static short valid_irqs[] = {9, 10, 5, 7};
        unsigned char tmp;
-       int i;                          /* A variable with secret power */
 
        if (!already_initialized)       /* The MSS port must be initialized first */
                return 0;
@@ -737,7 +681,6 @@ static int __init probe_mad16_mpu(struct address_info *hw_config)
        {
 
 #ifdef CONFIG_MAD16_OLDCARD
-               unsigned char   tmp;
 
                tmp = mad_read(MC3_PORT);
 
@@ -787,9 +730,6 @@ static int __init probe_mad16_mpu(struct address_info *hw_config)
                 * to set MPU register. TODO - add probing
                 */
 
-               
-               unsigned char tmp;
-
                tmp = mad_read(MC8_PORT);
 
                switch (hw_config->irq)
@@ -840,42 +780,50 @@ static int __init probe_mad16_mpu(struct address_info *hw_config)
        tmp = mad_read(MC6_PORT) & 0x83;
        tmp |= 0x80;            /* MPU-401 enable */
 
-/*
- * Set the MPU base bits
- */
+       /* Set the MPU base bits */
 
-       for (i = 0; i < 5; i++)
+       switch (hw_config->io_base)
        {
-               if (i > 3)      /* Out of array bounds */
-               {
-                       printk(KERN_ERR "MAD16 / Mozart: Invalid MIDI port 0x%x\n", hw_config->io_base);
-                       return 0;
-               }
-               if (valid_ports[i] == hw_config->io_base)
-               {
-                       tmp |= i << 5;
+               case 0x300:
+                       tmp |= 0x60;
                        break;
-               }
+               case 0x310:
+                       tmp |= 0x40;
+                       break;
+               case 0x320:
+                       tmp |= 0x20;
+                       break;
+               case 0x330:
+                       tmp |= 0x00;
+                       break;
+               default:
+                       printk(KERN_ERR "MAD16: Invalid MIDI port 0x%x\n", hw_config->io_base);
+                       return 0;
        }
 
-/*
- * Set the MPU IRQ bits
- */
+       /* Set the MPU IRQ bits */
 
-       for (i = 0; i < 5; i++)
+       switch (hw_config->irq)
        {
-               if (i > 3)      /* Out of array bounds */
-               {
-                       printk(KERN_ERR "MAD16 / Mozart: Invalid MIDI IRQ %d\n", hw_config->irq);
-                       return 0;
-               }
-               if (valid_irqs[i] == hw_config->irq)
-               {
-                       tmp |= i << 3;
+               case 5:
+                       tmp |= 0x10;
+                       break;
+               case 7:
+                       tmp |= 0x18;
+                       break;
+               case 9:
+                       tmp |= 0x00;
+                       break;
+               case 10:
+                       tmp |= 0x08;
+                       break;
+               default:
+                       printk(KERN_ERR "MAD16: Invalid MIDI IRQ %d\n", hw_config->irq);
                        break;
-               }
        }
+                       
        mad_write(MC6_PORT, tmp);       /* Write MPU401 config */
+
 #ifndef CONFIG_MAD16_OLDCARD
 probe_401:
 #endif
index 7470ffb0349f23798f48979b4a4520b561f1ad72..f609d1e2b794375d1ae801a3cc0d7ec6bab37511 100644 (file)
@@ -1727,18 +1727,18 @@ static struct page * via_mm_nopage (struct vm_area_struct * vma,
 }
 
 
-#ifndef VM_RESERVE
+#ifndef VM_RESERVED
 static int via_mm_swapout (struct page *page, struct file *filp)
 {
        return 0;
 }
-#endif /* VM_RESERVE */
+#endif /* VM_RESERVED */
 
 
 struct vm_operations_struct via_mm_ops = {
        nopage:         via_mm_nopage,
 
-#ifndef VM_RESERVE
+#ifndef VM_RESERVED
        swapout:        via_mm_swapout,
 #endif
 };
@@ -1789,8 +1789,8 @@ static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma)
        vma->vm_ops = &via_mm_ops;
        vma->vm_private_data = card;
 
-#ifdef VM_RESERVE
-       vma->vm_flags |= VM_RESERVE;
+#ifdef VM_RESERVED
+       vma->vm_flags |= VM_RESERVED;
 #endif
 
        if (rd)
index 8aa7b3f06e019b81b53630f39f8ad3fdeb2973ce..601276bbc7c86ea81ce235976a2dfa884376da2d 100644 (file)
@@ -122,16 +122,17 @@ union bdflush_param {
                                  when trying to refill buffers. */
                int interval; /* jiffies delay between kupdate flushes */
                int age_buffer;  /* Time for normal buffer to age before we flush it */
-               int dummy1;    /* unused, was age_super */
+               int nfract_sync; /* Percentage of buffer cache dirty to 
+                                   activate bdflush synchronously */
                int dummy2;    /* unused */
                int dummy3;    /* unused */
        } b_un;
        unsigned int data[N_PARAM];
-} bdf_prm = {{40, 500, 64, 256, 5*HZ, 30*HZ, 5*HZ, 1884, 2}};
+} bdf_prm = {{40, 500, 64, 256, 5*HZ, 30*HZ, 80, 0, 0}};
 
 /* These are the min and max parameter values that we will allow to be assigned */
-int bdflush_min[N_PARAM] = {  0,  10,    5,   25,  0,   1*HZ,   1*HZ, 1, 1};
-int bdflush_max[N_PARAM] = {100,50000, 20000, 20000,600*HZ, 6000*HZ, 6000*HZ, 2047, 5};
+int bdflush_min[N_PARAM] = {  0,  10,    5,   25,  0,   1*HZ,   0, 0, 0};
+int bdflush_max[N_PARAM] = {100,50000, 20000, 20000,600*HZ, 6000*HZ, 100, 0, 0};
 
 /*
  * Rewrote the wait-routines to use the "new" wait-queue functionality,
@@ -337,9 +338,10 @@ int file_fsync(struct file *filp, struct dentry *dentry, int datasync)
 
        /* sync the superblock to buffers */
        sb = inode->i_sb;
-       wait_on_super(sb);
+       lock_super(sb);
        if (sb->s_op && sb->s_op->write_super)
                sb->s_op->write_super(sb);
+       unlock_super(sb);
 
        /* .. finally sync the buffers to disk */
        dev = inode->i_dev;
@@ -1032,9 +1034,9 @@ int balance_dirty_state(kdev_t dev)
        dirty = size_buffers_type[BUF_DIRTY] >> PAGE_SHIFT;
        tot = nr_free_buffer_pages();
 
-       dirty *= 200;
+       dirty *= 100;
        soft_dirty_limit = tot * bdf_prm.b_un.nfract;
-       hard_dirty_limit = soft_dirty_limit * 2;
+       hard_dirty_limit = tot * bdf_prm.b_un.nfract_sync;
 
        /* First, check for the "real" dirty limit. */
        if (dirty > soft_dirty_limit) {
index 6a096c0ff49e2bb0e25a9d61535063363fb40b21..898ab8178303da632f89f63f21d5564cbae31a9c 100644 (file)
@@ -269,6 +269,11 @@ static int ramfs_symlink(struct inode * dir, struct dentry *dentry, const char *
        return error;
 }
 
+static int ramfs_sync_file(struct file * file, struct dentry *dentry, int datasync)
+{
+       return 0;
+}
+
 static struct address_space_operations ramfs_aops = {
        readpage:       ramfs_readpage,
        writepage:      ramfs_writepage,
@@ -279,12 +284,14 @@ static struct address_space_operations ramfs_aops = {
 static struct file_operations ramfs_file_operations = {
        read:           generic_file_read,
        write:          generic_file_write,
-       mmap:           generic_file_mmap
+       mmap:           generic_file_mmap,
+       fsync:          ramfs_sync_file,
 };
 
 static struct file_operations ramfs_dir_operations = {
        read:           generic_read_dir,
        readdir:        dcache_readdir,
+       fsync:          ramfs_sync_file,
 };
 
 static struct inode_operations ramfs_dir_inode_operations = {
index 58813f17fc95b43216e853b7f1c7a571baa0cbc6..49f228f3f1be87f6c492fa8c6278d67d978b6573 100644 (file)
@@ -122,6 +122,9 @@ extern void release_thread(struct task_struct *);
 /* Create a kernel thread without removing it from tasklists.  */
 extern long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
 
+#define copy_segments(tsk, mm)         do { } while (0)
+#define release_segments(mm)           do { } while (0)
+
 unsigned long get_wchan(struct task_struct *p);
 
 /* See arch/alpha/kernel/ptrace.c for details.  */
index d1311d01479b72dc9074e4942b0af41052e19fff..537d7ec12aa648cea28c7a4abb65d173df28aea1 100644 (file)
@@ -106,6 +106,10 @@ struct task_struct;
 /* Free all resources held by a thread. */
 extern void release_thread(struct task_struct *);
 
+/* Copy and release all segment info associated with a VM */
+#define copy_segments(tsk, mm)         do { } while (0)
+#define release_segments(mm)           do { } while (0)
+
 unsigned long get_wchan(struct task_struct *p);
 
 #define THREAD_SIZE    (8192)
index 1b9b19301044cca3836bf3efa34d01e3e5ae0d2d..cdfb99b4d237a8a927dfa2c3fa2b2f22e109b8cb 100644 (file)
@@ -6,9 +6,11 @@
 #include <asm/atomic.h>
 #include <asm/pgalloc.h>
 
-/* Segment information */
-extern void destroy_context(struct mm_struct *);
-extern int init_new_context(struct task_struct *, struct mm_struct *);
+/*
+ * possibly do the LDT unload here?
+ */
+#define destroy_context(mm)            do { } while(0)
+#define init_new_context(tsk,mm)       0
 
 #ifdef CONFIG_SMP
 
index 2572459091457e6fc4ed8ae24c046e49ff81fa43..364bb475fe2fe74c41a5ec8c382a81b9b8f2233b 100644 (file)
@@ -427,6 +427,10 @@ extern void release_thread(struct task_struct *);
  */
 extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
+/* Copy and release all segment info associated with a VM */
+extern void copy_segments(struct task_struct *p, struct mm_struct * mm);
+extern void release_segments(struct mm_struct * mm);
+
 /*
  * Return saved PC of a blocked thread.
  */
index 0fb8f37ccd1483cdbd0f07ef296d127980135a84..2fb8e357f50a3a99e255708796dbf0224bcbb926 100644 (file)
@@ -375,6 +375,10 @@ struct task_struct;
  */
 extern int kernel_thread (int (*fn)(void *), void *arg, unsigned long flags);
 
+/* Copy and release all segment info associated with a VM */
+#define copy_segments(tsk, mm)                 do { } while (0)
+#define release_segments(mm)                   do { } while (0)
+
 /* Get wait channel for task P.  */
 extern unsigned long get_wchan (struct task_struct *p);
 
index 43af4d09c46cd89ec60c35244111e9c68457c44d..0ad99a7f8936d7f28a929d96275ec95fdd6ed808 100644 (file)
@@ -109,6 +109,9 @@ static inline void release_thread(struct task_struct *dead_task)
 
 extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
+#define copy_segments(tsk, mm)         do { } while (0)
+#define release_segments(mm)           do { } while (0)
+
 /*
  * Free current thread data structures etc..
  */
index d38127794433005cf72509a4ef088381ef4f9c9b..7e160259c67101c5c91ee49694dff339443d786b 100644 (file)
@@ -190,6 +190,10 @@ struct thread_struct {
 
 extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
+/* Copy and release all segment info associated with a VM */
+#define copy_segments(p, mm) do { } while(0)
+#define release_segments(mm) do { } while(0)
+
 /*
  * Return saved PC of a blocked thread.
  */
index 2d9b9df5b95e08f9e2aa588e14bef54ceda76cbe..8a3cad9f3e731ed5501375f5c8dea4c391fe2e7d 100644 (file)
@@ -236,6 +236,10 @@ struct thread_struct {
 
 extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
+/* Copy and release all segment info associated with a VM */
+#define copy_segments(p, mm) do { } while(0)
+#define release_segments(mm) do { } while(0)
+
 /*
  * Return saved PC of a blocked thread.
  */
index 08a4d900403e53c2d568611049881e89def78705..12ae777a5296bdd0c3a93a48a0dcd99f44ff453d 100644 (file)
@@ -310,6 +310,9 @@ struct task_struct;
 extern void release_thread(struct task_struct *);
 extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
+#define copy_segments(tsk, mm) do { } while (0)
+#define release_segments(mm)   do { } while (0)
+
 extern inline unsigned long get_wchan(struct task_struct *p)
 {
        return 0xdeadbeef; /* XXX */
index 5dfc547adeb45b7bb5f399ad16c4fd082d13e488..5908d7f32edc3b05af57a434fc7f0ce3c1211070 100644 (file)
@@ -673,6 +673,9 @@ static inline unsigned long thread_saved_pc(struct thread_struct *t)
        return (t->regs) ? t->regs->nip : 0;
 }
 
+#define copy_segments(tsk, mm)         do { } while (0)
+#define release_segments(mm)           do { } while (0)
+
 unsigned long get_wchan(struct task_struct *p);
 
 #define KSTK_EIP(tsk)  ((tsk)->thread.regs->nip)
index 3e87e4697792d3a1698d07320890841297d6843c..4629c822be66d87282e956196cee3a73b0d45aa5 100644 (file)
@@ -124,6 +124,10 @@ struct mm_struct;
 extern void release_thread(struct task_struct *);
 extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
+/* Copy and release all segment info associated with a VM */
+#define copy_segments(nr, mm)           do { } while (0)
+#define release_segments(mm)            do { } while (0)
+
 /*
  * Return saved PC of a blocked thread. used in kernel/sched
  */
index 0e25867e648e455061b9a0b1cc3702a190910dc2..8c4ba40d8bb5b635429589945861b4eaeb6d9b18 100644 (file)
@@ -151,6 +151,10 @@ extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 #define MCA_bus__is_a_macro /* for versions in ksyms.c */
 
 
+/* Copy and release all segment info associated with a VM */
+#define copy_segments(p, mm)   do { } while(0)
+#define release_segments(mm)   do { } while(0)
+
 /*
  * FPU lazy state save handling.
  */
index 970e87ff7ec27ff98e80d8318bc8f0e57413cb21..2be6f36f0c1a36e47b6e147fc0256a6cf4a6736e 100644 (file)
@@ -153,6 +153,9 @@ extern __inline__ void start_thread(struct pt_regs * regs, unsigned long pc,
 extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
 
+#define copy_segments(tsk, mm)         do { } while (0)
+#define release_segments(mm)           do { } while (0)
+
 #define get_wchan(__TSK) \
 ({     extern void scheduling_functions_start_here(void); \
        extern void scheduling_functions_end_here(void); \
index 921290a9f8065a4def59be2a04735ec663ae0782..f26dd254d15f57b592b452a949ab7abfc68c6d1e 100644 (file)
@@ -218,6 +218,9 @@ do { \
 
 extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
 
+#define copy_segments(tsk, mm)         do { } while (0)
+#define release_segments(mm)           do { } while (0)
+
 #define get_wchan(__TSK) \
 ({     extern void scheduling_functions_start_here(void); \
        extern void scheduling_functions_end_here(void); \
index eb4d603e7530098e99cb8c3f89c365d0352b904d..11db342a3c688b47475b3e00907a51f3c8eed807 100644 (file)
@@ -163,6 +163,14 @@ static void floppy_off(unsigned int nr);
 #define DEVICE_INTR do_st  
 #define DEVICE_NR(device) (MINOR(device) & 0x7f)
 
+#elif (MAJOR_NR == OSST_MAJOR)
+
+#define DEVICE_NAME "onstream" 
+#define DEVICE_INTR do_osst
+#define DEVICE_NR(device) (MINOR(device) & 0x7f) 
+#define DEVICE_ON(device) 
+#define DEVICE_OFF(device) 
+
 #elif (MAJOR_NR == SCSI_CDROM_MAJOR)
 
 #define DEVICE_NAME "CD-ROM"
@@ -328,7 +336,7 @@ static void floppy_off(unsigned int nr);
 #define DEVICE_OFF(device) do {} while (0)
 #endif
 
-#if (MAJOR_NR != SCSI_TAPE_MAJOR)
+#if (MAJOR_NR != SCSI_TAPE_MAJOR) && (MAJOR_NR != OSST_MAJOR)
 #if !defined(IDE_DRIVER)
 
 #ifndef CURRENT
index 2aca5e2c8bb2f2841c152db051239affbb6c222c..5310a15e01499ca27463063270409311321adf2f 100644 (file)
@@ -568,16 +568,16 @@ typedef struct {
 
 /******************************************************************************
 * 
-* The DAA Analog GAIN sets 2 parameters at one time, the recieve gain (AGRR), 
+* The DAA Analog GAIN sets 2 parameters at one time, the receive gain (AGRR), 
 * and the transmit gain (AGX).  OR together the components and pass them
 * as the parameter to IXJCTL_DAA_AGAIN.  The default setting is both at 0dB.
 * 
 ******************************************************************************/
 #define IXJCTL_DAA_AGAIN               _IOW ('q', 0xD2, int)
 
-#define AGRR00DB       0x00    // Analog gain in recieve direction 0dB
-#define AGRR3_5DB      0x10    // Analog gain in recieve direction 3.5dB
-#define AGRR06DB       0x30    // Analog gain in recieve direction 6dB
+#define AGRR00DB       0x00    // Analog gain in receive direction 0dB
+#define AGRR3_5DB      0x10    // Analog gain in receive direction 3.5dB
+#define AGRR06DB       0x30    // Analog gain in receive direction 6dB
 
 #define AGX00DB                0x00    // Analog gain in transmit direction 0dB
 #define AGX_6DB                0x04    // Analog gain in transmit direction -6dB
index 372e1841a3ac45e61cc419b4dabf55748ca0ef0f..405e9f8acce77e77a8d2a16d60ff6e6cabc1c02e 100644 (file)
 #define MSR_MAJOR              202
 #define CPUID_MAJOR            203
 
+#define OSST_MAJOR     206     /* OnStream-SCx0 SCSI tape */
+
 /*
  * Tests for SCSI devices.
  */
index 5b67b326d67b24a1aba2f966cfe48b2643c85e23..2452012415d3eaa90b9c17ca2b3624ea12b17a7f 100644 (file)
@@ -120,11 +120,7 @@ extern pgprot_t protection_map[16];
 struct vm_operations_struct {
        void (*open)(struct vm_area_struct * area);
        void (*close)(struct vm_area_struct * area);
-       void (*unmap)(struct vm_area_struct *area, unsigned long, size_t);
-       void (*protect)(struct vm_area_struct *area, unsigned long, size_t, unsigned int newprot);
-       int (*sync)(struct vm_area_struct *area, unsigned long, size_t, unsigned int flags);
        struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, int write_access);
-       struct page * (*wppage)(struct vm_area_struct * area, unsigned long address, struct page * page);
 };
 
 /*
index c794ed898ac506d4fa33cc3637e60611ce9206e2..8b0f4aa64f859864ad44b3a5cbcf77a0e2e27fd1 100644 (file)
@@ -105,6 +105,8 @@ struct      mtget {
 #define MT_ISEVEREX_FT40A      0x32    /* Everex FT40A (QIC-40) */
 #define MT_ISDDS1              0x51    /* DDS device without partitions */
 #define MT_ISDDS2              0x52    /* DDS device with partitions */
+#define MT_ISONSTREAM_SC        0x61   /* OnStream SCSI tape drives (SC-x0)
+                                         and SCSI emulated (DI, DP, USB) */
 #define MT_ISSCSI1             0x71    /* Generic ANSI SCSI-1 tape unit */
 #define MT_ISSCSI2             0x72    /* Generic ANSI SCSI-2 tape unit */
 
@@ -134,6 +136,7 @@ struct mt_tape_info {
        {MT_ISWT5099EEN24,      "Wangtek 5099-een24, 60MB"}, \
        {MT_ISTEAC_MT2ST,       "Teac MT-2ST 155mb data cassette drive"}, \
        {MT_ISEVEREX_FT40A,     "Everex FT40A, QIC-40"}, \
+       {MT_ISONSTREAM_SC,      "OnStream SC-, DI-, DP-, or USB tape drive"}, \
        {MT_ISSCSI1,            "Generic SCSI-1 tape"}, \
        {MT_ISSCSI2,            "Generic SCSI-2 tape"}, \
        {0, NULL} \
index d98a3a01a4daeb05afe5ad0c5fdea4385a3f6483..a78410716707e4c156efa79d6a89d68f9e29f0fb 100644 (file)
@@ -681,6 +681,7 @@ extern int pci_pci_problems;
 #define PCIPCI_FAIL            1
 #define PCIPCI_TRITON          2
 #define PCIPCI_NATOMA          4
+#define PCIPCI_VIAETBF         8
 
 #endif /* __KERNEL__ */
 #endif /* LINUX_PCI_H */
index 415d4eb9e30096357be4edfe173c7392829972c7..f5d3a83df7ea18989dbc8849c443d0dcbe009ac5 100644 (file)
@@ -317,6 +317,11 @@ static inline int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
        if (retval)
                goto free_pt;
 
+       /*
+        * child gets a private LDT (if there was an LDT in the parent)
+        */
+       copy_segments(tsk, mm);
+
        if (init_new_context(tsk,mm))
                goto free_pt;
 
index c9ab9a4e2fc5c0d9d6191893bd0ed5f3e1a389d1..69fe404669dfc651780a622cb51fa29dbb1dfde2 100644 (file)
@@ -1692,7 +1692,6 @@ int filemap_sync(struct vm_area_struct * vma, unsigned long address,
  * backing-store for swapping..
  */
 static struct vm_operations_struct file_shared_mmap = {
-       sync:           filemap_sync,
        nopage:         filemap_nopage,
 };
 
@@ -1735,19 +1734,19 @@ int generic_file_mmap(struct file * file, struct vm_area_struct * vma)
 static int msync_interval(struct vm_area_struct * vma,
        unsigned long start, unsigned long end, int flags)
 {
-       if (vma->vm_file && vma->vm_ops && vma->vm_ops->sync) {
+       struct file * file = vma->vm_file;
+       if (file && (vma->vm_flags & VM_SHARED)) {
                int error;
-               error = vma->vm_ops->sync(vma, start, end-start, flags);
+               error = filemap_sync(vma, start, end-start, flags);
+
                if (!error && (flags & MS_SYNC)) {
-                       struct file * file = vma->vm_file;
-                       if (file && file->f_op && file->f_op->fsync) {
-                               struct inode * inode = file->f_dentry->d_inode;
-                               down(&inode->i_sem);
-                               filemap_fdatasync(inode->i_mapping);
+                       struct inode * inode = file->f_dentry->d_inode;
+                       down(&inode->i_sem);
+                       filemap_fdatasync(inode->i_mapping);
+                       if (file->f_op && file->f_op->fsync)
                                error = file->f_op->fsync(file, file->f_dentry, 1);
-                               filemap_fdatawait(inode->i_mapping);
-                               up(&inode->i_sem);
-                       }
+                       filemap_fdatawait(inode->i_mapping);
+                       up(&inode->i_sem);
                }
                return error;
        }
index f74b2487d8a8b038cd1f076b6a2f95bb5cd4f0c3..e5b3a989ed20c1944a24735ae96f418a1441e783 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -732,9 +732,6 @@ int do_munmap(struct mm_struct *mm, unsigned long addr, size_t len)
                end = end > mpnt->vm_end ? mpnt->vm_end : end;
                size = end - st;
 
-               if (mpnt->vm_ops && mpnt->vm_ops->unmap)
-                       mpnt->vm_ops->unmap(mpnt, st, size);
-
                if (mpnt->vm_flags & VM_DENYWRITE &&
                    (st != mpnt->vm_start || end != mpnt->vm_end) &&
                    (file = mpnt->vm_file) != NULL) {
@@ -878,6 +875,7 @@ void exit_mmap(struct mm_struct * mm)
 {
        struct vm_area_struct * mpnt;
 
+       release_segments(mm);
        spin_lock(&mm->page_table_lock);
        mpnt = mm->mmap;
        mm->mmap = mm->mmap_avl = mm->mmap_cache = NULL;
@@ -892,8 +890,6 @@ void exit_mmap(struct mm_struct * mm)
                unsigned long size = end - start;
 
                if (mpnt->vm_ops) {
-                       if (mpnt->vm_ops->unmap)
-                               mpnt->vm_ops->unmap(mpnt, start, size);
                        if (mpnt->vm_ops->close)
                                mpnt->vm_ops->close(mpnt);
                }
index 6b68d7995952509dad468b80bd655133f19a074e..035058033abe73c00e0db3d000ac3a579dd01057 100644 (file)
@@ -772,10 +772,10 @@ lecd_attach(struct atm_vcc *vcc, int arg)
                 size = sizeof(struct lec_priv);
 #ifdef CONFIG_TR
                 if (is_trdev)
-                        dev_lec[i] = prepare_trdev(NULL, size);
+                        dev_lec[i] = init_trdev(NULL, size);
                 else
 #endif
-                dev_lec[i] = prepare_etherdev(NULL, size);
+                dev_lec[i] = init_etherdev(NULL, size);
                 if (!dev_lec[i])
                         return -ENOMEM;
 
@@ -783,7 +783,6 @@ lecd_attach(struct atm_vcc *vcc, int arg)
                 priv->is_trdev = is_trdev;
                 sprintf(dev_lec[i]->name, "lec%d", i);
                 lec_init(dev_lec[i]);
-               publish_netdev(dev_lec[i]);
         } else {
                 priv = dev_lec[i]->priv;
                 if (priv->lecd)
@@ -858,7 +857,7 @@ void cleanup_module(void)
         for (i = 0; i < MAX_LEC_ITF; i++) {
                 if (dev_lec[i] != NULL) {
                         priv = (struct lec_priv *)dev_lec[i]->priv;
-                        unregister_netdev(dev_lec[i]);
+                        unregister_trdev(dev_lec[i]);
                         kfree(dev_lec[i]);
                         dev_lec[i] = NULL;
                 }