From 2bec152847973a023426dfcdc1ef8ca0bf6a7dea Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:41:10 -0500 Subject: [PATCH] The LDT fixes in particular fix some potentially random strange behaviour. 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) --- Documentation/devices.txt | 578 +- Documentation/kernel-parameters.txt | 6 + Documentation/video4linux/bttv/Insmod-options | 1 + Documentation/video4linux/bttv/Modules.conf | 4 - Documentation/video4linux/bttv/README | 5 +- Makefile | 2 +- arch/alpha/lib/Makefile | 2 +- arch/alpha/lib/ev6-memcpy.S | 2 +- arch/alpha/lib/ev67-strrchr.S | 109 + arch/alpha/lib/memmove.S | 99 +- arch/alpha/math-emu/Makefile | 22 +- arch/i386/kernel/process.c | 10 +- drivers/media/radio/radio-gemtek.c | 2 +- drivers/media/video/bttv-cards.c | 155 +- drivers/media/video/bttv-driver.c | 146 +- drivers/media/video/bttv.h | 7 + drivers/media/video/bttvp.h | 4 +- drivers/net/3c523.c | 9 +- drivers/net/Config.in | 2 +- drivers/net/acenic.c | 282 +- drivers/net/acenic.h | 56 +- drivers/net/acenic_firmware.h | 7 +- drivers/net/appletalk/cops.c | 4 +- drivers/net/arlan.c | 2 +- drivers/net/fc/iph5526.c | 38 +- drivers/net/fc/tach_structs.h | 2 +- drivers/net/hydra.c | 29 +- drivers/net/lance.c | 24 +- drivers/net/mace.c | 9 +- drivers/net/ni5010.c | 24 +- drivers/net/ni52.c | 41 +- drivers/net/oaknet.c | 11 +- drivers/net/stnic.c | 23 +- drivers/net/tulip/ChangeLog | 5 + drivers/net/tulip/eeprom.c | 11 + drivers/net/tulip/timer.c | 1 + drivers/net/tulip/tulip.h | 1 + drivers/net/tulip/tulip_core.c | 13 +- drivers/net/winbond-840.c | 465 +- drivers/pci/quirks.c | 14 + drivers/scsi/Makefile | 1 + drivers/scsi/README.osst | 219 + drivers/scsi/osst.c | 5306 +++++++++++++++++ drivers/scsi/osst.h | 634 ++ drivers/scsi/osst_detect.h | 6 + drivers/scsi/osst_options.h | 100 + drivers/scsi/st.c | 2 +- drivers/sound/i810_audio.c | 89 +- drivers/sound/mad16.c | 128 +- drivers/sound/via82cxxx_audio.c | 10 +- fs/buffer.c | 16 +- fs/ramfs/inode.c | 9 +- include/asm-alpha/processor.h | 3 + include/asm-arm/processor.h | 4 + include/asm-i386/mmu_context.h | 8 +- include/asm-i386/processor.h | 4 + include/asm-ia64/processor.h | 4 + include/asm-m68k/processor.h | 3 + include/asm-mips/processor.h | 4 + include/asm-mips64/processor.h | 4 + include/asm-parisc/processor.h | 3 + include/asm-ppc/processor.h | 3 + include/asm-s390/processor.h | 4 + include/asm-sh/processor.h | 4 + include/asm-sparc/processor.h | 3 + include/asm-sparc64/processor.h | 3 + include/linux/blk.h | 10 +- include/linux/ixjuser.h | 8 +- include/linux/major.h | 2 + include/linux/mm.h | 4 - include/linux/mtio.h | 3 + include/linux/pci.h | 1 + kernel/fork.c | 5 + mm/filemap.c | 21 +- mm/mmap.c | 6 +- net/atm/lec.c | 7 +- 76 files changed, 8058 insertions(+), 810 deletions(-) create mode 100644 arch/alpha/lib/ev67-strrchr.S create mode 100644 drivers/scsi/README.osst create mode 100644 drivers/scsi/osst.c create mode 100644 drivers/scsi/osst.h create mode 100644 drivers/scsi/osst_detect.h create mode 100644 drivers/scsi/osst_options.h diff --git a/Documentation/devices.txt b/Documentation/devices.txt index 6332b478edf8..5c250ac97faf 100644 --- a/Documentation/devices.txt +++ b/Documentation/devices.txt @@ -1,7 +1,7 @@ LINUX ALLOCATED DEVICES - Maintained by H. Peter Anvin + Maintained by H. Peter Anvin - Last revised: March 23, 2000 + Last revised: December 29, 2000 This list is the Linux Device List, the official registry of allocated device numbers and /dev directory nodes for the Linux operating @@ -36,7 +36,7 @@ on this list. Any such information requests will be deleted without reply. - **** PLEASE READ THIS BEFORE SUBMITTING A DEVICE ENTRY **** + **** DEVICE DRIVERS AUTHORS PLEASE READ THIS **** To have a major number allocated, or a minor number in situations where that applies (e.g. busmice), please contact me with the @@ -44,24 +44,32 @@ appropriate device information. Also, if you have additional information regarding any of the devices listed below, or if I have made a mistake, I would greatly appreciate a note. -I do, however, make two requests about the nature of your report. +I do, however, make a few requests about the nature of your report. This is necessary for me to be able to keep this list up to date and -correct in a timely manner. First of all, *please* include the word -"device" in the subject so your mail won't accidentally get buried! I -receive hundreds of email messages a day, so mail sent with other -subjects may very well get lost in the avalanche. +correct in a timely manner. First of all, *please* send it to the +correct address... . I receive hundreds of email +messages a day, so mail sent to other addresses may very well get lost +in the avalanche. Please put in a descriptive subject, so I can find +your mail again should I need to. Too many people send me email +saying just "device number request" in the subject. Second, please include a description of the device *in the same format as this list*. The reason for this is that it is the only way I have found to ensure I have all the requisite information to publish your device and avoid conflicts. +Third, please don't assume that the distributed version of the list is +up to date. Due to the number of registrations I have to maintain it +in "batch mode", so there is likely additional registrations that +haven't been listed yet. + Finally, sometimes I have to play "namespace police." Please don't be offended. I often get submissions for /dev names that would be bound to cause conflicts down the road. I am trying to avoid getting in a situation where we would have to suffer an incompatible forward -change. - +change. Therefore, please consult with me *before* you make your +device names and numbers in any way public, at least to the point +where it would be at all difficult to get them changed. Your cooperation is appreciated. @@ -79,16 +87,18 @@ Your cooperation is appreciated. 7 = /dev/full Returns ENOSPC on write 8 = /dev/random Nondeterministic random number gen. 9 = /dev/urandom Faster, less secure random number gen. + 10 = /dev/aio Asyncronous I/O notification interface block RAM disk 0 = /dev/ram0 First RAM disk + 1 = /dev/ram1 Second RAM disk ... - 7 = /dev/ram7 Eighth RAM disk - 250 = /dev/initrd Initial RAM disk + 250 = /dev/initrd Initial RAM disk {2.6} Older kernels had /dev/ramdisk (1, 1) here. /dev/initrd refers to a RAM disk which was preloaded - by the boot loader. - + by the boot loader; newer kernels use /dev/ram0 for + the initrd. + 2 char Pseudo-TTY masters 0 = /dev/ptyp0 First PTY master 1 = /dev/ptyp1 Second PTY master @@ -199,6 +209,8 @@ Your cooperation is appreciated. ... 255 = /dev/ttyS191 192nd UART serial port + UART serial ports refer to 8250/16450/16550 series devices. + Older versions of the Linux kernel used this major number for BSD PTY devices. As of Linux 2.1.115, this is no longer supported. Use major numbers 2 and 3. @@ -310,6 +322,8 @@ Your cooperation is appreciated. 10 = /dev/adbmouse Apple Desktop Bus mouse 11 = /dev/vrtpanel Vr41xx embedded touch panel 13 = /dev/vpcmouse Connectix Virtual PC Mouse + 14 = /dev/touchscreen/ucb1x00 UCB 1x00 touchscreen + 15 = /dev/touchscreen/mk712 MK712 touchscreen 128 = /dev/beep Fancy beep device 129 = /dev/modreq Kernel module load request {2.6} 130 = /dev/watchdog Watchdog timer port @@ -321,7 +335,7 @@ Your cooperation is appreciated. 139 = /dev/openprom SPARC OpenBoot PROM 140 = /dev/relay8 Berkshire Products Octal relay card 141 = /dev/relay16 Berkshire Products ISO-16 relay card - 142 = /dev/msr x86 model-specific registers + 142 = /dev/msr x86 model-specific registers {2.6} 143 = /dev/pciconf PCI configuration space 144 = /dev/nvram Non-volatile configuration RAM 145 = /dev/hfmodem Soundcard shortwave modem control {2.6} @@ -363,6 +377,26 @@ Your cooperation is appreciated. 182 = /dev/perfctr Performance-monitoring counters 183 = /dev/intel_rng Intel i8x0 random number generator 184 = /dev/cpu/microcode CPU microcode update interface + 186 = /dev/atomicps Atomic shapshot of process state data + 187 = /dev/irnet IrNET device + 188 = /dev/smbusbios SMBus BIOS + 189 = /dev/ussp_ctl User space serial port control + 190 = /dev/crash Mission Critical Linux crash dump facility + 191 = /dev/pcl181 + 192 = /dev/nas_xbus NAS xbus LCD/buttons access + 193 = /dev/d7s SPARC 7-segment display + 194 = /dev/zkshim Zero-Knowledge network shim control + 195 = /dev/elographics/e2201 Elographics touchscreen E271-2201 + 198 = /dev/sexec Signed executable interface + 199 = /dev/scanners/cuecat :CueCat barcode scanner + 200 = /dev/net/tun TAP/TUN network device + 201 = /dev/button/gulpb Transmeta GULP-B buttons + 204 = /dev/video/em8300 EM8300 DVD decoder control + 205 = /dev/video/em8300_mv EM8300 DVD decoder video + 206 = /dev/video/em8300_ma EM8300 DVD decoder audio + 207 = /dev/video/em8300_sp EM8300 DVD decoder subpicture + 208 = /dev/compaq/cpqphpc Compaq PCI Hot Plug Controller + 209 = /dev/compaq/cpqrid Compaq Remote Insight Driver 240-255 Reserved for local use 11 char Raw keyboard device @@ -901,29 +935,62 @@ Your cooperation is appreciated. 0 = /dev/ttyL0 First RISCom port 1 = /dev/ttyL1 Second RISCom port ... - block Reserved for Mylex DAC960 PCI RAID controller + block Mylex DAC960 PCI RAID controller; first controller + 0 = /dev/rd/c0d0 First disk, whole disk + 8 = /dev/rd/c0d1 Second disk, whole disk + ... + 248 = /dev/rd/c0d15 16th disk, whole disk + + For partitions add: + 0 = /dev/rd/c?d? Whole disk + 1 = /dev/rd/c?d?p1 First partition + ... + 7 = /dev/rd/c?d?p7 Seventh partition 49 char SDL RISCom serial card - alternate devices 0 = /dev/cul0 Callout device for ttyL0 1 = /dev/cul1 Callout device for ttyL1 ... - block Reserved for Mylex DAC960 PCI RAID controller + block Mylex DAC960 PCI RAID controller; second controller + 0 = /dev/rd/c1d0 First disk, whole disk + 8 = /dev/rd/c1d1 Second disk, whole disk + ... + 248 = /dev/rd/c1d15 16th disk, whole disk + + Partitions are handled as for major 48. 50 char Reserved for GLINT - block Reserved for Mylex DAC960 PCI RAID controller + + block Mylex DAC960 PCI RAID controller; third controller + 0 = /dev/rd/c2d0 First disk, whole disk + 8 = /dev/rd/c2d1 Second disk, whole disk + ... + 248 = /dev/rd/c2d15 16th disk, whole disk 51 char Baycom radio modem 0 = /dev/bc0 First Baycom radio modem 1 = /dev/bc1 Second Baycom radio modem ... - block Reserved for Mylex DAC960 PCI RAID controller + block Mylex DAC960 PCI RAID controller; fourth controller + 0 = /dev/rd/c3d0 First disk, whole disk + 8 = /dev/rd/c3d1 Second disk, whole disk + ... + 248 = /dev/rd/c3d15 16th disk, whole disk + + Partitions are handled as for major 48. 52 char Spellcaster DataComm/BRI ISDN card 0 = /dev/dcbri0 First DataComm card 1 = /dev/dcbri1 Second DataComm card 2 = /dev/dcbri2 Third DataComm card 3 = /dev/dcbri3 Fourth DataComm card - block Reserved for Mylex DAC960 PCI RAID controller + block Mylex DAC960 PCI RAID controller; fifth controller + 0 = /dev/rd/c4d0 First disk, whole disk + 8 = /dev/rd/c4d1 Second disk, whole disk + ... + 248 = /dev/rd/c4d15 16th disk, whole disk + + Partitions are handled as for major 48. 53 char BDM interface for remote debugging MC683xx microcontrollers 0 = /dev/pd_bdm0 PD BDM interface on lp0 @@ -939,7 +1006,13 @@ Your cooperation is appreciated. Domain Interface and ICD is the commercial interface by P&E. - block Reserved for Mylex DAC960 PCI RAID controller + block Mylex DAC960 PCI RAID controller; sixth controller + 0 = /dev/rd/c5d0 First disk, whole disk + 8 = /dev/rd/c5d1 Second disk, whole disk + ... + 248 = /dev/rd/c5d15 16th disk, whole disk + + Partitions are handled as for major 48. 54 char Electrocardiognosis Holter serial card 0 = /dev/holter0 First Holter port @@ -950,11 +1023,23 @@ Your cooperation is appreciated. to transfer data from Holter 24-hour heart monitoring equipment. - block Reserved for Mylex DAC960 PCI RAID controller + block Mylex DAC960 PCI RAID controller; seventh controller + 0 = /dev/rd/c6d0 First disk, whole disk + 8 = /dev/rd/c6d1 Second disk, whole disk + ... + 248 = /dev/rd/c6d15 16th disk, whole disk + + Partitions are handled as for major 48. 55 char DSP56001 digital signal processor 0 = /dev/dsp56k First DSP56001 - block Reserved for Mylex DAC960 PCI RAID controller + block Mylex DAC960 PCI RAID controller; eigth controller + 0 = /dev/rd/c7d0 First disk, whole disk + 8 = /dev/rd/c7d1 Second disk, whole disk + ... + 248 = /dev/rd/c7d15 16th disk, whole disk + + Partitions are handled as for major 48. 56 char Apple Desktop Bus 0 = /dev/adb ADB bus control @@ -1001,6 +1086,8 @@ Your cooperation is appreciated. running small fs translation drivers) through serial / IRDA / parallel links. + NAMING CONFLICT -- PROPOSED REVISED NAME /dev/rpda0 etc + 60-63 LOCAL/EXPERIMENTAL USE Allocated for local/experimental use. For devices not assigned official numbers, these ranges should be @@ -1175,6 +1262,16 @@ Your cooperation is appreciated. ... 255 = /dev/cuf255 Callout device for ttyF255 + block Compaq Intelligent Drive Array, first controller + 0 = /dev/ida/c0d0 First logical drive whole disk + 16 = /dev/ida/c0d1 Second logical drive whole disk + ... + 240 = /dev/ida/c0d15 16th logical drive whole disk + + Partitions are handled the same way as for Mylex + DAC960 (see major number 48) except that the limit on + partitions is 15. + 73 char Computone IntelliPort II serial card - control devices 0 = /dev/ip2ipl0 Loadware device for board 0 1 = /dev/ip2stat0 Status device for board 0 @@ -1185,6 +1282,16 @@ Your cooperation is appreciated. 12 = /dev/ip2ipl3 Loadware device for board 3 13 = /dev/ip2stat3 Status device for board 3 + block Compaq Intelligent Drive Array, second controller + 0 = /dev/ida/c1d0 First logical drive whole disk + 16 = /dev/ida/c1d1 Second logical drive whole disk + ... + 240 = /dev/ida/c1d15 16th logical drive whole disk + + Partitions are handled the same way as for Mylex + DAC960 (see major number 48) except that the limit on + partitions is 15. + 74 char SCI bridge 0 = /dev/SCI/0 SCI device 0 1 = /dev/SCI/1 SCI device 1 @@ -1193,6 +1300,16 @@ Your cooperation is appreciated. Currently for Dolphin Interconnect Solutions' PCI-SCI bridge. + block Compaq Intelligent Drive Array, third controller + 0 = /dev/ida/c2d0 First logical drive whole disk + 16 = /dev/ida/c2d1 Second logical drive whole disk + ... + 240 = /dev/ida/c2d15 16th logical drive whole disk + + Partitions are handled the same way as for Mylex + DAC960 (see major number 48) except that the limit on + partitions is 15. + 75 char Specialix IO8+ serial card 0 = /dev/ttyW0 First IO8+ port, first card 1 = /dev/ttyW1 Second IO8+ port, first card @@ -1200,6 +1317,16 @@ Your cooperation is appreciated. 8 = /dev/ttyW8 First IO8+ port, second card ... + block Compaq Intelligent Drive Array, fourth controller + 0 = /dev/ida/c3d0 First logical drive whole disk + 16 = /dev/ida/c3d1 Second logical drive whole disk + ... + 240 = /dev/ida/c3d15 16th logical drive whole disk + + Partitions are handled the same way as for Mylex + DAC960 (see major number 48) except that the limit on + partitions is 15. + 76 char Specialix IO8+ serial card - alternate devices 0 = /dev/cuw0 Callout device for ttyW0 1 = /dev/cuw1 Callout device for ttyW1 @@ -1207,19 +1334,63 @@ Your cooperation is appreciated. 8 = /dev/cuw8 Callout device for ttyW8 ... + block Compaq Intelligent Drive Array, fifth controller + 0 = /dev/ida/c4d0 First logical drive whole disk + 16 = /dev/ida/c4d1 Second logical drive whole disk + ... + 240 = /dev/ida/c4d15 16th logical drive whole disk + + Partitions are handled the same way as for Mylex + DAC960 (see major number 48) except that the limit on + partitions is 15. + + 77 char ComScire Quantum Noise Generator 0 = /dev/qng ComScire Quantum Noise Generator + block Compaq Intelligent Drive Array, sixth controller + 0 = /dev/ida/c5d0 First logical drive whole disk + 16 = /dev/ida/c5d1 Second logical drive whole disk + ... + 240 = /dev/ida/c5d15 16th logical drive whole disk + + Partitions are handled the same way as for Mylex + DAC960 (see major number 48) except that the limit on + partitions is 15. + + 78 char PAM Software's multimodem boards 0 = /dev/ttyM0 First PAM modem 1 = /dev/ttyM1 Second PAM modem ... + block Compaq Intelligent Drive Array, seventh controller + 0 = /dev/ida/c6d0 First logical drive whole disk + 16 = /dev/ida/c6d1 Second logical drive whole disk + ... + 240 = /dev/ida/c6d15 16th logical drive whole disk + + Partitions are handled the same way as for Mylex + DAC960 (see major number 48) except that the limit on + partitions is 15. + + 79 char PAM Software's multimodem boards - alternate devices 0 = /dev/cum0 Callout device for ttyM0 1 = /dev/cum1 Callout device for ttyM1 ... + block Compaq Intelligent Drive Array, eigth controller + 0 = /dev/ida/c7d0 First logical drive whole disk + 16 = /dev/ida/c7d1 Second logical drive whole disk + ... + 240 = /dev/ida/c715 16th logical drive whole disk + + Partitions are handled the same way as for Mylex + DAC960 (see major number 48) except that the limit on + partitions is 15. + + 80 char Photometrics AT200 CCD camera 0 = /dev/at200 Photometrics AT200 CCD camera @@ -1496,14 +1667,13 @@ Your cooperation is appreciated. 1 = /dev/parport1 Second parallel port ... -100 char POTS (analogue telephone) A/B port {2.6} - 0 = /dev/phone0 First telephone port - 1 = /dev/phone1 Second telephone port - ... + block JavaStation flash disk + 0 = /dev/jsfd JavaStation flash disk - The names have been reallocated to Telephony For - Linux, major 159. All use of major 100 should be - considered legacy and deprecated. +100 char Telephony for Linux + 0 = /dev/phone0 First telephony device + 1 = /dev/phone1 Second telephony device + ... 101 char Motorola DSP 56xxx board 0 = /dev/mdspstat Status information @@ -1511,6 +1681,19 @@ Your cooperation is appreciated. ... 16 = /dev/mdsp16 16th DSP board I/O controls + block AMI HyperDisk RAID controller + 0 = /dev/amiraid/ar0 First array whole disk + 16 = /dev/amiraid/ar1 Second array whole disk + ... + 240 = /dev/amiraid/ar15 16th array whole disk + + For each device, partitions are added as: + 0 = /dev/amiraid/ar? Whole disk + 1 = /dev/amiraid/ar?p1 First partition + 2 = /dev/amiraid/ar?p2 Second partition + ... + 15 = /dev/amiraid/ar?p15 15th partition + 102 char Philips SAA5249 Teletext signal decoder {2.6} 0 = /dev/tlk0 First Teletext decoder 1 = /dev/tlk1 Second Teletext decoder @@ -1527,36 +1710,119 @@ Your cooperation is appreciated. to the arla announce mailing list by sending a mail to . + block Audit device + 0 = /dev/audit Audit device + 104 char Flash BIOS support + block Compaq Next Generation Drive Array, first controller + 0 = /dev/cciss/c0d0 First logical drive, whole disk + 16 = /dev/cciss/c0d1 Second logical drive, whole disk + ... + 240 = /dev/cciss/c0d15 16th logical drive, whole disk + + Partitions are handled the same way as for Mylex + DAC960 (see major number 48) except that the limit on + partitions is 15. + 105 char Comtrol VS-1000 serial controller 0 = /dev/ttyV0 First VS-1000 port 1 = /dev/ttyV1 Second VS-1000 port ... + block Compaq Next Generation Drive Array, second controller + 0 = /dev/cciss/c1d0 First logical drive, whole disk + 16 = /dev/cciss/c1d1 Second logical drive, whole disk + ... + 240 = /dev/cciss/c1d15 16th logical drive, whole disk + + Partitions are handled the same way as for Mylex + DAC960 (see major number 48) except that the limit on + partitions is 15. + 106 char Comtrol VS-1000 serial controller - alternate devices 0 = /dev/cuv0 First VS-1000 port 1 = /dev/cuv1 Second VS-1000 port ... + block Compaq Next Generation Drive Array, third controller + 0 = /dev/cciss/c2d0 First logical drive, whole disk + 16 = /dev/cciss/c2d1 Second logical drive, whole disk + ... + 240 = /dev/cciss/c2d15 16th logical drive, whole disk + + Partitions are handled the same way as for Mylex + DAC960 (see major number 48) except that the limit on + partitions is 15. + 107 char 3Dfx Voodoo Graphics device 0 = /dev/3dfx Primary 3Dfx graphics device + block Compaq Next Generation Drive Array, fourth controller + 0 = /dev/cciss/c3d0 First logical drive, whole disk + 16 = /dev/cciss/c3d1 Second logical drive, whole disk + ... + 240 = /dev/cciss/c3d15 16th logical drive, whole disk + + Partitions are handled the same way as for Mylex + DAC960 (see major number 48) except that the limit on + partitions is 15. + 108 char Device independent PPP interface 0 = /dev/ppp Device independent PPP interface + block Compaq Next Generation Drive Array, fifth controller + 0 = /dev/cciss/c4d0 First logical drive, whole disk + 16 = /dev/cciss/c4d1 Second logical drive, whole disk + ... + 240 = /dev/cciss/c4d15 16th logical drive, whole disk + + Partitions are handled the same way as for Mylex + DAC960 (see major number 48) except that the limit on + partitions is 15. + 109 char Reserved for logical volume manager + block Compaq Next Generation Drive Array, sixth controller + 0 = /dev/cciss/c5d0 First logical drive, whole disk + 16 = /dev/cciss/c5d1 Second logical drive, whole disk + ... + 240 = /dev/cciss/c5d15 16th logical drive, whole disk + + Partitions are handled the same way as for Mylex + DAC960 (see major number 48) except that the limit on + partitions is 15. + 110 char miroMEDIA Surround board 0 = /dev/srnd0 First miroMEDIA Surround board 1 = /dev/srnd1 Second miroMEDIA Surround board ... + block Compaq Next Generation Drive Array, seventh controller + 0 = /dev/cciss/c6d0 First logical drive, whole disk + 16 = /dev/cciss/c6d1 Second logical drive, whole disk + ... + 240 = /dev/cciss/c6d15 16th logical drive, whole disk + + Partitions are handled the same way as for Mylex + DAC960 (see major number 48) except that the limit on + partitions is 15. + 111 char Philips SAA7146-based audio/video card {2.6} 0 = /dev/av0 First A/V card 1 = /dev/av1 Second A/V card ... + block Compaq Next Generation Drive Array, eigth controller + 0 = /dev/cciss/c7d0 First logical drive, whole disk + 16 = /dev/cciss/c7d1 Second logical drive, whole disk + ... + 240 = /dev/cciss/c7d15 16th logical drive, whole disk + + Partitions are handled the same way as for Mylex + DAC960 (see major number 48) except that the limit on + partitions is 15. + 112 char ISI serial card 0 = /dev/ttyM0 First ISI port 1 = /dev/ttyM1 Second ISI port @@ -1717,10 +1983,7 @@ Your cooperation is appreciated. 1 = /dev/gfax1 GammaLink channel 1 ... -159 char Telephony for Linux - 0 = /dev/phone0 First telephony device - 1 = /dev/phone1 Second telephony device - ... +159 RESERVED 160 char General Purpose Instrument Bus (GPIB) 0 = /dev/gpib0 First GPIB bus @@ -1944,7 +2207,244 @@ Your cooperation is appreciated. 240 = /dev/mvideo/status15 16th device ... -195-239 UNALLOCATED +195 char Nvidia graphics devices + 0 = /dev/nvidia0 First Nvidia card + 1 = /dev/nvidia1 Second Nvidia card + ... + 255 = /dev/nvidiactl Nvidia card control device + +196-197 UNASSIGNED + +198 char Total Impact TPMP2 quad coprocessor PCI card + 0 = /dev/tpmp2/0 First card + 1 = /dev/tpmp2/1 Second card + ... + +199 char Veritas volume manager (VxVM) volumes + 0 = /dev/vx/rdsk/*/* First volume + 1 = /dev/vx/rdsk/*/* Second volume + ... + block Veritas volume manager (VxVM) volumes + 0 = /dev/vx/dsk/*/* First volume + 1 = /dev/vx/dsk/*/* First volume + ... + + The namespace in these directories is maintained by + the user space VxVM software. + +200 char Veritas VxVM configuration interface + 0 = /dev/vx/config Configuration access node + 1 = /dev/vx/trace Volume i/o trace access node + 2 = /dev/vx/iod Volume i/o daemon access node + 3 = /dev/vx/info Volume information access node + 4 = /dev/vx/task Volume tasks access node + 5 = /dev/vx/taskmon Volume tasks monitor daemon + +201 char Veritas VxVM dynamic multipathing driver + 0 = /dev/vx/rdmp/* First multipath device + 1 = /dev/vx/rdmp/* Second multipath device + ... + block Veritas VxVM dynamic multipathing driver + 0 = /dev/vx/dmp/* First multipath device + 1 = /dev/vx/dmp/* Second multipath device + ... + + The namespace in these directories is maintained by + the user space VxVM software. + +202 char CPU model-specific registers + 0 = /dev/cpu/0/msr MSRs on CPU 0 + 1 = /dev/cpu/1/msr MSRs on CPU 1 + ... + +203 char CPU CPUID information + 0 = /dev/cpu/0/cpuid CPUID on CPU 0 + 1 = /dev/cpu/1/cpuid CPUID on CPU 1 + ... + +204 char Low-density serial ports + 0 = /dev/ttyLU0 LinkUp Systems L72xx UART - port 0 + 1 = /dev/ttyLU1 LinkUp Systems L72xx UART - port 1 + 2 = /dev/ttyLU2 LinkUp Systems L72xx UART - port 2 + 3 = /dev/ttyLU3 LinkUp Systems L72xx UART - port 3 + 4 = /dev/ttyFB0 Intel Footbridge (ARM) + 5 = /dev/ttySA0 StrongARM builtin serial port 0 + 6 = /dev/ttySA1 StrongARM builtin serial port 1 + 7 = /dev/ttySA2 StrongARM builtin serial port 2 + 8 = /dev/ttySC0 SCI serial port (SuperH) - port 0 + 9 = /dev/ttySC1 SCI serial port (SuperH) - port 1 + 10 = /dev/ttySC2 SCI serial port (SuperH) - port 2 + 11 = /dev/ttySC3 SCI serial port (SuperH) - port 3 + 12 = /dev/ttyFW0 Firmware console - port 0 + 13 = /dev/ttyFW1 Firmware console - port 1 + 14 = /dev/ttyFW2 Firmware console - port 2 + 15 = /dev/ttyFW3 Firmware console - port 3 + 16 = /dev/ttyAM0 ARM "AMBA" serial port 0 + ... + 31 = /dev/ttyAM15 ARM "AMBA" serial port 15 + +205 char Low-density serial ports (alternate device) + 0 = /dev/culu0 Callout device for ttyLU0 + 1 = /dev/culu1 Callout device for ttyLU1 + 2 = /dev/culu2 Callout device for ttyLU2 + 3 = /dev/culu3 Callout device for ttyLU3 + 4 = /dev/cufb0 Callout device for ttyFB0 + 5 = /dev/cusa0 Callout device for ttySA0 + 6 = /dev/cusa1 Callout device for ttySA1 + 7 = /dev/cusa2 Callout device for ttySA2 + 8 = /dev/cusc0 Callout device for ttySC0 + 9 = /dev/cusc1 Callout device for ttySC1 + 10 = /dev/cusc2 Callout device for ttySC2 + 11 = /dev/cusc3 Callout device for ttySC3 + 12 = /dev/cufw0 Callout device for ttyFW0 + 13 = /dev/cufw1 Callout device for ttyFW1 + 14 = /dev/cufw2 Callout device for ttyFW2 + 15 = /dev/cufw3 Callout device for ttyFW3 + 16 = /dev/cuam0 Callout device for ttyAM0 + ... + 31 = /dev/cuam15 Callout device for ttyAM15 + +206 char OnStream SC-x0 tape devices + 0 = /dev/osst0 First OnStream SCSI tape, mode 0 + 1 = /dev/osst1 Second OnStream SCSI tape, mode 0 + ... + 32 = /dev/osst0l First OnStream SCSI tape, mode 1 + 33 = /dev/osst1l Second OnStream SCSI tape, mode 1 + ... + 64 = /dev/osst0m First OnStream SCSI tape, mode 2 + 65 = /dev/osst1m Second OnStream SCSI tape, mode 2 + ... + 96 = /dev/osst0a First OnStream SCSI tape, mode 3 + 97 = /dev/osst1a Second OnStream SCSI tape, mode 3 + ... + 128 = /dev/nosst0 No rewind version of /dev/osst0 + 129 = /dev/nosst1 No rewind version of /dev/osst1 + ... + 160 = /dev/nosst0l No rewind version of /dev/osst0l + 161 = /dev/nosst1l No rewind version of /dev/osst1l + ... + 192 = /dev/nosst0m No rewind version of /dev/osst0m + 193 = /dev/nosst1m No rewind version of /dev/osst1m + ... + 224 = /dev/nosst0a No rewind version of /dev/osst0a + 225 = /dev/nosst1a No rewind version of /dev/osst1a + ... + + The OnStream SC-x0 SCSI tapes do not support the + standard SCSI SASD command set and therefore need + their own driver "osst". Note that the IDE, USB (and + maybe ParPort) versions may be driven via ide-scsi or + usb-storage SCSI emulation and this osst device and + driver as well. The ADR-x0 drives are QIC-157 + compliant and don't need osst. + +207 char Compaq ProLiant health feature indicate + 0 = /dev/cpqhealth/cpqw Redirector interface + 1 = /dev/cpqhealth/crom EISA CROM + 2 = /dev/cpqhealth/cdt Data Table + 3 = /dev/cpqhealth/cevt Event Log + 4 = /dev/cpqhealth/casr Automatic Server Recovery + 5 = /dev/cpqhealth/cecc ECC Memory + 6 = /dev/cpqhealth/cmca Machine Check Architecture + 7 = /dev/cpqhealth/ccsm Deprecated CDT + 8 = /dev/cpqhealth/cnmi NMI Handling + 9 = /dev/cpqhealth/css Sideshow Management + 10 = /dev/cpqhealth/cram CMOS interface + 11 = /dev/cpqhealth/cpci PCI IRQ interface + +208 char User space serial ports + 0 = /dev/ttyU0 First user space serial port + 1 = /dev/ttyU1 Second user space serial port + ... + +209 char User space serial ports (alternate devices) + 0 = /dev/cuu0 Callout device for ttyU0 + 1 = /dev/cuu1 Callout device for ttyU1 + ... + +210 char SBE, Inc. sync/async serial card + 0 = /dev/sbei/wxcfg0 Configuration device for board 0 + 1 = /dev/sbei/dld0 Download device for board 0 + 2 = /dev/sbei/wan00 WAN device, port 0, board 0 + 3 = /dev/sbei/wan01 WAN device, port 1, board 0 + 4 = /dev/sbei/wan02 WAN device, port 2, board 0 + 5 = /dev/sbei/wan03 WAN device, port 3, board 0 + 6 = /dev/sbei/wanc00 WAN clone device, port 0, board 0 + 7 = /dev/sbei/wanc01 WAN clone device, port 1, board 0 + 8 = /dev/sbei/wanc02 WAN clone device, port 2, board 0 + 9 = /dev/sbei/wanc03 WAN clone device, port 3, board 0 + 10 = /dev/sbei/wxcfg1 Configuration device for board 1 + 11 = /dev/sbei/dld1 Download device for board 1 + 12 = /dev/sbei/wan10 WAN device, port 0, board 1 + 13 = /dev/sbei/wan11 WAN device, port 1, board 1 + 14 = /dev/sbei/wan12 WAN device, port 2, board 1 + 15 = /dev/sbei/wan13 WAN device, port 3, board 1 + 16 = /dev/sbei/wanc10 WAN clone device, port 0, board 1 + 17 = /dev/sbei/wanc11 WAN clone device, port 1, board 1 + 18 = /dev/sbei/wanc12 WAN clone device, port 2, board 1 + 19 = /dev/sbei/wanc13 WAN clone device, port 3, board 1 + ... + + Yes, each board is really spaced 10 (decimal) apart. + +211 char Addinum CPCI1500 digital I/O card + 0 = /dev/addinum/cpci1500/0 First CPCI1500 card + 1 = /dev/addinum/cpci1500/1 Second CPCI1500 card + ... + +216 char USB BlueTooth devices + 0 = /dev/ttyUB0 First USB BlueTooth device + 1 = /dev/ttyUB1 Second USB BlueTooth device + ... + +217 char USB BlueTooth devices (alternate devices) + 0 = /dev/cuub0 Callout device for ttyUB0 + 1 = /dev/cuub1 Callout device for ttyUB1 + ... + +218 char The Logical Company bus Unibus/Qbus adapters + 0 = /dev/logicalco/bci/0 First bus adapter + 1 = /dev/logicalco/bci/1 First bus adapter + ... + +219 char The Logical Company DCI-1300 digital I/O card + 0 = /dev/logicalco/dci1300/0 First DCI-1300 card + 1 = /dev/logicalco/dci1300/1 Second DCI-1300 card + ... + +220 char Myricom Myrinet "GM" board + 0 = /dev/myricom/gm0 First Myrinet GM board + 1 = /dev/myricom/gmp0 First board "root access" + 2 = /dev/myricom/gm1 Second Myrinet GM board + 3 = /dev/myricom/gmp1 Second board "root access" + ... + +221 char VME bus + 0 = /dev/bus/vme/m0 First master image + 1 = /dev/bus/vme/m1 Second master image + 2 = /dev/bus/vme/m2 Third master image + 3 = /dev/bus/vme/m3 Fourth master image + 4 = /dev/bus/vme/s0 First slave image + 5 = /dev/bus/vme/s1 Second slave image + 6 = /dev/bus/vme/s2 Third slave image + 7 = /dev/bus/vme/s3 Fourth slave image + 8 = /dev/bus/vme/ctl Control + + It is expected that all VME bus drivers will use the + same interface. For interface documentation see + http://www.vmelinux.org/. + +224 char A2232 serial card + 0 = /dev/ttyY0 First A2232 port + 1 = /dev/cuy0 Second A2232 port + ... + +225 char A2232 serial card (alternate devices) + 0 = /dev/cuy0 Callout device for ttyY0 + 1 = /dev/cuy1 Callout device for ttyY1 + ... + +226-239 UNASSIGNED 240-254 LOCAL/EXPERIMENTAL USE @@ -2025,6 +2525,14 @@ Non-transient sockets and named pipes may exist in /dev. Common entries are: /dev/log socket syslog local socket /dev/gpmdata socket gpm mouse multiplexer + Mount points + +The following names are reserved for mounting special filesystems +under /dev. These special filesystems provide kernel interfaces that +cannot be provided with standard device nodes. + +/dev/pts devpts PTY slave filesystem +/dev/shm shmfs POSIX shared memory maintenance access **** TERMINAL DEVICES diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 495c9a0be7ac..e1f608b84aa4 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -43,6 +43,7 @@ restrictions referred to are that the relevant option is valid if: SERIAL Serial support is enabled. SMP The kernel is an SMP kernel. SOUND Appropriate sound system support is enabled. + V4L Video For Linux support is enabled. VGA The VGA console has been enabled. VT Virtual terminal support is enabled. XT IBM PC/XT MFM hard disk support is enabled. @@ -116,6 +117,11 @@ running once the system is up. bmouse= [HW,MOUSE,PS2] Bus mouse. + bttv.card= [HW,V4L] bttv (bt848 + bt878 based grabber cards), most + bttv.radio= important insmod options are available as kernel args too. + bttv.pll= see Documentation/video4linux/bttv/Insmod-options + bttv.tuner= and Documentation/video4linux/bttv/CARDLIST + BusLogic= [HW,SCSI] cdu31a= [HW,CD] diff --git a/Documentation/video4linux/bttv/Insmod-options b/Documentation/video4linux/bttv/Insmod-options index 4cb55586dda3..97b87f674c0f 100644 --- a/Documentation/video4linux/bttv/Insmod-options +++ b/Documentation/video4linux/bttv/Insmod-options @@ -4,6 +4,7 @@ bttv.o insmod args: card=n card type, see CARDLIST for a list. + tuner=n tuner type, see CARDLIST for a list. radio=0/1 card supports radio pll=0/1/2 pll settings 0: don't use PLL diff --git a/Documentation/video4linux/bttv/Modules.conf b/Documentation/video4linux/bttv/Modules.conf index 84c79d659ceb..55f14650d8cd 100644 --- a/Documentation/video4linux/bttv/Modules.conf +++ b/Documentation/video4linux/bttv/Modules.conf @@ -9,7 +9,3 @@ alias char-major-81-0 bttv options bttv card=2 radio=1 options tuner debug=1 -# make alsa + msp3400 play nicely -options snd-card-ens snd_index=0 -options msp3400 mixer=1 - diff --git a/Documentation/video4linux/bttv/README b/Documentation/video4linux/bttv/README index bce7e55eef08..4af739a5cef1 100644 --- a/Documentation/video4linux/bttv/README +++ b/Documentation/video4linux/bttv/README @@ -31,8 +31,9 @@ Compile bttv ------------ If you are compiling the kernel version, just say 'm' if you are asked -for bttv. I /strongly/ suggest to compile bttv as module, because -there are some insmod options for configuring the driver. +for bttv. I /strongly/ recommend to compile bttv as module, because +there are some insmod options for configuring the driver. Starting +with 0.7.49 the most important ones are available as kernel args too. If you downloaded the separate bttv bundle: You need configured kernel sources to compile the bttv driver. The driver uses some Makefile diff --git a/Makefile b/Makefile index 1308977149a2..5a67ef713891 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 0 -EXTRAVERSION = -test13-pre6 +EXTRAVERSION = -test13-pre7 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) diff --git a/arch/alpha/lib/Makefile b/arch/alpha/lib/Makefile index cb23a987d491..1e3e485b5a59 100644 --- a/arch/alpha/lib/Makefile +++ b/arch/alpha/lib/Makefile @@ -35,7 +35,7 @@ OBJS = __divqu.o __remqu.o __divlu.o __remlu.o \ $(ev6)stxcpy.o \ $(ev6)stxncpy.o \ $(ev67)strchr.o \ - strrchr.o \ + $(ev67)strrchr.o \ $(ev6)memchr.o \ $(ev6)copy_user.o \ $(ev6)clear_user.o \ diff --git a/arch/alpha/lib/ev6-memcpy.S b/arch/alpha/lib/ev6-memcpy.S index 7ebcbc27b7de..c708a6fb9617 100644 --- a/arch/alpha/lib/ev6-memcpy.S +++ b/arch/alpha/lib/ev6-memcpy.S @@ -76,7 +76,7 @@ $single_head_quad: $do_unroll: addq $16, 64, $7 # E : Initial (+1 trip) wh64 address - cmple $18, 63, $1 # E : Can we go through the unrolled loop? + cmple $18, 127, $1 # E : Can we go through the unrolled loop? bne $1, $tail_quads # U : Nope nop # E : diff --git a/arch/alpha/lib/ev67-strrchr.S b/arch/alpha/lib/ev67-strrchr.S new file mode 100644 index 000000000000..7fe1be0e5ea5 --- /dev/null +++ b/arch/alpha/lib/ev67-strrchr.S @@ -0,0 +1,109 @@ +/* + * arch/alpha/lib/ev67-strrchr.S + * 21264 version by Rick Gorton + * + * 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 + + .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 diff --git a/arch/alpha/lib/memmove.S b/arch/alpha/lib/memmove.S index 3c8567e4e58e..73aed92537d0 100644 --- a/arch/alpha/lib/memmove.S +++ b/arch/alpha/lib/memmove.S @@ -26,12 +26,16 @@ memmove: bne $1,memcpy and $2,7,$2 /* Test for src/dest co-alignment. */ - bne $2,$misaligned + and $16,7,$1 + cmpule $16,$17,$3 + bne $3,$memmove_up /* dest < src */ and $4,7,$1 - beq $1,$skip_aligned_byte_loop_head + bne $2,$misaligned_dn + unop + beq $1,$skip_aligned_byte_loop_head_dn -$aligned_byte_loop_head: +$aligned_byte_loop_head_dn: lda $4,-1($4) lda $5,-1($5) unop @@ -48,13 +52,13 @@ $aligned_byte_loop_head: and $4,7,$6 stq_u $1,0($4) - bne $6,$aligned_byte_loop_head + bne $6,$aligned_byte_loop_head_dn -$skip_aligned_byte_loop_head: +$skip_aligned_byte_loop_head_dn: lda $18,-8($18) - blt $18,$skip_aligned_word_loop + blt $18,$skip_aligned_word_loop_dn -$aligned_word_loop: +$aligned_word_loop_dn: ldq $1,-8($5) nop lda $5,-8($5) @@ -63,22 +67,22 @@ $aligned_word_loop: stq $1,-8($4) nop lda $4,-8($4) - bge $18,$aligned_word_loop + bge $18,$aligned_word_loop_dn -$skip_aligned_word_loop: +$skip_aligned_word_loop_dn: lda $18,8($18) - bgt $18,$byte_loop_tail + bgt $18,$byte_loop_tail_dn unop ret $31,($26),1 .align 4 -$misaligned: +$misaligned_dn: nop fnop unop beq $18,$egress -$byte_loop_tail: +$byte_loop_tail_dn: ldq_u $3,-1($5) ldq_u $2,-1($4) lda $5,-1($5) @@ -91,8 +95,77 @@ $byte_loop_tail: bis $1,$2,$1 stq_u $1,0($4) + bgt $18,$byte_loop_tail_dn + br $egress + +$memmove_up: + mov $16,$4 + mov $17,$5 + bne $2,$misaligned_up + beq $1,$skip_aligned_byte_loop_head_up + +$aligned_byte_loop_head_up: + unop + ble $18,$egress + ldq_u $3,0($5) + ldq_u $2,0($4) + + lda $18,-1($18) + extbl $3,$5,$1 + insbl $1,$4,$1 + mskbl $2,$4,$2 + + bis $1,$2,$1 + lda $5,1($5) + stq_u $1,0($4) + lda $4,1($4) + + and $4,7,$6 + bne $6,$aligned_byte_loop_head_up + +$skip_aligned_byte_loop_head_up: + lda $18,-8($18) + blt $18,$skip_aligned_word_loop_up + +$aligned_word_loop_up: + ldq $1,0($5) + nop + lda $5,8($5) + lda $18,-8($18) + + stq $1,0($4) + nop + lda $4,8($4) + bge $18,$aligned_word_loop_up + +$skip_aligned_word_loop_up: + lda $18,8($18) + bgt $18,$byte_loop_tail_up + unop + ret $31,($26),1 + + .align 4 +$misaligned_up: + nop + fnop + unop + beq $18,$egress + +$byte_loop_tail_up: + ldq_u $3,0($5) + ldq_u $2,0($4) + lda $18,-1($18) + extbl $3,$5,$1 + + insbl $1,$4,$1 + mskbl $2,$4,$2 + bis $1,$2,$1 + stq_u $1,0($4) + + lda $5,1($5) + lda $4,1($4) nop - bgt $18,$byte_loop_tail + bgt $18,$byte_loop_tail_up $egress: ret $31,($26),1 diff --git a/arch/alpha/math-emu/Makefile b/arch/alpha/math-emu/Makefile index 91e5ba660ecc..4486b79e338d 100644 --- a/arch/alpha/math-emu/Makefile +++ b/arch/alpha/math-emu/Makefile @@ -1,18 +1,22 @@ # # Makefile for the FPU instruction emulation. # -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definition is now in the main makefile... -O_TARGET := math-emu.o -O_OBJS := math.o qrnnd.o CFLAGS += -I. -I$(TOPDIR)/include/math-emu -w -ifeq ($(CONFIG_MATHEMU),m) -M_OBJS := $(O_TARGET) +ifeq ($(CONFIG_MATHEMU),y) + +O_TARGET := math-emu.o +obj-y := math.o qrnnd.o + +else + +list-multi := math-emu.o +math-emu-objs := math.o qrnnd.o +obj-m := math-emu.o +math-emu.o: $(math-emu-objs) + $(LD) -r -o $@ $(math-emu-objs) + endif include $(TOPDIR)/Rules.make diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index 686821205500..ab052e2ff1a7 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -412,7 +412,7 @@ void show_regs(struct pt_regs * regs) /* * No need to lock the MM as we are the last user */ -void destroy_context(struct mm_struct *mm) +void release_segments(struct mm_struct *mm) { void * ldt = mm->context.segments; @@ -493,7 +493,7 @@ void release_thread(struct task_struct *dead_task) * we do not have to muck with descriptors here, that is * done in switch_mm() as needed. */ -int init_new_context(struct task_struct *p, struct mm_struct *new_mm) +void copy_segments(struct task_struct *p, struct mm_struct *new_mm) { struct mm_struct * old_mm; void *old_ldt, *ldt; @@ -506,11 +506,11 @@ int init_new_context(struct task_struct *p, struct mm_struct *new_mm) */ ldt = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE); if (!ldt) - return -ENOMEM; - memcpy(ldt, old_ldt, LDT_ENTRIES*LDT_ENTRY_SIZE); + printk(KERN_WARNING "ldt allocation failed\n"); + else + memcpy(ldt, old_ldt, LDT_ENTRIES*LDT_ENTRY_SIZE); } new_mm->context.segments = ldt; - return 0; } /* diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c index 32351f653b86..9e140d114466 100644 --- a/drivers/media/radio/radio-gemtek.c +++ b/drivers/media/radio/radio-gemtek.c @@ -265,7 +265,7 @@ static int __init gemtek_init(void) return -EINVAL; } - if (request_region(io, 4, "gemtek")) + if (!request_region(io, 4, "gemtek")) { printk(KERN_ERR "gemtek: port 0x%x already in use\n", io); return -EBUSY; diff --git a/drivers/media/video/bttv-cards.c b/drivers/media/video/bttv-cards.c index 886782f18f00..d21eddec890c 100644 --- a/drivers/media/video/bttv-cards.c +++ b/drivers/media/video/bttv-cards.c @@ -1,7 +1,8 @@ /* - bttv-cards.c -- this file has card-specific stuff + bttv-cards.c - bttv - Bt848 frame grabber driver + this file has configuration informations - card-specific stuff + like the big tvcards array for the most part Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) & Marcus Metzler (mocm@thp.uni-koeln.de) @@ -50,28 +51,55 @@ static void avermedia_tvphone_audio(struct bttv *btv, struct video_audio *v, static void terratv_audio(struct bttv *btv, struct video_audio *v, int set); static void gvbctv3pci_audio(struct bttv *btv, struct video_audio *v, int set); -MODULE_PARM(card,"1-4i"); -MODULE_PARM_DESC(card,"specify TV/grabber card model, see CARDLIST file for a list"); -MODULE_PARM(pll,"1-4i"); -MODULE_PARM_DESC(pll,"specify installed crystal (0=none, 28=28 MHz, 35=35 MHz)"); -MODULE_PARM(autoload,"i"); -MODULE_PARM_DESC(autoload,"automatically load i2c modules like tuner.o, default is 1 (yes)"); - -static unsigned int card[4] = { -1, -1, -1, -1 }; -static unsigned int pll[4] = { -1, -1, -1, -1 }; +/* config variables */ +static int triton1=0; +static unsigned int card[4] = { -1, -1, -1, -1 }; +static unsigned int pll[4] = { -1, -1, -1, -1 }; +static unsigned int tuner[4] = { -1, -1, -1, -1 }; #ifdef MODULE static unsigned int autoload = 1; #else static unsigned int autoload = 0; #endif +static unsigned int gpiomask = -1; +static unsigned int audioall = -1; +static unsigned int audiomux[5] = { -1, -1, -1, -1, -1 }; +/* insmod options */ +MODULE_PARM(triton1,"i"); +MODULE_PARM(card,"1-4i"); +MODULE_PARM_DESC(card,"specify TV/grabber card model, see CARDLIST file for a list"); +MODULE_PARM(pll,"1-4i"); +MODULE_PARM_DESC(pll,"specify installed crystal (0=none, 28=28 MHz, 35=35 MHz)"); +MODULE_PARM(tuner,"1-4i"); +MODULE_PARM_DESC(tuner,"specify installed tuner type"); +MODULE_PARM(autoload,"i"); +MODULE_PARM_DESC(autoload,"automatically load i2c modules like tuner.o, default is 1 (yes)"); MODULE_PARM(gpiomask,"i"); MODULE_PARM(audioall,"i"); MODULE_PARM(audiomux,"1-5i"); -static unsigned int gpiomask = -1; -static unsigned int audioall = -1; -static unsigned int audiomux[5] = { -1, -1, -1, -1, -1 }; +/* kernel args */ +#ifndef MODULE +static int __init p_card(char *str) { return bttv_parse(str,BTTV_MAX,card); } +static int __init p_pll(char *str) { return bttv_parse(str,BTTV_MAX,pll); } +static int __init p_tuner(char *str) { return bttv_parse(str,BTTV_MAX,tuner); } +__setup("bttv.card=", p_card); +__setup("bttv.pll=", p_pll); +__setup("bttv.tuner=", p_tuner); + +int __init bttv_parse(char *str, int max, int *vals) +{ + int i,number,res = 2; + + for (i = 0; res == 2 && i < max; i++) { + res = get_option(&str,&number); + if (res) + vals[i] = number; + } + return 1; +} +#endif /* ----------------------------------------------------------------------- */ /* list of card IDs for bt878+ cards */ @@ -176,12 +204,12 @@ struct tvcard bttv_tvcards[] = { tuner_type: -1, },{ name: "Diamond DTV2000", - video_inputs: 3, + video_inputs: 4, audio_inputs: 1, tuner: 0, svhs: 2, gpiomask: 3, - muxsel: { 2, 3, 1, 1}, + muxsel: { 2, 3, 1, 0}, audiomux: { 0, 1, 0, 1, 3}, needs_tvaudio: 1, tuner_type: -1, @@ -606,7 +634,7 @@ struct tvcard bttv_tvcards[] = { audio_inputs: 4, tuner: 0, svhs: 2, - gpiomask: 4, + gpiomask: 12, muxsel: { 2, 3, 1, 1}, audiomux: { 13, 14, 11, 7, 0, 0}, needs_tvaudio: 1, @@ -907,9 +935,11 @@ void __devinit bttv_init_card(struct bttv *btv) } } - /* tuner configuration */ + /* tuner configuration (from card list / insmod option) */ if (-1 != bttv_tvcards[btv->type].tuner_type) btv->tuner_type = bttv_tvcards[btv->type].tuner_type; + if (-1 != tuner[btv->nr]) + btv->tuner_type = tuner[btv->nr]; if (btv->tuner_type != -1) bttv_call_i2c_clients(btv,TUNER_SET_TYPE,&btv->tuner_type); @@ -1296,7 +1326,26 @@ gvbctv3pci_audio(struct bttv *btv, struct video_audio *v, int set) static void avermedia_tvphone_audio(struct bttv *btv, struct video_audio *v, int set) { - /* TODO */ +#if 0 /* needs more testing -- might be we need two versions for PAL/NTSC */ + int val = 0; + + if (set) { + if (v->mode & VIDEO_SOUND_LANG1) /* SAP */ + val = 0xce; + if (v->mode & VIDEO_SOUND_STEREO) + val = 0xcd; + if (val) { + btaor(val, 0xff, BT848_GPIO_OUT_EN); + btaor(val, 0xff, BT848_GPIO_DATA); + if (bttv_gpio) + bttv_gpio_tracking(btv,"avermedia"); + } + } else { + v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO | + VIDEO_SOUND_LANG1; + return; + } +#endif } static void @@ -1319,6 +1368,74 @@ terratv_audio(struct bttv *btv, struct video_audio *v, int set) } } + +/* ----------------------------------------------------------------------- */ +/* motherboard chipset specific stuff */ + +static struct { + char *name; + unsigned short vendor; + unsigned short device; +} needs_etbf[] __devinitdata = { + { "Intel 82437FX [Triton PIIX]", + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437 }, + { "VIA VT82C597 [Apollo VP3]", + PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C597_0 }, + { NULL, 0, 0 } +}; + +void __devinit bttv_check_chipset(void) +{ + int i; + struct pci_dev *dev = NULL; + + if(pci_pci_problems & PCIPCI_FAIL) + printk(KERN_WARNING "BT848 and your chipset may not work together.\n"); + + while ((dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82441, dev))) { + unsigned char b; + pci_read_config_byte(dev, 0x53, &b); + if (bttv_debug) + printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, " + "bufcon=0x%02x\n",b); + } + + if(pci_pci_problems & (PCIPCI_TRITON|PCIPCI_VIAETBF)) + { + printk(KERN_INFO "bttv: Host bridge needs ETBF enabled.\n"); + triton1=1; + } +} + +int __devinit bttv_handle_chipset(struct bttv *btv) +{ + unsigned char command; + + if (!triton1) + return 0; + + if (bttv_verbose) + printk("bttv%d: enabling 430FX/VP3 compatibilty\n",btv->nr); + + if (btv->id < 878) { + /* bt848 (mis)uses a bit in the irq mask */ + btv->triton1 = BT848_INT_ETBF; + } else { + /* bt878 has a bit in the pci config space for it */ + pci_read_config_byte(btv->dev, BT878_DEVCTRL, &command); + command |= BT878_EN_TBFX; + pci_write_config_byte(btv->dev, BT878_DEVCTRL, command); + pci_read_config_byte(btv->dev, BT878_DEVCTRL, &command); + if (!(command&BT878_EN_TBFX)) { + printk("bttv: 430FX compatibility could not be enabled\n"); + return -1; + } + } + return 0; +} + + #ifndef MODULE static int __init bttv_card_setup(char *str) diff --git a/drivers/media/video/bttv-driver.c b/drivers/media/video/bttv-driver.c index 248bda8a02f0..7ce4a4cf8939 100644 --- a/drivers/media/video/bttv-driver.c +++ b/drivers/media/video/bttv-driver.c @@ -57,8 +57,23 @@ static void bt848_set_risc_jmps(struct bttv *btv, int state); int bttv_num; /* number of Bt848s in use */ struct bttv bttvs[BTTV_MAX]; -/* insmod args */ -MODULE_PARM(triton1,"i"); +/* configuration variables */ +#if defined(__sparc__) || defined(__powerpc__) || defined(__hppa__) +static unsigned int bigendian=1; +#else +static unsigned int bigendian=0; +#endif +static unsigned int radio[BTTV_MAX]; +static unsigned int fieldnr = 0; +static unsigned int irq_debug = 0; +static unsigned int gbuffers = 2; +static unsigned int gbufsize = BTTV_MAX_FBUF; +static unsigned int combfilter = 0; +unsigned int bttv_debug = 0; +unsigned int bttv_verbose = 1; +unsigned int bttv_gpio = 0; + +/* insmod options */ MODULE_PARM(radio,"1-4i"); MODULE_PARM_DESC(radio,"The TV card supports radio, default is 0 (no)"); MODULE_PARM(bigendian,"i"); @@ -82,21 +97,11 @@ MODULE_PARM(combfilter,"i"); MODULE_DESCRIPTION("bttv - v4l driver module for bt848/878 based cards"); MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr"); -#if defined(__sparc__) || defined(__powerpc__) -static unsigned int bigendian=1; -#else -static unsigned int bigendian=0; +/* kernel args */ +#ifndef MODULE +static int __init p_radio(char *str) { return bttv_parse(str,BTTV_MAX,radio); } +__setup("bttv.radio=", p_radio); #endif -static int triton1=0; -static unsigned int radio[BTTV_MAX]; -static unsigned int fieldnr = 0; -static unsigned int irq_debug = 0; -static unsigned int gbuffers = 2; -static unsigned int gbufsize = BTTV_MAX_FBUF; -static unsigned int combfilter = 0; -unsigned int bttv_debug = 0; -unsigned int bttv_verbose = 1; -unsigned int bttv_gpio = 0; #define I2C_TIMING (0x7<<4) #define I2C_DELAY 10 @@ -382,7 +387,8 @@ static int set_pll(struct bttv *btv) /* printk ("bttv%d: PLL: is off\n",btv->nr); */ return 0; } - printk ("bttv%d: PLL: switching off\n",btv->nr); + if (bttv_verbose) + printk ("bttv%d: PLL: switching off\n",btv->nr); btwrite(0x00,BT848_TGCTRL); btwrite(0x00,BT848_PLL_XCI); btv->pll.pll_current = 0; @@ -2341,44 +2347,6 @@ static struct video_device radio_template= }; -#define TRITON_PCON 0x50 -#define TRITON_BUS_CONCURRENCY (1<<0) -#define TRITON_STREAMING (1<<1) -#define TRITON_WRITE_BURST (1<<2) -#define TRITON_PEER_CONCURRENCY (1<<3) - - -static void __devinit handle_chipset(void) -{ - struct pci_dev *dev = NULL; - - /* Just in case some nut set this to something dangerous */ - if (triton1) - triton1=BT848_INT_ETBF; - - while ((dev = pci_find_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_496, dev))) - { - /* Beware the SiS 85C496 my friend - rev 49 don't work with a bttv */ - printk(KERN_WARNING "BT848 and SIS 85C496 chipset don't always work together.\n"); - } - - while ((dev = pci_find_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_82441, dev))) - { - unsigned char b; - pci_read_config_byte(dev, 0x53, &b); - DEBUG(printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, ")); - DEBUG(printk("bufcon=0x%02x\n",b)); - } - - while ((dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437, dev))) - { - printk(KERN_INFO "bttv: Host bridge 82437FX Triton PIIX\n"); - triton1=BT848_INT_ETBF; - } -} - - static void bt848_set_risc_jmps(struct bttv *btv, int flags) { if (-1 == flags) { @@ -2485,10 +2453,6 @@ static void bt848_set_risc_jmps(struct bttv *btv, int flags) static int __devinit init_video_dev(struct bttv *btv) { - memcpy(&btv->video_dev,&bttv_template, sizeof(bttv_template)); - memcpy(&btv->vbi_dev,&vbi_template, sizeof(vbi_template)); - memcpy(&btv->radio_dev,&radio_template,sizeof(radio_template)); - audio(btv, AUDIO_MUTE, 1); if(video_register_device(&btv->video_dev,VFL_TYPE_GRABBER)<0) @@ -2914,7 +2878,7 @@ static void __devexit bttv_remove(struct pci_dev *pci_dev) static int __devinit bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) { int result; - unsigned char command,lat; + unsigned char lat; struct bttv *btv; #if defined(__powerpc__) unsigned int cmd; @@ -2936,7 +2900,11 @@ static int __devinit bttv_probe(struct pci_dev *dev, const struct pci_device_id btv->s_lock = SPIN_LOCK_UNLOCKED; init_waitqueue_head(&btv->gpioq); btv->shutdown=0; - + + memcpy(&btv->video_dev,&bttv_template, sizeof(bttv_template)); + memcpy(&btv->vbi_dev,&vbi_template, sizeof(vbi_template)); + memcpy(&btv->radio_dev,&radio_template,sizeof(radio_template)); + btv->id=dev->device; btv->irq=dev->irq; btv->bt848_adr=pci_resource_start(dev,0); @@ -2985,36 +2953,22 @@ static int __devinit bttv_probe(struct pci_dev *dev, const struct pci_device_id { printk(KERN_ERR "bttv%d: Bad irq number or handler\n", bttv_num); - goto fail; + goto fail1; } if (result==-EBUSY) { printk(KERN_ERR "bttv%d: IRQ %d busy, change your PnP config in BIOS\n",bttv_num,btv->irq); - goto fail; + goto fail1; } if (result < 0) - goto fail; + goto fail1; + if (0 != bttv_handle_chipset(btv)) { + result = -1; + goto fail2; + } + pci_set_master(dev); - - btv->triton1=triton1 ? BT848_INT_ETBF : 0; - if (triton1 && btv->id >= 878) - { - btv->triton1 = 0; - printk("bttv: Enabling 430FX compatibilty for bt878\n"); - pci_read_config_byte(dev, BT878_DEVCTRL, &command); - command|=BT878_EN_TBFX; - pci_write_config_byte(dev, BT878_DEVCTRL, command); - pci_read_config_byte(dev, BT878_DEVCTRL, &command); - if (!(command&BT878_EN_TBFX)) - { - printk("bttv: 430FX compatibility could not be enabled\n"); - free_irq(btv->irq,btv); - result = -1; - goto fail; - } - } - pci_set_drvdata(dev,btv); if(init_bt848(btv) < 0) { @@ -3024,8 +2978,10 @@ static int __devinit bttv_probe(struct pci_dev *dev, const struct pci_device_id bttv_num++; return 0; - - fail: + + fail2: + free_irq(btv->irq,btv); + fail1: release_mem_region(pci_resource_start(btv->dev,0), pci_resource_len(btv->dev,0)); return result; @@ -3068,7 +3024,7 @@ int bttv_init_module(void) printk(KERN_INFO "bttv: using %d buffers with %dk (%dk total) for capture\n", gbuffers,gbufsize/1024,gbuffers*gbufsize/1024); - handle_chipset(); + bttv_check_chipset(); return pci_module_init(&bttv_pci_driver); } @@ -3082,24 +3038,6 @@ void bttv_cleanup_module(void) module_init(bttv_init_module); module_exit(bttv_cleanup_module); -#ifndef MODULE - -static int __init bttv_radio_setup(char *str) -{ - int i,number,res = 2; - - for (i = 0; res == 2 && i < BTTV_MAX; i++) { - res = get_option(&str,&number); - if (res) - radio[i] = number; - } - return 1; -} - -__setup("bttv_radio=", bttv_radio_setup); - -#endif /* not MODULE */ - /* * Local variables: * c-basic-offset: 8 diff --git a/drivers/media/video/bttv.h b/drivers/media/video/bttv.h index c7aaa8bf3b5a..c88e0a906080 100644 --- a/drivers/media/video/bttv.h +++ b/drivers/media/video/bttv.h @@ -134,6 +134,13 @@ extern void bttv_init_card(struct bttv *btv); extern void tea5757_set_freq(struct bttv *btv, unsigned short freq); extern void bttv_hauppauge_boot_msp34xx(struct bttv *btv); +/* kernel cmd line parse helper */ +extern int bttv_parse(char *str, int max, int *vals); + +/* extra tweaks for some chipsets */ +extern void bttv_check_chipset(void); +extern int bttv_handle_chipset(struct bttv *btv); + /* ---------------------------------------------------------- */ /* exported by bttv-if.c */ /* interface for gpio access by other modules */ diff --git a/drivers/media/video/bttvp.h b/drivers/media/video/bttvp.h index 4676ee61b5ac..40fa9c5c680a 100644 --- a/drivers/media/video/bttvp.h +++ b/drivers/media/video/bttvp.h @@ -25,7 +25,7 @@ #ifndef _BTTVP_H_ #define _BTTVP_H_ -#define BTTV_VERSION_CODE KERNEL_VERSION(0,7,48) +#define BTTV_VERSION_CODE KERNEL_VERSION(0,7,50) #include @@ -42,7 +42,7 @@ /* ---------------------------------------------------------- */ /* bttv-driver.c */ -/* insmod options */ +/* insmod options / kernel args */ extern unsigned int bttv_verbose; extern unsigned int bttv_debug; extern unsigned int bttv_gpio; diff --git a/drivers/net/3c523.c b/drivers/net/3c523.c index 12f9f0a9d1b2..bf17e006fda4 100644 --- a/drivers/net/3c523.c +++ b/drivers/net/3c523.c @@ -158,15 +158,10 @@ sizeof(nop_cmd) = 8; /**************************************************************************/ -#define DELAY(x) {int i=jiffies; \ - if(loops_per_sec == 1) \ - while(time_after(i+(x), jiffies)); \ - else \ - __delay((loops_per_sec>>5)*x); \ - } +#define DELAY(x) { mdelay(32 * x); } /* a much shorter delay: */ -#define DELAY_16(); { __delay( (loops_per_sec>>16)+1 ); } +#define DELAY_16(); { udelay(16) ; } /* wait for command with timeout: */ #define WAIT_4_SCB_CMD() { int i; \ diff --git a/drivers/net/Config.in b/drivers/net/Config.in index e578ca3f8c27..8c2a53c2e51e 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -258,7 +258,7 @@ source drivers/net/tokenring/Config.in bool 'Fibre Channel driver support' CONFIG_NET_FC if [ "$CONFIG_NET_FC" = "y" ]; then - dep_tristate ' Interphase 5526 Tachyon chipset based adapter support' CONFIG_IPHASE5526 $CONFIG_SCSI + dep_tristate ' Interphase 5526 Tachyon chipset based adapter support' CONFIG_IPHASE5526 $CONFIG_SCSI $CONFIG_PCI fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then diff --git a/drivers/net/acenic.c b/drivers/net/acenic.c index 19bf1ef0fa70..7bd21d874bfb 100644 --- a/drivers/net/acenic.c +++ b/drivers/net/acenic.c @@ -2,7 +2,7 @@ * acenic.c: Linux driver for the Alteon AceNIC Gigabit Ethernet card * and other Tigon based cards. * - * Copyright 1998-2000 by Jes Sorensen, . + * Copyright 1998-2000 by Jes Sorensen, . * * Thanks to Alteon and 3Com for providing hardware and documentation * enabling me to write this driver. @@ -39,6 +39,11 @@ * where the driver would disable * bus master mode if it had to disable * write and invalidate. + * Stephen Hack : Fixed ace_set_mac_addr for little + * endian systems. + * Val Henson : Reset Jumbo skb producer and + * rx producer index when + * flushing the Jumbo ring. */ #include @@ -55,10 +60,12 @@ #include #include #include +#include -#undef INDEX_DEBUG - +#ifdef SIOCETHTOOL #include +#endif + #include #include @@ -69,6 +76,9 @@ #include +#undef INDEX_DEBUG +#define TX_HOST_RING 1 + #ifdef CONFIG_ACENIC_OMIT_TIGON_I #define ACE_IS_TIGON_I(ap) 0 #else @@ -93,6 +103,7 @@ #define PCI_DEVICE_ID_NETGEAR_GA620T 0x630a #endif + /* * Farallon used the DEC vendor ID by mistake and they seem not * to care - stinky! @@ -107,6 +118,31 @@ #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 @@ -119,6 +155,19 @@ #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] @@ -130,10 +179,6 @@ #define net_device device #endif -#if (LINUX_VERSION_CODE >= 0x02031b) -#define NEW_NETINIT -#endif - #if (LINUX_VERSION_CODE < 0x02032a) typedef u32 dma_addr_t; @@ -154,10 +199,20 @@ static inline void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, #if (LINUX_VERSION_CODE < 0x02032b) /* * SoftNet + * + * For pre-softnet kernels we need to tell the upper layer not to + * re-enter start_xmit() while we are in there. However softnet + * guarantees not to enter while we are in there so there is no need + * to do the netif_stop_queue() dance unless the transmit queue really + * gets stuck. This should also improve performance according to tests + * done by Aman Singla. */ -#define dev_kfree_skb_irq(a) dev_kfree_skb(a) -#define netif_wake_queue(dev) clear_bit(0, &dev->tbusy) -#define netif_stop_queue(dev) set_bit(0, &dev->tbusy) +#define dev_kfree_skb_irq(a) dev_kfree_skb(a) +#define netif_wake_queue(dev) clear_bit(0, &dev->tbusy) +#define netif_stop_queue(dev) set_bit(0, &dev->tbusy) +#define late_stop_netif_stop_queue(dev) {do{} while(0);} +#define early_stop_netif_stop_queue(dev) test_and_set_bit(0,&dev->tbusy) +#define early_stop_netif_wake_queue(dev) netif_wake_queue(dev) static inline void netif_start_queue(struct net_device *dev) { @@ -166,16 +221,42 @@ static inline void netif_start_queue(struct net_device *dev) dev->start = 1; } -#define ace_mark_net_bh(foo) mark_bh(foo) -#define netif_queue_stopped(dev) dev->tbusy -#define netif_running(dev) dev->start -#define ace_if_down(dev) {do{dev->start = 0;}while (0);} +#define ace_mark_net_bh() mark_bh(NET_BH) +#define netif_queue_stopped(dev) dev->tbusy +#define netif_running(dev) dev->start +#define ace_if_down(dev) {do{dev->start = 0;} while(0);} + +#define tasklet_struct tq_struct +static inline void tasklet_schedule(struct tasklet_struct *tasklet) +{ + queue_task(tasklet, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static inline void tasklet_init(struct tasklet_struct *tasklet, + void (*func)(unsigned long), + unsigned long data) +{ + tasklet->next = NULL; + tasklet->sync = 0; + tasklet->routine = (void (*)(void *))func; + tasklet->data = (void *)data; +} +#define tasklet_kill(tasklet) {do{} while(0);} #else -#define NET_BH 0 -#define ace_mark_net_bh(foo) {do{} while(0);} -#define ace_if_down(dev) {do{} while(0);} +#define late_stop_netif_stop_queue(dev) netif_stop_queue(dev) +#define early_stop_netif_stop_queue(dev) 0 +#define early_stop_netif_wake_queue(dev) {do{} while(0);} +#define ace_mark_net_bh() {do{} while(0);} +#define ace_if_down(dev) {do{} while(0);} #endif +#if (LINUX_VERSION_CODE >= 0x02031b) +#define NEW_NETINIT +#define ACE_PROBE_ARG void +#else +#define ACE_PROBE_ARG struct net_device *dev +#endif #define ACE_MAX_MOD_PARMS 8 #define BOARD_IDX_STATIC 0 @@ -400,19 +481,15 @@ static int tx_ratio[ACE_MAX_MOD_PARMS]; static int dis_pci_mem_inval[ACE_MAX_MOD_PARMS] = {1, 1, 1, 1, 1, 1, 1, 1}; static char version[] __initdata = - "acenic.c: v0.47 09/18/2000 Jes Sorensen, linux-acenic@SunSITE.auc.dk\n" + "acenic.c: v0.49 12/13/2000 Jes Sorensen, linux-acenic@SunSITE.auc.dk\n" " http://home.cern.ch/~jes/gige/acenic.html\n"; -static struct net_device *root_dev; +static struct net_device *root_dev = NULL; static int probed __initdata = 0; -#ifdef NEW_NETINIT -int __init acenic_probe (void) -#else -int __init acenic_probe (struct net_device *dev) -#endif +int __init acenic_probe (ACE_PROBE_ARG) { #ifdef NEW_NETINIT struct net_device *dev; @@ -425,7 +502,7 @@ int __init acenic_probe (struct net_device *dev) if (probed) return -ENODEV; - probed ++; + probed++; if (!pci_present()) /* is PCI support present? */ return -ENODEV; @@ -436,7 +513,7 @@ int __init acenic_probe (struct net_device *dev) if (!((pdev->vendor == PCI_VENDOR_ID_ALTEON) && ((pdev->device == PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE) || - (pdev->device == PCI_DEVICE_ID_ALTEON_ACENIC_COPPER)) ) && + (pdev->device == PCI_DEVICE_ID_ALTEON_ACENIC_COPPER)))&& !((pdev->vendor == PCI_VENDOR_ID_3COM) && (pdev->device == PCI_DEVICE_ID_3COM_3C985)) && !((pdev->vendor == PCI_VENDOR_ID_NETGEAR) && @@ -460,6 +537,8 @@ int __init acenic_probe (struct net_device *dev) break; } + SET_MODULE_OWNER(dev); + if (!dev->priv) dev->priv = kmalloc(sizeof(*ap), GFP_KERNEL); if (!dev->priv) { @@ -615,7 +694,7 @@ int __init acenic_probe (struct net_device *dev) #ifdef MODULE -MODULE_AUTHOR("Jes Sorensen "); +MODULE_AUTHOR("Jes Sorensen "); MODULE_DESCRIPTION("AceNIC/3C985/GA620 Gigabit Ethernet driver"); MODULE_PARM(link, "1-" __MODULE_STRING(8) "i"); MODULE_PARM(trace, "1-" __MODULE_STRING(8) "i"); @@ -634,8 +713,8 @@ void __exit ace_module_cleanup(void) short i; while (root_dev) { - next = ((struct ace_private *)root_dev->priv)->next; ap = root_dev->priv; + next = ap->next; regs = ap->regs; @@ -730,8 +809,8 @@ int __init ace_module_init(void) } -#ifdef MODULE #if (LINUX_VERSION_CODE < 0x02032a) +#ifdef MODULE int init_module(void) { return ace_module_init(); @@ -742,11 +821,11 @@ void cleanup_module(void) { ace_module_cleanup(); } +#endif #else module_init(ace_module_init); module_exit(ace_module_cleanup); #endif -#endif static void ace_free_descriptors(struct net_device *dev) @@ -815,12 +894,18 @@ static int ace_allocate_descriptors(struct net_device *dev) size = (sizeof(struct event) * EVT_RING_ENTRIES); - ap->evt_ring = pci_alloc_consistent(ap->pdev, size, - &ap->evt_ring_dma); + ap->evt_ring = pci_alloc_consistent(ap->pdev, size, &ap->evt_ring_dma); if (ap->evt_ring == NULL) goto fail; + size = (sizeof(struct tx_desc) * TX_RING_ENTRIES); + + ap->tx_ring = pci_alloc_consistent(ap->pdev, size, &ap->tx_ring_dma); + + if (ap->tx_ring == NULL) + goto fail; + ap->evt_prd = pci_alloc_consistent(ap->pdev, sizeof(u32), &ap->evt_prd_dma); if (ap->evt_prd == NULL) @@ -1070,7 +1155,7 @@ static int __init ace_init(struct net_device *dev) } else if (ap->pci_command & PCI_COMMAND_INVALIDATE) { printk(KERN_INFO " PCI memory write & invalidate " "enabled by BIOS, enabling counter measures\n"); - + switch(SMP_CACHE_BYTES) { case 16: tmp |= DMA_WRITE_MAX_16; @@ -1084,7 +1169,7 @@ static int __init ace_init(struct net_device *dev) default: printk(KERN_INFO " Cache line size %i not " "supported, PCI write and invalidate " - "disabled\n", L1_CACHE_BYTES); + "disabled\n", SMP_CACHE_BYTES); ap->pci_command &= ~PCI_COMMAND_INVALIDATE; pci_write_config_word(ap->pdev, PCI_COMMAND, ap->pci_command); @@ -1170,7 +1255,7 @@ static int __init ace_init(struct net_device *dev) ap->fw_running = 0; tmp_ptr = (unsigned long) ap->info_dma; -#if (BITS_PER_LONG == 64) +#ifdef ACE_64BIT_PTR writel(tmp_ptr >> 32, ®s->InfoPtrHi); #else writel(0, ®s->InfoPtrHi); @@ -1269,18 +1354,19 @@ static int __init ace_init(struct net_device *dev) *(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); @@ -1524,9 +1610,9 @@ static void ace_timer(unsigned long data) } -static void ace_bh(struct net_device *dev) +static void ace_tasklet(unsigned long dev) { - struct ace_private *ap = dev->priv; + struct ace_private *ap = ((struct net_device *)dev)->priv; int cur_size; cur_size = atomic_read(&ap->cur_rx_bufs); @@ -1558,7 +1644,7 @@ static void ace_bh(struct net_device *dev) #endif ace_load_jumbo_rx_ring(ap, RX_JUMBO_SIZE - cur_size); } - ap->bh_pending = 0; + ap->tasklet_pending = 0; } @@ -1580,7 +1666,7 @@ static void ace_dump_trace(struct ace_private *ap) * * Loading rings is safe without holding the spin lock since this is * done only before the device is enabled, thus no interrupts are - * generated and by the interrupt handler/bh handler. + * generated and by the interrupt handler/tasklet handler. */ static void ace_load_std_rx_ring(struct ace_private *ap, int nr_bufs) { @@ -1838,7 +1924,20 @@ static u32 ace_handle_event(struct net_device *dev, u32 evtcsm, u32 evtprd) ap->skb->rx_jumbo_skbuff[i].skb = NULL; } } + + if (ACE_IS_TIGON_I(ap)) { + struct cmd cmd; + cmd.evt = C_SET_RX_JUMBO_PRD_IDX; + cmd.code = 0; + cmd.idx = 0; + ace_issue_cmd(ap->regs, &cmd); + } else { + writel(0, &((ap->regs)->RxJumboPrd)); + wmb(); + } + ap->jumbo = 0; + ap->rx_jumbo_skbprd = 0; printk(KERN_INFO "%s: Jumbo ring flushed\n", dev->name); if (!ap->tx_full) @@ -2048,11 +2147,11 @@ static void ace_interrupt(int irq, void *dev_id, struct pt_regs *ptregs) * caused by the NIC actually trying to access * these incorrectly. */ -#if (BITS_PER_LONG == 64) - writel(0, &ap->tx_ring[idx].addr.addrhi); +#ifdef ACE_64BIT_PTR + ap->tx_ring[idx].addr.addrhi = 0; #endif - writel(0, &ap->tx_ring[idx].addr.addrlo); - writel(0, &ap->tx_ring[idx].flagsize); + ap->tx_ring[idx].addr.addrlo = 0; + ap->tx_ring[idx].flagsize = 0; idx = (idx + 1) % TX_RING_ENTRIES; } while (idx != txcsm); @@ -2069,7 +2168,7 @@ static void ace_interrupt(int irq, void *dev_id, struct pt_regs *ptregs) * I've seen cases where it would fail otherwise ;-( */ netif_wake_queue(dev); - ace_mark_net_bh(NET_BH); + ace_mark_net_bh(); /* * TX ring is no longer full, aka the @@ -2096,7 +2195,7 @@ static void ace_interrupt(int irq, void *dev_id, struct pt_regs *ptregs) */ if (netif_running(dev)) { int cur_size; - int run_bh = 0; + int run_tasklet = 0; cur_size = atomic_read(&ap->cur_rx_bufs); if (cur_size < RX_LOW_STD_THRES) { @@ -2108,7 +2207,7 @@ static void ace_interrupt(int irq, void *dev_id, struct pt_regs *ptregs) ace_load_std_rx_ring(ap, RX_RING_SIZE - cur_size); } else - run_bh = 1; + run_tasklet = 1; } if (!ACE_IS_TIGON_I(ap)) { @@ -2123,7 +2222,7 @@ static void ace_interrupt(int irq, void *dev_id, struct pt_regs *ptregs) #endif ace_load_mini_rx_ring(ap, RX_MINI_SIZE - cur_size); } else - run_bh = 1; + run_tasklet = 1; } } @@ -2139,13 +2238,12 @@ static void ace_interrupt(int irq, void *dev_id, struct pt_regs *ptregs) #endif ace_load_jumbo_rx_ring(ap, RX_JUMBO_SIZE - cur_size); } else - run_bh = 1; + run_tasklet = 1; } } - if (run_bh && !ap->bh_pending) { - ap->bh_pending = 1; - queue_task(&ap->immediate, &tq_immediate); - mark_bh(IMMEDIATE_BH); + if (run_tasklet && !ap->tasklet_pending) { + ap->tasklet_pending = 1; + tasklet_schedule(&ap->ace_tasklet); } } @@ -2211,7 +2309,7 @@ static int ace_open(struct net_device *dev) netif_start_queue(dev); - MOD_INC_USE_COUNT; + ACE_MOD_INC_USE_COUNT; /* * Setup the timer @@ -2223,11 +2321,7 @@ static int ace_open(struct net_device *dev) /* * Setup the bottom half rx ring refill handler */ - INIT_LIST_HEAD(&ap->immediate.list); - ap->immediate.sync = 0; - ap->immediate.routine = (void *)(void *)ace_bh; - ap->immediate.data = dev; - + tasklet_init(&ap->ace_tasklet, ace_tasklet, (unsigned long)dev); return 0; } @@ -2261,6 +2355,8 @@ static int ace_close(struct net_device *dev) cmd.idx = 0; ace_issue_cmd(regs, &cmd); + tasklet_kill(&ap->ace_tasklet); + /* * Make sure one CPU is not processing packets while * buffers are being released by another. @@ -2275,9 +2371,7 @@ static int ace_close(struct net_device *dev) skb = ap->skb->tx_skbuff[i].skb; mapping = ap->skb->tx_skbuff[i].mapping; if (skb) { - writel(0, &ap->tx_ring[i].addr.addrhi); - writel(0, &ap->tx_ring[i].addr.addrlo); - writel(0, &ap->tx_ring[i].flagsize); + memset(&ap->tx_ring[i].addr, 0, sizeof(struct tx_desc)); pci_unmap_single(ap->pdev, mapping, skb->len, PCI_DMA_TODEVICE); dev_kfree_skb(skb); @@ -2294,7 +2388,7 @@ static int ace_close(struct net_device *dev) restore_flags(flags); - MOD_DEC_USE_COUNT; + ACE_MOD_DEC_USE_COUNT; return 0; } @@ -2307,14 +2401,10 @@ static int ace_start_xmit(struct sk_buff *skb, struct net_device *dev) u32 idx, flagsize; /* - * ARGH, there is just no pretty way to do this + * This only happens with pre-softnet, ie. 2.2.x kernels. */ -#if (LINUX_VERSION_CODE < 0x02032b) - if (test_and_set_bit(0, &dev->tbusy)) + if (early_stop_netif_stop_queue(dev)) return 1; -#else - netif_stop_queue(dev); -#endif idx = ap->tx_prd; @@ -2332,12 +2422,9 @@ static int ace_start_xmit(struct sk_buff *skb, struct net_device *dev) pci_map_single(ap->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); addr = (unsigned long) ap->skb->tx_skbuff[idx].mapping; -#if (BITS_PER_LONG == 64) - writel(addr >> 32, &ap->tx_ring[idx].addr.addrhi); -#endif - writel(addr & 0xffffffff, &ap->tx_ring[idx].addr.addrlo); flagsize = (skb->len << 16) | (BD_FLG_END) ; - writel(flagsize, &ap->tx_ring[idx].flagsize); + set_aceaddr(&ap->tx_ring[idx].addr, addr); + ap->tx_ring[idx].flagsize = flagsize; wmb(); idx = (idx + 1) % TX_RING_ENTRIES; @@ -2358,7 +2445,8 @@ static int ace_start_xmit(struct sk_buff *skb, struct net_device *dev) */ mod_timer(&ap->timer, jiffies + (3 * HZ)); - /* The following check will fix a race between the interrupt + /* + * The following check will fix a race between the interrupt * handler increasing the tx_ret_csm and testing for tx_full * and this tx routine's testing the tx_ret_csm and setting * the tx_full; note that this fix makes assumptions on the @@ -2369,13 +2457,17 @@ static int ace_start_xmit(struct sk_buff *skb, struct net_device *dev) if (((idx + 2) % TX_RING_ENTRIES != ap->tx_ret_csm) && xchg(&ap->tx_full, 0)) { del_timer(&ap->timer); + /* + * We may not need this one in the post softnet era + * in this case this can be changed to a + * early_stop_netif_wake_queue(dev); + */ netif_wake_queue(dev); + } else { + late_stop_netif_stop_queue(dev); } } else { - /* - * No need for it to be atomic - seems it needs to be - */ - netif_wake_queue(dev); + early_stop_netif_wake_queue(dev); } dev->trans_start = jiffies; @@ -2408,7 +2500,7 @@ static int ace_change_mtu(struct net_device *dev, int new_mtu) while (test_and_set_bit(0, &ap->jumbo_refill_busy)); synchronize_irq(); ace_set_rxtx_parms(dev, 0); - if (ap->jumbo){ + if (ap->jumbo) { struct cmd cmd; cmd.evt = C_RESET_JUMBO_RNG; @@ -2424,6 +2516,7 @@ static int ace_change_mtu(struct net_device *dev, int new_mtu) static int ace_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { +#ifdef SIOCETHTOOL struct ace_private *ap = dev->priv; struct ace_regs *regs = ap->regs; struct ethtool_cmd ecmd; @@ -2483,7 +2576,11 @@ static int ace_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) ecmd.autoneg = AUTONEG_DISABLE; #if 0 + /* + * Current struct ethtool_cmd is insufficient + */ ecmd.trace = readl(®s->TuneTrace); + ecmd.txcoal = readl(®s->TuneTxCoalTicks); ecmd.rxcoal = readl(®s->TuneRxCoalTicks); #endif @@ -2551,6 +2648,7 @@ static int ace_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) } return 0; } +#endif return -EOPNOTSUPP; } @@ -2563,7 +2661,7 @@ static int ace_set_mac_addr(struct net_device *dev, void *p) { struct sockaddr *addr=p; struct ace_regs *regs; - u16 *da; + u8 *da; struct cmd cmd; if(netif_running(dev)) @@ -2571,11 +2669,11 @@ static int ace_set_mac_addr(struct net_device *dev, void *p) memcpy(dev->dev_addr, addr->sa_data,dev->addr_len); - da = (u16 *)dev->dev_addr; + da = (u8 *)dev->dev_addr; regs = ((struct ace_private *)dev->priv)->regs; - writel(da[0], ®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; @@ -3000,6 +3098,6 @@ static int __init read_eeprom_byte(struct net_device *dev, /* * Local variables: - * compile-command: "gcc -D__KERNEL__ -DMODULE -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -DMODVERSIONS -include ../../include/linux/modversions.h -c -o acenic.o acenic.c" + * compile-command: "gcc -D__SMP__ -D__KERNEL__ -DMODULE -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -DMODVERSIONS -include ../../include/linux/modversions.h -c -o acenic.o acenic.c" * End: */ diff --git a/drivers/net/acenic.h b/drivers/net/acenic.h index b08695c93465..c67121e4790d 100644 --- a/drivers/net/acenic.h +++ b/drivers/net/acenic.h @@ -83,9 +83,9 @@ struct ace_regs { u32 Mb2Hi; u32 TxPrd; u32 Mb3Hi; - u32 RxStdPrd; /* RxStdPrd */ + u32 RxStdPrd; u32 Mb4Hi; - u32 RxJumboPrd; /* RxJumboPrd */ + u32 RxJumboPrd; u32 Mb5Hi; u32 RxMiniPrd; u32 Mb6Hi; @@ -409,6 +409,7 @@ struct cmd { #define RCB_FLG_IP_SUM 0x02 #define RCB_FLG_VLAN_ASSIST 0x10 #define RCB_FLG_COAL_INT_ONLY 0x20 +#define RCB_FLG_TX_HOST_RING 0x40 #define RCB_FLG_IEEE_SNAP_SUM 0x80 #define RCB_FLG_EXT_RX_BD 0x100 #define RCB_FLG_RNG_DISABLE 0x200 @@ -417,7 +418,7 @@ struct cmd { /* * TX ring */ -#define TX_RING_ENTRIES 128 +#define TX_RING_ENTRIES 256 #define TX_RING_SIZE (TX_RING_ENTRIES * sizeof(struct tx_desc)) #define TX_RING_BASE 0x3800 @@ -593,49 +594,56 @@ struct ace_skb */ struct ace_private { - struct ace_skb *skb; + struct ace_info *info; struct ace_regs *regs; /* register base */ - volatile int fw_running; - int version, fw_up, link; + struct ace_skb *skb; + dma_addr_t info_dma; /* 32/64 bit */ + + int version, link; int promisc, mcast_all; + /* - * The send ring is located in the shared memory window + * TX elements */ - struct ace_info *info; - struct tx_desc *tx_ring; - dma_addr_t info_dma; + struct tx_desc *tx_ring + __attribute__ ((aligned (SMP_CACHE_BYTES))); + struct timer_list timer; /* used by TX handling only */ u32 tx_prd; volatile u32 tx_full, tx_ret_csm; - struct timer_list timer; + /* + * RX elements + */ unsigned long std_refill_busy __attribute__ ((aligned (SMP_CACHE_BYTES))); unsigned long mini_refill_busy, jumbo_refill_busy; - atomic_t cur_rx_bufs, - cur_mini_bufs, - cur_jumbo_bufs; + atomic_t cur_rx_bufs; + atomic_t cur_mini_bufs; + atomic_t cur_jumbo_bufs; u32 rx_std_skbprd, rx_mini_skbprd, rx_jumbo_skbprd; u32 cur_rx; - struct tq_struct immediate; - int bh_pending, jumbo; - /* - * These elements are allocated using consistent PCI dma memory. - */ + struct rx_desc *rx_std_ring; struct rx_desc *rx_jumbo_ring; struct rx_desc *rx_mini_ring; struct rx_desc *rx_return_ring; - dma_addr_t rx_ring_base_dma; + + int tasklet_pending, jumbo; + struct tasklet_struct ace_tasklet; struct event *evt_ring; - dma_addr_t evt_ring_dma; volatile u32 *evt_prd, *rx_ret_prd, *tx_csm; + + dma_addr_t tx_ring_dma; /* 32/64 bit */ + dma_addr_t rx_ring_base_dma; + dma_addr_t evt_ring_dma; dma_addr_t evt_prd_dma, rx_ret_prd_dma, tx_csm_dma; unsigned char *trace_buf; struct pci_dev *pdev; struct net_device *next; + volatile int fw_running; int board_idx; u16 pci_command; u8 pci_latency; @@ -652,7 +660,7 @@ struct ace_private static inline void set_aceaddr(aceaddr *aa, dma_addr_t addr) { unsigned long baddr = (unsigned long) addr; -#if (BITS_PER_LONG == 64) +#ifdef ACE_64BIT_PTR aa->addrlo = baddr & 0xffffffff; aa->addrhi = baddr >> 32; #else @@ -668,7 +676,7 @@ static inline void *get_aceaddr(aceaddr *aa) { unsigned long addr; mb(); -#if (BITS_PER_LONG == 64) +#ifdef ACE_64BIT_PTR addr = (u64)aa->addrhi << 32 | aa->addrlo; #else addr = aa->addrlo; @@ -710,7 +718,7 @@ static int ace_open(struct net_device *dev); static int ace_start_xmit(struct sk_buff *skb, struct net_device *dev); static int ace_close(struct net_device *dev); static void ace_timer(unsigned long data); -static void ace_bh(struct net_device *dev); +static void ace_tasklet(unsigned long dev); static void ace_dump_trace(struct ace_private *ap); static void ace_set_multicast_list(struct net_device *dev); static int ace_change_mtu(struct net_device *dev, int new_mtu); diff --git a/drivers/net/acenic_firmware.h b/drivers/net/acenic_firmware.h index cb2441285a68..2a906a7bb350 100644 --- a/drivers/net/acenic_firmware.h +++ b/drivers/net/acenic_firmware.h @@ -17,10 +17,11 @@ #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, diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c index 7a4c1a1d6e49..a2eeef4ce9c4 100644 --- a/drivers/net/appletalk/cops.c +++ b/drivers/net/appletalk/cops.c @@ -375,7 +375,7 @@ static int __init cops_irq (int ioaddr, int board) { outb(0, ioaddr+DAYNA_RESET); inb(ioaddr+DAYNA_RESET); - udelay(333333); + mdelay(333); } if(board==TANGENT) { @@ -504,7 +504,7 @@ static void cops_reset(struct net_device *dev, int sleep) schedule(); } else - udelay(333333); + mdelay(333); } netif_wake_queue(dev); return; diff --git a/drivers/net/arlan.c b/drivers/net/arlan.c index aee2bcbddf0e..d172382a084a 100644 --- a/drivers/net/arlan.c +++ b/drivers/net/arlan.c @@ -1323,7 +1323,7 @@ static int arlan_open(struct net_device *dev) priv->timer.function = &arlan_registration_timer; /* timer handler */ arlan_command(dev, ARLAN_COMMAND_POWERUP | ARLAN_COMMAND_LONG_WAIT_NOW); - udelay(200000); + mdelay(200); add_timer(&priv->timer); #ifdef CONFIG_PROC_FS diff --git a/drivers/net/fc/iph5526.c b/drivers/net/fc/iph5526.c index 5cdad9da4bab..ba550b23b4b2 100644 --- a/drivers/net/fc/iph5526.c +++ b/drivers/net/fc/iph5526.c @@ -1,7 +1,7 @@ /********************************************************************** * iph5526.c: IP/SCSI driver for the Interphase 5526 PCI Fibre Channel * Card. - * Copyright (C) 1999 Vineet M Abraham + * Copyright (C) 1999 Vineet M Abraham * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -33,10 +33,9 @@ Vineet M Abraham */ static const char *version = - "iph5526.c:v1.0 07.08.99 Vineet Abraham (vma@iol.unh.edu)\n"; + "iph5526.c:v1.0 07.08.99 Vineet Abraham (vmabraham@hotmail.com)\n"; #include -#include #include #include #include @@ -112,9 +111,16 @@ static const char *version = #define ALIGNED_ADDR(addr, len) ((((unsigned long)(addr) + (len - 1)) & ~(len - 1)) - (unsigned long)(addr)) +static struct pci_device_id iph5526_pci_tbl[] __initdata = { + { PCI_VENDOR_ID_INTERPHASE, PCI_DEVICE_ID_INTERPHASE_5526, PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_INTERPHASE, PCI_DEVICE_ID_INTERPHASE_55x6, PCI_ANY_ID, PCI_ANY_ID, }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(pci, iph5526_pci_tbl); + #define MAX_FC_CARDS 2 static struct fc_info *fc[MAX_FC_CARDS+1]; -static unsigned int pci_irq_line = 0; +static unsigned int pci_irq_line; static struct { unsigned short vendor_id; unsigned short device_id; @@ -220,32 +226,23 @@ Scsi_Host_Template driver_template = IPH5526_SCSI_FC; static void iph5526_timeout(struct net_device *dev); -#ifdef CONFIG_PCI static int iph5526_probe_pci(struct net_device *dev); -#endif - int __init iph5526_probe(struct net_device *dev) { -#ifdef CONFIG_PCI if (pci_present() && (iph5526_probe_pci(dev) == 0)) return 0; -#endif return -ENODEV; } -#ifdef CONFIG_PCI static int __init iph5526_probe_pci(struct net_device *dev) { -#ifndef MODULE -struct fc_info *fi; -static int count = 0; -#endif #ifdef MODULE -struct fc_info *fi = (struct fc_info *)dev->priv; -#endif - -#ifndef MODULE + struct fc_info *fi = (struct fc_info *)dev->priv; +#else + struct fc_info *fi; + static int count = 0; + if(fc[count] != NULL) { if (dev == NULL) { dev = init_fcdev(NULL, 0); @@ -277,7 +274,6 @@ struct fc_info *fi = (struct fc_info *)dev->priv; display_cache(fi); return 0; } -#endif /* CONFIG_PCI */ static int __init fcdev_init(struct net_device *dev) { @@ -2247,7 +2243,7 @@ static void reset_ichip(struct fc_info *fi) /* (i)chip reset */ writel(ICHIP_HCR_RESET, fi->i_r.ptr_ichip_hw_control_reg); /*wait for chip to get reset */ - udelay(10000); + mdelay(10); /*de-assert reset */ writel(ICHIP_HCR_DERESET, fi->i_r.ptr_ichip_hw_control_reg); @@ -3882,7 +3878,7 @@ struct pci_dev *pdev = NULL; /* This is to make sure that the ACC to the PRLI comes in * for the last ALPA. */ - udelay(1000000); /* Ugly! Let the Gods forgive me */ + mdelay(1000); /* Ugly! Let the Gods forgive me */ DPRINTK1("leaving iph5526_detect\n"); return no_of_hosts; diff --git a/drivers/net/fc/tach_structs.h b/drivers/net/fc/tach_structs.h index 0b7bc011e244..ef7ecd7dce8c 100644 --- a/drivers/net/fc/tach_structs.h +++ b/drivers/net/fc/tach_structs.h @@ -1,7 +1,7 @@ /********************************************************************** * iph5526.c: Structures for the Interphase 5526 PCI Fibre Channel * IP/SCSI driver. - * Copyright (C) 1999 Vineet M Abraham + * Copyright (C) 1999 Vineet M Abraham **********************************************************************/ #ifndef _TACH_STRUCT_H diff --git a/drivers/net/hydra.c b/drivers/net/hydra.c index e12f65a7989f..a840222afaa5 100644 --- a/drivers/net/hydra.c +++ b/drivers/net/hydra.c @@ -186,6 +186,7 @@ int __init hydra_probe(struct net_device *dev) release_mem_region(board, 0x4000); continue; } + SET_MODULE_OWNER(dev); for(j = 0; j < ETHER_ADDR_LEN; j++) dev->dev_addr[j] = *((u8 *)ZTWO_VADDR(board + HYDRA_ADDRPROM + 2*j)); @@ -284,15 +285,11 @@ static int hydra_open(struct net_device *dev) /* take interface out of loopback */ WRITE_REG(NIC_TCR, 0); - dev->tbusy = 0; - dev->interrupt = 0; - dev->start = 1; + netif_start_queue(dev); - if(request_irq(IRQ_AMIGA_PORTS, hydra_interrupt, SA_SHIRQ, - "Hydra Ethernet", dev)) - return(-EAGAIN); - - MOD_INC_USE_COUNT; + i = request_irq(IRQ_AMIGA_PORTS, hydra_interrupt, SA_SHIRQ, + dev->name, dev); + if (i) return i; return(0); } @@ -304,8 +301,7 @@ static int hydra_close(struct net_device *dev) volatile u8 *nicbase = (u8 *)dev->base_addr; int n = 5000; - dev->start = 0; - dev->tbusy = 1; + netif_stop_queue(dev); #ifdef HYDRA_DEBUG printk("%s: Shutting down ethercard\n", dev->name); @@ -319,8 +315,6 @@ static int hydra_close(struct net_device *dev) free_irq(IRQ_AMIGA_PORTS, dev); - MOD_DEC_USE_COUNT; - return(0); } @@ -668,20 +662,13 @@ static void set_multicast_list(struct net_device *dev, int num_addrs, void *addr #ifdef MODULE -static char devicename[9] = { 0, }; - -static struct net_device hydra_dev = -{ - devicename, /* filled in by register_netdev() */ - 0, 0, 0, 0, /* memory */ - 0, 0, /* base, irq */ - 0, 0, 0, NULL, hydra_probe, -}; +static struct net_device hydra_dev; int init_module(void) { int err; + hydra_dev.init = hydra_probe; if ((err = register_netdev(&hydra_dev))) { if (err == -EIO) printk("No Hydra board found. Module not loaded.\n"); diff --git a/drivers/net/lance.c b/drivers/net/lance.c index 4351442911fc..963fb543ff90 100644 --- a/drivers/net/lance.c +++ b/drivers/net/lance.c @@ -58,7 +58,7 @@ static const char *version = "lance.c:v1.15ac 1999/11/13 dplatt@3do.com, becker@ static unsigned int lance_portlist[] __initdata = { 0x300, 0x320, 0x340, 0x360, 0}; int lance_probe(struct net_device *dev); -int lance_probe1(struct net_device *dev, int ioaddr, int irq, int options); +static int lance_probe1(struct net_device *dev, int ioaddr, int irq, int options); #ifdef LANCE_DEBUG int lance_debug = LANCE_DEBUG; @@ -287,21 +287,15 @@ static void lance_tx_timeout (struct net_device *dev); #ifdef MODULE #define MAX_CARDS 8 /* Max number of interfaces (cards) per module */ -static int io[MAX_CARDS] = { 0, }; -static int dma[MAX_CARDS] = { 0, }; -static int irq[MAX_CARDS] = { 0, }; +static struct net_device dev_lance[MAX_CARDS]; +static int io[MAX_CARDS]; +static int dma[MAX_CARDS]; +static int irq[MAX_CARDS]; MODULE_PARM(io, "1-" __MODULE_STRING(MAX_CARDS) "i"); MODULE_PARM(dma, "1-" __MODULE_STRING(MAX_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_CARDS) "i"); -static struct net_device dev_lance[MAX_CARDS] = -{{ - "", /* device name is inserted by linux/drivers/net/net_init.c */ - 0, 0, 0, 0, - 0, 0, - 0, 0, 0, NULL, NULL}}; - int init_module(void) { int this_dev, found = 0; @@ -374,7 +368,7 @@ int lance_probe(struct net_device *dev) return -ENODEV; } -int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int options) +static int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int options) { struct lance_private *lp; short dma_channels; /* Mark spuriously-busy DMA channels */ @@ -437,6 +431,9 @@ int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int options /* We can't use init_etherdev() to allocate dev->priv because it must a ISA DMA-able region. */ dev = init_etherdev(dev, 0); + if (!dev) + return -ENOMEM; + SET_MODULE_OWNER(dev); dev->open = lance_open_fail; chipname = chip_table[lance_version].name; printk("%s: %s at %#3x,", dev->name, chipname, ioaddr); @@ -653,8 +650,6 @@ lance_open(struct net_device *dev) return -EAGAIN; } - MOD_INC_USE_COUNT; - /* We used to allocate DMA here, but that was silly. DMA lines can't be shared! We now permanently allocate them. */ @@ -1143,7 +1138,6 @@ lance_close(struct net_device *dev) lance_purge_ring(dev); - MOD_DEC_USE_COUNT; return 0; } diff --git a/drivers/net/mace.c b/drivers/net/mace.c index ac0771bd88e3..213b9b964394 100644 --- a/drivers/net/mace.c +++ b/drivers/net/mace.c @@ -133,9 +133,11 @@ static void __init mace_probe1(struct device_node *mace) } dev = init_etherdev(0, PRIV_BYTES); - memset(dev->priv, 0, PRIV_BYTES); + if (!dev) + return; + SET_MODULE_OWNER(dev); - mp = (struct mace_data *) dev->priv; + mp = dev->priv; dev->base_addr = mace->addrs[0].address; mp->mace = (volatile struct mace *) ioremap(mace->addrs[0].address, 0x1000); @@ -353,7 +355,6 @@ static int mace_open(struct net_device *dev) /* enable all interrupts except receive interrupts */ out_8(&mb->imr, RCVINT); - MOD_INC_USE_COUNT; return 0; } @@ -392,8 +393,6 @@ static int mace_close(struct net_device *dev) mace_clean_rings(mp); - MOD_DEC_USE_COUNT; - return 0; } diff --git a/drivers/net/ni5010.c b/drivers/net/ni5010.c index 7e11e5483381..e7204dd3b029 100644 --- a/drivers/net/ni5010.c +++ b/drivers/net/ni5010.c @@ -74,7 +74,7 @@ static char *version = "ni5010.c: v1.00 06/23/97 Jan-Pascal van Best and Andreas Mohr\n"; /* bufsize_rcv == 0 means autoprobing */ -unsigned int bufsize_rcv = 0; +static unsigned int bufsize_rcv = 0; #define jumpered_interrupts /* IRQ line jumpered on board */ #undef jumpered_dma /* No DMA used */ @@ -114,8 +114,8 @@ static void reset_receiver(struct net_device *dev); static int process_xmt_interrupt(struct net_device *dev); #define tx_done(dev) 1 -extern void hardware_send_packet(struct net_device *dev, char *buf, int length); -extern void chipset_init(struct net_device *dev, int startp); +static void hardware_send_packet(struct net_device *dev, char *buf, int length); +static void chipset_init(struct net_device *dev, int startp); static void dump_packet(void *buf, int len); static void show_registers(struct net_device *dev); @@ -123,11 +123,12 @@ static void show_registers(struct net_device *dev); int __init ni5010_probe(struct net_device *dev) { int *port; - - int base_addr = dev ? dev->base_addr : 0; + int base_addr = dev->base_addr; PRINTK2((KERN_DEBUG "%s: Entering ni5010_probe\n", dev->name)); - + + SET_MODULE_OWNER(dev); + if (base_addr > 0x1ff) /* Check a single specified location. */ return ni5010_probe1(dev, base_addr); else if (base_addr != 0) /* Don't probe at all. */ @@ -158,7 +159,7 @@ static inline int rd_port(int ioaddr) return inb(IE_SAPROM); } -void __init trigger_irq(int ioaddr) +static void __init trigger_irq(int ioaddr) { outb(0x00, EDLC_RESET); /* Clear EDLC hold RESET state */ outb(0x00, IE_RESET); /* Board reset */ @@ -398,7 +399,6 @@ static int ni5010_open(struct net_device *dev) if (NI5010_DEBUG) show_registers(dev); - MOD_INC_USE_COUNT; PRINTK((KERN_DEBUG "%s: open successful\n", dev->name)); return 0; } @@ -612,7 +612,6 @@ static int ni5010_close(struct net_device *dev) netif_stop_queue(dev); - MOD_DEC_USE_COUNT; PRINTK((KERN_DEBUG "%s: %s closed down\n", dev->name, boardname)); return 0; @@ -662,7 +661,7 @@ static void ni5010_set_multicast_list(struct net_device *dev) } } -extern void hardware_send_packet(struct net_device *dev, char *buf, int length) +static void hardware_send_packet(struct net_device *dev, char *buf, int length) { struct ni5010_local *lp = (struct ni5010_local *)dev->priv; int ioaddr = dev->base_addr; @@ -713,7 +712,7 @@ extern void hardware_send_packet(struct net_device *dev, char *buf, int length) if (NI5010_DEBUG) show_registers(dev); } -extern void chipset_init(struct net_device *dev, int startp) +static void chipset_init(struct net_device *dev, int startp) { /* FIXME: Move some stuff here */ PRINTK3((KERN_DEBUG "%s: doing NOTHING in chipset_init\n", dev->name)); @@ -733,7 +732,7 @@ static void show_registers(struct net_device *dev) } #ifdef MODULE -static struct net_device dev_ni5010 = { init: ni5010_probe }; +static struct net_device dev_ni5010; static int io; static int irq; @@ -759,6 +758,7 @@ int init_module(void) PRINTK2((KERN_DEBUG "%s: init_module irq=%#2x, io=%#3x\n", boardname, irq, io)); dev_ni5010.irq=irq; dev_ni5010.base_addr=io; + dev_ni5010.init=ni5010_probe; if ((result = register_netdev(&dev_ni5010)) != 0) { PRINTK((KERN_WARNING "%s: register_netdev returned %d.\n", boardname, result)); diff --git a/drivers/net/ni52.c b/drivers/net/ni52.c index 9efbd33ef055..e05e75484e24 100644 --- a/drivers/net/ni52.c +++ b/drivers/net/ni52.c @@ -160,9 +160,9 @@ sizeof(nop_cmd) = 8; /**************************************************************************/ /* different DELAYs */ -#define DELAY(x) __delay((loops_per_sec>>5)*(x)); -#define DELAY_16(); { __delay( (loops_per_sec>>16)+1 ); } -#define DELAY_18(); { __delay( (loops_per_sec>>18)+1 ); } +#define DELAY(x) mdelay(32 * x); +#define DELAY_16(); { udelay(16); } +#define DELAY_18(); { udelay(4); } /* wait for command with timeout: */ #define WAIT_4_SCB_CMD() \ @@ -219,7 +219,8 @@ struct priv struct net_device_stats stats; unsigned long base; char *memtop; - int lock,reseted; + long int lock; + int reseted; volatile struct rfd_struct *rfd_last,*rfd_top,*rfd_first; volatile struct scp_struct *scp; /* volatile is important */ volatile struct iscp_struct *iscp; /* volatile is important */ @@ -246,7 +247,6 @@ static int ni52_close(struct net_device *dev) ni_reset586(); /* the hard way to stop the receiver */ netif_stop_queue(dev); - MOD_DEC_USE_COUNT; return 0; } @@ -256,20 +256,22 @@ static int ni52_close(struct net_device *dev) */ static int ni52_open(struct net_device *dev) { + int ret; + ni_disint(); alloc586(dev); init586(dev); startrecv586(dev); ni_enaint(); - if(request_irq(dev->irq, &ni52_interrupt,0,"ni5210",dev)) + ret = request_irq(dev->irq, &ni52_interrupt,0,dev->name,dev); + if (ret) { ni_reset586(); - return -EAGAIN; + return ret; } netif_start_queue(dev); - MOD_INC_USE_COUNT; return 0; /* most done by init */ } @@ -319,7 +321,7 @@ static int check586(struct net_device *dev,char *where,unsigned size) /****************************************************************** * set iscp at the right place, called by ni52_probe1 and open586. */ -void alloc586(struct net_device *dev) +static void alloc586(struct net_device *dev) { struct priv *p = (struct priv *) dev->priv; @@ -362,6 +364,8 @@ int __init ni52_probe(struct net_device *dev) #endif int base_addr = dev->base_addr; + SET_MODULE_OWNER(dev); + if (base_addr > 0x1ff) /* Check a single specified location. */ return ni52_probe1(dev, base_addr); else if (base_addr > 0) /* Don't probe at all. */ @@ -393,8 +397,8 @@ static int __init ni52_probe1(struct net_device *dev,int ioaddr) { int i, size, retval; - if (!request_region(ioaddr, NI52_TOTAL_SIZE, "ni5210")) - return -ENODEV; + if (!request_region(ioaddr, NI52_TOTAL_SIZE, dev->name)) + return -EBUSY; if( !(inb(ioaddr+NI52_MAGIC1) == NI52_MAGICVAL1) || !(inb(ioaddr+NI52_MAGIC2) == NI52_MAGICVAL2)) { @@ -1278,17 +1282,13 @@ static void set_multicast_list(struct net_device *dev) } #ifdef MODULE -static struct net_device dev_ni52 = { - "", /* device name inserted by net_init.c */ - 0, 0, 0, 0, - 0x300, 9, /* I/O address, IRQ */ - 0, 0, 0, NULL, ni52_probe }; +static struct net_device dev_ni52; /* set: io,irq,memstart,memend or set it when calling insmod */ -int irq=9; -int io=0x300; -long memstart=0; /* e.g 0xd0000 */ -long memend=0; /* e.g 0xd4000 */ +static int irq=9; +static int io=0x300; +static long memstart=0; /* e.g 0xd0000 */ +static long memend=0; /* e.g 0xd4000 */ MODULE_PARM(io, "i"); MODULE_PARM(irq, "i"); @@ -1301,6 +1301,7 @@ int init_module(void) printk("ni52: Autoprobing not allowed for modules.\nni52: Set symbols 'io' 'irq' 'memstart' and 'memend'\n"); return -ENODEV; } + dev_ni52.init = ni52_probe; dev_ni52.irq = irq; dev_ni52.base_addr = io; dev_ni52.mem_end = memend; diff --git a/drivers/net/oaknet.c b/drivers/net/oaknet.c index f3d2c5426a72..9455d64539fe 100644 --- a/drivers/net/oaknet.c +++ b/drivers/net/oaknet.c @@ -140,7 +140,7 @@ static int __init oaknet_init(void) dev->base_addr = 0; release_region(dev->base_addr, OAKNET_IO_SIZE); - return (ENODEV); + return (-ENODEV); } /* @@ -148,7 +148,12 @@ static int __init oaknet_init(void) * our own device structure. */ - dev = init_etherdev(0, 0); + dev = init_etherdev(NULL, 0); + if (!dev) { + release_region(dev->base_addr, OAKNET_IO_SIZE); + return (-ENOMEM); + } + SET_MODULE_OWNER(dev); oaknet_devs = dev; /* @@ -238,7 +243,6 @@ static int oaknet_open(struct net_device *dev) { int status = ei_open(dev); - MOD_INC_USE_COUNT; return (status); } @@ -265,7 +269,6 @@ static int oaknet_close(struct net_device *dev) { int status = ei_close(dev); - MOD_DEC_USE_COUNT; return (status); } diff --git a/drivers/net/stnic.c b/drivers/net/stnic.c index 12c8b2b554a1..373e535fbe05 100644 --- a/drivers/net/stnic.c +++ b/drivers/net/stnic.c @@ -95,18 +95,18 @@ STNIC_WRITE (int reg, byte val) int __init stnic_probe(void) { - struct net_device tmp, *dev = NULL; + struct net_device *dev; int i; - tmp.base_addr = 0x1000; - dev = &tmp; - /* If we are not running on a SolutionEngine, give up now */ if (! MACH_SE) return -ENODEV; /* New style probing API */ - dev = init_etherdev (0, 0); + dev = init_etherdev (NULL, 0); + if (!dev) + return -ENOMEM; + SET_MODULE_OWNER(dev); stnic_dev = dev; /* Allocate dev->priv and fill in 8390 specific dev fields. */ @@ -127,15 +127,16 @@ int __init stnic_probe(void) /* Snarf the interrupt now. There's no point in waiting since we cannot share and the board will usually be enabled. */ - if (request_irq (dev->irq, ei_interrupt, 0, "DP83902A", dev)) - { + i = request_irq (dev->irq, ei_interrupt, 0, dev->name, dev); + if (i) { printk (" unable to get IRQ %d.\n", dev->irq); + unregister_netdev(dev); kfree(dev->priv); - dev->priv = NULL; - return -EAGAIN; + kfree(dev); + return i; } - ei_status.name = "eth0"; + ei_status.name = dev->name; ei_status.word16 = 1; ei_status.tx_start_page = START_PG; ei_status.rx_start_page = START_PG + TX_PAGES; @@ -159,7 +160,6 @@ stnic_open (struct net_device *dev) printk ("stnic open\n"); #endif ei_open (dev); - MOD_INC_USE_COUNT; return 0; } @@ -167,7 +167,6 @@ static int stnic_close (struct net_device *dev) { ei_close (dev); - MOD_DEC_USE_COUNT; return 0; } diff --git a/drivers/net/tulip/ChangeLog b/drivers/net/tulip/ChangeLog index 6c37fb68326f..dfac943492b9 100644 --- a/drivers/net/tulip/ChangeLog +++ b/drivers/net/tulip/ChangeLog @@ -1,3 +1,8 @@ +2000-12-17 Alan Cox + + * merge support for the Davicom's quirks into the main tulip. Patch + by Tobias Ringstrom + 2000-11-02 Jeff Garzik * tulip_core.c (set_rx_mode): This is synchronized via diff --git a/drivers/net/tulip/eeprom.c b/drivers/net/tulip/eeprom.c index e23ad7a6ec60..49e8bf42e772 100644 --- a/drivers/net/tulip/eeprom.c +++ b/drivers/net/tulip/eeprom.c @@ -198,12 +198,23 @@ subsequent_board: if (p[1] == 0x05) { mtable->has_reset = i; leaf->media = p[2] & 0x0f; + } else if (tp->chip_id == DM910X && p[1] == 0x80) { + /* Hack to ignore Davicom delay period block */ + mtable->leafcount--; + count--; + i--; + leaf->leafdata = p + 2; + p += (p[0] & 0x3f) + 1; + continue; } else if (p[1] & 1) { mtable->has_mii = 1; leaf->media = 11; } else { mtable->has_nonmii = 1; leaf->media = p[2] & 0x0f; + /* Davicom's media number for 100BaseTX is strange */ + if (tp->chip_id == DM910X && leaf->media == 1) + leaf->media = 3; switch (leaf->media) { case 0: new_advertise |= 0x0020; break; case 4: new_advertise |= 0x0040; break; diff --git a/drivers/net/tulip/timer.c b/drivers/net/tulip/timer.c index 357454359d44..b0dcf1da1978 100644 --- a/drivers/net/tulip/timer.c +++ b/drivers/net/tulip/timer.c @@ -90,6 +90,7 @@ void tulip_timer(unsigned long data) case DC21142: case MX98713: case COMPEX9881: + case DM910X: default: { struct medialeaf *mleaf; unsigned char *p; diff --git a/drivers/net/tulip/tulip.h b/drivers/net/tulip/tulip.h index d0668cf42d82..dfe3b2a4f551 100644 --- a/drivers/net/tulip/tulip.h +++ b/drivers/net/tulip/tulip.h @@ -76,6 +76,7 @@ enum chips { COMET, COMPEX9881, I21145, + DM910X, }; diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index f5d261027c1f..40fc7921dba5 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c @@ -28,7 +28,7 @@ #include static char version[] __devinitdata = - "Linux Tulip driver version 0.9.11 (November 3, 2000)\n"; + "Linux Tulip driver version 0.9.12 (December 17, 2000)\n"; /* A few user-configurable values. */ @@ -147,6 +147,9 @@ struct tulip_chip_table tulip_tbl[] = { { "Intel DS21145 Tulip", 128, 0x0801fbff, HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI | HAS_NWAY, t21142_timer }, + { "Davicom DM9102/DM9102A", 128, 0x0001ebef, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | HAS_ACPI, + tulip_timer }, {0}, }; @@ -171,8 +174,8 @@ static struct pci_device_id tulip_pci_tbl[] __devinitdata = { { 0x104A, 0x2774, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, { 0x11F6, 0x9881, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMPEX9881 }, { 0x8086, 0x0039, PCI_ANY_ID, PCI_ANY_ID, 0, 0, I21145 }, - { 0x1282, 0x9100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21140 }, - { 0x1282, 0x9102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DC21140 }, + { 0x1282, 0x9100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DM910X }, + { 0x1282, 0x9102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DM910X }, { 0x1113, 0x1217, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98715 }, {0, } }; @@ -467,7 +470,8 @@ static void tulip_tx_timeout(struct net_device *dev) tulip_select_media(dev, 0); } } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142 - || tp->chip_id == MX98713 || tp->chip_id == COMPEX9881) { + || tp->chip_id == MX98713 || tp->chip_id == COMPEX9881 + || tp->chip_id == DM910X) { printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, " "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n", dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12), @@ -1355,6 +1359,7 @@ static int __devinit tulip_init_one (struct pci_dev *pdev, outl(0x00000004, ioaddr + CSR13); break; case DC21140: + case DM910X: default: if (tp->mtable) outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12); diff --git a/drivers/net/winbond-840.c b/drivers/net/winbond-840.c index 2996fb6f9951..417e9e5d9b7b 100644 --- a/drivers/net/winbond-840.c +++ b/drivers/net/winbond-840.c @@ -21,11 +21,26 @@ 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 \n"; +"winbond-840.c:v1.01 (2.4 port) 5/15/2000 Donald Becker \n"; static const char version2[] = " http://www.scyld.com/network/drivers.html\n"; @@ -81,6 +96,8 @@ static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; #define TX_FIFO_SIZE (2048) #define TX_BUG_FIFO_LIMIT (TX_FIFO_SIZE-1514-16) +#define TX_BUFLIMIT (1024-128) + /* Operational parameters that usually are not changed. */ /* Time in jiffies before concluding the transmitter is hung. */ #define TX_TIMEOUT (2*HZ) @@ -113,12 +130,7 @@ static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; #include /* Processor type for cache alignment. */ #include #include - -/* 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 MODULE_AUTHOR("Donald Becker "); MODULE_DESCRIPTION("Winbond W89c840 Ethernet driver"); @@ -280,7 +292,7 @@ struct w840_rx_desc { s32 status; s32 length; u32 buffer1; - u32 next_desc; + u32 buffer2; }; struct w840_tx_desc { @@ -293,14 +305,21 @@ struct w840_tx_desc { enum desc_status_bits { DescOwn=0x80000000, DescEndRing=0x02000000, DescUseLink=0x01000000, DescWholePkt=0x60000000, DescStartPkt=0x20000000, DescEndPkt=0x40000000, +}; + +/* Bits in w840_tx_desc.length */ +enum desc_length_bits { DescIntr=0x80000000, }; #define PRIV_ALIGN 15 /* Required alignment mask */ struct netdev_private { - /* Descriptor rings first for alignment. */ - struct w840_rx_desc rx_ring[RX_RING_SIZE]; - struct w840_tx_desc tx_ring[TX_RING_SIZE]; + struct w840_rx_desc *rx_ring; + dma_addr_t rx_addr[RX_RING_SIZE]; + struct w840_tx_desc *tx_ring; + dma_addr_t tx_addr[RX_RING_SIZE]; + dma_addr_t ring_dma_addr; + struct pci_dev *pdev; /* The addresses of receive-in-place skbuffs. */ struct sk_buff* rx_skbuff[RX_RING_SIZE]; /* The saved address of a sent-in-place packet/buffer, for later free(). */ @@ -334,13 +353,15 @@ static void mdio_write(struct net_device *dev, int phy_id, int location, int val static int netdev_open(struct net_device *dev); static void check_duplex(struct net_device *dev); static void netdev_timer(unsigned long data); +static void init_rxtx_rings(struct net_device *dev); +static void free_rxtx_rings(struct netdev_private *np); +static void init_registers(struct net_device *dev); static void tx_timeout(struct net_device *dev); -static void init_ring(struct net_device *dev); +static int alloc_ring(struct net_device *dev); static int start_tx(struct sk_buff *skb, struct net_device *dev); static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs); static void netdev_error(struct net_device *dev, int intr_status); static int netdev_rx(struct net_device *dev); -static void netdev_error(struct net_device *dev, int intr_status); static inline unsigned ether_crc(int length, unsigned char *data); static void set_rx_mode(struct net_device *dev); static struct net_device_stats *get_stats(struct net_device *dev); @@ -364,6 +385,11 @@ static int __devinit w840_probe1 (struct pci_dev *pdev, return -EIO; pci_set_master(pdev); + if(!pci_dma_supported(pdev,0xFFFFffff)) { + printk(KERN_WARNING "Winbond-840: Device %s disabled due to DMA limitations.\n", + pdev->name); + return -EIO; + } dev = init_etherdev(NULL, sizeof(*np)); if (!dev) return -ENOMEM; @@ -403,6 +429,7 @@ static int __devinit w840_probe1 (struct pci_dev *pdev, np = dev->priv; np->chip_id = chip_idx; np->drv_flags = pci_id_tbl[chip_idx].drv_flags; + np->pdev = pdev; spin_lock_init(&np->lock); pdev->driver_data = dev; @@ -632,60 +659,12 @@ static int netdev_open(struct net_device *dev) printk(KERN_DEBUG "%s: w89c840_open() irq %d.\n", dev->name, dev->irq); - init_ring(dev); - - writel(virt_to_bus(np->rx_ring), ioaddr + RxRingPtr); - writel(virt_to_bus(np->tx_ring), ioaddr + TxRingPtr); - - for (i = 0; i < 6; i++) - writeb(dev->dev_addr[i], ioaddr + StationAddr + i); - - /* Initialize other registers. */ - /* Configure the PCI bus bursts and FIFO thresholds. - 486: Set 8 longword cache alignment, 8 longword burst. - 586: Set 16 longword cache alignment, no burst limit. - Cache alignment bits 15:14 Burst length 13:8 - 0000 0000 align to cache 0800 8 longwords - 4000 8 longwords 0100 1 longword 1000 16 longwords - 8000 16 longwords 0200 2 longwords 2000 32 longwords - C000 32 longwords 0400 4 longwords - Wait the specified 50 PCI cycles after a reset by initializing - Tx and Rx queues and the address filter list. */ -#if defined(__powerpc__) /* Big-endian */ - writel(0x00100080 | 0xE010, ioaddr + PCIBusCfg); -#elif defined(__alpha__) - writel(0xE010, ioaddr + PCIBusCfg); -#elif defined(__i386__) -#if defined(MODULE) - writel(0xE010, ioaddr + PCIBusCfg); -#else - /* When not a module we can work around broken '486 PCI boards. */ -#define x86 boot_cpu_data.x86 - writel((x86 <= 4 ? 0x4810 : 0xE010), ioaddr + PCIBusCfg); - if (x86 <= 4) - printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache " - "alignment to %x.\n", dev->name, - (x86 <= 4 ? 0x4810 : 0x8010)); -#endif -#else - writel(0xE010, ioaddr + PCIBusCfg); -#warning Processor architecture undefined! -#endif - - if (dev->if_port == 0) - dev->if_port = np->default_port; + if((i=alloc_ring(dev))) + return i; - writel(0, ioaddr + RxStartDemand); - np->csr6 = 0x20022002; - check_duplex(dev); - set_rx_mode(dev); + init_registers(dev); netif_start_queue(dev); - - /* Clear and Enable interrupts by setting the interrupt mask. */ - writel(0x1A0F5, ioaddr + IntrStatus); - writel(0x1A0F5, ioaddr + IntrEnable); - if (debug > 2) printk(KERN_DEBUG "%s: Done netdev_open().\n", dev->name); @@ -733,15 +712,149 @@ static void netdev_timer(unsigned long data) "config %8.8x.\n", dev->name, (int)readl(ioaddr + IntrStatus), (int)readl(ioaddr + NetworkConfig)); + spin_lock_irq(&np->lock); check_duplex(dev); if (np->csr6 != old_csr6) { writel(np->csr6 & ~0x0002, ioaddr + NetworkConfig); writel(np->csr6 | 0x2002, ioaddr + NetworkConfig); } + spin_unlock_irq(&np->lock); np->timer.expires = jiffies + next_tick; add_timer(&np->timer); } +static void init_rxtx_rings(struct net_device *dev) +{ + struct netdev_private *np = (struct netdev_private *)dev->priv; + int i; + + np->rx_head_desc = &np->rx_ring[0]; + np->tx_ring = (struct w840_tx_desc*)&np->rx_ring[RX_RING_SIZE]; + + /* Initial all Rx descriptors. */ + for (i = 0; i < RX_RING_SIZE; i++) { + np->rx_ring[i].length = cpu_to_le32(np->rx_buf_sz); + np->rx_ring[i].status = 0; + np->rx_skbuff[i] = 0; + } + /* Mark the last entry as wrapping the ring. */ + np->rx_ring[i-1].length |= cpu_to_le32(DescEndRing); + + /* Fill in the Rx buffers. Handle allocation failure gracefully. */ + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz); + np->rx_skbuff[i] = skb; + if (skb == NULL) + break; + skb->dev = dev; /* Mark as being used by this device. */ + np->rx_addr[i] = pci_map_single(np->pdev,skb->tail, + skb->len,PCI_DMA_FROMDEVICE); + + np->rx_ring[i].buffer1 = cpu_to_le32(np->rx_addr[i]); + np->rx_ring[i].status = cpu_to_le32(DescOwn); + } + + np->cur_rx = 0; + np->dirty_rx = (unsigned int)(i - RX_RING_SIZE); + + /* Initialize the Tx descriptors */ + for (i = 0; i < TX_RING_SIZE; i++) { + np->tx_skbuff[i] = 0; + np->tx_ring[i].status = 0; + } + np->tx_full = 0; + np->tx_q_bytes = np->dirty_tx = np->cur_tx = 0; + + writel(np->ring_dma_addr, dev->base_addr + RxRingPtr); + writel(np->ring_dma_addr+sizeof(struct w840_rx_desc)*RX_RING_SIZE, + dev->base_addr + TxRingPtr); + +} + +static void free_rxtx_rings(struct netdev_private* np) +{ + int i; + /* Free all the skbuffs in the Rx queue. */ + for (i = 0; i < RX_RING_SIZE; i++) { + np->rx_ring[i].status = 0; + if (np->rx_skbuff[i]) { + pci_unmap_single(np->pdev, + np->rx_addr[i], + np->rx_skbuff[i]->len, + PCI_DMA_FROMDEVICE); + dev_kfree_skb(np->rx_skbuff[i]); + } + np->rx_skbuff[i] = 0; + } + for (i = 0; i < TX_RING_SIZE; i++) { + if (np->tx_skbuff[i]) { + pci_unmap_single(np->pdev, + np->tx_addr[i], + np->tx_skbuff[i]->len, + PCI_DMA_TODEVICE); + dev_kfree_skb(np->tx_skbuff[i]); + } + np->tx_skbuff[i] = 0; + } +} + +static void init_registers(struct net_device *dev) +{ + struct netdev_private *np = (struct netdev_private *)dev->priv; + long ioaddr = dev->base_addr; + int i; + + for (i = 0; i < 6; i++) + writeb(dev->dev_addr[i], ioaddr + StationAddr + i); + + /* Initialize other registers. */ + /* Configure the PCI bus bursts and FIFO thresholds. + 486: Set 8 longword cache alignment, 8 longword burst. + 586: Set 16 longword cache alignment, no burst limit. + Cache alignment bits 15:14 Burst length 13:8 + 0000 0000 align to cache 0800 8 longwords + 4000 8 longwords 0100 1 longword 1000 16 longwords + 8000 16 longwords 0200 2 longwords 2000 32 longwords + C000 32 longwords 0400 4 longwords + Wait the specified 50 PCI cycles after a reset by initializing + Tx and Rx queues and the address filter list. */ +#if defined(__powerpc__) /* Big-endian */ + writel(0x00100080 | 0xE010, ioaddr + PCIBusCfg); +#elif defined(__alpha__) + writel(0xE010, ioaddr + PCIBusCfg); +#elif defined(__i386__) +#if defined(MODULE) + writel(0xE010, ioaddr + PCIBusCfg); +#else + /* When not a module we can work around broken '486 PCI boards. */ +#define x86 boot_cpu_data.x86 + writel((x86 <= 4 ? 0x4810 : 0xE010), ioaddr + PCIBusCfg); + if (x86 <= 4) + printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache " + "alignment to %x.\n", dev->name, + (x86 <= 4 ? 0x4810 : 0x8010)); +#endif +#else + writel(0xE010, ioaddr + PCIBusCfg); +#warning Processor architecture undefined! +#endif + + if (dev->if_port == 0) + dev->if_port = np->default_port; + + /* Fast Ethernet; 128 byte Tx threshold; + Transmit on; Receive on; */ + np->csr6 = 0x20022002; + check_duplex(dev); + set_rx_mode(dev); + writel(0, ioaddr + RxStartDemand); + + /* Clear and Enable interrupts by setting the interrupt mask. */ + writel(0x1A0F5, ioaddr + IntrStatus); + writel(0x1A0F5, ioaddr + IntrEnable); + +} + static void tx_timeout(struct net_device *dev) { struct netdev_private *np = (struct netdev_private *)dev->priv; @@ -758,70 +871,60 @@ static void tx_timeout(struct net_device *dev) printk(" %8.8x", (unsigned int)np->rx_ring[i].status); printk("\n"KERN_DEBUG" Tx ring %8.8x: ", (int)np->tx_ring); for (i = 0; i < TX_RING_SIZE; i++) - printk(" %4.4x", np->tx_ring[i].status); + printk(" %8.8x", np->tx_ring[i].status); printk("\n"); } + printk(KERN_DEBUG "Tx cur %d Tx dirty %d Tx Full %d, q bytes %d.\n", + np->cur_tx, np->dirty_tx, np->tx_full,np->tx_q_bytes); + printk(KERN_DEBUG "Tx Descriptor addr %xh.\n",readl(ioaddr+0x4C)); + #endif + spin_lock_irq(&np->lock); + /* + * Under high load dirty_tx and the internal tx descriptor pointer + * come out of sync, thus perform a software reset and reinitialize + * everything. + */ + + writel(1, dev->base_addr+PCIBusCfg); + udelay(1); + + free_rxtx_rings(np); + init_rxtx_rings(dev); + init_registers(dev); + set_rx_mode(dev); - /* Perhaps we should reinitialize the hardware here. Just trigger a - Tx demand for now. */ - writel(0, ioaddr + TxStartDemand); - dev->if_port = 0; - /* Stop and restart the chip's Tx processes . */ + spin_unlock_irq(&np->lock); + netif_wake_queue(dev); dev->trans_start = jiffies; np->stats.tx_errors++; return; } - /* Initialize the Rx and Tx rings, along with various 'dev' bits. */ -static void init_ring(struct net_device *dev) +static int alloc_ring(struct net_device *dev) { struct netdev_private *np = (struct netdev_private *)dev->priv; - int i; - - np->tx_full = 0; - np->tx_q_bytes = np->cur_rx = np->cur_tx = 0; - np->dirty_rx = np->dirty_tx = 0; np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); - np->rx_head_desc = &np->rx_ring[0]; - /* Initial all Rx descriptors. */ - for (i = 0; i < RX_RING_SIZE; i++) { - np->rx_ring[i].length = cpu_to_le32(np->rx_buf_sz); - np->rx_ring[i].status = 0; - np->rx_ring[i].next_desc = virt_to_le32desc(&np->rx_ring[i+1]); - np->rx_skbuff[i] = 0; - } - /* Mark the last entry as wrapping the ring. */ - np->rx_ring[i-1].length |= cpu_to_le32(DescEndRing); - np->rx_ring[i-1].next_desc = virt_to_le32desc(&np->rx_ring[0]); - - /* Fill in the Rx buffers. Handle allocation failure gracefully. */ - for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz); - np->rx_skbuff[i] = skb; - if (skb == NULL) - break; - skb->dev = dev; /* Mark as being used by this device. */ - np->rx_ring[i].buffer1 = virt_to_le32desc(skb->tail); - np->rx_ring[i].status = cpu_to_le32(DescOwn | DescIntr); - } - np->dirty_rx = (unsigned int)(i - RX_RING_SIZE); - - for (i = 0; i < TX_RING_SIZE; i++) { - np->tx_skbuff[i] = 0; - np->tx_ring[i].status = 0; - } - return; + np->rx_ring = pci_alloc_consistent(np->pdev, + sizeof(struct w840_rx_desc)*RX_RING_SIZE + + sizeof(struct w840_tx_desc)*TX_RING_SIZE, + &np->ring_dma_addr); + if(!np->rx_ring) + return -ENOMEM; + init_rxtx_rings(dev); + return 0; } + static int start_tx(struct sk_buff *skb, struct net_device *dev) { struct netdev_private *np = (struct netdev_private *)dev->priv; unsigned entry; + int len1, len2; /* Caution: the write order is important here, set the field with the "ownership" bits last. */ @@ -830,76 +933,35 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev) entry = np->cur_tx % TX_RING_SIZE; np->tx_skbuff[entry] = skb; - np->tx_ring[entry].buffer1 = virt_to_le32desc(skb->data); - -#define one_buffer -#define BPT 1022 -#if defined(one_buffer) - np->tx_ring[entry].length = cpu_to_le32(DescWholePkt | skb->len); + np->tx_addr[entry] = pci_map_single(np->pdev, + skb->data,skb->len, PCI_DMA_TODEVICE); + np->tx_ring[entry].buffer1 = cpu_to_le32(np->tx_addr[entry]); + len2 = 0; + len1 = skb->len; + if(len1 > TX_BUFLIMIT) { + len1 = TX_BUFLIMIT; + len2 = skb->len-len1; + np->tx_ring[entry].buffer2 = cpu_to_le32(np->tx_addr[entry]+TX_BUFLIMIT); + } + np->tx_ring[entry].length = cpu_to_le32(DescWholePkt | (len2 << 11) | len1); if (entry >= TX_RING_SIZE-1) /* Wrap ring */ np->tx_ring[entry].length |= cpu_to_le32(DescIntr | DescEndRing); - np->tx_ring[entry].status = cpu_to_le32(DescOwn); np->cur_tx++; -#elif defined(two_buffer) - if (skb->len > BPT) { - unsigned int entry1 = ++np->cur_tx % TX_RING_SIZE; - np->tx_ring[entry].length = cpu_to_le32(DescStartPkt | BPT); - np->tx_ring[entry1].length = cpu_to_le32(DescEndPkt | (skb->len - BPT)); - np->tx_ring[entry1].buffer1 = virt_to_le32desc((skb->data) + BPT); - np->tx_ring[entry1].status = cpu_to_le32(DescOwn); - np->tx_ring[entry].status = cpu_to_le32(DescOwn); - if (entry >= TX_RING_SIZE-1) - np->tx_ring[entry].length |= cpu_to_le32(DescIntr|DescEndRing); - else if (entry1 >= TX_RING_SIZE-1) - np->tx_ring[entry1].length |= cpu_to_le32(DescIntr|DescEndRing); - np->cur_tx++; - } else { - np->tx_ring[entry].length = cpu_to_le32(DescWholePkt | skb->len); - if (entry >= TX_RING_SIZE-1) /* Wrap ring */ - np->tx_ring[entry].length |= cpu_to_le32(DescIntr | DescEndRing); - np->tx_ring[entry].status = cpu_to_le32(DescOwn); - np->cur_tx++; - } -#elif defined(split_buffer) - { - /* Work around the Tx-FIFO-full bug by splitting our transmit packet - into two pieces, the first which may be loaded without overflowing - the FIFO, and the second which contains the remainder of the - packet. When we get a Tx-done interrupt that frees enough room - in the FIFO we mark the remainder of the packet as loadable. - - This has the problem that the Tx descriptors are written both - here and in the interrupt handler. - */ - - int buf1size = TX_FIFO_SIZE - np->tx_q_bytes; - int buf2size = skb->len - buf1size; - - if (buf2size <= 0) { /* We fit into one descriptor. */ - np->tx_ring[entry].length = cpu_to_le32(DescWholePkt | skb->len); - } else { /* We must use two descriptors. */ - unsigned int entry2; - np->tx_ring[entry].length = - cpu_to_le32(DescIntr | DescStartPkt | buf1size); - if (entry >= TX_RING_SIZE-1) { /* Wrap ring */ - np->tx_ring[entry].length |= cpu_to_le32(DescEndRing); - entry2 = 0; - } else - entry2 = entry + 1; - np->cur_tx++; - np->tx_ring[entry2].buffer1 = - virt_to_le32desc(skb->data + buf1size); - np->tx_ring[entry2].length = cpu_to_le32(DescEndPkt | buf2size); - if (entry2 >= TX_RING_SIZE-1) /* Wrap ring */ - np->tx_ring[entry2].length |= cpu_to_le32(DescEndRing); - } - np->tx_ring[entry].status = cpu_to_le32(DescOwn); - np->cur_tx++; - } -#endif - np->tx_q_bytes += skb->len; + + /* The spinlock protects against 2 races: + * - tx_q_bytes is updated by this function and intr_handler + * - our hardware is extremely fast and finishes the packet between + * our check for "queue full" and netif_stop_queue. + * Thus setting DescOwn and netif_stop_queue must be atomic. + */ + spin_lock_irq(&np->lock); + + wmb(); /* flush length, buffer1, buffer2 */ + np->tx_ring[entry].status = cpu_to_le32(DescOwn); + wmb(); /* flush status and kick the hardware */ writel(0, dev->base_addr + TxStartDemand); + np->tx_q_bytes += skb->len; /* Work around horrible bug in the chip by marking the queue as full when we do not have FIFO room for a maximum sized packet. */ if (np->cur_tx - np->dirty_tx > TX_QUEUE_LEN) @@ -911,6 +973,7 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev) netif_stop_queue(dev); dev->trans_start = jiffies; + spin_unlock_irq(&np->lock); if (debug > 4) { printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n", @@ -977,6 +1040,9 @@ static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs) np->stats.tx_packets++; } /* Free the original skb. */ + pci_unmap_single(np->pdev,np->tx_addr[entry], + np->tx_skbuff[entry]->len, + PCI_DMA_TODEVICE); np->tx_q_bytes -= np->tx_skbuff[entry]->len; dev_kfree_skb_irq(np->tx_skbuff[entry]); np->tx_skbuff[entry] = 0; @@ -1070,6 +1136,9 @@ static int netdev_rx(struct net_device *dev) && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ + pci_dma_sync_single(np->pdev,np->rx_addr[entry], + np->rx_skbuff[entry]->len, + PCI_DMA_FROMDEVICE); /* Call copy + cksum if available. */ #if HAS_IP_COPYSUM eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0); @@ -1079,15 +1148,11 @@ static int netdev_rx(struct net_device *dev) pkt_len); #endif } else { - char *temp = skb_put(skb = np->rx_skbuff[entry], pkt_len); + pci_unmap_single(np->pdev,np->rx_addr[entry], + np->rx_skbuff[entry]->len, + PCI_DMA_FROMDEVICE); + skb_put(skb = np->rx_skbuff[entry], pkt_len); np->rx_skbuff[entry] = NULL; -#ifndef final_version /* Remove after testing. */ - if (le32desc_to_virt(desc->buffer1) != temp) - printk(KERN_ERR "%s: Internal fault: The skbuff addresses " - "do not match in netdev_rx: %p vs. %p / %p.\n", - dev->name, le32desc_to_virt(desc->buffer1), - skb->head, temp); -#endif } #ifndef final_version /* Remove after testing. */ /* You will want this info for the initial debug. */ @@ -1122,8 +1187,12 @@ static int netdev_rx(struct net_device *dev) if (skb == NULL) break; /* Better luck next round. */ skb->dev = dev; /* Mark as being used by this device. */ - np->rx_ring[entry].buffer1 = virt_to_le32desc(skb->tail); + np->rx_addr[entry] = pci_map_single(np->pdev, + skb->tail, + skb->len, PCI_DMA_FROMDEVICE); + np->rx_ring[entry].buffer1 = cpu_to_le32(np->rx_addr[entry]); } + wmb(); np->rx_ring[entry].status = cpu_to_le32(DescOwn); } @@ -1141,7 +1210,21 @@ static void netdev_error(struct net_device *dev, int intr_status) if (intr_status == 0xffffffff) return; if (intr_status & TxFIFOUnderflow) { - np->csr6 += 0x4000; /* Bump up the Tx threshold */ + /* Bump up the Tx threshold */ +#if 0 + /* This causes lots of dropped packets, + * and under high load even tx_timeouts + */ + np->csr6 += 0x4000; +#else + int cur = (np->csr6 >> 14)&0x7f; + if (cur < 64) + cur *= 2; + else + cur = 0; /* load full packet before starting */ + np->csr6 &= ~(0x7F << 14); + np->csr6 |= cur<<14; +#endif printk(KERN_DEBUG "%s: Tx underflow, increasing threshold to %8.8x.\n", dev->name, np->csr6); writel(np->csr6, ioaddr + NetworkConfig); @@ -1270,13 +1353,13 @@ static int netdev_close(struct net_device *dev) #ifdef __i386__ if (debug > 2) { printk("\n"KERN_DEBUG" Tx ring at %8.8x:\n", - (int)virt_to_le32desc(np->tx_ring)); + (int)np->tx_ring); for (i = 0; i < TX_RING_SIZE; i++) printk(" #%d desc. %4.4x %4.4x %8.8x.\n", i, np->tx_ring[i].length, np->tx_ring[i].status, np->tx_ring[i].buffer1); printk("\n"KERN_DEBUG " Rx ring %8.8x:\n", - (int)virt_to_le32desc(np->rx_ring)); + (int)np->rx_ring); for (i = 0; i < RX_RING_SIZE; i++) { printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x\n", i, np->rx_ring[i].length, @@ -1289,19 +1372,7 @@ static int netdev_close(struct net_device *dev) del_timer_sync(&np->timer); - /* Free all the skbuffs in the Rx queue. */ - for (i = 0; i < RX_RING_SIZE; i++) { - np->rx_ring[i].status = 0; - if (np->rx_skbuff[i]) { - dev_kfree_skb(np->rx_skbuff[i]); - } - np->rx_skbuff[i] = 0; - } - for (i = 0; i < TX_RING_SIZE; i++) { - if (np->tx_skbuff[i]) - dev_kfree_skb(np->tx_skbuff[i]); - np->tx_skbuff[i] = 0; - } + free_rxtx_rings(np); return 0; } @@ -1311,7 +1382,7 @@ static void __devexit w840_remove1 (struct pci_dev *pdev) struct net_device *dev = pdev->driver_data; /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ - while (dev) { + if (dev) { struct netdev_private *np = (void *)(dev->priv); unregister_netdev(dev); #ifdef USE_IO_OPS diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 9913c1998ce9..6ee9208e9bd6 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -84,6 +84,19 @@ static void __init quirk_triton(struct pci_dev *dev) } } +/* + * VIA Apollo VP3 needs ETBF on BT848/878 + */ + +static void __init quirk_viaetbf(struct pci_dev *dev) +{ + if((pci_pci_problems&PCIPCI_VIAETBF)==0) + { + printk(KERN_INFO "Limiting direct PCI/PCI transfers.\n"); + pci_pci_problems|=PCIPCI_VIAETBF; + } +} + /* * Natoma has some interesting boundary conditions with Zoran stuff * at least @@ -262,6 +275,7 @@ static struct pci_fixup pci_fixups[] __initdata = { { PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_2, quirk_natoma }, { PCI_FIXUP_FINAL, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5597, quirk_nopcipci }, { PCI_FIXUP_FINAL, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_496, quirk_nopcipci }, + { PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C597_0, quirk_viaetbf }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C597_0, quirk_vt82c598_id }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3, quirk_vt82c586_acpi }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4, quirk_vt82c686_acpi }, diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 36ef8ea7fe0c..ae06b7bfa4bd 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -113,6 +113,7 @@ obj-$(CONFIG_SCSI_DEBUG) += scsi_debug.o obj-$(CONFIG_SCSI_FCAL) += fcal.o obj-$(CONFIG_CHR_DEV_ST) += st.o +obj-$(CONFIG_CHR_DEV_OSST) += osst.o obj-$(CONFIG_BLK_DEV_SD) += sd_mod.o obj-$(CONFIG_BLK_DEV_SR) += sr_mod.o obj-$(CONFIG_CHR_DEV_SG) += sg.o diff --git a/drivers/scsi/README.osst b/drivers/scsi/README.osst new file mode 100644 index 000000000000..74f9b3573e63 --- /dev/null +++ b/drivers/scsi/README.osst @@ -0,0 +1,219 @@ +README file for the osst driver +=============================== +(w) Kurt Garloff 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 + +a CVS repository and some web pages. +The tester's pages which contain recent news and updated drivers to download +can be found on +http://linux1.onstream.nl/test/ + +If you find any problems, please have a look at the tester's page in order +to see whether the problem is already known and solved. Otherwise, please +report it to the mailing list. Your feedback is welcome. (This holds also +for reports of successful usage, of course.) +In case of trouble, please do always provide the following info: +* driver and kernel version used (see syslog) +* driver messages (syslog) +* SCSI config and OnStream Firmware (/proc/scsi/scsi) +* description of error. Is it reproducible? +* software and commands used + +You may subscribe to the mailing list, BTW, it's a majordomo list. + + +Status +------ +0.8.0 was the first widespread BETA release. Since then a lot of reports +have been sent, but mostly reported success or only minor trouble. +All the issues have been addressed. +Check the web pages for more info about the current developments. +0.9.x is the tree for the 2.3/2.4 kernel. + + +Acknowledgments +---------------- +The driver has been started by making a copy of Kai Makisara's st driver. +Most of the development has been done by Willem Riede. The presence of the +userspace program osg (onstreamsg) from Terry Hardie has been rather +helpful. The same holds for Gadi Oxman's ide-tape support for the DI-30. +I did add some patches to those drivers as well and coordinated things a +little bit. +Note that most of them did mostly spend their spare time for the creation of +this driver. +The people from OnStream, especially Jack Bombeeck did support this project +and always tried to answer HW or FW related questions. Furthermore, he +pushed the FW developers to do the right things. +SuSE did support this project by allowing me to work on it during my working +time for them and by integrating the driver into their distro. + +More people did help by sending useful comments. Sorry to those who have +been forgotten. Thanks to all the GNU/FSF and Linux developers who made this +platform such an interesting, nice and stable platform. +Thanks go to those who tested the drivers and did send useful reports. Your +help is needed! + + +Makedevs.sh +----------- +#!/bin/sh +# Script to create OnStream SC-x0 device nodes (major 206) +# Usage: Makedevs.sh [nos [path to dev]] +# $Id: README.osst.kernel,v 1.4 2000/12/20 14:13:15 garloff Exp $ +major=206 +nrs=4 +dir=/dev +test -z "$1" || nrs=$1 +test -z "$2" || dir=$2 +declare -i nr +nr=0 +test -d $dir || mkdir -p $dir +while test $nr -lt $nrs; do + mknod $dir/osst$nr c $major $nr + chown 0.disk $dir/osst$nr; chmod 660 $dir/osst$nr; + mknod $dir/nosst$nr c $major $[nr+128] + chown 0.disk $dir/nosst$nr; chmod 660 $dir/nosst$nr; + mknod $dir/osst${nr}l c $major $[nr+32] + chown 0.disk $dir/osst${nr}l; chmod 660 $dir/osst${nr}l; + mknod $dir/nosst${nr}l c $major $[nr+160] + chown 0.disk $dir/nosst${nr}l; chmod 660 $dir/nosst${nr}l; + mknod $dir/osst${nr}m c $major $[nr+64] + chown 0.disk $dir/osst${nr}m; chmod 660 $dir/osst${nr}m; + mknod $dir/nosst${nr}m c $major $[nr+192] + chown 0.disk $dir/nosst${nr}m; chmod 660 $dir/nosst${nr}m; + mknod $dir/osst${nr}a c $major $[nr+96] + chown 0.disk $dir/osst${nr}a; chmod 660 $dir/osst${nr}a; + mknod $dir/nosst${nr}a c $major $[nr+224] + chown 0.disk $dir/nosst${nr}a; chmod 660 $dir/nosst${nr}a; + let nr+=1 +done diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c new file mode 100644 index 000000000000..2eca1b6737e8 --- /dev/null +++ b/drivers/scsi/osst.c @@ -0,0 +1,5306 @@ +/* + SCSI Tape Driver for Linux version 1.1 and newer. See the accompanying + file README.st for more information. + + History: + + OnStream SCSI Tape support (osst) cloned from st.c by + Willem Riede (osst@riede.org) Feb 2000 + Fixes ... Kurt Garloff 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 + +#include "scsi.h" +#include "hosts.h" +#include + +#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); + + +/* 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; +} + + + +/* 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_cntfilemark_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_cntfilemark_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; ifilemark_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; +} + + + +/* 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; +} + + +/* 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; ilogical_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; +} + + +/* 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; +} + + +/* 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) 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; idevice == SDp) { + tpnt->device = NULL; +#ifdef CONFIG_DEVFS_FS + for (mode = 0; mode < ST_NBR_MODES; ++mode) { + devfs_unregister (tpnt->de_r[mode]); + tpnt->de_r[mode] = NULL; + devfs_unregister (tpnt->de_n[mode]); + tpnt->de_n[mode] = NULL; + } +#endif + kfree(tpnt); + os_scsi_tapes[i] = NULL; + SDp->attached--; + osst_template.nr_dev--; + osst_template.dev_noticed--; + return; + } + } + return; +} + + +static int __init init_osst(void) +{ + int result; + + if (buffer_kbs > 0) + osst_buffer_size = buffer_kbs * ST_KILOBYTE; + if (write_threshold_kbs > 0) + osst_write_threshold = write_threshold_kbs * ST_KILOBYTE; + if (osst_write_threshold > osst_buffer_size) + osst_write_threshold = osst_buffer_size; + if (max_buffers > 0) + osst_max_buffers = max_buffers; + if (max_sg_segs >= OSST_FIRST_SG) + osst_max_sg_segs = max_sg_segs; + printk(KERN_INFO "osst: bufsize %d, wrt %d, max buffers %d, s/g segs %d.\n", + osst_buffer_size, osst_write_threshold, osst_max_buffers, osst_max_sg_segs); +//printk(OSST_DEB_MSG "osst: sizeof(header) = %d (%s)\n",sizeof(os_header_t),sizeof(os_header_t)==OS_DATA_SIZE?"ok":"error"); + osst_template.module = THIS_MODULE; + result = scsi_register_module(MODULE_SCSI_DEV, &osst_template); + if (result) + return result; + + return 0; +} + +static void __exit exit_osst (void) +{ + int i; + OS_Scsi_Tape * STp; + + scsi_unregister_module(MODULE_SCSI_DEV, &osst_template); +#ifdef CONFIG_DEVFS_FS + devfs_unregister_chrdev(MAJOR_NR, "osst"); +#else + unregister_chrdev(MAJOR_NR, "osst"); +#endif + osst_registered--; + if(os_scsi_tapes != NULL) { + for (i=0; i < osst_template.dev_max; ++i) { + if ((STp = os_scsi_tapes[i])) { + if (STp->header_cache != NULL) vfree(STp->header_cache); + kfree(STp); + } + } + kfree(os_scsi_tapes); + + if (osst_buffers != NULL) { + for (i=0; i < osst_nbr_buffers; i++) + if (osst_buffers[i] != NULL) { + osst_buffers[i]->orig_sg_segs = 0; + normalize_buffer(osst_buffers[i]); + kfree(osst_buffers[i]); + } + + kfree(osst_buffers); + } + } + osst_template.dev_max = 0; + printk(KERN_INFO "osst: Unloaded.\n"); +} + +module_init(init_osst); +module_exit(exit_osst); diff --git a/drivers/scsi/osst.h b/drivers/scsi/osst.h new file mode 100644 index 000000000000..97d9f43989ce --- /dev/null +++ b/drivers/scsi/osst.h @@ -0,0 +1,634 @@ +/* + * $Header: /home/cvsroot/Driver/osst.h,v 1.9 2000/10/08 03:09:43 riede Exp $ + */ + +#include +#include +#ifdef CONFIG_DEVFS_FS +#include +#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 " +#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 " +#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 " +#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 " +#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 " +#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 " +#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 " +#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 " +#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 " +#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 " +#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 " +#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 " +#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 " +#endif + u8 reserved2; + u8 density; + u8 reserved3,reserved4; + u16 segtrk; + u16 trks; + u8 reserved5,reserved6,reserved7,reserved8,reserved9,reserved10; +} osst_tape_paramtr_page_t; + +/* OnStream definitions */ + +#define OS_CONFIG_PARTITION (0xff) +#define OS_DATA_PARTITION (0) +#define OS_PARTITION_VERSION (1) + +/* + * partition + */ +typedef struct os_partition_s { + __u8 partition_num; + __u8 par_desc_ver; + __u16 wrt_pass_cntr; + __u32 first_frame_ppos; + __u32 last_frame_ppos; + __u32 eod_frame_ppos; +} os_partition_t; + +/* + * DAT entry + */ +typedef struct os_dat_entry_s { + __u32 blk_sz; + __u16 blk_cnt; + __u8 flags; + __u8 reserved; +} os_dat_entry_t; + +/* + * DAT + */ +#define OS_DAT_FLAGS_DATA (0xc) +#define OS_DAT_FLAGS_MARK (0x1) + +typedef struct os_dat_s { + __u8 dat_sz; + __u8 reserved1; + __u8 entry_cnt; + __u8 reserved3; + os_dat_entry_t dat_list[16]; +} os_dat_t; + +/* + * Frame types + */ +#define OS_FRAME_TYPE_FILL (0) +#define OS_FRAME_TYPE_EOD (1 << 0) +#define OS_FRAME_TYPE_MARKER (1 << 1) +#define OS_FRAME_TYPE_HEADER (1 << 3) +#define OS_FRAME_TYPE_DATA (1 << 7) + +/* + * AUX + */ +typedef struct os_aux_s { + __u32 format_id; /* hardware compability AUX is based on */ + char application_sig[4]; /* driver used to write this media */ + __u32 hdwr; /* reserved */ + __u32 update_frame_cntr; /* for configuration frame */ + __u8 frame_type; + __u8 frame_type_reserved; + __u8 reserved_18_19[2]; + os_partition_t partition; + __u8 reserved_36_43[8]; + __u32 frame_seq_num; + __u32 logical_blk_num_high; + __u32 logical_blk_num; + os_dat_t dat; + __u8 reserved188_191[4]; + __u32 filemark_cnt; + __u32 phys_fm; + __u32 last_mark_ppos; + __u8 reserved204_223[20]; + + /* + * __u8 app_specific[32]; + * + * Linux specific fields: + */ + __u32 next_mark_ppos; /* when known, points to next marker */ + __u8 linux_specific[28]; + + __u8 reserved_256_511[256]; +} os_aux_t; + +#define OS_FM_TAB_MAX 1024 + +typedef struct os_fm_tab_s { + __u8 fm_part_num; + __u8 reserved_1; + __u8 fm_tab_ent_sz; + __u8 reserved_3; + __u16 fm_tab_ent_cnt; + __u8 reserved6_15[10]; + __u32 fm_tab_ent[OS_FM_TAB_MAX]; +} os_fm_tab_t; + +typedef struct os_ext_trk_ey_s { + __u8 et_part_num; + __u8 fmt; + __u16 fm_tab_off; + __u8 reserved4_7[4]; + __u32 last_hlb_hi; + __u32 last_hlb; + __u32 last_pp; + __u8 reserved20_31[12]; +} os_ext_trk_ey_t; + +typedef struct os_ext_trk_tb_s { + __u8 nr_stream_part; + __u8 reserved_1; + __u8 et_ent_sz; + __u8 reserved3_15[13]; + os_ext_trk_ey_t dat_ext_trk_ey; + os_ext_trk_ey_t qfa_ext_trk_ey; +} os_ext_trk_tb_t; + +typedef struct os_header_s { + char ident_str[8]; + __u8 major_rev; + __u8 minor_rev; + __u16 ext_trk_tb_off; + __u8 reserved12_15[4]; + __u8 pt_par_num; + __u8 pt_reserved1_3[3]; + os_partition_t partition[16]; + __u32 cfg_col_width; + __u32 dat_col_width; + __u32 qfa_col_width; + __u8 cartridge[16]; + __u8 reserved304_511[208]; + __u32 old_filemark_list[16680/4]; /* in ADR 1.4 __u8 track_table[16680] */ + os_ext_trk_tb_t ext_track_tb; + __u8 reserved17272_17735[464]; + os_fm_tab_t dat_fm_tab; + os_fm_tab_t qfa_fm_tab; + __u8 reserved25960_32767[6808]; +} os_header_t; + + +/* + * OnStream ADRL frame + */ +#define OS_FRAME_SIZE (32 * 1024 + 512) +#define OS_DATA_SIZE (32 * 1024) +#define OS_AUX_SIZE (512) +//#define OSST_MAX_SG 2 + +/* The tape buffer descriptor. */ +typedef struct { + unsigned char in_use; + unsigned char dma; /* DMA-able buffer */ + int buffer_size; + int buffer_blocks; + int buffer_bytes; + int read_pointer; + int writing; + int midlevel_result; + int syscall_result; + Scsi_Request *last_SRpnt; + unsigned char *b_data; + os_aux_t *aux; /* onstream AUX structure at end of each block */ + unsigned short use_sg; /* zero or number of segments for this adapter */ + unsigned short sg_segs; /* total number of allocated segments */ + unsigned short orig_sg_segs; /* number of segments allocated at first try */ + struct scatterlist sg[1]; /* MUST BE last item */ +} OSST_buffer; + +/* The tape drive descriptor */ +typedef struct { + kdev_t devt; + unsigned capacity; + Scsi_Device* device; + struct semaphore lock; /* for serialization */ + struct semaphore sem; /* for SCSI commands */ + OSST_buffer * buffer; + + /* Drive characteristics */ + unsigned char omit_blklims; + unsigned char do_auto_lock; + unsigned char can_bsr; + unsigned char can_partitions; + unsigned char two_fm; + unsigned char fast_mteom; + unsigned char restr_dma; + unsigned char scsi2_logical; + unsigned char default_drvbuffer; /* 0xff = don't touch, value 3 bits */ + int write_threshold; + int timeout; /* timeout for normal commands */ + int long_timeout; /* timeout for commands known to take long time*/ + + /* Mode characteristics */ + ST_mode modes[ST_NBR_MODES]; + int current_mode; +#ifdef CONFIG_DEVFS_FS + devfs_handle_t de_r[ST_NBR_MODES]; /* Rewind entries */ + devfs_handle_t de_n[ST_NBR_MODES]; /* No-rewind entries */ +#endif + + /* Status variables */ + int partition; + int new_partition; + int nbr_partitions; /* zero until partition support enabled */ + ST_partstat ps[ST_NBR_PARTITIONS]; + unsigned char dirty; + unsigned char ready; + unsigned char write_prot; + unsigned char drv_write_prot; + unsigned char in_use; + unsigned char blksize_changed; + unsigned char density_changed; + unsigned char compression_changed; + unsigned char drv_buffer; + unsigned char density; + unsigned char door_locked; + unsigned char rew_at_close; + unsigned char inited; + int block_size; + int min_block; + int max_block; + int recover_count; /* from tape opening */ + int recover_erreg; /* from last status call */ + /* + * OnStream specific data + */ + int os_fw_rev; /* the firmware revision * 10000 */ + unsigned char raw; /* flag OnStream raw access (32.5KB block size) */ + unsigned char poll; /* flag that this drive needs polling (IDE|firmware) */ + unsigned char logical_blk_in_buffer; /* flag that the block as per logical_blk_num + * has been read into STp->buffer and is valid */ + int logical_blk_num; /* logical block number */ + unsigned first_frame_position; /* physical frame to be transfered to/from host */ + unsigned last_frame_position; /* physical frame to be transferd to/from tape */ + int cur_frames; /* current number of frames in internal buffer */ + int max_frames; /* max number of frames in internal buffer */ + char application_sig[5]; /* application signature */ + unsigned char fast_open; /* flag that reminds us we didn't check headers at open */ + unsigned short wrt_pass_cntr; /* write pass counter */ + int update_frame_cntr; /* update frame counter */ + int onstream_write_error; /* write error recovery active */ + int header_ok; /* header frame verified ok */ + int linux_media; /* reading linux-specifc media */ + int linux_media_version; + os_header_t * header_cache; /* cache is kept for filemark positions */ + int filemark_cnt; + int first_mark_ppos; + int last_mark_ppos; + int first_data_ppos; + int eod_frame_ppos; + int eod_frame_lfa; + int write_type; /* used in write error recovery */ + int read_error_frame; /* used in read error recovery */ + unsigned long cmd_start_time; + unsigned long max_cmd_time; + +#if DEBUG + unsigned char write_pending; + int nbr_finished; + int nbr_waits; + unsigned char last_cmnd[6]; + unsigned char last_sense[16]; +#endif +} OS_Scsi_Tape; + +/* Values of write_type */ +#define OS_WRITE_DATA 0 +#define OS_WRITE_EOD 1 +#define OS_WRITE_NEW_MARK 2 +#define OS_WRITE_LAST_MARK 3 +#define OS_WRITE_HEADER 4 +#define OS_WRITE_FILLER 5 + diff --git a/drivers/scsi/osst_detect.h b/drivers/scsi/osst_detect.h new file mode 100644 index 000000000000..21717d0e6974 --- /dev/null +++ b/drivers/scsi/osst_detect.h @@ -0,0 +1,6 @@ +#define SIGS_FROM_OSST \ + {"OnStream", "SC-", "", "osst"}, \ + {"OnStream", "DI-", "", "osst"}, \ + {"OnStream", "DP-", "", "osst"}, \ + {"OnStream", "FW-", "", "osst"}, \ + {"OnStream", "USB", "", "osst"} diff --git a/drivers/scsi/osst_options.h b/drivers/scsi/osst_options.h new file mode 100644 index 000000000000..7b48e1019b88 --- /dev/null +++ b/drivers/scsi/osst_options.h @@ -0,0 +1,100 @@ +/* + The compile-time configurable defaults for the Linux SCSI tape driver. + + Copyright 1995 Kai Makisara. + + Last modified: Wed Sep 2 21:24:07 1998 by root@home + + Changed (and renamed) for OnStream SCSI drives garloff@suse.de + 2000-06-21 + + $Header: /home/cvsroot/Driver/osst_options.h,v 1.4 2000/06/26 01:44:01 riede Exp $ +*/ + +#ifndef _OSST_OPTIONS_H +#define _OSST_OPTIONS_H + +/* The minimum limit for the number of SCSI tape devices is determined by + OSST_MAX_TAPES. If the number of tape devices and the "slack" defined by + OSST_EXTRA_DEVS exceeds OSST_MAX_TAPES, the large number is used. */ +#define OSST_MAX_TAPES 4 + +/* If OSST_IN_FILE_POS is nonzero, the driver positions the tape after the + record been read by the user program even if the tape has moved further + because of buffered reads. Should be set to zero to support also drives + that can't space backwards over records. NOTE: The tape will be + spaced backwards over an "accidentally" crossed filemark in any case. */ +#define OSST_IN_FILE_POS 0 + +/* The tape driver buffer size in kilobytes. */ +/* Don't change, as this is the HW blocksize */ +#define OSST_BUFFER_BLOCKS 32 + +/* The number of kilobytes of data in the buffer that triggers an + asynchronous write in fixed block mode. See also OSST_ASYNC_WRITES + below. */ +#define OSST_WRITE_THRESHOLD_BLOCKS 30 + +/* The maximum number of tape buffers the driver allocates. The number + is also constrained by the number of drives detected. Determines the + maximum number of concurrently active tape drives. */ +#define OSST_MAX_BUFFERS OSST_MAX_TAPES + +/* Maximum number of scatter/gather segments */ +/* Fit one buffer in pages and add one for the AUX header */ +#define OSST_MAX_SG (((OSST_BUFFER_BLOCKS*1024) / PAGE_SIZE) + 1) + +/* The number of scatter/gather segments to allocate at first try (must be + smaller or equal to the maximum). */ +#define OSST_FIRST_SG ((OSST_BUFFER_BLOCKS*1024) / PAGE_SIZE) + +/* The size of the first scatter/gather segments (determines the maximum block + size for SCSI adapters not supporting scatter/gather). The default is set + to try to allocate the buffer as one chunk. */ +#define OSST_FIRST_ORDER 5 + + +/* The following lines define defaults for properties that can be set + separately for each drive using the MTSTOPTIONS ioctl. */ + +/* If OSST_TWO_FM is non-zero, the driver writes two filemarks after a + file being written. Some drives can't handle two filemarks at the + end of data. */ +#define OSST_TWO_FM 0 + +/* If OSST_BUFFER_WRITES is non-zero, writes in fixed block mode are + buffered until the driver buffer is full or asynchronous write is + triggered. May make detection of End-Of-Medium early enough fail. */ +#define OSST_BUFFER_WRITES 1 + +/* If OSST_ASYNC_WRITES is non-zero, the SCSI write command may be started + without waiting for it to finish. May cause problems in multiple + tape backups. */ +#define OSST_ASYNC_WRITES 1 + +/* If OSST_READ_AHEAD is non-zero, blocks are read ahead in fixed block + mode. */ +#define OSST_READ_AHEAD 1 + +/* If OSST_AUTO_LOCK is non-zero, the drive door is locked at the first + read or write command after the device is opened. The door is opened + when the device is closed. */ +#define OSST_AUTO_LOCK 0 + +/* If OSST_FAST_MTEOM is non-zero, the MTEOM ioctl is done using the + direct SCSI command. The file number status is lost but this method + is fast with some drives. Otherwise MTEOM is done by spacing over + files and the file number status is retained. */ +#define OSST_FAST_MTEOM 0 + +/* If OSST_SCSI2LOGICAL is nonzero, the logical block addresses are used for + MTIOCPOS and MTSEEK by default. Vendor addresses are used if OSST_SCSI2LOGICAL + is zero. */ +#define OSST_SCSI2LOGICAL 0 + +/* If OSST_SYSV is non-zero, the tape behaves according to the SYS V semantics. + The default is BSD semantics. */ +#define OSST_SYSV 0 + + +#endif diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 88608c5f54a8..2e4dbf1e38f7 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -175,7 +175,7 @@ static int update_partition(Scsi_Tape *); static int st_int_ioctl(Scsi_Tape *, unsigned int, unsigned long); -/* #include "osst_detect.h" */ +#include "osst_detect.h" #ifndef SIGS_FROM_OSST #define SIGS_FROM_OSST \ {"OnStream", "SC-", "", "osst"}, \ diff --git a/drivers/sound/i810_audio.c b/drivers/sound/i810_audio.c index c4d2c50cec90..20403413190c 100644 --- a/drivers/sound/i810_audio.c +++ b/drivers/sound/i810_audio.c @@ -507,21 +507,19 @@ static void i810_rec_setup(struct i810_state *state) extern __inline__ unsigned i810_get_dma_addr(struct i810_state *state) { struct dmabuf *dmabuf = &state->dmabuf; - u32 offset; + unsigned int civ, offset; struct i810_channel *c = dmabuf->channel; if (!dmabuf->enable) return 0; - offset = inb(state->card->iobase+c->port+OFF_CIV); - offset++; - offset&=31; - /* Offset has to compensate for the fact we finished the segment - on the IRQ so we are at next_segment,0 */ -// printk("BANK%d ", offset); - offset *= (dmabuf->dmasize/SG_LEN); -// printk("DMASZ=%d", dmabuf->dmasize); -// offset += 1024-(4*inw(state->card->iobase+c->port+OFF_PICB)); -// printk("OFF%d ", offset); + do { + civ = inb(state->card->iobase+c->port+OFF_CIV); + offset = (civ + 1) * (dmabuf->dmasize/SG_LEN) - + 2 * inw(state->card->iobase+c->port+OFF_PICB); + /* CIV changed before we read PICB (very seldom) ? + * then PICB was rubbish, so try again */ + } while (civ != inb(state->card->iobase+c->port+OFF_CIV)); + return offset; } @@ -730,10 +728,13 @@ static int prog_dmabuf(struct i810_state *state, unsigned rec) sg->control|=CON_IOC; sg++; } + spin_lock_irqsave(&state->card->lock, flags); + outb(2, state->card->iobase+dmabuf->channel->port+OFF_CR); /* reset DMA machine */ outl(virt_to_bus(&dmabuf->channel->sg[0]), state->card->iobase+dmabuf->channel->port+OFF_BDBAR); outb(16, state->card->iobase+dmabuf->channel->port+OFF_LVI); outb(0, state->card->iobase+dmabuf->channel->port+OFF_CIV); + if (rec) { i810_rec_setup(state); } else { @@ -753,14 +754,10 @@ static int prog_dmabuf(struct i810_state *state, unsigned rec) return 0; } - -/* we are doing quantum mechanics here, the buffer can only be empty, half or full filled i.e. - |------------|------------| or |xxxxxxxxxxxx|------------| or |xxxxxxxxxxxx|xxxxxxxxxxxx| - but we almost always get this - |xxxxxx------|------------| or |xxxxxxxxxxxx|xxxxx-------| - so we have to clear the tail space to "silence" - |xxxxxx000000|------------| or |xxxxxxxxxxxx|xxxxxx000000| -*/ +/* + * Clear the rest of the last i810 dma buffer, normally there is no rest + * because the OSS fragment size is the same as the size of this buffer. + */ static void i810_clear_tail(struct i810_state *state) { struct dmabuf *dmabuf = &state->dmabuf; @@ -773,14 +770,8 @@ static void i810_clear_tail(struct i810_state *state) swptr = dmabuf->swptr; spin_unlock_irqrestore(&state->card->lock, flags); - if (swptr == 0 || swptr == dmabuf->dmasize / 2 || swptr == dmabuf->dmasize) - return; - - if (swptr < dmabuf->dmasize/2) - len = dmabuf->dmasize/2 - swptr; - else - len = dmabuf->dmasize - swptr; - + len = swptr % (dmabuf->dmasize/SG_LEN); + memset(dmabuf->rawbuf + swptr, silence, len); spin_lock_irqsave(&state->card->lock, flags); @@ -1188,11 +1179,16 @@ static unsigned int i810_poll(struct file *file, struct poll_table_struct *wait) unsigned long flags; unsigned int mask = 0; - if (file->f_mode & FMODE_WRITE) + if (file->f_mode & FMODE_WRITE) { + if (!dmabuf->ready && prog_dmabuf(state, 0)) + return 0; poll_wait(file, &dmabuf->wait, wait); - if (file->f_mode & FMODE_READ) + } + if (file->f_mode & FMODE_READ) { + if (!dmabuf->ready && prog_dmabuf(state, 1)) + return 0; poll_wait(file, &dmabuf->wait, wait); - + } spin_lock_irqsave(&state->card->lock, flags); i810_update_ptr(state); if (file->f_mode & FMODE_READ) { @@ -1531,7 +1527,6 @@ static int i810_ioctl(struct inode *inode, struct file *file, unsigned int cmd, static int i810_open(struct inode *inode, struct file *file) { int i = 0; - int minor = MINOR(inode->i_rdev); struct i810_card *card = devs; struct i810_state *state = NULL; struct dmabuf *dmabuf = NULL; @@ -1779,18 +1774,42 @@ static int __init i810_ac97_init(struct i810_card *card) card->ac97_features = eid; + /* Now check the codec for useful features to make up for + the dumbness of the 810 hardware engine */ + if(!(eid&0x0001)) printk(KERN_WARNING "i810_audio: only 48Khz playback available.\n"); - + else + { + /* Enable variable rate mode */ + i810_ac97_set(codec, AC97_EXTENDED_STATUS, 9); + i810_ac97_set(codec,AC97_EXTENDED_STATUS, + i810_ac97_get(codec, AC97_EXTENDED_STATUS)|0xE800); + /* power up everything, modify this when implementing power saving */ + i810_ac97_set(codec, AC97_POWER_CONTROL, + i810_ac97_get(codec, AC97_POWER_CONTROL) & ~0x7f00); + /* wait for analog ready */ + for (i=10; + i && ((i810_ac97_get(codec, AC97_POWER_CONTROL) & 0xf) != 0xf); + i--) + { + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ/20); + } + + if(!(i810_ac97_get(codec, AC97_EXTENDED_STATUS)&1)) + { + printk(KERN_WARNING "i810_audio: Codec refused to allow VRA, using 48Khz only.\n"); + card->ac97_features&=~1; + } + } + if ((codec->dev_mixer = register_sound_mixer(&i810_mixer_fops, -1)) < 0) { printk(KERN_ERR "i810_audio: couldn't register mixer!\n"); kfree(codec); break; } - /* Now check the codec for useful features to make up for - the dumbness of the 810 hardware engine */ - card->ac97_codec[num_ac97] = codec; /* if there is no secondary codec at all, don't probe any more */ diff --git a/drivers/sound/mad16.c b/drivers/sound/mad16.c index 31ec19d10e97..11fe4201263d 100644 --- a/drivers/sound/mad16.c +++ b/drivers/sound/mad16.c @@ -20,38 +20,6 @@ * issues of the card, using the OTI-605 chip, have an MPU-401 compatable Midi * port. This port is configured differently to that of the OPTi audio chips. * - * NOTE! If you want to set CD-ROM address and/or joystick enable, define - * MAD16_CONF in local.h as combination of the following bits: - * - * 0x01 - joystick disabled - * - * CD-ROM type selection (select just one): - * 0x00 - none - * 0x02 - Sony 31A - * 0x04 - Mitsumi - * 0x06 - Panasonic (type "LaserMate", not "Sound Blaster") - * 0x08 - Secondary IDE (address 0x170) - * 0x0a - Primary IDE (address 0x1F0) - * - * For example Mitsumi with joystick disabled = 0x04|0x01 = 0x05 - * For example LaserMate (for use with sbpcd) plus joystick = 0x06 - * - * MAD16_CDSEL: - * This defaults to CD I/O 0x340, no IRQ and DMA3 - * (DMA5 with Mitsumi or IDE). If you like to change these, define - * MAD16_CDSEL with the following bits: - * - * CD-ROM port: 0x00=340, 0x40=330, 0x80=360 or 0xc0=320 - * OPL4 select: 0x20=OPL4, 0x00=OPL3 - * CD-ROM irq: 0x00=disabled, 0x04=IRQ5, 0x08=IRQ7, 0x0c=IRQ3, 0x10=IRQ9, - * 0x14=IRQ10 and 0x18=IRQ11. - * - * CD-ROM DMA (Sony or Panasonic): 0x00=DMA3, 0x01=DMA2, 0x02=DMA1 or 0x03=disabled - * or - * CD-ROM DMA (Mitsumi or IDE): 0x00=DMA5, 0x01=DMA6, 0x02=DMA7 or 0x03=disabled - * - * For use with sbpcd, address 0x340, set MAD16_CDSEL to 0x03 or 0x23. - * * Changes * * Alan Cox Clean up, added module selections. @@ -67,6 +35,8 @@ * 25-Nov-1999 * Christoph Hellwig Adapted to module_init/module_exit. * Arnaldo C. de Melo got rid of attach_uart401 21-Sep-2000 + * + * Pavel Rabel Clean up Nov-2000 */ #include @@ -395,10 +365,6 @@ static int __init init_c930(struct address_info *hw_config) { unsigned char cfg = 0; -#ifdef MAD16_CONF - cfg |= (0x0f & MAD16_CONF); -#endif - if(c931_detected) { /* Bit 0 has reversd meaning. Bits 1 and 2 sese @@ -435,14 +401,9 @@ static int __init init_c930(struct address_info *hw_config) /* bit 2 of MC4 reverses it's meaning between the C930 and the C931. */ cfg = c931_detected ? 0x04 : 0x00; -#ifdef MAD16_CDSEL - if(MAD16_CDSEL & 0x20) - mad_write(MC4_PORT, 0x62|cfg); /* opl4 */ - else - mad_write(MC4_PORT, 0x52|cfg); /* opl3 */ -#else + mad_write(MC4_PORT, 0x52|cfg); -#endif + mad_write(MC5_PORT, 0x3C); /* Init it into mode2 */ mad_write(MC6_PORT, 0x02); /* Enable WSS, Disable MPU and SB */ mad_write(MC7_PORT, 0xCB); @@ -590,20 +551,9 @@ static int __init probe_mad16(struct address_info *hw_config) */ tmp &= ~0x0f; -#if defined(MAD16_CONF) - tmp |= ((MAD16_CONF) & 0x0f); /* CD-ROM and joystick bits */ -#endif mad_write(MC1_PORT, tmp); -#if defined(MAD16_CONF) && defined(MAD16_CDSEL) - tmp = MAD16_CDSEL; -#else tmp = mad_read(MC2_PORT); -#endif - -#ifdef MAD16_OPL4 - tmp |= 0x20; /* Enable OPL4 access */ -#endif mad_write(MC2_PORT, tmp); mad_write(MC3_PORT, 0xf0); /* Disable SB */ @@ -718,13 +668,7 @@ static void __init attach_mad16(struct address_info *hw_config) static int __init probe_mad16_mpu(struct address_info *hw_config) { static int mpu_attached = 0; - static int valid_ports[] = { - 0x330, 0x320, 0x310, 0x300 - }; - - static short valid_irqs[] = {9, 10, 5, 7}; unsigned char tmp; - int i; /* A variable with secret power */ if (!already_initialized) /* The MSS port must be initialized first */ return 0; @@ -737,7 +681,6 @@ static int __init probe_mad16_mpu(struct address_info *hw_config) { #ifdef CONFIG_MAD16_OLDCARD - unsigned char tmp; tmp = mad_read(MC3_PORT); @@ -787,9 +730,6 @@ static int __init probe_mad16_mpu(struct address_info *hw_config) * to set MPU register. TODO - add probing */ - - unsigned char tmp; - tmp = mad_read(MC8_PORT); switch (hw_config->irq) @@ -840,42 +780,50 @@ static int __init probe_mad16_mpu(struct address_info *hw_config) tmp = mad_read(MC6_PORT) & 0x83; tmp |= 0x80; /* MPU-401 enable */ -/* - * Set the MPU base bits - */ + /* Set the MPU base bits */ - for (i = 0; i < 5; i++) + switch (hw_config->io_base) { - if (i > 3) /* Out of array bounds */ - { - printk(KERN_ERR "MAD16 / Mozart: Invalid MIDI port 0x%x\n", hw_config->io_base); - return 0; - } - if (valid_ports[i] == hw_config->io_base) - { - tmp |= i << 5; + case 0x300: + tmp |= 0x60; break; - } + case 0x310: + tmp |= 0x40; + break; + case 0x320: + tmp |= 0x20; + break; + case 0x330: + tmp |= 0x00; + break; + default: + printk(KERN_ERR "MAD16: Invalid MIDI port 0x%x\n", hw_config->io_base); + return 0; } -/* - * Set the MPU IRQ bits - */ + /* Set the MPU IRQ bits */ - for (i = 0; i < 5; i++) + switch (hw_config->irq) { - if (i > 3) /* Out of array bounds */ - { - printk(KERN_ERR "MAD16 / Mozart: Invalid MIDI IRQ %d\n", hw_config->irq); - return 0; - } - if (valid_irqs[i] == hw_config->irq) - { - tmp |= i << 3; + case 5: + tmp |= 0x10; + break; + case 7: + tmp |= 0x18; + break; + case 9: + tmp |= 0x00; + break; + case 10: + tmp |= 0x08; + break; + default: + printk(KERN_ERR "MAD16: Invalid MIDI IRQ %d\n", hw_config->irq); break; - } } + mad_write(MC6_PORT, tmp); /* Write MPU401 config */ + #ifndef CONFIG_MAD16_OLDCARD probe_401: #endif diff --git a/drivers/sound/via82cxxx_audio.c b/drivers/sound/via82cxxx_audio.c index 7470ffb0349f..f609d1e2b794 100644 --- a/drivers/sound/via82cxxx_audio.c +++ b/drivers/sound/via82cxxx_audio.c @@ -1727,18 +1727,18 @@ static struct page * via_mm_nopage (struct vm_area_struct * vma, } -#ifndef VM_RESERVE +#ifndef VM_RESERVED static int via_mm_swapout (struct page *page, struct file *filp) { return 0; } -#endif /* VM_RESERVE */ +#endif /* VM_RESERVED */ struct vm_operations_struct via_mm_ops = { nopage: via_mm_nopage, -#ifndef VM_RESERVE +#ifndef VM_RESERVED swapout: via_mm_swapout, #endif }; @@ -1789,8 +1789,8 @@ static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma) vma->vm_ops = &via_mm_ops; vma->vm_private_data = card; -#ifdef VM_RESERVE - vma->vm_flags |= VM_RESERVE; +#ifdef VM_RESERVED + vma->vm_flags |= VM_RESERVED; #endif if (rd) diff --git a/fs/buffer.c b/fs/buffer.c index 8aa7b3f06e01..601276bbc7c8 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -122,16 +122,17 @@ union bdflush_param { when trying to refill buffers. */ int interval; /* jiffies delay between kupdate flushes */ int age_buffer; /* Time for normal buffer to age before we flush it */ - int dummy1; /* unused, was age_super */ + int nfract_sync; /* Percentage of buffer cache dirty to + activate bdflush synchronously */ int dummy2; /* unused */ int dummy3; /* unused */ } b_un; unsigned int data[N_PARAM]; -} bdf_prm = {{40, 500, 64, 256, 5*HZ, 30*HZ, 5*HZ, 1884, 2}}; +} bdf_prm = {{40, 500, 64, 256, 5*HZ, 30*HZ, 80, 0, 0}}; /* These are the min and max parameter values that we will allow to be assigned */ -int bdflush_min[N_PARAM] = { 0, 10, 5, 25, 0, 1*HZ, 1*HZ, 1, 1}; -int bdflush_max[N_PARAM] = {100,50000, 20000, 20000,600*HZ, 6000*HZ, 6000*HZ, 2047, 5}; +int bdflush_min[N_PARAM] = { 0, 10, 5, 25, 0, 1*HZ, 0, 0, 0}; +int bdflush_max[N_PARAM] = {100,50000, 20000, 20000,600*HZ, 6000*HZ, 100, 0, 0}; /* * Rewrote the wait-routines to use the "new" wait-queue functionality, @@ -337,9 +338,10 @@ int file_fsync(struct file *filp, struct dentry *dentry, int datasync) /* sync the superblock to buffers */ sb = inode->i_sb; - wait_on_super(sb); + lock_super(sb); if (sb->s_op && sb->s_op->write_super) sb->s_op->write_super(sb); + unlock_super(sb); /* .. finally sync the buffers to disk */ dev = inode->i_dev; @@ -1032,9 +1034,9 @@ int balance_dirty_state(kdev_t dev) dirty = size_buffers_type[BUF_DIRTY] >> PAGE_SHIFT; tot = nr_free_buffer_pages(); - dirty *= 200; + dirty *= 100; soft_dirty_limit = tot * bdf_prm.b_un.nfract; - hard_dirty_limit = soft_dirty_limit * 2; + hard_dirty_limit = tot * bdf_prm.b_un.nfract_sync; /* First, check for the "real" dirty limit. */ if (dirty > soft_dirty_limit) { diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index 6a096c0ff49e..898ab8178303 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c @@ -269,6 +269,11 @@ static int ramfs_symlink(struct inode * dir, struct dentry *dentry, const char * return error; } +static int ramfs_sync_file(struct file * file, struct dentry *dentry, int datasync) +{ + return 0; +} + static struct address_space_operations ramfs_aops = { readpage: ramfs_readpage, writepage: ramfs_writepage, @@ -279,12 +284,14 @@ static struct address_space_operations ramfs_aops = { static struct file_operations ramfs_file_operations = { read: generic_file_read, write: generic_file_write, - mmap: generic_file_mmap + mmap: generic_file_mmap, + fsync: ramfs_sync_file, }; static struct file_operations ramfs_dir_operations = { read: generic_read_dir, readdir: dcache_readdir, + fsync: ramfs_sync_file, }; static struct inode_operations ramfs_dir_inode_operations = { diff --git a/include/asm-alpha/processor.h b/include/asm-alpha/processor.h index 58813f17fc95..49f228f3f1be 100644 --- a/include/asm-alpha/processor.h +++ b/include/asm-alpha/processor.h @@ -122,6 +122,9 @@ extern void release_thread(struct task_struct *); /* Create a kernel thread without removing it from tasklists. */ extern long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags); +#define copy_segments(tsk, mm) do { } while (0) +#define release_segments(mm) do { } while (0) + unsigned long get_wchan(struct task_struct *p); /* See arch/alpha/kernel/ptrace.c for details. */ diff --git a/include/asm-arm/processor.h b/include/asm-arm/processor.h index d1311d01479b..537d7ec12aa6 100644 --- a/include/asm-arm/processor.h +++ b/include/asm-arm/processor.h @@ -106,6 +106,10 @@ struct task_struct; /* Free all resources held by a thread. */ extern void release_thread(struct task_struct *); +/* Copy and release all segment info associated with a VM */ +#define copy_segments(tsk, mm) do { } while (0) +#define release_segments(mm) do { } while (0) + unsigned long get_wchan(struct task_struct *p); #define THREAD_SIZE (8192) diff --git a/include/asm-i386/mmu_context.h b/include/asm-i386/mmu_context.h index 1b9b19301044..cdfb99b4d237 100644 --- a/include/asm-i386/mmu_context.h +++ b/include/asm-i386/mmu_context.h @@ -6,9 +6,11 @@ #include #include -/* 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 diff --git a/include/asm-i386/processor.h b/include/asm-i386/processor.h index 257245909145..364bb475fe2f 100644 --- a/include/asm-i386/processor.h +++ b/include/asm-i386/processor.h @@ -427,6 +427,10 @@ extern void release_thread(struct task_struct *); */ extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); +/* Copy and release all segment info associated with a VM */ +extern void copy_segments(struct task_struct *p, struct mm_struct * mm); +extern void release_segments(struct mm_struct * mm); + /* * Return saved PC of a blocked thread. */ diff --git a/include/asm-ia64/processor.h b/include/asm-ia64/processor.h index 0fb8f37ccd14..2fb8e357f50a 100644 --- a/include/asm-ia64/processor.h +++ b/include/asm-ia64/processor.h @@ -375,6 +375,10 @@ struct task_struct; */ extern int kernel_thread (int (*fn)(void *), void *arg, unsigned long flags); +/* Copy and release all segment info associated with a VM */ +#define copy_segments(tsk, mm) do { } while (0) +#define release_segments(mm) do { } while (0) + /* Get wait channel for task P. */ extern unsigned long get_wchan (struct task_struct *p); diff --git a/include/asm-m68k/processor.h b/include/asm-m68k/processor.h index 43af4d09c46c..0ad99a7f8936 100644 --- a/include/asm-m68k/processor.h +++ b/include/asm-m68k/processor.h @@ -109,6 +109,9 @@ static inline void release_thread(struct task_struct *dead_task) extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); +#define copy_segments(tsk, mm) do { } while (0) +#define release_segments(mm) do { } while (0) + /* * Free current thread data structures etc.. */ diff --git a/include/asm-mips/processor.h b/include/asm-mips/processor.h index d38127794433..7e160259c671 100644 --- a/include/asm-mips/processor.h +++ b/include/asm-mips/processor.h @@ -190,6 +190,10 @@ struct thread_struct { extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); +/* Copy and release all segment info associated with a VM */ +#define copy_segments(p, mm) do { } while(0) +#define release_segments(mm) do { } while(0) + /* * Return saved PC of a blocked thread. */ diff --git a/include/asm-mips64/processor.h b/include/asm-mips64/processor.h index 2d9b9df5b95e..8a3cad9f3e73 100644 --- a/include/asm-mips64/processor.h +++ b/include/asm-mips64/processor.h @@ -236,6 +236,10 @@ struct thread_struct { extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); +/* Copy and release all segment info associated with a VM */ +#define copy_segments(p, mm) do { } while(0) +#define release_segments(mm) do { } while(0) + /* * Return saved PC of a blocked thread. */ diff --git a/include/asm-parisc/processor.h b/include/asm-parisc/processor.h index 08a4d900403e..12ae777a5296 100644 --- a/include/asm-parisc/processor.h +++ b/include/asm-parisc/processor.h @@ -310,6 +310,9 @@ struct task_struct; extern void release_thread(struct task_struct *); extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); +#define copy_segments(tsk, mm) do { } while (0) +#define release_segments(mm) do { } while (0) + extern inline unsigned long get_wchan(struct task_struct *p) { return 0xdeadbeef; /* XXX */ diff --git a/include/asm-ppc/processor.h b/include/asm-ppc/processor.h index 5dfc547adeb4..5908d7f32edc 100644 --- a/include/asm-ppc/processor.h +++ b/include/asm-ppc/processor.h @@ -673,6 +673,9 @@ static inline unsigned long thread_saved_pc(struct thread_struct *t) return (t->regs) ? t->regs->nip : 0; } +#define copy_segments(tsk, mm) do { } while (0) +#define release_segments(mm) do { } while (0) + unsigned long get_wchan(struct task_struct *p); #define KSTK_EIP(tsk) ((tsk)->thread.regs->nip) diff --git a/include/asm-s390/processor.h b/include/asm-s390/processor.h index 3e87e4697792..4629c822be66 100644 --- a/include/asm-s390/processor.h +++ b/include/asm-s390/processor.h @@ -124,6 +124,10 @@ struct mm_struct; extern void release_thread(struct task_struct *); extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); +/* Copy and release all segment info associated with a VM */ +#define copy_segments(nr, mm) do { } while (0) +#define release_segments(mm) do { } while (0) + /* * Return saved PC of a blocked thread. used in kernel/sched */ diff --git a/include/asm-sh/processor.h b/include/asm-sh/processor.h index 0e25867e648e..8c4ba40d8bb5 100644 --- a/include/asm-sh/processor.h +++ b/include/asm-sh/processor.h @@ -151,6 +151,10 @@ extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); #define MCA_bus__is_a_macro /* for versions in ksyms.c */ +/* Copy and release all segment info associated with a VM */ +#define copy_segments(p, mm) do { } while(0) +#define release_segments(mm) do { } while(0) + /* * FPU lazy state save handling. */ diff --git a/include/asm-sparc/processor.h b/include/asm-sparc/processor.h index 970e87ff7ec2..2be6f36f0c1a 100644 --- a/include/asm-sparc/processor.h +++ b/include/asm-sparc/processor.h @@ -153,6 +153,9 @@ extern __inline__ void start_thread(struct pt_regs * regs, unsigned long pc, extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); +#define copy_segments(tsk, mm) do { } while (0) +#define release_segments(mm) do { } while (0) + #define get_wchan(__TSK) \ ({ extern void scheduling_functions_start_here(void); \ extern void scheduling_functions_end_here(void); \ diff --git a/include/asm-sparc64/processor.h b/include/asm-sparc64/processor.h index 921290a9f806..f26dd254d15f 100644 --- a/include/asm-sparc64/processor.h +++ b/include/asm-sparc64/processor.h @@ -218,6 +218,9 @@ do { \ extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); +#define copy_segments(tsk, mm) do { } while (0) +#define release_segments(mm) do { } while (0) + #define get_wchan(__TSK) \ ({ extern void scheduling_functions_start_here(void); \ extern void scheduling_functions_end_here(void); \ diff --git a/include/linux/blk.h b/include/linux/blk.h index eb4d603e7530..11db342a3c68 100644 --- a/include/linux/blk.h +++ b/include/linux/blk.h @@ -163,6 +163,14 @@ static void floppy_off(unsigned int nr); #define DEVICE_INTR do_st #define DEVICE_NR(device) (MINOR(device) & 0x7f) +#elif (MAJOR_NR == OSST_MAJOR) + +#define DEVICE_NAME "onstream" +#define DEVICE_INTR do_osst +#define DEVICE_NR(device) (MINOR(device) & 0x7f) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + #elif (MAJOR_NR == SCSI_CDROM_MAJOR) #define DEVICE_NAME "CD-ROM" @@ -328,7 +336,7 @@ static void floppy_off(unsigned int nr); #define DEVICE_OFF(device) do {} while (0) #endif -#if (MAJOR_NR != SCSI_TAPE_MAJOR) +#if (MAJOR_NR != SCSI_TAPE_MAJOR) && (MAJOR_NR != OSST_MAJOR) #if !defined(IDE_DRIVER) #ifndef CURRENT diff --git a/include/linux/ixjuser.h b/include/linux/ixjuser.h index 2aca5e2c8bb2..5310a15e0149 100644 --- a/include/linux/ixjuser.h +++ b/include/linux/ixjuser.h @@ -568,16 +568,16 @@ typedef struct { /****************************************************************************** * -* The DAA Analog GAIN sets 2 parameters at one time, the recieve gain (AGRR), +* The DAA Analog GAIN sets 2 parameters at one time, the receive gain (AGRR), * and the transmit gain (AGX). OR together the components and pass them * as the parameter to IXJCTL_DAA_AGAIN. The default setting is both at 0dB. * ******************************************************************************/ #define IXJCTL_DAA_AGAIN _IOW ('q', 0xD2, int) -#define AGRR00DB 0x00 // Analog gain in recieve direction 0dB -#define AGRR3_5DB 0x10 // Analog gain in recieve direction 3.5dB -#define AGRR06DB 0x30 // Analog gain in recieve direction 6dB +#define AGRR00DB 0x00 // Analog gain in receive direction 0dB +#define AGRR3_5DB 0x10 // Analog gain in receive direction 3.5dB +#define AGRR06DB 0x30 // Analog gain in receive direction 6dB #define AGX00DB 0x00 // Analog gain in transmit direction 0dB #define AGX_6DB 0x04 // Analog gain in transmit direction -6dB diff --git a/include/linux/major.h b/include/linux/major.h index 372e1841a3ac..405e9f8acce7 100644 --- a/include/linux/major.h +++ b/include/linux/major.h @@ -154,6 +154,8 @@ #define MSR_MAJOR 202 #define CPUID_MAJOR 203 +#define OSST_MAJOR 206 /* OnStream-SCx0 SCSI tape */ + /* * Tests for SCSI devices. */ diff --git a/include/linux/mm.h b/include/linux/mm.h index 5b67b326d67b..2452012415d3 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -120,11 +120,7 @@ extern pgprot_t protection_map[16]; struct vm_operations_struct { void (*open)(struct vm_area_struct * area); void (*close)(struct vm_area_struct * area); - void (*unmap)(struct vm_area_struct *area, unsigned long, size_t); - void (*protect)(struct vm_area_struct *area, unsigned long, size_t, unsigned int newprot); - int (*sync)(struct vm_area_struct *area, unsigned long, size_t, unsigned int flags); struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, int write_access); - struct page * (*wppage)(struct vm_area_struct * area, unsigned long address, struct page * page); }; /* diff --git a/include/linux/mtio.h b/include/linux/mtio.h index c794ed898ac5..8b0f4aa64f85 100644 --- a/include/linux/mtio.h +++ b/include/linux/mtio.h @@ -105,6 +105,8 @@ struct mtget { #define MT_ISEVEREX_FT40A 0x32 /* Everex FT40A (QIC-40) */ #define MT_ISDDS1 0x51 /* DDS device without partitions */ #define MT_ISDDS2 0x52 /* DDS device with partitions */ +#define MT_ISONSTREAM_SC 0x61 /* OnStream SCSI tape drives (SC-x0) + and SCSI emulated (DI, DP, USB) */ #define MT_ISSCSI1 0x71 /* Generic ANSI SCSI-1 tape unit */ #define MT_ISSCSI2 0x72 /* Generic ANSI SCSI-2 tape unit */ @@ -134,6 +136,7 @@ struct mt_tape_info { {MT_ISWT5099EEN24, "Wangtek 5099-een24, 60MB"}, \ {MT_ISTEAC_MT2ST, "Teac MT-2ST 155mb data cassette drive"}, \ {MT_ISEVEREX_FT40A, "Everex FT40A, QIC-40"}, \ + {MT_ISONSTREAM_SC, "OnStream SC-, DI-, DP-, or USB tape drive"}, \ {MT_ISSCSI1, "Generic SCSI-1 tape"}, \ {MT_ISSCSI2, "Generic SCSI-2 tape"}, \ {0, NULL} \ diff --git a/include/linux/pci.h b/include/linux/pci.h index d98a3a01a4da..a78410716707 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -681,6 +681,7 @@ extern int pci_pci_problems; #define PCIPCI_FAIL 1 #define PCIPCI_TRITON 2 #define PCIPCI_NATOMA 4 +#define PCIPCI_VIAETBF 8 #endif /* __KERNEL__ */ #endif /* LINUX_PCI_H */ diff --git a/kernel/fork.c b/kernel/fork.c index 415d4eb9e300..f5d3a83df7ea 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -317,6 +317,11 @@ static inline int copy_mm(unsigned long clone_flags, struct task_struct * tsk) if (retval) goto free_pt; + /* + * child gets a private LDT (if there was an LDT in the parent) + */ + copy_segments(tsk, mm); + if (init_new_context(tsk,mm)) goto free_pt; diff --git a/mm/filemap.c b/mm/filemap.c index c9ab9a4e2fc5..69fe404669df 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1692,7 +1692,6 @@ int filemap_sync(struct vm_area_struct * vma, unsigned long address, * backing-store for swapping.. */ static struct vm_operations_struct file_shared_mmap = { - sync: filemap_sync, nopage: filemap_nopage, }; @@ -1735,19 +1734,19 @@ int generic_file_mmap(struct file * file, struct vm_area_struct * vma) static int msync_interval(struct vm_area_struct * vma, unsigned long start, unsigned long end, int flags) { - if (vma->vm_file && vma->vm_ops && vma->vm_ops->sync) { + struct file * file = vma->vm_file; + if (file && (vma->vm_flags & VM_SHARED)) { int error; - error = vma->vm_ops->sync(vma, start, end-start, flags); + error = filemap_sync(vma, start, end-start, flags); + if (!error && (flags & MS_SYNC)) { - struct file * file = vma->vm_file; - if (file && file->f_op && file->f_op->fsync) { - struct inode * inode = file->f_dentry->d_inode; - down(&inode->i_sem); - filemap_fdatasync(inode->i_mapping); + struct inode * inode = file->f_dentry->d_inode; + down(&inode->i_sem); + filemap_fdatasync(inode->i_mapping); + if (file->f_op && file->f_op->fsync) error = file->f_op->fsync(file, file->f_dentry, 1); - filemap_fdatawait(inode->i_mapping); - up(&inode->i_sem); - } + filemap_fdatawait(inode->i_mapping); + up(&inode->i_sem); } return error; } diff --git a/mm/mmap.c b/mm/mmap.c index f74b2487d8a8..e5b3a989ed20 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -732,9 +732,6 @@ int do_munmap(struct mm_struct *mm, unsigned long addr, size_t len) end = end > mpnt->vm_end ? mpnt->vm_end : end; size = end - st; - if (mpnt->vm_ops && mpnt->vm_ops->unmap) - mpnt->vm_ops->unmap(mpnt, st, size); - if (mpnt->vm_flags & VM_DENYWRITE && (st != mpnt->vm_start || end != mpnt->vm_end) && (file = mpnt->vm_file) != NULL) { @@ -878,6 +875,7 @@ void exit_mmap(struct mm_struct * mm) { struct vm_area_struct * mpnt; + release_segments(mm); spin_lock(&mm->page_table_lock); mpnt = mm->mmap; mm->mmap = mm->mmap_avl = mm->mmap_cache = NULL; @@ -892,8 +890,6 @@ void exit_mmap(struct mm_struct * mm) unsigned long size = end - start; if (mpnt->vm_ops) { - if (mpnt->vm_ops->unmap) - mpnt->vm_ops->unmap(mpnt, start, size); if (mpnt->vm_ops->close) mpnt->vm_ops->close(mpnt); } diff --git a/net/atm/lec.c b/net/atm/lec.c index 6b68d7995952..035058033abe 100644 --- a/net/atm/lec.c +++ b/net/atm/lec.c @@ -772,10 +772,10 @@ lecd_attach(struct atm_vcc *vcc, int arg) size = sizeof(struct lec_priv); #ifdef CONFIG_TR if (is_trdev) - dev_lec[i] = prepare_trdev(NULL, size); + dev_lec[i] = init_trdev(NULL, size); else #endif - dev_lec[i] = prepare_etherdev(NULL, size); + dev_lec[i] = init_etherdev(NULL, size); if (!dev_lec[i]) return -ENOMEM; @@ -783,7 +783,6 @@ lecd_attach(struct atm_vcc *vcc, int arg) priv->is_trdev = is_trdev; sprintf(dev_lec[i]->name, "lec%d", i); lec_init(dev_lec[i]); - publish_netdev(dev_lec[i]); } else { priv = dev_lec[i]->priv; if (priv->lecd) @@ -858,7 +857,7 @@ void cleanup_module(void) for (i = 0; i < MAX_LEC_ITF; i++) { if (dev_lec[i] != NULL) { priv = (struct lec_priv *)dev_lec[i]->priv; - unregister_netdev(dev_lec[i]); + unregister_trdev(dev_lec[i]); kfree(dev_lec[i]); dev_lec[i] = NULL; } -- 2.39.5