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)
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
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
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.
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
...
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.
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
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}
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
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
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
<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
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
...
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
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
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
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
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
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
...
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
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
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
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
/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
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.
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]
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
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
-
------------
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
VERSION = 2
PATCHLEVEL = 4
SUBLEVEL = 0
-EXTRAVERSION = -test13-pre6
+EXTRAVERSION = -test13-pre7
KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
$(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 \
$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 :
--- /dev/null
+/*
+ * 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
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
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)
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)
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
#
# 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
/*
* 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;
* 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;
*/
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;
}
/*
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;
/*
- 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)
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 */
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,
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,
}
}
- /* 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);
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
}
}
+
+/* ----------------------------------------------------------------------- */
+/* 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)
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");
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
/* 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;
};
-#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) {
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)
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;
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);
{
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) {
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;
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);
}
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
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 */
#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>
/* ---------------------------------------------------------- */
/* bttv-driver.c */
-/* insmod options */
+/* insmod options / kernel args */
extern unsigned int bttv_verbose;
extern unsigned int bttv_debug;
extern unsigned int bttv_gpio;
/**************************************************************************/
-#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; \
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
* 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>
#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;
#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)
{
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
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;
if (probed)
return -ENODEV;
- probed ++;
+ probed++;
if (!pci_present()) /* is PCI support present? */
return -ENODEV;
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) &&
break;
}
+ SET_MODULE_OWNER(dev);
+
if (!dev->priv)
dev->priv = kmalloc(sizeof(*ap), GFP_KERNEL);
if (!dev->priv) {
#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");
short i;
while (root_dev) {
- next = ((struct ace_private *)root_dev->priv)->next;
ap = root_dev->priv;
+ next = ap->next;
regs = ap->regs;
}
-#ifdef MODULE
#if (LINUX_VERSION_CODE < 0x02032a)
+#ifdef MODULE
int init_module(void)
{
return ace_module_init();
{
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)
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)
} 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;
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);
ap->fw_running = 0;
tmp_ptr = (unsigned long) ap->info_dma;
-#if (BITS_PER_LONG == 64)
+#ifdef ACE_64BIT_PTR
writel(tmp_ptr >> 32, ®s->InfoPtrHi);
#else
writel(0, ®s->InfoPtrHi);
*(ap->rx_ret_prd) = 0;
writel(TX_RING_BASE, ®s->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);
}
-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);
#endif
ace_load_jumbo_rx_ring(ap, RX_JUMBO_SIZE - cur_size);
}
- ap->bh_pending = 0;
+ ap->tasklet_pending = 0;
}
*
* 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)
{
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)
* 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);
* 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
*/
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) {
ace_load_std_rx_ring(ap,
RX_RING_SIZE - cur_size);
} else
- run_bh = 1;
+ run_tasklet = 1;
}
if (!ACE_IS_TIGON_I(ap)) {
#endif
ace_load_mini_rx_ring(ap, RX_MINI_SIZE - cur_size);
} else
- run_bh = 1;
+ run_tasklet = 1;
}
}
#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);
}
}
netif_start_queue(dev);
- MOD_INC_USE_COUNT;
+ ACE_MOD_INC_USE_COUNT;
/*
* Setup the timer
/*
* 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;
}
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.
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);
restore_flags(flags);
- MOD_DEC_USE_COUNT;
+ ACE_MOD_DEC_USE_COUNT;
return 0;
}
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;
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;
*/
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
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;
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;
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;
ecmd.autoneg = AUTONEG_DISABLE;
#if 0
+ /*
+ * Current struct ethtool_cmd is insufficient
+ */
ecmd.trace = readl(®s->TuneTrace);
+
ecmd.txcoal = readl(®s->TuneTxCoalTicks);
ecmd.rxcoal = readl(®s->TuneRxCoalTicks);
#endif
}
return 0;
}
+#endif
return -EOPNOTSUPP;
}
{
struct sockaddr *addr=p;
struct ace_regs *regs;
- u16 *da;
+ u8 *da;
struct cmd cmd;
if(netif_running(dev))
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], ®s->MacAddrHi);
- writel((da[1] << 16) | da[2], ®s->MacAddrLo);
+ writel(da[0] << 8 | da[1], ®s->MacAddrHi);
+ writel((da[2] << 24) | (da[3] << 16) | (da[4] << 8) | da[5] , ®s->MacAddrLo);
cmd.evt = C_SET_MAC_ADDR;
cmd.code = 0;
/*
* 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:
*/
u32 Mb2Hi;
u32 TxPrd;
u32 Mb3Hi;
- u32 RxStdPrd; /* RxStdPrd */
+ u32 RxStdPrd;
u32 Mb4Hi;
- u32 RxJumboPrd; /* RxJumboPrd */
+ u32 RxJumboPrd;
u32 Mb5Hi;
u32 RxMiniPrd;
u32 Mb6Hi;
#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
/*
* 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
*/
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;
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
{
unsigned long addr;
mb();
-#if (BITS_PER_LONG == 64)
+#ifdef ACE_64BIT_PTR
addr = (u64)aa->addrhi << 32 | aa->addrlo;
#else
addr = aa->addrlo;
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);
#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,
{
outb(0, ioaddr+DAYNA_RESET);
inb(ioaddr+DAYNA_RESET);
- udelay(333333);
+ mdelay(333);
}
if(board==TANGENT)
{
schedule();
}
else
- udelay(333333);
+ mdelay(333);
}
netif_wake_queue(dev);
return;
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
/**********************************************************************
* 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
*/
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>
#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;
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);
display_cache(fi);
return 0;
}
-#endif /* CONFIG_PCI */
static int __init fcdev_init(struct net_device *dev)
{
/* (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);
/* 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;
/**********************************************************************
* 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
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));
/* 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);
}
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);
free_irq(IRQ_AMIGA_PORTS, dev);
- MOD_DEC_USE_COUNT;
-
return(0);
}
#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");
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;
#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;
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 */
/* 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);
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. */
lance_purge_ring(dev);
- MOD_DEC_USE_COUNT;
return 0;
}
}
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);
/* enable all interrupts except receive interrupts */
out_8(&mb->imr, RCVINT);
- MOD_INC_USE_COUNT;
return 0;
}
mace_clean_rings(mp);
- MOD_DEC_USE_COUNT;
-
return 0;
}
"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 */
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);
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. */
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 */
if (NI5010_DEBUG) show_registers(dev);
- MOD_INC_USE_COUNT;
PRINTK((KERN_DEBUG "%s: open successful\n", dev->name));
return 0;
}
netif_stop_queue(dev);
- MOD_DEC_USE_COUNT;
PRINTK((KERN_DEBUG "%s: %s closed down\n", dev->name, boardname));
return 0;
}
}
-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;
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));
}
#ifdef MODULE
-static struct net_device dev_ni5010 = { init: ni5010_probe };
+static struct net_device dev_ni5010;
static int io;
static int irq;
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));
/**************************************************************************/
/* 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() \
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 */
ni_reset586(); /* the hard way to stop the receiver */
netif_stop_queue(dev);
- MOD_DEC_USE_COUNT;
return 0;
}
*/
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 */
}
/******************************************************************
* 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;
#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. */
{
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)) {
}
#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");
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;
dev->base_addr = 0;
release_region(dev->base_addr, OAKNET_IO_SIZE);
- return (ENODEV);
+ return (-ENODEV);
}
/*
* 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;
/*
oaknet_open(struct net_device *dev)
{
int status = ei_open(dev);
- MOD_INC_USE_COUNT;
return (status);
}
oaknet_close(struct net_device *dev)
{
int status = ei_close(dev);
- MOD_DEC_USE_COUNT;
return (status);
}
\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. */
/* 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;
printk ("stnic open\n");
#endif
ei_open (dev);
- MOD_INC_USE_COUNT;
return 0;
}
stnic_close (struct net_device *dev)
{
ei_close (dev);
- MOD_DEC_USE_COUNT;
return 0;
}
+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
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;
case DC21142:
case MX98713:
case COMPEX9881:
+ case DM910X:
default: {
struct medialeaf *mleaf;
unsigned char *p;
COMET,
COMPEX9881,
I21145,
+ DM910X,
};
#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. */
{ "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},
};
{ 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, }
};
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),
outl(0x00000004, ioaddr + CSR13);
break;
case DC21140:
+ case DM910X:
default:
if (tp->mtable)
outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12);
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";
#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)
#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");
s32 status;
s32 length;
u32 buffer1;
- u32 next_desc;
+ u32 buffer2;
};
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(). */
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);
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;
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;
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);
"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;
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. */
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)
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",
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;
&& (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);
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. */
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);
}
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);
#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,
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;
}
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
}
}
+/*
+ * 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
{ 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 },
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
--- /dev/null
+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
--- /dev/null
+/*
+ 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);
--- /dev/null
+/*
+ * $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
+
--- /dev/null
+#define SIGS_FROM_OSST \
+ {"OnStream", "SC-", "", "osst"}, \
+ {"OnStream", "DI-", "", "osst"}, \
+ {"OnStream", "DP-", "", "osst"}, \
+ {"OnStream", "FW-", "", "osst"}, \
+ {"OnStream", "USB", "", "osst"}
--- /dev/null
+/*
+ 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
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"}, \
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;
}
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 {
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;
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);
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) {
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;
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 */
* 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.
* 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>
{
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
/* 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);
*/
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 */
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;
{
#ifdef CONFIG_MAD16_OLDCARD
- unsigned char tmp;
tmp = mad_read(MC3_PORT);
* to set MPU register. TODO - add probing
*/
-
- unsigned char tmp;
-
tmp = mad_read(MC8_PORT);
switch (hw_config->irq)
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
}
-#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
};
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)
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,
/* 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;
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) {
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,
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 = {
/* 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. */
/* 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)
#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
*/
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.
*/
*/
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);
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..
*/
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.
*/
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.
*/
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 */
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)
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
*/
#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.
*/
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); \
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); \
#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"
#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
/******************************************************************************
*
-* 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
#define MSR_MAJOR 202
#define CPUID_MAJOR 203
+#define OSST_MAJOR 206 /* OnStream-SCx0 SCSI tape */
+
/*
* Tests for SCSI devices.
*/
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);
};
/*
#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 */
{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} \
#define PCIPCI_FAIL 1
#define PCIPCI_TRITON 2
#define PCIPCI_NATOMA 4
+#define PCIPCI_VIAETBF 8
#endif /* __KERNEL__ */
#endif /* LINUX_PCI_H */
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;
* backing-store for swapping..
*/
static struct vm_operations_struct file_shared_mmap = {
- sync: filemap_sync,
nopage: filemap_nopage,
};
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;
}
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) {
{
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;
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);
}
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;
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)
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;
}